diff --git a/docs/bundler/bytecode.mdx b/docs/bundler/bytecode.mdx
index 465d1fe83488..65c0958c649e 100644
--- a/docs/bundler/bytecode.mdx
+++ b/docs/bundler/bytecode.mdx
@@ -195,7 +195,7 @@ ls -lh dist/
The `.jsc` file should be 2-8x larger than the `.js` file.
-To log whether the bytecode is used, set `BUN_JSC_verboseDiskCache=1` in your environment.
+To log whether Bun uses the bytecode, set `BUN_JSC_verboseDiskCache=1` in your environment.
On a cache hit, Bun logs:
@@ -213,7 +213,7 @@ Several cache-miss lines are normal: Bun doesn't bytecode-cache the JavaScript i
### Common issues
-**Bytecode silently ignored**: Usually caused by a Bun version update. The cache version doesn't match, so bytecode is rejected. Regenerate to fix.
+**Bytecode silently ignored**: Usually caused by a Bun version update. The cache version doesn't match, so Bun rejects the bytecode. Regenerate to fix.
**File size too large**: This is expected. Consider:
@@ -226,8 +226,8 @@ Several cache-miss lines are normal: Bun doesn't bytecode-cache the JavaScript i
When you run JavaScript, the JavaScript engine doesn't execute your source code directly. Instead, it goes through several steps:
1. **Parsing**: The engine reads your JavaScript source code and converts it into an Abstract Syntax Tree (AST)
-2. **Bytecode compilation**: The AST is compiled into bytecode - a lower-level representation that's faster to execute
-3. **Execution**: The bytecode is executed by the engine's interpreter or JIT compiler
+2. **Bytecode compilation**: The engine compiles the AST into bytecode - a lower-level representation that's faster to execute
+3. **Execution**: The engine's interpreter or JIT compiler executes the bytecode
Bytecode is an intermediate representation - it's lower-level than JavaScript source code, but higher-level than machine code. Think of it as assembly language for a virtual machine. Each bytecode instruction represents a single operation like "load this variable," "add two numbers," or "call this function."
@@ -237,7 +237,7 @@ With bytecode caching, Bun moves steps 1 and 2 to the build step. At runtime, th
### Why lazy parsing makes this even better
-Modern JavaScript engines use an optimization called **lazy parsing**. They don't parse all your code upfront - instead, functions are only parsed when they're first called:
+Modern JavaScript engines use an optimization called **lazy parsing**. They don't parse all your code upfront. Instead, they parse each function only when it's first called:
```js
// Without bytecode caching:
@@ -252,7 +252,7 @@ function main() {
}
```
-This means parsing overhead isn't just a startup cost - it happens throughout your application's lifetime as different code paths execute. With bytecode caching, **all functions are pre-compiled**, even the ones the engine would otherwise parse lazily.
+Lazy parsing means parsing overhead isn't just a startup cost. It happens throughout your application's lifetime as different code paths execute. With bytecode caching, Bun **pre-compiles all functions**, even the ones the engine would otherwise parse lazily.
## The bytecode format
@@ -287,7 +287,7 @@ A `.jsc` file contains a serialized bytecode structure.
**Function metadata** (for each function in your code):
- **Register allocation**: How many registers (local variables) the function needs - `thisRegister`, `scopeRegister`, `numVars`, `numCalleeLocals`, `numParameters`.
-- **Code features**: A bitmask of function characteristics: is it a constructor? an arrow function? does it use `super`? does it have tail calls? These affect how the function is executed.
+- **Code features**: A bitmask of function characteristics: is it a constructor? an arrow function? does it use `super`? does it have tail calls? These affect how the engine executes the function.
- **Lexically scoped features**: Strict mode and other lexical context.
- **Parse mode**: The mode in which the function was parsed (normal, async, generator, async generator).
@@ -327,13 +327,13 @@ Compiles to bytecode that:
- Creates the arrow function (which itself has bytecode)
- Loads the initial value `0`
- Sets up the call with the right number of arguments
-- Actually performs the call
+- Performs the call
- Stores the result in `sum`
Each of these steps is a separate bytecode instruction with its own metadata.
**Constant pools store everything**:
-Every string literal, number, property name - everything gets stored in the constant pool. Even if your source code has `"hello"` a hundred times, the constant pool stores it once, but the identifier table and constant references add overhead.
+Every string literal, number, property name - everything gets stored in the constant pool. Even if your source code has `"hello"` a hundred times, the constant pool stores it once. The identifier table and constant references still add overhead.
**Per-function metadata**:
Each function - even small one-line functions - gets its own complete metadata:
@@ -398,11 +398,11 @@ The cache version in the `.jsc` file header is a hash of the JavaScriptCore fram
1. It extracts the cache version from the `.jsc` file
2. It computes the current JavaScriptCore version
-3. If they don't match, the bytecode is **silently rejected**
+3. If they don't match, Bun **silently rejects** the bytecode
4. Bun falls back to parsing the `.js` source code
**Graceful degradation**:
-This design means bytecode caching "fails open" - if anything goes wrong (version mismatch, corrupted file, missing file), your code still runs normally. You might see slower startup, but you won't see errors.
+This design means bytecode caching "fails open." If anything goes wrong (version mismatch, corrupted file, missing file), your code still runs normally. You might see slower startup, but you won't see errors.
## Unlinked vs. linked bytecode
@@ -435,7 +435,7 @@ When Bun runs bytecode, it "links" it - creating a runtime wrapper that adds:
- **JIT compilation state**: References to baseline JIT or optimizing JIT (DFG/FTL) compiled versions of hot code.
- **Runtime objects**: Pointers to actual JavaScript objects, prototypes, scopes, etc.
-This linked representation is created fresh every time you run your code. This separation allows:
+Bun creates this linked representation fresh every time you run your code. This separation allows:
1. **Caching the expensive work** (parsing and compilation to unlinked bytecode)
2. **Still collecting runtime profiling data** to guide optimizations
diff --git a/docs/bundler/css.mdx b/docs/bundler/css.mdx
index 63e7ad1d014b..20cd7ffa9749 100644
--- a/docs/bundler/css.mdx
+++ b/docs/bundler/css.mdx
@@ -590,7 +590,11 @@ The converted selectors keep the specificity and behavior of the original.
### Math functions
-CSS includes standard math functions (`round()`, `mod()`, `rem()`, `abs()`, `sign()`), trigonometric functions (`sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()`), and exponential functions (`pow()`, `sqrt()`, `exp()`, `log()`, `hypot()`).
+CSS includes the following math functions:
+
+- Standard math functions: `round()`, `mod()`, `rem()`, `abs()`, `sign()`
+- Trigonometric functions: `sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()`
+- Exponential functions: `pow()`, `sqrt()`, `exp()`, `log()`, `hypot()`
```css title="styles.css" icon="file-code"
.dynamic-sizing {
@@ -961,8 +965,12 @@ This is the same as writing:
Two rules apply when using `composes`:
- **Composition Rules:** - A `composes` property must come before any regular CSS properties or declarations - You can
- only use `composes` on a simple selector with a single class name
+
+**Composition Rules:**
+
+- A `composes` property must come before any regular CSS properties or declarations
+- You can only use `composes` on a simple selector with a single class name
+
```css title="styles.module.css" icon="file-code"
diff --git a/docs/bundler/esbuild.mdx b/docs/bundler/esbuild.mdx
index e33b78a379fd..41b3d572e964 100644
--- a/docs/bundler/esbuild.mdx
+++ b/docs/bundler/esbuild.mdx
@@ -52,7 +52,7 @@ In Bun's CLI, boolean flags like `--minify` take no argument. Flags that take on
| `--splitting` | `--splitting` | No differences |
| `--target` | n/a | Not supported. Bun's bundler performs no syntactic down-leveling. |
| `--watch` | `--watch` | No differences |
-| `--allow-overwrite` | n/a | Overwriting is never allowed |
+| `--allow-overwrite` | n/a | Bun never allows overwriting |
| `--analyze` | n/a | Not supported |
| `--asset-names` | `--asset-naming` | Renamed for consistency with naming in JS API |
| `--banner` | `--banner` | Only applies to js bundles |
@@ -76,7 +76,7 @@ In Bun's CLI, boolean flags like `--minify` take no argument. Flags that take on
| `--keep-names` | `--keep-names` | |
| `--keyfile` | n/a | Not applicable |
| `--legal-comments` | n/a | Not supported |
-| `--log-level` | n/a | Not supported. This can be set in `bunfig.toml` as `logLevel`. |
+| `--log-level` | n/a | Not supported. You can set the log level in `bunfig.toml` as `logLevel`. |
| `--log-limit` | n/a | Not supported |
| `--log-override:X=Y` | n/a | Not supported |
| `--main-fields` | n/a | Not supported |
@@ -111,16 +111,16 @@ In Bun's CLI, boolean flags like `--minify` take no argument. Flags that take on
| `absWorkingDir` | n/a | Always set to `process.cwd()` |
| `alias` | n/a | Not supported |
| `allowOverwrite` | n/a | Always false |
-| `assetNames` | `naming.asset` | Uses the same templating syntax as esbuild, but `[ext]` must be included explicitly.
`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
asset: "[name].[ext]",
},
});
` |
+| `assetNames` | `naming.asset` | Uses the same templating syntax as esbuild, but you must include `[ext]` explicitly.
`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
asset: "[name].[ext]",
},
});
` |
| `banner` | `banner` | Only applies to js bundles |
| `bundle` | n/a | Always true. Use `Bun.Transpiler` to transpile without bundling. |
| `charset` | n/a | Not supported |
-| `chunkNames` | `naming.chunk` | Uses the same templating syntax as esbuild, but `[ext]` must be included explicitly.
`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
chunk: "[name].[ext]",
},
});
` |
+| `chunkNames` | `naming.chunk` | Uses the same templating syntax as esbuild, but you must include `[ext]` explicitly.
`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
chunk: "[name].[ext]",
},
});
` |
| `color` | n/a | Bun returns logs in the `logs` property of the build result. |
| `conditions` | `conditions` | No differences |
| `define` | `define` | |
| `drop` | `drop` | |
-| `entryNames` | `naming` or `naming.entry` | Bun supports a `naming` key that can either be a string or an object. Uses the same templating syntax as esbuild, but `[ext]` must be included explicitly.
`ts
Bun.build({
entrypoints: ["./index.tsx"],
// when string, this is equivalent to entryNames
naming: "[name].[ext]",
// granular naming options
naming: {
entry: "[name].[ext]",
asset: "[name].[ext]",
chunk: "[name].[ext]",
},
});
` |
+| `entryNames` | `naming` or `naming.entry` | Bun supports a `naming` key that can either be a string or an object. Uses the same templating syntax as esbuild, but you must include `[ext]` explicitly.
`ts
Bun.build({
entrypoints: ["./index.tsx"],
// when string, this is equivalent to entryNames
naming: "[name].[ext]",
// granular naming options
naming: {
entry: "[name].[ext]",
asset: "[name].[ext]",
chunk: "[name].[ext]",
},
});
` |
| `entryPoints` | `entrypoints` | Capitalization difference |
| `external` | `external` | No differences |
| `footer` | `footer` | Only applies to js bundles |
@@ -136,7 +136,7 @@ In Bun's CLI, boolean flags like `--minify` take no argument. Flags that take on
| `jsxSideEffects` | `jsx.sideEffects` | |
| `keepNames` | `minify.keepNames` | |
| `legalComments` | n/a | Not supported |
-| `loader` | `loader` | Bun supports a different set of built-in loaders than esbuild; see [loaders](/bundler/loaders). The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not implemented. |
+| `loader` | `loader` | Bun supports a different set of built-in loaders than esbuild; see [loaders](/bundler/loaders). Bun does not implement the esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty`. |
| `logLevel` | n/a | Not supported |
| `logLimit` | n/a | Not supported |
| `logOverride` | n/a | Not supported |
@@ -175,14 +175,14 @@ In Bun's CLI, boolean flags like `--minify` take no argument. Flags that take on
## Plugin API
-Bun's plugin API is designed to be esbuild-compatible. Bun doesn't support esbuild's entire plugin API surface, but the core functionality is implemented, and many third-party esbuild plugins work with Bun without modification.
+Bun's plugin API is designed to be esbuild-compatible. Bun doesn't support esbuild's entire plugin API surface, but it implements the core functionality. Many third-party esbuild plugins work with Bun without modification.
Long term, we aim for feature parity with esbuild's API. If something doesn't work, file an issue to help us
prioritize.
-Plugins in Bun and esbuild are defined with a builder object.
+In both Bun and esbuild, you define plugins with a builder object.
```ts title="myPlugin.ts" icon="/icons/typescript.svg"
import type { BunPlugin } from "bun";
@@ -195,7 +195,7 @@ const myPlugin: BunPlugin = {
};
```
-The builder object's methods hook into parts of the bundling process. Bun implements `onStart`, `onEnd`, `onResolve`, and `onLoad`; it does not implement the esbuild hooks `onDispose` and `resolve`. `initialOptions` is partially implemented: it's read-only and exposes only a subset of esbuild's options. Use `config` (the same thing in Bun's `BuildConfig` format) instead.
+The builder object's methods hook into parts of the bundling process. Bun implements `onStart`, `onEnd`, `onResolve`, and `onLoad`; it does not implement the esbuild hooks `onDispose` and `resolve`. Bun partially implements `initialOptions`: the object is read-only and exposes only a subset of esbuild's options. Use `config` (the same thing in Bun's `BuildConfig` format) instead.
```ts title="myPlugin.ts" icon="/icons/typescript.svg"
import type { BunPlugin } from "bun";
diff --git a/docs/bundler/executables.mdx b/docs/bundler/executables.mdx
index 77cb6a6ba3c4..f3311ff7d61d 100644
--- a/docs/bundler/executables.mdx
+++ b/docs/bundler/executables.mdx
@@ -27,7 +27,7 @@ Bun's bundler implements a `--compile` flag for generating a standalone binary f
console.log("Hello world!");
```
-This bundles `cli.ts` into an executable you can run directly:
+Bun bundles `cli.ts` into an executable you can run directly:
```bash terminal icon="terminal"
./mycli
@@ -37,7 +37,7 @@ This bundles `cli.ts` into an executable you can run directly:
Hello world!
```
-All imported files and packages are bundled into the executable, along with a copy of the Bun runtime. All built-in Bun and Node.js APIs are supported.
+Bun bundles all imported files and packages into the executable, along with a copy of the Bun runtime. All built-in Bun and Node.js APIs are supported.
---
@@ -341,7 +341,7 @@ Using bytecode compilation, `tsc` starts 2x faster:

-Bytecode compilation moves parsing overhead for large input files from runtime to bundle time. Your app starts faster, in exchange for making the `bun build` command a little slower. It doesn't obscure source code.
+Bytecode compilation moves parsing overhead for large input files from runtime to bundle time. Your app starts faster, in exchange for making the `bun build` command a little slower. Bytecode compilation doesn't obscure source code.
Bytecode compilation supports both `cjs` and `esm` formats when used with `--compile`.
@@ -495,7 +495,7 @@ Normally, running `./such-bun` with arguments executes the script.
you shouldn't see this
```
-However, with the `BUN_BE_BUN=1` environment variable, it acts like the `bun` binary:
+However, with the `BUN_BE_BUN=1` environment variable, the executable acts like the `bun` binary:
```bash icon="terminal" terminal
# With the env var, the executable acts like the `bun` CLI
@@ -592,7 +592,7 @@ The result is a single file you can deploy anywhere without installing Node.js,
./myapp
```
-Bun serves the frontend assets with the correct MIME types and cache headers. The HTML import is replaced with a manifest object that `Bun.serve` uses to serve the pre-bundled assets.
+Bun serves the frontend assets with the correct MIME types and cache headers. Bun replaces the HTML import with a manifest object that `Bun.serve` uses to serve the pre-bundled assets.
For more on building full-stack applications, see the [full-stack guide](/bundler/fullstack).
@@ -631,9 +631,9 @@ new Worker(new URL("./my-worker.ts", import.meta.url));
new Worker(new URL("./my-worker.ts", import.meta.url).href);
```
-When you add multiple entrypoints to a standalone executable, each is bundled separately into the executable.
+When you add multiple entrypoints to a standalone executable, Bun bundles each one separately into the executable.
-We may eventually detect statically-known paths in `new Worker(path)` and bundle them automatically, but for now you need to list the worker file as an entrypoint, as in the earlier example.
+We may eventually detect statically-known paths in `new Worker(path)` and bundle them automatically. For now, you need to list the worker file as an entrypoint, as in the earlier example.
If you use a relative path to a file not included in the standalone executable, Bun loads that path from disk relative to the process's current working directory, and errors if it doesn't exist.
@@ -643,7 +643,7 @@ If you use a relative path to a file not included in the standalone executable,
You can use `bun:sqlite` imports with `bun build --compile`.
-By default, the database is resolved relative to the current working directory of the process.
+By default, Bun resolves the database relative to the current working directory of the process.
```ts index.ts icon="/icons/typescript.svg"
import db from "./my.db" with { type: "sqlite" };
@@ -819,11 +819,11 @@ bun build --compile ./index.ts --outfile mycli
The database file must exist on disk when you run `bun build --compile`. The `embed: "true"` attribute tells the
- bundler to include the database contents inside the compiled executable. When running normally with `bun run`, the
- database file is loaded from disk just like a regular SQLite import.
+ bundler to include the database contents inside the compiled executable. When running normally with `bun run`, Bun
+ loads the database file from disk like a regular SQLite import.
-In the compiled executable, the embedded database is read-write, but all changes are lost when the executable exits (since it's stored in memory).
+In the compiled executable, the embedded database is read-write. Because the database is stored in memory, all changes are lost when the executable exits.
### Embed N-API Addons
@@ -835,7 +835,7 @@ const addon = require("./addon.node");
console.log(addon.hello());
```
-If you're using `@mapbox/node-pre-gyp` or similar tools, the `.node` file must be required directly or it won't bundle correctly.
+If you're using `@mapbox/node-pre-gyp` or similar tools, require the `.node` file directly, or it won't bundle correctly.
### Embed directories
@@ -873,9 +873,9 @@ for (const entry of fs.readdirSync(publicDir, { withFileTypes: true })) {
const html = await Bun.file(path.join(publicDir, "index.html")).text();
```
-Pass `--asset` multiple times to embed several directories (for example `--asset ./client --asset ./prerendered` for a SvelteKit build). Only regular files are embedded; symlinks and empty subdirectories inside the tree are skipped.
+Pass `--asset` multiple times to embed several directories (for example `--asset ./client --asset ./prerendered` for a SvelteKit build). Bun embeds only regular files; it skips symlinks and empty subdirectories inside the tree.
-You can also embed individual files the old way, by adding them as extra entry points; imported assets are renamed according to `--asset-naming` (default `[name]-[hash].[ext]`):
+You can also embed individual files the old way, by adding them as extra entry points. Bun renames imported assets according to `--asset-naming` (default `[name]-[hash].[ext]`):
```ts
import icon from "./public/assets/icon.png" with { type: "file" };
@@ -951,7 +951,7 @@ serve({
#### Content hash
-By default, embedded files have a content hash appended to their name, which helps with cache invalidation when you serve them from a URL or CDN. To keep the original name instead, configure asset naming:
+By default, Bun appends a content hash to the name of each embedded file, which helps with cache invalidation when you serve the files from a URL or CDN. To keep the original name instead, configure asset naming:
@@ -1065,8 +1065,7 @@ Available Windows options:
- `copyright` - Copyright notice in file properties
- With the exception of `hideConsole`, these flags cannot be used when cross-compiling because they depend on Windows
- APIs.
+ Except for `hideConsole`, you can't use these flags when cross-compiling because they depend on Windows APIs.
---
diff --git a/docs/bundler/fullstack.mdx b/docs/bundler/fullstack.mdx
index 0dd2f374d3f2..c4597c9ec941 100644
--- a/docs/bundler/fullstack.mdx
+++ b/docs/bundler/fullstack.mdx
@@ -207,7 +207,7 @@ When `development` is `true`, Bun:
- Includes the SourceMap header in the response so that devtools can show the original source code
- Disables minification
- Re-bundles assets on each request to a `.html` file
-- Enables hot module reloading (unless `hmr: false` is set)
+- Enables hot module reloading (unless you set `hmr: false`)
### Advanced Development Configuration
@@ -272,7 +272,7 @@ serve({
If you'd rather not add a build step, set `development: false` in `Bun.serve()`.
-This:
+With this setting, Bun:
- Enables in-memory caching of bundled assets. Bun bundles assets lazily on the first request to an `.html` file and caches the result in memory until the server restarts.
- Enables `Cache-Control` and `ETag` headers
@@ -399,7 +399,7 @@ serve({
## Plugins
-Bun's bundler plugins are also supported when bundling static routes.
+Bun also supports bundler plugins when bundling static routes.
To configure plugins for `Bun.serve`, add a `plugins` array in the `[serve.static]` section of your `bunfig.toml`.
@@ -452,7 +452,7 @@ Alternatively, you can import TailwindCSS in your CSS file:
### Custom Plugins
-Any JS file or module that exports a valid bundler plugin object (an object with a `name` and a `setup` field) can be placed in the plugins array:
+The plugins array accepts any JS file or module that exports a valid bundler plugin object (an object with a `name` and a `setup` field):
```toml title="bunfig.toml" icon="settings"
[serve.static]
@@ -498,7 +498,7 @@ env = "PUBLIC_*" # only inline env vars starting with PUBLIC_ (recommended)
```
- This only works with literal `process.env.FOO` references, not `import.meta.env` or indirect access like `const env =
+ Bun only replaces literal `process.env.FOO` references, not `import.meta.env` or indirect access like `const env =
process.env; env.FOO`.
If an environment variable is not set, you may see runtime errors like `ReferenceError: process
@@ -510,7 +510,7 @@ See [HTML & static sites](/bundler/html-static#inline-environment-variables) for
## Sourcemaps
-In development, Bun generates linked sourcemaps for bundled routes and serves them alongside the JavaScript and CSS chunks. In production (`development: false`), sourcemaps are disabled by default so your original source code is not exposed by the server.
+In development, Bun generates linked sourcemaps for bundled routes and serves them alongside the JavaScript and CSS chunks. In production (`development: false`), sourcemaps are disabled by default so the server does not expose your original source code.
To override the default, set the `sourcemap` option in your `bunfig.toml`:
@@ -524,7 +524,7 @@ sourcemap = "linked" # serve sourcemaps in production too
## How It Works
-Bun uses `HTMLRewriter` to scan for `");
diff --git a/docs/guides/util/sleep.mdx b/docs/guides/util/sleep.mdx
index de6611684f93..4ea6b8c84627 100644
--- a/docs/guides/util/sleep.mdx
+++ b/docs/guides/util/sleep.mdx
@@ -13,7 +13,7 @@ await Bun.sleep(1000);
---
-Internally, it is equivalent to the following [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout) snippet.
+Internally, `Bun.sleep()` is equivalent to the following [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout) snippet.
```ts
await new Promise(resolve => setTimeout(resolve, ms));
diff --git a/docs/guides/websocket/compression.mdx b/docs/guides/websocket/compression.mdx
index 63163251013e..ce94cca7bbb1 100644
--- a/docs/guides/websocket/compression.mdx
+++ b/docs/guides/websocket/compression.mdx
@@ -4,7 +4,7 @@ sidebarTitle: Enable compression
mode: center
---
-Set the `perMessageDeflate` parameter to enable the [permessage-deflate](https://tools.ietf.org/html/rfc7692) WebSocket extension. This negotiates compression with clients that support it; messages sent with `ws.send()` are still uncompressed unless you opt in per message (see below).
+Set the `perMessageDeflate` parameter to enable the [permessage-deflate](https://tools.ietf.org/html/rfc7692) WebSocket extension. Bun then negotiates compression with clients that support it. Messages sent with `ws.send()` are still uncompressed unless you opt in per message (see below).
```ts server.ts icon="/icons/typescript.svg"
Bun.serve({
@@ -18,7 +18,7 @@ Bun.serve({
---
-To enable compression for individual messages, pass `true` as the second parameter to `ws.send()`. This requires `perMessageDeflate` to be enabled; otherwise the message is sent uncompressed.
+To enable compression for individual messages, pass `true` as the second parameter to `ws.send()`. This requires `perMessageDeflate` to be enabled; otherwise Bun sends the message uncompressed.
```ts server.ts icon="/icons/typescript.svg"
Bun.serve({
diff --git a/docs/guides/write-file/filesink.mdx b/docs/guides/write-file/filesink.mdx
index 8f8c2fb079d1..c1c3505a340d 100644
--- a/docs/guides/write-file/filesink.mdx
+++ b/docs/guides/write-file/filesink.mdx
@@ -6,7 +6,7 @@ mode: center
Bun provides an API for incrementally writing to a file. Use it for large files, or when writing to a file over a long period of time.
-Call `.writer()` on a `BunFile` to retrieve a `FileSink` instance. It buffers data; call `.flush()` to write the buffer to disk. You can write & flush many times.
+Call `.writer()` on a `BunFile` to retrieve a `FileSink` instance. The `FileSink` buffers data. Call `.flush()` to write the buffer to disk. You can write & flush many times.
```ts
const file = Bun.file("/path/to/file.txt");
diff --git a/docs/guides/write-file/response.mdx b/docs/guides/write-file/response.mdx
index 3bd093fba6a3..d8a9c1b36c4b 100644
--- a/docs/guides/write-file/response.mdx
+++ b/docs/guides/write-file/response.mdx
@@ -4,7 +4,7 @@ sidebarTitle: Write Response
mode: center
---
-Use [`Bun.write()`](/runtime/file-io#writing-files-bun-write) to write a `Response` to disk. The body of the `Response` is written to the destination.
+Use [`Bun.write()`](/runtime/file-io#writing-files-bun-write) to write a `Response` to disk. Bun writes the body of the `Response` to the destination.
The first argument is a _destination_, like an absolute path or `BunFile` instance. The second argument is the _data_ to write.
diff --git a/docs/guides/write-file/stream.mdx b/docs/guides/write-file/stream.mdx
index 46d1e0e23223..0994b454c676 100644
--- a/docs/guides/write-file/stream.mdx
+++ b/docs/guides/write-file/stream.mdx
@@ -4,7 +4,7 @@ sidebarTitle: Write stream
mode: center
---
-To write a `ReadableStream` to disk, call `.writer()` on a `BunFile` to get a [`FileSink`](/runtime/file-io#incremental-writing-with-filesink). The stream is an async iterable, so write each of its chunks to the `FileSink` with `for await`, then call `.end()` to flush the buffer and close the file.
+To write a `ReadableStream` to disk, call `.writer()` on a `BunFile` to get a [`FileSink`](/runtime/file-io#incremental-writing-with-filesink). The stream is an async iterable, so write each of its chunks to the `FileSink` with `for await`. Then call `.end()` to flush the buffer and close the file.
```ts
const stream: ReadableStream = ...;
@@ -20,7 +20,7 @@ await writer.end();
---
-`.writer()` creates the file if it doesn't exist, but it does not truncate an existing file. If the file may already exist, delete it first.
+`.writer()` creates the file if it doesn't exist, but does not truncate an existing file. If the file may already exist, delete it first.
---
diff --git a/docs/index.mdx b/docs/index.mdx
index 705eb549a929..794d88e06c54 100644
--- a/docs/index.mdx
+++ b/docs/index.mdx
@@ -79,7 +79,7 @@ Bun ships as a single, dependency-free binary and includes a runtime, package ma
- Test Runner: Jest-compatible, TypeScript-first tests with snapshots, DOM, and watch mode.
- Bundler: Native bundling for JS/TS/JSX with splitting, plugins, and HTML imports.
-Explore each area using the cards above. Each section is structured with an overview, quick examples, reference, and best practices for fast scanning and deep dives.
+Explore each area using the cards above. Each section is structured with an overview, short examples, reference, and best practices for fast scanning and deep dives.
---
@@ -93,7 +93,7 @@ At its core is the _Bun runtime_, a fast JavaScript runtime designed as **a drop
bun run index.tsx # TS and JSX supported by default
```
-The `bun` command-line tool also implements a test runner, script runner, and Node.js-compatible package manager, all significantly faster than existing tools and usable in existing Node.js projects with little to no changes.
+The `bun` command-line tool also implements a test runner, script runner, and Node.js-compatible package manager. All three are significantly faster than existing tools, and you can use them in existing Node.js projects with little to no changes.
```bash terminal icon="terminal"
bun run start # run the `start` script
@@ -105,7 +105,7 @@ bunx cowsay 'Hello, world!' # execute a package
## What is a runtime?
-JavaScript (or, more formally, ECMAScript) is just a _specification_ for a programming language. Anyone can write a JavaScript _engine_ that ingests a valid JavaScript program and executes it. The two most popular engines in use today are V8 (developed by Google)
+JavaScript (or, more formally, ECMAScript) is only a _specification_ for a programming language. Anyone can write a JavaScript _engine_ that ingests a valid JavaScript program and executes it. The two most popular engines in use today are V8 (developed by Google)
and JavaScriptCore (developed by Apple). Both are open source.
But most JavaScript programs don't run in a vacuum. They need a way to access the outside world to perform useful tasks. This is where _runtimes_ come in. They implement additional APIs and make them available to the JavaScript programs they execute.
diff --git a/docs/installation.mdx b/docs/installation.mdx
index ab289c04ba4c..c1ce6bf26b6f 100644
--- a/docs/installation.mdx
+++ b/docs/installation.mdx
@@ -21,7 +21,7 @@ Bun ships as a single, dependency-free executable. Install it with the install s
- **Linux users:** The `unzip` package is required to install Bun (`sudo apt install unzip`). Kernel version 5.6 or higher is recommended; Bun runs on kernels as old as 3.10 (RHEL 7) with graceful degradation of newer syscalls. Use `uname -r` to check your kernel version.
+ **Linux users:** You need the `unzip` package to install Bun (`sudo apt install unzip`). We recommend kernel version 5.6 or higher. Bun runs on kernels as old as 3.10 (RHEL 7) with graceful degradation of newer syscalls. Use `uname -r` to check your kernel version.
diff --git a/docs/pm/bunx.mdx b/docs/pm/bunx.mdx
index 3ed14dddf5d7..ff13577863ef 100644
--- a/docs/pm/bunx.mdx
+++ b/docs/pm/bunx.mdx
@@ -44,7 +44,7 @@ Run these executables with `bunx`:
bunx my-cli
```
-As with `npx`, `bunx` checks for a locally installed package first, then falls back to auto-installing it from `npm`. Installed packages are stored in Bun's [global cache](/pm/global-cache) for future use.
+As with `npx`, `bunx` checks for a locally installed package first, then falls back to auto-installing it from `npm`. `bunx` stores installed packages in Bun's [global cache](/pm/global-cache) for future use.
## Arguments and flags
@@ -64,7 +64,7 @@ By default, Bun respects shebangs. If an executable is marked with `#!/usr/bin/e
bunx --bun my-cli
```
-The `--bun` flag must occur _before_ the executable name. Flags that appear _after_ the name are passed through to the executable.
+The `--bun` flag must occur _before_ the executable name. `bunx` passes flags that appear _after_ the name through to the executable.
```bash terminal icon="terminal"
bunx --bun my-cli # good
diff --git a/docs/pm/catalogs.mdx b/docs/pm/catalogs.mdx
index 023ae04f945f..6bd61b92428f 100644
--- a/docs/pm/catalogs.mdx
+++ b/docs/pm/catalogs.mdx
@@ -13,7 +13,7 @@ Instead of each workspace package specifying its own versions, you:
2. Reference those versions with the `catalog:` protocol
3. Update every package at once by changing the version in one place
-This matters most in large monorepos where dozens of packages depend on the same versions of key dependencies.
+Catalogs matter most in large monorepos where dozens of packages depend on the same versions of key dependencies.
## How to Use Catalogs
@@ -240,7 +240,7 @@ Then run `bun install` to update all packages.
## Adding to the catalog with `bun add`
-`bun add --catalog` (or `--catalog=`) adds the entry to the root catalog and writes `"catalog:"` to the current package. An existing catalog entry is reused unless you pass an explicit version. See [`bun add --catalog`](/pm/cli/add#--catalog).
+`bun add --catalog` (or `--catalog=`) adds the entry to the root catalog and writes `"catalog:"` to the current package. Bun reuses an existing catalog entry unless you pass an explicit version. See [`bun add --catalog`](/pm/cli/add#--catalog).
```bash terminal icon="terminal"
bun add react --catalog
@@ -293,10 +293,10 @@ Bun's lockfile tracks catalog versions, so installs are consistent across enviro
## Limitations and Edge Cases
- Catalog references must match a dependency defined in either `catalog` or one of the named `catalogs`
-- Empty strings and whitespace in catalog names are ignored (treated as default catalog)
-- `catalog:default` is the same as `catalog:`. The default catalog can be defined as either `catalog` or `catalogs.default`, but a package listed in both is an error
+- Bun ignores empty strings and whitespace in catalog names and treats them as the default catalog
+- `catalog:default` is the same as `catalog:`. You can define the default catalog as either `catalog` or `catalogs.default`, but a package listed in both is an error
- Invalid dependency versions in catalogs fail to resolve during `bun install`
-- `catalog:` only works in the root and workspace `package.json` files. Inside a published package it fails to resolve — publish with `bun publish` or `bun pm pack`, which replace it with the real range (see [Publishing](#publishing))
+- `catalog:` only works in the root and workspace `package.json` files. Inside a published package it fails to resolve. Publish with `bun publish` or `bun pm pack`, which replace it with the real range (see [Publishing](#publishing))
## Publishing
diff --git a/docs/pm/cli/add.mdx b/docs/pm/cli/add.mdx
index cc41ac017089..15b4b286eb8e 100644
--- a/docs/pm/cli/add.mdx
+++ b/docs/pm/cli/add.mdx
@@ -19,7 +19,7 @@ bun add zod@^3.0.0
bun add zod@latest
```
-The package is written to `dependencies` unless `--dev`, `--optional`, or `--peer` is given. If `package.json` already lists it in another group, that entry is updated in place.
+Bun writes the package to `dependencies` unless you pass `--dev`, `--optional`, or `--peer`. If `package.json` already lists it in another group, Bun updates that entry in place.
## `--dev`
@@ -110,11 +110,11 @@ bun add vitest --catalog=testing
}
```
-- If the catalog already has an entry, it is reused and only `"catalog:"` is written to the current package. Pass an explicit version (`bun add react@19 --catalog`) to replace the entry — this affects every package that references it.
-- Without a version, a range already in the current `package.json` (`"react": "^18.2.0"`) is what gets cataloged.
+- If the catalog already has an entry, Bun reuses it and writes only `"catalog:"` to the current package. Pass an explicit version (`bun add react@19 --catalog`) to replace the entry — this affects every package that references it.
+- If you omit the version and the current `package.json` already has a range (`"react": "^18.2.0"`), Bun catalogs that range.
- A package that already references `"catalog:"` keeps using that catalog.
-- The name must be attached with `=`: `--catalog=testing`, not `--catalog testing`.
-- Tarball and git specifiers are cataloged under the package's real name. Relative paths and workspace packages are rejected.
+- Attach the name with `=`: `--catalog=testing`, not `--catalog testing`.
+- Bun catalogs tarball and git specifiers under the package's real name. It rejects relative paths and workspace packages.
Even without the flag, `bun add react` (no version) writes `"catalog:"` if the default catalog already lists `react`. Pass a version to write a concrete range instead.
@@ -132,9 +132,9 @@ bun remove zod --filter '*' --filter '!api'
```
- `*` matches every workspace package but not the root. To include the root, name it: `--filter '*' --filter ''`.
-- If no workspace matches, nothing is written and the command fails.
-- Local paths are resolved from the current directory and rewritten relative to each selected package.
-- `bun.lock` is updated for the whole repo, but only the selected workspaces are linked into `node_modules`, as with `bun install --filter`.
+- If no workspace matches, Bun writes nothing and the command fails.
+- Bun resolves local paths from the current directory and rewrites them relative to each selected package.
+- Bun updates `bun.lock` for the whole repo but links only the selected workspaces into `node_modules`, as with `bun install --filter`.
- Cannot be combined with `--global`.
## `--global`
diff --git a/docs/pm/cli/audit.mdx b/docs/pm/cli/audit.mdx
index 6682c37c0fab..8fa35e1734fb 100644
--- a/docs/pm/cli/audit.mdx
+++ b/docs/pm/cli/audit.mdx
@@ -9,11 +9,11 @@ Run the command in a project with a `bun.lock` file:
bun audit
```
-Bun reads the package list from `bun.lock` (no `node_modules` required), sends it to the npm advisory endpoint, and prints a report. Packages from a scoped registry are sent to that registry instead; if it has no advisory endpoint, those packages are listed as skipped and don't affect the exit code.
+Bun reads the package list from `bun.lock` (no `node_modules` required), sends it to the npm advisory endpoint, and prints a report. Bun sends packages from a scoped registry to that registry instead. If that registry has no advisory endpoint, Bun lists those packages as skipped and they don't affect the exit code.
`bun audit` never modifies `package.json`, `bun.lock`, or `node_modules`. To apply fixes, use [`bun audit fix`](#bun-audit-fix).
-If no vulnerabilities are found, the command prints:
+If Bun finds no vulnerabilities, the command prints:
```
No vulnerabilities found
@@ -48,7 +48,7 @@ bun audit --prod
bun audit --omit=optional --omit=peer
```
-**`--ignore `** - Ignore an advisory by GHSA ID or numeric ID. Repeatable. (CVE IDs are not in the registry data and won't match.)
+**`--ignore `** - Ignore an advisory by GHSA ID or numeric ID. Repeatable. (CVE IDs are not in the registry data and don't match.)
```bash terminal icon="terminal"
bun audit --ignore GHSA-c2qf-rxjj-qqgw --ignore 1112918
@@ -72,7 +72,7 @@ The JSON is unfiltered — `--audit-level` and `--ignore` only affect the exit c
bun audit fix
```
-Runs the audit, then upgrades each vulnerable package to the lowest non-vulnerable version that every dependent's range allows, and installs. Only `bun.lock` and `node_modules` change, with one exception: a direct dependency pinned to an exact version is treated as `^version`, and the pin in `package.json` (or the catalog entry) is rewritten if a fix is found.
+Runs the audit, then upgrades each vulnerable package to the lowest non-vulnerable version that every dependent's range allows, and installs. Only `bun.lock` and `node_modules` change, with one exception: Bun treats a direct dependency pinned to an exact version as `^version`. If Bun finds a fix, it rewrites the pin in `package.json` (or the catalog entry).
```
fixing:
@@ -98,13 +98,13 @@ Fixed 2 vulnerabilities in 2 packages
- **blocked by a dependent's range** — no safe version fits a dependent's declared range. If the range is in your own `package.json` or catalog, `bun audit fix --latest` gets past it. Otherwise, update the dependent or add an [`overrides`](/pm/overrides) entry.
- **no published version fixes** — every published version is vulnerable. Replace the package, or silence the advisory with the printed `--ignore` command.
- If no newer version is safe but an older one is, Bun downgrades and marks the row `(downgrade)`.
-- A safe version newer than `--minimum-release-age` is still installed, marked `(newer than --minimum-release-age)`.
-- Patched dependencies (`patchedDependencies`) are upgraded like any other package; re-create the patch afterwards with `bun patch`.
+- Bun still installs a safe version newer than `--minimum-release-age` and marks the row `(newer than --minimum-release-age)`.
+- Bun upgrades patched dependencies (`patchedDependencies`) like any other package. Re-create the patch afterwards with `bun patch`.
- After installing, Bun re-audits the new lockfile. The `remaining` count and exit code reflect that second audit, so they match what a follow-up `bun audit` would report.
- `--dry-run` prints the plan without installing.
- `--json` prints a single JSON object describing the plan and result (`fixes`, `blocked`, `unfixable`, `unmatched`, `unaudited`, `vulnerableAfterInstall`, `fixed`, `remaining`, `dryRun`). Pass `--ignore-scripts` if lifecycle scripts might write to stdout.
- A [security scanner](/pm/security-scanner-api) configured in `bunfig.toml` runs on the packages about to be installed, as with `bun update`.
-- `--prod`, `--frozen-lockfile`, and `--no-save` are rejected since they prevent writing `bun.lock`.
+- Bun rejects `--prod`, `--frozen-lockfile`, and `--no-save` since they prevent writing `bun.lock`.
### `bun audit fix --latest`
@@ -112,10 +112,10 @@ Fixed 2 vulnerabilities in 2 packages
bun audit fix --latest
```
-Same as `bun audit fix`, but ranges in your own `package.json` files and catalogs no longer block a fix — they are rewritten to accept the new version, keeping their style (`^5.0.0` → `^6.3.1`, `~5.7.1` → `~6.3.1`, exact stays exact). Ranges declared by third-party packages still block; use `overrides` for those.
+Same as `bun audit fix`, but ranges in your own `package.json` files and catalogs no longer block a fix. Bun rewrites them to accept the new version, keeping their style (`^5.0.0` → `^6.3.1`, `~5.7.1` → `~6.3.1`, exact stays exact). Ranges declared by third-party packages still block; use `overrides` for those.
### Exit code
-`0` if no vulnerabilities remain after `--audit-level` and `--ignore` are applied, `1` otherwise. For `bun audit fix`, this is based on the re-audit after installing (or the plan, with `--dry-run`).
+`0` if no vulnerabilities remain after Bun applies `--audit-level` and `--ignore`, `1` otherwise. For `bun audit fix`, this is based on the re-audit after installing (or the plan, with `--dry-run`).
If the registry request fails, both commands print `audit request failed` to stderr and exit `1`.
diff --git a/docs/pm/cli/dedupe.mdx b/docs/pm/cli/dedupe.mdx
index 8cd23df24416..67c09efadb3a 100644
--- a/docs/pm/cli/dedupe.mdx
+++ b/docs/pm/cli/dedupe.mdx
@@ -3,7 +3,7 @@ title: "bun dedupe"
description: "Remove duplicate versions of packages from bun.lock"
---
-Over time, `bun.lock` can accumulate several versions of the same package even though one of them satisfies every range — for example `esbuild@0.15.10` and `esbuild@0.15.11` when the ranges are `^0.15.7` and `^0.15.8`. `bun dedupe` collapses these onto the smallest set of already-locked versions (preferring newer ones), saves `bun.lock`, and installs.
+Over time, `bun.lock` can accumulate several versions of the same package even though one of them satisfies every range. For example, it can contain both `esbuild@0.15.10` and `esbuild@0.15.11` when the ranges are `^0.15.7` and `^0.15.8`. `bun dedupe` collapses these onto the smallest set of already-locked versions (preferring newer ones), saves `bun.lock`, and installs.
```bash terminal icon="terminal"
bun dedupe
@@ -18,13 +18,13 @@ bun dedupe v1.4.0 (abc12345)
2 duplicate versions removed, 3 packages installed (checked 5 packages) [12.00ms]
```
-Each row is a version that was removed and the version its dependents now use.
+Each row is a version Bun removed and the version its dependents now use.
-`bun dedupe` only chooses between versions already in the lockfile. It never fetches new versions from the registry and never moves a dependency outside its range — use [`bun update`](/pm/cli/update) for that. `package.json` is never modified.
+`bun dedupe` only chooses between versions already in the lockfile and never modifies `package.json`. It never fetches new versions from the registry and never moves a dependency outside its range. Use [`bun update`](/pm/cli/update) for that.
### `--check` and `--dry-run`
-`--check` reports what would be removed without changing anything, and exits `1` if there are duplicates. Use it in CI:
+`--check` reports what Bun would remove without changing anything, and exits `1` if there are duplicates. Use it in CI:
```bash terminal icon="terminal"
bun dedupe --check
@@ -46,10 +46,10 @@ bun dedupe v1.4.0 (abc12345)
### Notes
-- Overrides and catalogs are respected; each dependency is re-pointed using its effective range.
-- A direct dependency may be moved to an _older_ locked version if that's the only way to remove a duplicate (e.g. a transitive dependency pins it exactly). Use `bun update` or an [override](/pm/overrides) if you want the newer one to win.
-- Versions in `patchedDependencies` are never removed. If that forces another version to be kept too, Bun prints a `kept …` line explaining why.
+- Bun respects overrides and catalogs. It re-points each dependency using its effective range.
+- Bun may move a direct dependency to an _older_ locked version if that's the only way to remove a duplicate (e.g. a transitive dependency pins it exactly). Use `bun update` or an [override](/pm/overrides) if you want the newer one to win.
+- Bun never removes versions in `patchedDependencies`. If that forces another version to be kept too, Bun prints a `kept …` line explaining why.
- Dependencies on a dist-tag, git URL, or tarball keep their resolved version.
-- Requires a lockfile that matches `package.json`. If dependencies changed since the last install, it exits with `bun.lock does not match package.json` — run `bun install` first. A `package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml` is migrated automatically.
+- Requires a lockfile that matches `package.json`. If dependencies changed since the last install, it exits with `bun.lock does not match package.json`. Run `bun install` first. Bun migrates a `package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml` automatically.
- Cannot be combined with `--frozen-lockfile`, `--production`, or `--no-save`; use `--check` instead.
-- With the isolated linker, several copies of the _same_ version that differ only in peer dependencies are not duplicates and are not reported. Stale store entries are cleaned up by [`bun prune`](/pm/cli/prune).
+- With the isolated linker, several copies of the _same_ version that differ only in peer dependencies are not duplicates, and Bun does not report them. [`bun prune`](/pm/cli/prune) cleans up stale store entries.
diff --git a/docs/pm/cli/install.mdx b/docs/pm/cli/install.mdx
index 0b219f2b285e..ebb8bafd96bf 100644
--- a/docs/pm/cli/install.mdx
+++ b/docs/pm/cli/install.mdx
@@ -164,7 +164,7 @@ To install in production mode (without `devDependencies`):
bun install --production
```
-`--production` implies `--frozen-lockfile`. It only controls what gets installed — `devDependencies` already in `node_modules` from an earlier install are left there. Use [`bun prune --production`](/pm/cli/prune) to remove them.
+`--production` implies `--frozen-lockfile`. It only controls what gets installed. `devDependencies` already in `node_modules` from an earlier install stay there. Use [`bun prune --production`](/pm/cli/prune) to remove them.
For reproducible installs, use `--frozen-lockfile`. Bun installs the exact versions specified in the lockfile and does not update it. If your `package.json` disagrees with `bun.lock`, Bun exits with an error.
@@ -174,7 +174,7 @@ bun install --frozen-lockfile
Bun does not enable `--frozen-lockfile` automatically in CI; pass the flag or use `bun ci`. If there is no lockfile at all, `--frozen-lockfile` installs from `package.json` without writing one.
-`--frozen-lockfile` works on a pruned monorepo checkout (e.g. `turbo prune` output, or a Docker context with only some workspace folders copied in). Workspaces listed in `bun.lock` whose `package.json` is missing on disk are skipped with a `note:`, and their exclusive dependencies are not installed. If a remaining workspace depends on a skipped one, the install fails.
+`--frozen-lockfile` works on a pruned monorepo checkout (e.g. `turbo prune` output, or a Docker context with only some workspace folders copied in). If a workspace listed in `bun.lock` is missing its `package.json` on disk, Bun skips it with a `note:` and does not install its exclusive dependencies. If a remaining workspace depends on a skipped one, the install fails.
To validate the lockfile without installing, use `bun install --frozen-lockfile --dry-run`.
@@ -241,7 +241,7 @@ bun install --linker hoisted
### Isolated installs
-A pnpm-like approach that creates strict dependency isolation to prevent [phantom dependencies](/pm/isolated-installs), packages that can be imported without being declared in `package.json`:
+A pnpm-like approach that creates strict dependency isolation to prevent [phantom dependencies](/pm/isolated-installs), packages you can import without declaring them in `package.json`:
```bash terminal icon="terminal"
bun install --linker isolated
@@ -257,7 +257,7 @@ The default linker strategy depends on whether you're starting fresh or have an
- **New single-package projects**: `hoisted` (traditional npm behavior)
- **Existing projects (made pre-v1.3.2)**: `hoisted` (preserves backward compatibility)
-The default is controlled by a `configVersion` field in your lockfile. For a detailed explanation, see [isolated installs](/pm/isolated-installs).
+A `configVersion` field in your lockfile controls the default. For a detailed explanation, see [isolated installs](/pm/isolated-installs).
---
@@ -284,12 +284,12 @@ minimumReleaseAgeExcludes = ["@types/node", "typescript"]
When the minimum age filter is active:
- It only affects new package resolution; existing packages in `bun.lock` remain unchanged
-- All dependencies (direct and transitive) are filtered to meet the age requirement when resolved
-- When versions are blocked by the age gate, a stability check detects rapid bugfix patterns
+- Bun filters all dependencies (direct and transitive) to meet the age requirement when resolving them
+- When the age gate blocks versions, a stability check detects rapid bugfix patterns
- If multiple versions were published close together just outside your age gate, Bun extends the filter to skip those potentially unstable versions and selects an older, more mature version
- The check searches up to 7 days past the age gate; if releases are still rapid beyond that, Bun ignores the stability check
- Exact version requests (like `package@1.1.1`) still respect the age gate but bypass the stability check
-- Versions without a `time` field are treated as passing the age check (the npm registry should always provide timestamps)
+- Bun treats versions without a `time` field as passing the age check (the npm registry should always provide timestamps)
For more advanced security scanning, including integration with services and custom filtering, see the [Security Scanner API](/pm/security-scanner-api).
@@ -304,7 +304,7 @@ On `bun install`, `bun remove`, and `bun add`, Bun looks for `bunfig.toml` in:
1. `$XDG_CONFIG_HOME/.bunfig.toml` or `$HOME/.bunfig.toml`
2. `./bunfig.toml`
-If both are found, both are loaded, and keys set in the project's `bunfig.toml` override the same keys in the global file.
+If Bun finds both, it loads both. Keys set in the project's `bunfig.toml` override the same keys in the global file.
Configuring with `bunfig.toml` is optional. These are the default values:
@@ -368,7 +368,7 @@ When the `node_modules` folder exists, Bun decides whether to install a package
When a `bun.lock` doesn’t exist or `package.json` has changed dependencies, Bun downloads and extracts tarballs eagerly while resolving.
-When a `bun.lock` exists and `package.json` hasn’t changed, Bun downloads missing dependencies lazily. If the package with a matching `name` and `version` already exists in the expected location within `node_modules`, Bun won’t attempt to download the tarball.
+When a `bun.lock` exists and `package.json` hasn’t changed, Bun downloads missing dependencies lazily. If the package with a matching `name` and `version` already exists in the expected location within `node_modules`, Bun doesn’t attempt to download the tarball.
## CI/CD
@@ -420,7 +420,7 @@ jobs:
## Platform-specific dependencies?
-Bun stores normalized `cpu` and `os` values from npm in the lockfile, along with the resolved packages. It skips downloading, extracting, and installing packages disabled for the current target at runtime. This means the lockfile won't change between platforms/architectures even if the packages ultimately installed do change.
+Bun stores normalized `cpu` and `os` values from npm in the lockfile, along with the resolved packages. It skips downloading, extracting, and installing packages disabled for the current target at runtime. This means the lockfile doesn't change between platforms/architectures even if the packages ultimately installed do change.
### `--cpu` and `--os` flags
@@ -444,7 +444,7 @@ Bun handles peer dependencies like Yarn: `bun install` installs them automatical
`bun.lock` is Bun’s lockfile format. See [our blog post about the text lockfile](https://bun.com/blog/bun-lock-text-lockfile).
-Prior to Bun 1.2, the lockfile was binary and called `bun.lockb`. To upgrade an old lockfile to the new format, run `bun install --save-text-lockfile --frozen-lockfile --lockfile-only`, then delete `bun.lockb`.
+Before Bun 1.2, the lockfile was binary and called `bun.lockb`. To upgrade an old lockfile to the new format, run `bun install --save-text-lockfile --frozen-lockfile --lockfile-only`, then delete `bun.lockb`.
## Cache
@@ -474,7 +474,7 @@ rm -rf node_modules
bun install --backend clonefile
```
-**`clonefile_each_dir`** is similar to `clonefile`, except it clones each file individually per directory. It is only available on macOS and tends to perform slower than `clonefile`. Unlike `clonefile`, this does not recursively clone subdirectories in one system call.
+**`clonefile_each_dir`** is similar to `clonefile`, except it clones each file individually per directory. It is only available on macOS and tends to perform slower than `clonefile`. Unlike `clonefile`, `clonefile_each_dir` does not recursively clone subdirectories in one system call.
```bash
rm -rf node_modules
@@ -502,13 +502,13 @@ node --preserve-symlinks ./my-file.js # https://nodejs.org/api/cli.html#--preser
## npm registry metadata
Bun uses a binary format for caching npm registry responses. This loads much faster than JSON and tends to be smaller on disk.
-These files live in `~/.bun/install/cache/*.npm`. The filename pattern is `${hash(packageName)}.npm`. It’s a hash so that extra directories don’t need to be created for scoped packages.
+These files live in `~/.bun/install/cache/*.npm`. The filename pattern is `${hash(packageName)}.npm`. It’s a hash so that Bun doesn’t need to create extra directories for scoped packages.
Bun's usage of `Cache-Control` ignores `Age`. This improves performance, but means Bun may be about 5 minutes behind the latest package version metadata from npm.
## pnpm migration
-Bun migrates projects from pnpm automatically. When a `pnpm-lock.yaml` file is detected and no `bun.lock` file exists, Bun converts the lockfile to `bun.lock` during installation. The original `pnpm-lock.yaml` file remains unmodified.
+Bun migrates projects from pnpm automatically. When Bun detects a `pnpm-lock.yaml` file and no `bun.lock` file exists, it converts the lockfile to `bun.lock` during installation. The original `pnpm-lock.yaml` file remains unmodified.
```bash terminal icon="terminal"
bun install
@@ -570,7 +570,7 @@ Bun moves the workspace packages list and catalogs to the `workspaces` field in
### Catalog Dependencies
-Dependencies using pnpm's `catalog:` protocol are preserved:
+Bun preserves dependencies that use pnpm's `catalog:` protocol:
```json package.json icon="file-json"
{
diff --git a/docs/pm/cli/link.mdx b/docs/pm/cli/link.mdx
index 26255786df3b..246b17275992 100644
--- a/docs/pm/cli/link.mdx
+++ b/docs/pm/cli/link.mdx
@@ -24,7 +24,7 @@ Or add it in dependencies in your package.json file:
"cool-pkg": "link:cool-pkg"
```
-This package can now be "linked" into other projects using `bun link cool-pkg`, which creates a symlink in the target project's `node_modules` directory pointing to the local directory.
+You can now "link" this package into other projects using `bun link cool-pkg`. This command creates a symlink in the target project's `node_modules` directory pointing to the local directory.
```bash terminal icon="terminal"
cd /path/to/my-app
diff --git a/docs/pm/cli/patch.mdx b/docs/pm/cli/patch.mdx
index faa3878e98f6..2a133f506d68 100644
--- a/docs/pm/cli/patch.mdx
+++ b/docs/pm/cli/patch.mdx
@@ -12,11 +12,11 @@ Sometimes you need a small change to a package in `node_modules/` to fix a bug o
Features:
- Generates `.patch` files that Bun applies to dependencies in `node_modules` on install
-- `.patch` files can be committed to your repository and reused across installs, projects, and machines
+- You can commit `.patch` files to your repository and reuse them across installs, projects, and machines
- `"patchedDependencies"` in `package.json` keeps track of patched packages
- Patches packages in `node_modules/` while preserving the integrity of Bun's [Global Cache](/pm/global-cache)
- Test your changes locally before committing them with `bun patch --commit `
-- To preserve disk space and keep `bun install` fast, patched packages are committed to the Global Cache and shared across projects where possible
+- To preserve disk space and keep `bun install` fast, Bun commits patched packages to the Global Cache and shares them across projects where possible
#### Step 1. Prepare the package for patching
@@ -34,7 +34,7 @@ bun patch node_modules/react
```
-Don't skip `bun patch `. It ensures the package folder in `node_modules/` contains a fresh copy of the package with no symlinks or hardlinks to Bun's cache.
+Always run `bun patch ` first. It ensures the package folder in `node_modules/` contains a fresh copy of the package with no symlinks or hardlinks to Bun's cache.
If you skip it, you might end up editing the package globally in the cache.
diff --git a/docs/pm/cli/pm.mdx b/docs/pm/cli/pm.mdx
index 1319ddd2e218..f937d84b6b07 100644
--- a/docs/pm/cli/pm.mdx
+++ b/docs/pm/cli/pm.mdx
@@ -165,7 +165,7 @@ bun list --trusted
## licenses
-List every installed package grouped by license, as read from each package's `package.json` in `node_modules`. Packages without a `license` field are listed under `Unknown`. Packages only reachable through `devDependencies` are marked `(dev)`.
+List every installed package grouped by license, as read from each package's `package.json` in `node_modules`. Bun lists packages without a `license` field under `Unknown`. Bun marks packages only reachable through `devDependencies` with `(dev)`.
```bash terminal icon="terminal"
bun pm licenses
@@ -214,9 +214,9 @@ bun pm licenses --json --prod
}
```
-From a workspace root, every workspace's dependencies are listed; from inside a workspace package, only that package's. Use [`bun why`](/pm/cli/why) to find out what pulls in an unexpected package.
+From a workspace root, Bun lists every workspace's dependencies. From inside a workspace package, Bun lists only that package's dependencies. Use [`bun why`](/pm/cli/why) to find out what pulls in an unexpected package.
-Requires both `bun.lock` and `node_modules`. Packages in the lockfile but missing from `node_modules` (e.g. after `bun install --production`) are skipped with a warning.
+Requires both `bun.lock` and `node_modules`. Bun skips packages that are in the lockfile but missing from `node_modules` (e.g. after `bun install --production`) and prints a warning.
## whoami
diff --git a/docs/pm/cli/prune.mdx b/docs/pm/cli/prune.mdx
index f3b6a5b1f176..3988e1a0b28a 100644
--- a/docs/pm/cli/prune.mdx
+++ b/docs/pm/cli/prune.mdx
@@ -3,9 +3,9 @@ title: "bun prune"
description: "Remove packages that are not in bun.lock from node_modules"
---
-`bun prune` deletes everything in `node_modules` that the current `bun.lock` would not install — packages left behind after switching branches, removing a dependency, or installing with another package manager. With the isolated linker, this includes stale entries in `node_modules/.bun`.
+`bun prune` deletes everything in `node_modules` that the current `bun.lock` would not install: packages left behind after switching branches, removing a dependency, or installing with another package manager. With the isolated linker, this includes stale entries in `node_modules/.bun`.
-It never contacts the registry, never runs lifecycle scripts, and never modifies `bun.lock` or `package.json`.
+`bun prune` never contacts the registry, never runs lifecycle scripts, and never modifies `bun.lock` or `package.json`.
```bash terminal icon="terminal"
bun prune
@@ -53,7 +53,7 @@ bun prune v1.4.0 (abc12345)
### `--filter`
-Prune only the selected workspaces' `node_modules` folders (same patterns as [`bun install --filter`](/pm/filter)). Shared locations — the root `node_modules`, or `node_modules/.bun` with the isolated linker — are cleaned too, but anything an unselected workspace still needs is kept.
+Prune only the selected workspaces' `node_modules` folders (same patterns as [`bun install --filter`](/pm/filter)). Bun also cleans shared locations: the root `node_modules`, or `node_modules/.bun` with the isolated linker. In those locations, Bun keeps anything an unselected workspace still needs.
```bash terminal icon="terminal"
bun prune --production --filter app
@@ -63,11 +63,11 @@ bun prune --production --filter app
- Always runs from the workspace root and covers every workspace's `node_modules`, even when invoked inside a workspace package.
- Requires `bun.lock` to match `package.json`. If you edited dependencies since the last install, run `bun install` first.
-- Uses the same linker as `bun install` would. If `node_modules` was created with the other linker, `bun prune` refuses to run — pass the matching `--linker`, or run `bun install`.
-- Packages are matched by name. A package at the wrong version is left for `bun install` to replace. A nested copy (`node_modules/a/node_modules/b`) is only removed once the correct version is installed above it; otherwise Bun keeps it and prints a warning.
+- Uses the same linker as `bun install` would. If `node_modules` was created with the other linker, `bun prune` refuses to run. Pass the matching `--linker`, or run `bun install`.
+- Matches packages by name. If a package is at the wrong version, Bun leaves it for `bun install` to replace. Bun only removes a nested copy (`node_modules/a/node_modules/b`) once the correct version is installed above it; otherwise Bun keeps it and prints a warning.
- Never removes workspace folders, `.bin` entries still in use, dot-directories like `.cache`, plain files, or anything outside `node_modules`.
-- Packages disabled for the current `os`/`cpu` are removed. Pass `--os`/`--cpu` to prune for another platform.
+- Removes packages disabled for the current `os`/`cpu`. Pass `--os`/`--cpu` to prune for another platform.
- Works on a pruned monorepo checkout (e.g. `turbo prune` output) the same way `bun install --frozen-lockfile` does.
-- If any entry fails to delete, the rest are still removed and the command exits `1`.
+- If any entry fails to delete, the command still removes the rest and exits `1`.
- `--global` is not supported.
- To clean the global cache instead, use [`bun pm cache rm`](/pm/cli/pm#cache).
diff --git a/docs/pm/cli/publish.mdx b/docs/pm/cli/publish.mdx
index 8318465382e1..a27e89a2cf0f 100644
--- a/docs/pm/cli/publish.mdx
+++ b/docs/pm/cli/publish.mdx
@@ -5,7 +5,7 @@ description: Use `bun publish` to publish a package to the npm registry
import Publish from "/snippets/cli/publish.mdx";
-`bun publish` packs your package into a tarball, strips catalog and workspace protocols from the `package.json` (resolving versions if necessary), and publishes to the registry specified in your configuration files. Both `bunfig.toml` and `.npmrc` files are supported.
+`bun publish` packs your package into a tarball and strips catalog and workspace protocols from the `package.json`, resolving versions if necessary. It then publishes to the registry specified in your configuration files. Both `bunfig.toml` and `.npmrc` files are supported.
```sh terminal icon="terminal"
## Publishing the package from the current working directory
@@ -41,8 +41,8 @@ bun publish ./package.tgz
```
- `bun publish` does not run lifecycle scripts (`prepublishOnly/prepack/prepare/postpack/publish/postpublish`) if a
- tarball path is provided. Scripts run only when `bun publish` packs the package itself.
+ `bun publish` does not run lifecycle scripts (`prepublishOnly/prepack/prepare/postpack/publish/postpublish`) if you
+ provide a tarball path. Scripts run only when `bun publish` packs the package itself.
### `--access`
@@ -53,7 +53,7 @@ bun publish ./package.tgz
bun publish --access public
```
-`--access` can also be set in the `publishConfig` field of your `package.json`.
+You can also set `--access` in the `publishConfig` field of your `package.json`.
```json package.json icon="file-json"
{
@@ -71,7 +71,7 @@ Set the tag of the package version being published. By default, the tag is `late
bun publish --tag alpha
```
-`--tag` can also be set in the `publishConfig` field of your `package.json`.
+You can also set `--tag` in the `publishConfig` field of your `package.json`.
```json package.json icon="file-json"
{
diff --git a/docs/pm/cli/remove.mdx b/docs/pm/cli/remove.mdx
index 411fd15d6932..427a0e4e36fc 100644
--- a/docs/pm/cli/remove.mdx
+++ b/docs/pm/cli/remove.mdx
@@ -13,7 +13,7 @@ import Remove from "/snippets/cli/remove.mdx";
bun remove ts-node
```
-The package is removed from every dependency group in `package.json` that lists it, `bun.lock` is updated, and it is deleted from `node_modules` once nothing else depends on it.
+Bun removes the package from every dependency group in `package.json` that lists it and updates `bun.lock`. Once nothing else depends on the package, Bun deletes it from `node_modules`.
## `--filter`
diff --git a/docs/pm/cli/update.mdx b/docs/pm/cli/update.mdx
index 2c210acc91c0..169b8b4e8d6d 100644
--- a/docs/pm/cli/update.mdx
+++ b/docs/pm/cli/update.mdx
@@ -7,7 +7,7 @@ import Update from "/snippets/cli/update.mdx";
To upgrade your Bun CLI version, see [`bun upgrade`](/installation#upgrading).
-`bun update` (alias `bun up`) updates every dependency — direct and transitive — to the newest version allowed by the ranges that request it, then rewrites `package.json` and `bun.lock`. To ignore your declared ranges, use [`--latest`](#--latest).
+`bun update` (alias `bun up`) updates every dependency, direct and transitive, to the newest version allowed by the ranges that request it. It then rewrites `package.json` and `bun.lock`. To ignore your declared ranges, use [`--latest`](#--latest).
```sh terminal icon="terminal"
bun update
@@ -28,16 +28,16 @@ Updated packages appear in the install summary as `↑ name old → new`, with `
### How `package.json` is rewritten
-- `^1.1.0` → `^1.2.0`, `~1.1.0` → `~1.1.5`. The operator is preserved. With [`install.exact`](/runtime/bunfig#install-exact) or `--exact`, an exact version is written instead.
+- `^1.1.0` → `^1.2.0`, `~1.1.0` → `~1.1.5`. Bun preserves the operator. With [`install.exact`](/runtime/bunfig#install-exact) or `--exact`, Bun writes an exact version instead.
- Exact pins, dist-tags (`"latest"`, `"next"`), and other range forms (`*`, `1.x`, `>=1.0.0`) are left as written; only `bun.lock` moves. `--latest` rewrites them.
-- `catalog:` references are never rewritten; the catalog entry in the root `package.json` is updated instead.
+- Bun never rewrites `catalog:` references; it updates the catalog entry in the root `package.json` instead.
- `--no-save` updates `node_modules` only, leaving `package.json` and `bun.lock` untouched.
### What is held back
-- Ranges are never widened. A package that depends on `foo@^1.0.0` never gets `foo@2.x`.
-- Versions in `patchedDependencies` stay put as long as their range allows, and are reported as `kept name@version (patched, v1.2.3 available)`. `--latest` and [`bun audit fix`](/pm/cli/audit#bun-audit-fix) do move them; re-create the patch with [`bun patch`](/pm/cli/patch) afterwards.
-- If a registry request for a transitive package fails, that package keeps its locked version and a warning is printed. A failed request for a direct dependency is an error.
+- Bun never widens ranges. A package that depends on `foo@^1.0.0` never gets `foo@2.x`.
+- Versions in `patchedDependencies` stay put as long as their range allows. Bun reports them as `kept name@version (patched, v1.2.3 available)`. `--latest` and [`bun audit fix`](/pm/cli/audit#bun-audit-fix) do move them; re-create the patch with [`bun patch`](/pm/cli/patch) afterwards.
+- If a registry request for a transitive package fails, that package keeps its locked version and Bun prints a warning. A failed request for a direct dependency is an error.
## `--interactive`
@@ -48,7 +48,7 @@ bun update --interactive
bun update -i
```
-This opens a terminal interface listing every outdated direct dependency. The packages you select are updated as if you had run `bun update ...`; everything else keeps its locked version.
+`--interactive` opens a terminal interface listing every outdated direct dependency. Bun updates the packages you select as if you had run `bun update ...`; everything else keeps its locked version.
### Interactive Interface
@@ -121,10 +121,10 @@ Within each section, individual packages may have a suffix (` dev`, ` peer`, ` o
## `--recursive` and `--filter`
-In a monorepo, `bun update` only rewrites the `package.json` of the workspace you run it in (from the root, transitive dependencies of every workspace are still updated in `bun.lock`).
+In a monorepo, `bun update` only rewrites the `package.json` of the workspace you run it in. From the root, it still updates the transitive dependencies of every workspace in `bun.lock`.
- `--recursive` (`-r`) updates every workspace's `package.json`.
-- `--filter ` (`-F`) updates only the matching workspaces, using the [filter syntax](/pm/filter). Like `bun install --filter`, only the selected workspaces are linked afterwards.
+- `--filter ` (`-F`) updates only the matching workspaces, using the [filter syntax](/pm/filter). As with `bun install --filter`, Bun links only the selected workspaces afterwards.
Both combine with package names, `--latest`, `--dry-run`, and `--interactive` (which adds a "Workspace" column).
@@ -138,7 +138,13 @@ bun update zod --filter '...^ui'
## `--dev`, `--prod`, `--no-optional`
-Restrict which `package.json` entries are updated: `--dev` (`-D`) for `devDependencies` only, `--prod` (`-P`) for `dependencies` and `optionalDependencies` only, `--no-optional` to skip `optionalDependencies`. They combine with names, patterns, `--latest`, and `--interactive`.
+Restrict which `package.json` entries Bun updates:
+
+- `--dev` (`-D`) updates `devDependencies` only.
+- `--prod` (`-P`) updates `dependencies` and `optionalDependencies` only.
+- `--no-optional` skips `optionalDependencies`.
+
+They combine with names, patterns, `--latest`, and `--interactive`.
These flags only select what to update — `bun update --prod` still installs `devDependencies`.
@@ -162,7 +168,7 @@ bun update -g typescript
By default, `bun update` updates each dependency to the latest version that satisfies the version range in your `package.json`.
-To update direct dependencies to the latest version regardless of the declared range, use `--latest` (`-L`). The `package.json` entry is rewritten to a range of the same style on the new version. Transitive dependencies still respect the ranges their dependents declare, and a dependency already ahead of `latest` (e.g. a prerelease) is not downgraded.
+To update direct dependencies to the latest version regardless of the declared range, use `--latest` (`-L`). Bun rewrites the `package.json` entry to a range of the same style on the new version. Transitive dependencies still respect the ranges their dependents declare. Bun does not downgrade a dependency that is already ahead of `latest` (e.g. a prerelease).
```sh terminal icon="terminal"
bun update --latest
diff --git a/docs/pm/filter.mdx b/docs/pm/filter.mdx
index 6fe1b2d25b55..ec8a989f959a 100644
--- a/docs/pm/filter.mdx
+++ b/docs/pm/filter.mdx
@@ -5,7 +5,7 @@ description: "Select packages by pattern in a monorepo using the --filter flag"
The `--filter` (or `-F`) flag selects packages in a monorepo by pattern. A pattern is a package name glob, a `./path` glob, a `{dir}` directory selector, or a `...` dependency relation.
-It is supported by `bun run`, `bun install`, `bun add`, `bun remove`, `bun update`, `bun outdated`, `bun prune`, and `bun pm licenses`. For package-manager commands, put the flag after the subcommand (`bun install --filter api`), since `bun --filter ` runs `` as a script.
+The flag works with `bun run`, `bun install`, `bun add`, `bun remove`, `bun update`, `bun outdated`, `bun prune`, and `bun pm licenses`. For package-manager commands, put the flag after the subcommand (`bun install --filter api`), since `bun --filter ` runs `` as a script.
---
@@ -124,7 +124,7 @@ bun --filter '{./packages/apps}' dev
bun --filter '*' --filter '!docs' lint
```
-A `package.json` without a `name` can only be selected by a `./path` pattern. If no selected package has the script, `bun run` exits with an error (pass `--if-present` to exit 0 instead).
+Only a `./path` pattern can select a `package.json` without a `name`. If no selected package has the script, `bun run` exits with an error (pass `--if-present` to exit 0 instead).
### Running scripts in workspaces
@@ -161,7 +161,7 @@ bun run --parallel --no-exit-on-error --filter '*' test
bun run --parallel --filter '*' build lint
```
-Each line of output is prefixed with the package and script name (`pkg-a:build | ...`). Without `--filter`/`--workspaces`, the prefix is just the script name (`build | ...`). When a package's `package.json` has no `name` field, Bun uses the relative path from the workspace root instead.
+Bun prefixes each line of output with the package and script name (`pkg-a:build | ...`). Without `--filter`/`--workspaces`, the prefix is only the script name (`build | ...`). When a package's `package.json` has no `name` field, Bun uses the relative path from the workspace root instead.
Use `--if-present` with `--workspaces` to skip packages that don't have the requested script instead of erroring.
diff --git a/docs/pm/global-cache.mdx b/docs/pm/global-cache.mdx
index d3c4a88c86f1..601faabb6b34 100644
--- a/docs/pm/global-cache.mdx
+++ b/docs/pm/global-cache.mdx
@@ -3,7 +3,7 @@ title: "Global cache"
description: "How Bun stores and manages packages in its global cache"
---
-Bun stores every package downloaded from the registry in a global cache at `~/.bun/install/cache`, or the path set by the `BUN_INSTALL_CACHE_DIR` environment variable. Packages live in subdirectories named like `${name}@${version}`, so multiple versions of a package can be cached.
+Bun stores every package downloaded from the registry in a global cache at `~/.bun/install/cache`, or the path set by the `BUN_INSTALL_CACHE_DIR` environment variable. Packages live in subdirectories named like `${name}@${version}`, so Bun can cache multiple versions of a package.
@@ -29,11 +29,11 @@ disableManifest = false
When installing a package, if the cache already contains a version in the range specified by `package.json`, Bun uses the cached copy instead of downloading it again.
-If the semver version has a pre-release suffix (`1.0.0-beta.0`) or a build suffix (`1.0.0+20220101`), it is replaced with a hash of that value instead, to reduce the chance of errors from long file paths.
+If the semver version has a pre-release suffix (`1.0.0-beta.0`) or a build suffix (`1.0.0+20220101`), Bun replaces the suffix with a hash of that value instead, to reduce the chance of errors from long file paths.
When the `node_modules` folder exists, before installing, Bun checks that `node_modules` contains all expected packages with appropriate versions. If so, `bun install` completes. Bun uses a custom JSON parser which stops parsing as soon as it finds `"name"` and `"version"`.
-If a package is missing or has a version incompatible with the `package.json`, Bun checks for a compatible module in the cache. If found, it is installed into `node_modules`. Otherwise, Bun downloads the package from the registry, then installs it.
+If a package is missing or has a version incompatible with the `package.json`, Bun checks for a compatible module in the cache. If the cache has one, Bun installs it into `node_modules`. Otherwise, Bun downloads the package from the registry, then installs it.
@@ -47,7 +47,7 @@ Once a package is downloaded into the cache, Bun still needs to copy those files
## Saving disk space
-Since Bun uses hardlinks to "copy" a module into a project's `node_modules` directory on Linux and Windows, the contents of the package only exist in a single location on disk, greatly reducing the disk space used by `node_modules`.
+On Linux and Windows, Bun uses hardlinks to "copy" a module into a project's `node_modules` directory, so the contents of the package only exist in a single location on disk. This greatly reduces the disk space used by `node_modules`.
The same applies on macOS, with a caveat. There Bun uses `clonefile`, which is copy-on-write: the clone occupies no extra disk space, but it counts towards the drive's limit. Because the copy only happens on write, patching `node_modules/*` in one project can't affect other installations.
@@ -60,7 +60,7 @@ Configure this with the `--backend` flag, which all of Bun's package management
- **`copyfile`**: The fallback used when any of the above fail. It is the slowest option. On macOS, it uses `fcopyfile()`; on Linux it uses `copy_file_range()`.
- **`symlink`**: Used only for `file:` (and eventually `link:`) dependencies. To prevent infinite loops, it skips symlinking the `node_modules` folder.
-If you install with `--backend=symlink`, Node.js won't resolve node_modules of dependencies unless each dependency has its own `node_modules` folder or you pass `--preserve-symlinks` to `node`. See [Node.js documentation on `--preserve-symlinks`](https://nodejs.org/api/cli.html#--preserve-symlinks).
+If you install with `--backend=symlink`, Node.js doesn't resolve node_modules of dependencies unless each dependency has its own `node_modules` folder or you pass `--preserve-symlinks` to `node`. See [Node.js documentation on `--preserve-symlinks`](https://nodejs.org/api/cli.html#--preserve-symlinks).
```bash terminal icon="terminal"
bun install --backend symlink
diff --git a/docs/pm/global-store.mdx b/docs/pm/global-store.mdx
index a5dbd188fe51..f6a7c9eedc09 100644
--- a/docs/pm/global-store.mdx
+++ b/docs/pm/global-store.mdx
@@ -11,7 +11,7 @@ The result: warm installs are roughly **7× faster** (one symlink per package in
## Enabling
-The global virtual store is **off by default**. It only applies to the [isolated linker](/pm/isolated-installs); it is not used by the hoisted linker.
+The global virtual store is **off by default**. It only applies to the [isolated linker](/pm/isolated-installs); the hoisted linker does not use it.
To enable it for a project:
@@ -40,13 +40,13 @@ Sort by top of stack (bun install --linker isolated, warm cache):
__read_nocancel 10
```
-`clonefileat` on APFS holds a volume-wide kernel lock, so spreading the work across more threads barely helps — eight threads only improved a 2,830-directory clone from 959 ms to 743 ms. The fix is to not call it at all on the warm path.
+`clonefileat` on APFS holds a volume-wide kernel lock, so spreading the work across more threads barely helps. Eight threads only improved a 2,830-directory clone from 959 ms to 743 ms. The fix is to not call it at all on the warm path.
With the global store, the warm path is one `access()` (does the global entry exist?) plus one `symlink()` (point the project at it) per package.
## Benchmarks
-Warm CI install — lockfile present, package cache warm, `node_modules` deleted between runs — on a 1,400-package React/webpack/Babel/jest fixture, Apple Silicon macOS, `hyperfine --warmup 3 --runs 10`:
+Warm CI install on a 1,400-package React/webpack/Babel/jest fixture, Apple Silicon macOS, `hyperfine --warmup 3 --runs 10`. The lockfile is present, the package cache is warm, and `node_modules` is deleted between runs:
| | wall time | system time | `clonefileat` | total syscalls |
| ---------------------------------------- | ------------ | ----------- | ------------- | -------------- |
@@ -104,7 +104,7 @@ project/node_modules/
└── react -> .bun/react@18.3.1/node_modules/react
```
-The 16-hex `entry_hash` suffix encodes the entry's **resolved dependency closure**: the package's own store path and tarball integrity, plus the hash of every dependency it links to. Two projects that resolve `react@18.3.1` to the same set of transitive versions share one global directory; a project that resolves a transitive dependency to a different version gets a separate global entry whose dep symlinks point at the right siblings. Packages that participate in a dependency cycle share one hash computed over the whole strongly-connected component, so the key is independent of which member a given project's dependency graph happened to reach first.
+The 16-hex `entry_hash` suffix encodes the entry's **resolved dependency closure**: the package's own store path and tarball integrity, plus the hash of every dependency it links to. Two projects that resolve `react@18.3.1` to the same set of transitive versions share one global directory. A project that resolves a transitive dependency to a different version gets a separate global entry whose dep symlinks point at the right siblings. Packages that participate in a dependency cycle share one hash computed over the whole strongly-connected component. The key is therefore independent of which member a given project's dependency graph happened to reach first.
## What stays project-local
@@ -114,11 +114,11 @@ An entry only lives in the global store when it can be safely shared. Entries fa
- the package is listed in **`trustedDependencies`** (or trusted via `bun add --trust`) — its lifecycle script may mutate the install directory, and a script running through the project symlink would mutate the shared copy;
- the package, or **any** dependency it links to, is a `workspace:`, `file:`, or `link:` dependency — those resolve to project-local paths that other projects can't see.
-Ineligibility propagates: if `your-app` depends on `internal-utils` which is a workspace package, `internal-utils` is project-local, and so is every entry that links to it. An entry that loses eligibility between installs (newly patched, newly trusted) is detached from the global store and rebuilt project-locally on the next install; the shared entry is left untouched.
+Ineligibility propagates: if `your-app` depends on `internal-utils` which is a workspace package, `internal-utils` is project-local, and so is every entry that links to it. When an entry loses eligibility between installs (newly patched, newly trusted), Bun detaches it from the global store and rebuilds it project-locally on the next install. The shared entry stays untouched.
## Peer dependencies
-Resolved peer dependencies — required and optional — are folded into each global entry as dep symlinks and contribute to its hash. Bun synthesizes an implicit `"*"` optional peer for packages that list a name only in `peerDependenciesMeta` without a matching `peerDependencies` entry (matching pnpm and yarn), so a package like `webpack` that declares `webpack-cli` only in `peerDependenciesMeta` still gets a `webpack-cli` symlink in its global entry when one is installed in the project.
+Bun folds resolved peer dependencies (required and optional) into each global entry as dep symlinks. These peer dependencies contribute to the entry's hash. Bun synthesizes an implicit `"*"` optional peer for packages that list a name only in `peerDependenciesMeta` without a matching `peerDependencies` entry (matching pnpm and yarn). As a result, a package like `webpack` that declares `webpack-cli` only in `peerDependenciesMeta` still gets a `webpack-cli` symlink in its global entry when one is installed in the project.
## Tradeoffs
@@ -136,11 +136,11 @@ Tools that scan `node_modules` without following symlinks, or that compare file
### Disk usage
-Each unique `(package, version, resolved-dependency-set)` triple gets one directory in `/links/`. Across many projects that's a large net win — one copy on disk instead of one per checkout — but the store does grow over time as new versions and new peer-dependency combinations land. Run `bun pm cache rm` to clear the cache including the global store; the next install repopulates only what that project needs.
+Each unique `(package, version, resolved-dependency-set)` triple gets one directory in `/links/`. Across many projects that's a large net win: one copy on disk instead of one per checkout. But the store does grow over time as new versions and new peer-dependency combinations land. Run `bun pm cache rm` to clear the cache including the global store; the next install repopulates only what that project needs.
### Concurrency
-Multiple `bun install` processes (parallel CI jobs, concurrent workspace builds) may race to populate the same global entry. Each process builds the entire entry — package files, dependency symlinks, bin links — under a private `.tmp-/` staging directory and renames it into place as the final step. The loser of the rename sees `EEXIST` and discards its identical staging tree; a writer that crashes mid-build leaves only an unreferenced staging directory that the next install ignores. A published entry is therefore always complete; there is no separate completeness sentinel.
+Multiple `bun install` processes (parallel CI jobs, concurrent workspace builds) may race to populate the same global entry. Each process builds the entire entry (package files, dependency symlinks, bin links) under a private `.tmp-/` staging directory. As the final step, it renames that directory into place. The loser of the rename sees `EEXIST` and discards its identical staging tree. A writer that crashes mid-build leaves only an unreferenced staging directory that the next install ignores. A published entry is therefore always complete; there is no separate completeness sentinel.
## Related documentation
diff --git a/docs/pm/isolated-installs.mdx b/docs/pm/isolated-installs.mdx
index 0a148a16fb52..00359a99ac29 100644
--- a/docs/pm/isolated-installs.mdx
+++ b/docs/pm/isolated-installs.mdx
@@ -87,15 +87,15 @@ node_modules/
### Resolution algorithm
-1. **Central store** — All packages are installed in `node_modules/.bun/package@version/` directories
+1. **Central store** — Bun installs all packages in `node_modules/.bun/package@version/` directories
2. **Symlinks** — Top-level `node_modules` contains symlinks pointing to the central store
3. **Peer resolution** — Complex peer dependencies create specialized directory names
4. **Deduplication** — Packages with identical package IDs and peer dependency sets are shared
-5. **Re-linking** — On later installs, existing store entries are reused and their symlinks are re-pointed if a dependency was re-resolved. Store entries that are no longer referenced stay until you run [`bun prune`](/pm/cli/prune)
+5. **Re-linking** — On later installs, Bun reuses existing store entries and re-points their symlinks if a dependency was re-resolved. Store entries that are no longer referenced stay until you run [`bun prune`](/pm/cli/prune)
### Workspace handling
-In monorepos, workspace dependencies are handled specially:
+In monorepos, Bun handles workspace dependencies specially:
- **Workspace packages** — Symlinked directly to their source directories, not the store
- **Workspace dependencies** — Can access other workspace packages in the monorepo
@@ -127,7 +127,7 @@ The directory name includes both the package version and its peer dependency ver
### Strict resolution with `install.hoist = false`
-By default, Bun creates `node_modules/.bun/node_modules`, a fallback directory with a symlink to every installed package. Because it is an ancestor of every store entry, a package in the store can resolve dependencies it never declared. Set [`install.hoist = false`](/runtime/bunfig#install-hoist) (or `hoist=false` in `.npmrc`, matching pnpm) to skip creating this directory, so undeclared imports fail instead of depending on what else happens to be installed. One caveat, shared with pnpm: the root `node_modules` also sits above the store, so packages linked there (your direct dependencies, `publicHoistPattern` matches, and workspace packages) stay resolvable from any store package:
+By default, Bun creates `node_modules/.bun/node_modules`, a fallback directory with a symlink to every installed package. Because this directory is an ancestor of every store entry, a package in the store can resolve dependencies it never declared. Set [`install.hoist = false`](/runtime/bunfig#install-hoist) (or `hoist=false` in `.npmrc`, matching pnpm) to skip creating this directory. Undeclared imports then fail instead of depending on what else happens to be installed. One caveat, shared with pnpm: the root `node_modules` also sits above the store. As a result, packages linked there (your direct dependencies, `publicHoistPattern` matches, and workspace packages) stay resolvable from any store package:
```toml bunfig.toml icon="settings"
[install]
@@ -139,7 +139,7 @@ hoist = false
### Global virtual store
-When [`install.globalStore`](/runtime/bunfig#install-globalstore) is enabled, store entries are materialized once into a [global virtual store](/pm/global-store) at `/links/` and `node_modules/.bun/@` is a symlink into it. Warm installs after `rm -rf node_modules` only create one symlink per package instead of copying every package's files again, which is roughly **7× faster** on a typical mid-size project. The global store is **off by default**; see the [global store docs](/pm/global-store) for how to enable it, the full layout, benchmarks, and tradeoffs.
+When [`install.globalStore`](/runtime/bunfig#install-globalstore) is enabled, Bun materializes store entries once into a [global virtual store](/pm/global-store) at `/links/`, and `node_modules/.bun/@` is a symlink into it. Warm installs after `rm -rf node_modules` only create one symlink per package instead of copying every package's files again, which is roughly **7× faster** on a typical mid-size project. The global store is **off by default**; see the [global store docs](/pm/global-store) for how to enable it, the full layout, benchmarks, and tradeoffs.
### Backend strategies
@@ -230,7 +230,7 @@ The main difference is that Bun uses symlinks in `node_modules` while pnpm uses
- Working with legacy code that assumes flat `node_modules`
- Compatibility with existing build tools is required
- Working in environments where symlinks aren't well supported
-- You prefer the simpler traditional npm behavior
+- You prefer the traditional npm behavior
## Related documentation
diff --git a/docs/pm/lifecycle.mdx b/docs/pm/lifecycle.mdx
index d3364f430d6f..5c35c3224bd4 100644
--- a/docs/pm/lifecycle.mdx
+++ b/docs/pm/lifecycle.mdx
@@ -63,7 +63,7 @@ Defining `trustedDependencies` in `package.json` **replaces** the default list r
| `trustedDependencies: ["pkg-a", ...]` | **Only** the listed packages. The default list is ignored. |
| `trustedDependencies: []` | **No** packages, including none from the default list. |
-Set `trustedDependencies: []` when you want to opt out of the default allow list entirely without passing `--ignore-scripts` on every install. If you define `trustedDependencies` with an explicit list, include any packages from the [default list](https://github.com/oven-sh/bun/blob/main/src/install/default-trusted-dependencies.txt) whose lifecycle scripts you still need (for example, `sharp` or `esbuild`) — they are no longer trusted implicitly.
+Set `trustedDependencies: []` when you want to opt out of the default allow list entirely without passing `--ignore-scripts` on every install. If you define `trustedDependencies` with an explicit list, include any packages from the [default list](https://github.com/oven-sh/bun/blob/main/src/install/default-trusted-dependencies.txt) whose lifecycle scripts you still need (for example, `sharp` or `esbuild`). Bun no longer trusts those packages implicitly.
---
diff --git a/docs/pm/lockfile.mdx b/docs/pm/lockfile.mdx
index 94d5299f8f68..5bec794a3ad5 100644
--- a/docs/pm/lockfile.mdx
+++ b/docs/pm/lockfile.mdx
@@ -11,7 +11,7 @@ Yes
#### Generate a lockfile without installing?
-To generate a lockfile without installing to `node_modules`, use the `--lockfile-only` flag. The lockfile is always saved to disk, even if it is already up to date with your project's `package.json`(s), unless `--frozen-lockfile` (or `--production`) is set.
+To generate a lockfile without installing to `node_modules`, use the `--lockfile-only` flag. Bun always saves the lockfile to disk, even if it is already up to date with your project's `package.json`(s). The exception is when `--frozen-lockfile` (or `--production`) is set.
```bash terminal icon="terminal"
bun install --lockfile-only
@@ -56,12 +56,12 @@ For more on the format, see [the blog post](https://bun.com/blog/bun-lock-text-l
#### Automatic lockfile migration
-When running `bun install` in a project without a `bun.lock`, Bun automatically migrates existing lockfiles:
+When you run `bun install` in a project without a `bun.lock`, Bun automatically migrates existing lockfiles:
- `yarn.lock` (v1)
- `package-lock.json` (npm, `lockfileVersion` 2, 3 or 4)
- `pnpm-lock.yaml` (pnpm)
-A `package-lock.json` from npm 6 or older (`lockfileVersion` 1) is not migrated; Bun prints a warning and resolves from `package.json` instead.
+Bun does not migrate a `package-lock.json` from npm 6 or older (`lockfileVersion` 1); it prints a warning and resolves from `package.json` instead.
-The original lockfile is preserved and can be removed manually after verification.
+Bun preserves the original lockfile. You can remove it manually after verification.
diff --git a/docs/pm/npmrc.mdx b/docs/pm/npmrc.mdx
index 8985f582bda0..7514f0e18572 100644
--- a/docs/pm/npmrc.mdx
+++ b/docs/pm/npmrc.mdx
@@ -13,9 +13,9 @@ Configuration is loaded in this order, with later sources overriding earlier one
4. `BUN_CONFIG_REGISTRY` / `NPM_CONFIG_REGISTRY` and `BUN_CONFIG_TOKEN` / `NPM_CONFIG_TOKEN` environment variables
5. Command-line flags such as `--registry`
-Credentials in `.npmrc` (`///:_authToken`, etc.) are matched to registries by host and path, even if the registry URL itself was set in `bunfig.toml`.
+Bun matches credentials in `.npmrc` (`///:_authToken`, etc.) to registries by host and path, even if you set the registry URL itself in `bunfig.toml`.
-Values may reference environment variables: `${NAME}` is replaced with the variable's value (left as-is if unset), and `${NAME?}` becomes an empty string if unset.
+Values may reference environment variables. Bun replaces `${NAME}` with the variable's value, or leaves it as-is if the variable is unset. `${NAME?}` becomes an empty string if unset.
We recommend migrating your `.npmrc` file to Bun's [`bunfig.toml`](/runtime/bunfig) format, which supports more
@@ -78,7 +78,7 @@ myorg = "http://localhost:4873/"
//http://localhost:4873/:_auth=${NPM_AUTH}
```
-The following options are supported:
+Bun supports the following options:
- `_authToken`
- `username`
@@ -95,7 +95,7 @@ myorg = { url = "http://localhost:4873/", username = "myusername", password = "$
### `link-workspace-packages`: Control workspace package installation
-Controls how workspace packages are installed when available locally:
+Controls how Bun installs workspace packages when they are available locally:
```ini .npmrc icon="npm"
link-workspace-packages=true
@@ -135,7 +135,7 @@ This is equivalent to using the `--ignore-scripts` flag with `bun install`.
### `dry-run`: Preview changes without installing
-Shows what would be installed without installing anything:
+Shows what Bun would install without installing anything:
```ini .npmrc icon="npm"
dry-run=true
@@ -189,7 +189,7 @@ cafile=/path/to/ca-bundle.crt
### `omit` and `include`: Control dependency types
-Control which dependency types are installed:
+Control which dependency types Bun installs:
```ini .npmrc icon="npm"
# omit dev dependencies
@@ -207,7 +207,7 @@ Valid values: `dev`, `peer`, `optional`
### `install-strategy` and `node-linker`: Installation strategy
-Control how packages are laid out in `node_modules`. For compatibility with other package managers, Bun accepts both npm's `install-strategy` and pnpm/yarn's `node-linker`. See [isolated installs](/pm/isolated-installs) for how the hoisted and isolated layouts differ.
+Control how Bun lays out packages in `node_modules`. For compatibility with other package managers, Bun accepts both npm's `install-strategy` and pnpm/yarn's `node-linker`. See [isolated installs](/pm/isolated-installs) for how the hoisted and isolated layouts differ.
**npm's `install-strategy`:**
@@ -242,7 +242,7 @@ node-linker=node-modules
### `public-hoist-pattern` and `hoist-pattern`: Control hoisting
-Control which packages are hoisted to the root `node_modules`:
+Control which packages Bun hoists to the root `node_modules`:
```ini .npmrc icon="npm"
# packages matching this pattern will be hoisted to the root
diff --git a/docs/pm/overrides.mdx b/docs/pm/overrides.mdx
index ca5d277d9451..b9a459b5e58c 100644
--- a/docs/pm/overrides.mdx
+++ b/docs/pm/overrides.mdx
@@ -58,11 +58,11 @@ Add `bar` to the `"overrides"` field in `package.json`. Bun defers to the specif
}
```
-Overrides are only read from the root `package.json`, not from workspace packages. They apply to `peerDependencies` as well.
+Bun only reads overrides from the root `package.json`, not from workspace packages. Overrides apply to `peerDependencies` as well.
## `"resolutions"`
-`"resolutions"` is Yarn's alternative to `"overrides"`, with similar syntax. Bun supports it to make migration from Yarn easier.
+`"resolutions"` is Yarn's alternative to `"overrides"`, with similar syntax. Bun supports it to help projects migrate from Yarn.
{/* prettier-ignore */}
```json package.json icon="file-json"
@@ -95,7 +95,7 @@ A value of `"$name"` reuses the range you declared for `name` in your own depend
## Nested overrides
-A rule can be scoped to one parent package, so it only applies to that package's direct dependency. The npm object form, the pnpm `>` form, and a parent with a version range are all accepted:
+You can scope a rule to one parent package, so it only applies to that package's direct dependency. Bun accepts the npm object form, the pnpm `>` form, and a parent with a version range:
```json package.json icon="file-json"
{
@@ -113,7 +113,7 @@ A rule can be scoped to one parent package, so it only applies to that package's
`"."` inside an object overrides `micromatch` itself, like a top-level `"micromatch"` rule.
-`"resolutions"` accepts Yarn's path form. `**` is accepted for compatibility, but only the parent's direct dependency is affected either way:
+`"resolutions"` accepts Yarn's path form. Bun accepts `**` for compatibility, but either way the rule only affects the parent's direct dependency:
```json package.json icon="file-json"
{
@@ -144,10 +144,10 @@ The key can carry a version selector so the rule only applies to dependents whos
}
```
-The selector is compared with the range each dependent _declares_, not the resolved version. `"semver@<7.5.2"` applies to a dependent that declares `^7.3.0` (which could still pick `7.3.x`) but not to one that declares `^7.5.2`. Dependents using a dist-tag, `catalog:`, `workspace:`, git, or URL specifier never match a selector.
+Bun compares the selector with the range each dependent _declares_, not the resolved version. `"semver@<7.5.2"` applies to a dependent that declares `^7.3.0` (which could still pick `7.3.x`) but not to one that declares `^7.5.2`. Dependents using a dist-tag, `catalog:`, `workspace:`, git, or URL specifier never match a selector.
## Limitations
-- Only one parent level is supported. `a>b>c`, `a/b/c`, and deeper object nesting are ignored with a warning.
-- pnpm's `"pkg@"` (empty selector) and `"-"` (remove dependency) forms are not supported and are skipped with a warning.
-- A lockfile containing nested or version-scoped rules is written as `lockfileVersion` 3, which older versions of Bun cannot read.
+- Bun supports only one parent level. It ignores `a>b>c`, `a/b/c`, and deeper object nesting with a warning.
+- Bun does not support pnpm's `"pkg@"` (empty selector) and `"-"` (remove dependency) forms, and skips them with a warning.
+- Bun writes a lockfile containing nested or version-scoped rules as `lockfileVersion` 3, which older versions of Bun cannot read.
diff --git a/docs/pm/security-scanner-api.mdx b/docs/pm/security-scanner-api.mdx
index 514d7d8908a5..dbfccfc47462 100644
--- a/docs/pm/security-scanner-api.mdx
+++ b/docs/pm/security-scanner-api.mdx
@@ -20,7 +20,7 @@ With a scanner configured, Bun:
- Scans all packages before installation
- Displays security warnings and advisories
-- Cancels installation if fatal advisories are found
+- Cancels installation if the scanner finds fatal advisories
---
@@ -55,8 +55,8 @@ bun add -d @oven/bun-security-scanner
`@oven/bun-security-scanner` is an example package name, not a real package. Replace it with the scanner you want to
- use, and consult that scanner's documentation for the exact package name and installation instructions. Most scanners
- are installed with `bun add`.
+ use, and consult that scanner's documentation for the exact package name and installation instructions. You install
+ most scanners with `bun add`.
### Configuring the Scanner
diff --git a/docs/pm/workspaces.mdx b/docs/pm/workspaces.mdx
index 7b52fbb6cda1..7673b6111780 100644
--- a/docs/pm/workspaces.mdx
+++ b/docs/pm/workspaces.mdx
@@ -92,8 +92,8 @@ A specific version takes precedence over the package's `package.json` version:
Workspaces have a few major benefits.
-- **Code can be split into logical parts.** If one package relies on another, add it as a dependency in `package.json`. If package `b` depends on `a`, `bun install` installs your local `packages/a` directory into `node_modules` instead of downloading it from the npm registry.
-- **Dependencies can be de-duplicated.** If `a` and `b` share a common dependency, it is _hoisted_ to the root `node_modules` directory. This saves disk space and minimizes the "dependency hell" of multiple versions of a package installed at once.
+- **Split code into logical parts.** If one package relies on another, add it as a dependency in `package.json`. If package `b` depends on `a`, `bun install` installs your local `packages/a` directory into `node_modules` instead of downloading it from the npm registry.
+- **Bun can de-duplicate dependencies.** If `a` and `b` share a common dependency, Bun _hoists_ it to the root `node_modules` directory. This saves disk space and minimizes the "dependency hell" of multiple versions of a package installed at once.
- **Run scripts in multiple packages.** Use the [`--filter` flag](/pm/filter) to run `package.json` scripts in several packages at once, or `--workspaces` to run scripts across all workspaces.
## Share versions with Catalogs
diff --git a/docs/project/benchmarking.mdx b/docs/project/benchmarking.mdx
index 1863bdc95c62..fe85fcdbe144 100644
--- a/docs/project/benchmarking.mdx
+++ b/docs/project/benchmarking.mdx
@@ -3,7 +3,7 @@ title: Benchmarking
description: How to benchmark Bun
---
-Bun is designed for speed. Hot paths are extensively profiled and benchmarked. The source code for all of Bun's public benchmarks is in the [`/bench`](https://github.com/oven-sh/bun/tree/main/bench) directory of the Bun repo.
+Bun is designed for speed. We profile and benchmark hot paths extensively. The source code for all of Bun's public benchmarks is in the [`/bench`](https://github.com/oven-sh/bun/tree/main/bench) directory of the Bun repo.
## Measuring time
@@ -269,11 +269,11 @@ bun --heap-prof script.js
`--heap-prof` writes a full V8-format heap snapshot on exit, using Node.js's
diagnostic filename format
-(`Heap......heapprofile`). The content is the
-same as `v8.writeHeapSnapshot()` / `Bun.generateHeapSnapshot("v8")`: load it in
-Chrome DevTools via Memory tab → Load (pick "All Files" or rename to
-`.heapsnapshot` — the extension follows Node's `--heap-prof` filename contract,
-which the harness and tooling key on).
+(`Heap......heapprofile`). The extension
+follows Node's `--heap-prof` filename contract, which the harness and tooling
+key on. The content is the same as `v8.writeHeapSnapshot()` /
+`Bun.generateHeapSnapshot("v8")`. Load it in Chrome DevTools via
+Memory tab → Load. Pick "All Files", or rename the file to `.heapsnapshot`.
### Markdown output
@@ -283,7 +283,7 @@ Use `--heap-prof-md` to generate a markdown heap profile for CLI analysis:
bun --heap-prof-md script.js
```
-If both `--heap-prof` and `--heap-prof-md` are specified, the markdown format is used.
+If you specify both `--heap-prof` and `--heap-prof-md`, Bun uses the markdown format.
### Options
diff --git a/docs/project/bindgen.mdx b/docs/project/bindgen.mdx
index d800a0c85e02..f847b0efc4e0 100644
--- a/docs/project/bindgen.mdx
+++ b/docs/project/bindgen.mdx
@@ -78,7 +78,7 @@ In JS files in `src/js/`, `$bindgenFn("bindgen_test.bind.ts", "add")` returns
a handle to the implementation.
Exported bindgen functions are snake_cased on the Rust side
-(`requiredAndOptionalArg` → `required_and_optional_arg`), and the generated
+(`requiredAndOptionalArg` → `required_and_optional_arg`). The generated
callback constructor follows the same convention
(`create_required_and_optional_arg_callback`).
@@ -190,7 +190,7 @@ A `oneOf` is a union of two or more types. It is represented as a Rust
## Attributes
-Attributes can be chained onto `t.*` types. On all types:
+You can chain attributes onto `t.*` types. On all types:
- `.required`, in dictionary parameters only
- `.optional`, in function arguments only
@@ -217,8 +217,8 @@ pub fn required_and_optional_arg(a: bool, b: Option, c: i32, d: Option` or pass `--winsysroot=` (a user-writable path also lets configure manage the aliases for you). Configure validates the splat at the start of every cross build. CI agents bake the same splat into their images (`.buildkite/Dockerfile`, `scripts/bootstrap.sh`); when an agent doesn't have one, the build fetches it into its cache dir at configure time.
+The build looks for the sysroot at `/opt/winsysroot` (or `/opt/xwin`) automatically. If the sysroot is elsewhere, set `WINDOWS_SYSROOT=` or pass `--winsysroot=`. A user-writable path also lets configure manage the aliases for you. Configure validates the splat at the start of every cross build. CI agents bake the same splat into their images (`.buildkite/Dockerfile`, `scripts/bootstrap.sh`); when an agent doesn't have one, the build fetches it into its cache dir at configure time.
### Building
@@ -175,7 +175,7 @@ bun run build --profile=windows-arm64-release
Output lands in `build/debug-windows-x64/bun-debug.exe`, `build/release-windows-aarch64/bun-profile.exe` + `bun.exe`, and so on. Equivalent raw flags: `bun run build --os=windows --arch=aarch64`.
-Cross-compiled executables are not run on the host (the `--revision` smoke test is skipped), so test them on a Windows machine or under Wine.
+The build does not run cross-compiled executables on the host (it skips the `--revision` smoke test), so test them on a Windows machine or under Wine.
### LTO
@@ -185,4 +185,11 @@ x64 release cross builds support ThinLTO with cross-language (Rust↔C++) LTO. I
bun run build --profile=windows-x64-release --lto=on
```
-`--lto=on` compiles Bun's C/C++ with `-flto=thin`, makes rustc emit LLVM bitcode (`-Clinker-plugin-lto`), pulls the `bun-webkit-windows-amd64-lto` ThinLTO prebuilt, and links everything with rustc's bundled `lld-link` (its LLVM is new enough to read both compilers' bitcode). There is no LTO for arm64 (no `-lto` WebKit prebuilt: LLVM's CodeView emitter can't handle ARM64 NEON tuple registers during LTO codegen) or for `--baseline`.
+`--lto=on` does the following:
+
+- compiles Bun's C/C++ with `-flto=thin`
+- makes rustc emit LLVM bitcode (`-Clinker-plugin-lto`)
+- pulls the `bun-webkit-windows-amd64-lto` ThinLTO prebuilt
+- links everything with rustc's bundled `lld-link` (its LLVM is new enough to read both compilers' bitcode)
+
+There is no LTO for arm64, because there is no `-lto` WebKit prebuilt: LLVM's CodeView emitter can't handle ARM64 NEON tuple registers during LTO codegen. There is also no LTO for `--baseline`.
diff --git a/docs/project/license.mdx b/docs/project/license.mdx
index f08f3ceedc5a..dea093c2f526 100644
--- a/docs/project/license.mdx
+++ b/docs/project/license.mdx
@@ -37,7 +37,7 @@ Bun statically links these libraries:
| [`uSockets`](https://github.com/uNetworking/uSockets) | Apache 2.0 |
| [`zlib-ng`](https://github.com/zlib-ng/zlib-ng) | zlib |
| [`c-ares`](https://github.com/c-ares/c-ares) | MIT licensed |
-| [`libicu`](https://github.com/unicode-org/icu) 78 | [license here](https://github.com/unicode-org/icu/blob/main/icu4c/LICENSE) |
+| [`libicu`](https://github.com/unicode-org/icu) 78 | [ICU license](https://github.com/unicode-org/icu/blob/main/icu4c/LICENSE) |
| [`libbase64`](https://github.com/aklomp/base64/blob/master/LICENSE) | BSD 2-Clause |
| [`libuv`](https://github.com/libuv/libuv) (on Windows) | MIT |
| [`libdeflate`](https://github.com/ebiggers/libdeflate) | MIT |
diff --git a/docs/runtime/archive.mdx b/docs/runtime/archive.mdx
index 473f3edf80c7..87c38e3d4138 100644
--- a/docs/runtime/archive.mdx
+++ b/docs/runtime/archive.mdx
@@ -140,13 +140,13 @@ console.log(`Extracted ${count} entries`);
`extract()` creates the target directory if it doesn't exist and overwrites existing files. The returned count includes files, directories, and symlinks (on POSIX systems).
-**Note**: On Windows, Bun always skips symbolic links during extraction, regardless of privilege level. On Linux and macOS, symlinks are extracted normally.
+**Note**: On Windows, Bun always skips symbolic links during extraction, regardless of privilege level. On Linux and macOS, Bun extracts symlinks normally.
-**Security note**: Bun.Archive validates paths during extraction. It rejects absolute paths (POSIX `/`, Windows drive letters like `C:\` or `C:/`, and UNC paths like `\\server\share`) and unsafe symlink targets. Path traversal components (`..`) are normalized away to prevent directory escape attacks: `dir/sub/../file` becomes `dir/file`.
+**Security note**: Bun.Archive validates paths during extraction. It rejects absolute paths (POSIX `/`, Windows drive letters like `C:\` or `C:/`, and UNC paths like `\\server\share`) and unsafe symlink targets. It normalizes away path traversal components (`..`) to prevent directory escape attacks: `dir/sub/../file` becomes `dir/file`.
### Filtering Extracted Files
-Use glob patterns to extract only specific files. Patterns are matched against archive entry paths normalized to use forward slashes (`/`). Positive patterns specify what to include, and negative patterns (prefixed with `!`) specify what to exclude. When only negative patterns are provided, all entries that don't match them are included:
+Use glob patterns to extract only specific files. Bun matches patterns against archive entry paths normalized to use forward slashes (`/`). Positive patterns specify what to include, and negative patterns (prefixed with `!`) specify what to exclude. When you pass only negative patterns, Bun includes all entries that don't match them:
```ts
const tarball = await Bun.file("package.tar.gz").bytes();
@@ -229,7 +229,7 @@ try {
Common error scenarios:
-- **Corrupted/truncated archives** - `new Archive()` loads the archive data; errors may be deferred until read/extract operations
+- **Corrupted/truncated archives** - `new Archive()` loads the archive data; Bun may defer errors until read/extract operations
- **Permission denied** - `extract()` throws if the target directory is not writable
- **Disk full** - `extract()` throws if there's insufficient space
- **Invalid paths** - Operations throw for malformed file paths
@@ -290,7 +290,7 @@ Supported glob patterns (subset of [Bun.Glob](/docs/api/glob) syntax):
- `?` - Match single character
- `[abc]` - Match character set
- `{a,b}` - Match alternatives
-- `!pattern` - Exclude files matching pattern (negation). When only negative patterns are provided, all files not matching them are included.
+- `!pattern` - Exclude files matching pattern (negation). When you pass only negative patterns, Bun includes all files not matching them.
See [Bun.Glob](/docs/api/glob) for the full glob syntax including escaping and advanced patterns.
diff --git a/docs/runtime/auto-install.mdx b/docs/runtime/auto-install.mdx
index 7dac0b136e98..ca5e0d0b3a95 100644
--- a/docs/runtime/auto-install.mdx
+++ b/docs/runtime/auto-install.mdx
@@ -5,7 +5,7 @@ description: "Bun's automatic package installation feature for standalone script
If Bun finds no `node_modules` directory in the working directory or higher, it abandons Node.js-style module resolution in favor of the **Bun module resolution algorithm**.
-Under Bun-style module resolution, Bun auto-installs every imported package on the fly into a [global module cache](/pm/global-cache) during execution (the same cache used by [`bun install`](/pm/cli/install)).
+Under Bun-style module resolution, Bun auto-installs every imported package on the fly into a [global module cache](/pm/global-cache) during execution. [`bun install`](/pm/cli/install) uses the same cache.
```ts index.ts icon="/icons/typescript.svg"
import { foo } from "foo"; // install `latest` version
@@ -39,7 +39,7 @@ Once Bun determines a version or version range, it:
## Installation
-Bun installs and caches packages into `/@`, so multiple versions of the same package can be cached at once. It also creates a symlink under `//` to speed up looking up all cached versions of a package.
+Bun installs and caches packages into `/@`, so Bun can cache multiple versions of the same package at once. It also creates a symlink under `//` to speed up looking up all cached versions of a package.
---
diff --git a/docs/runtime/binary-data.mdx b/docs/runtime/binary-data.mdx
index 009e07bb7b26..11a2425f4055 100644
--- a/docs/runtime/binary-data.mdx
+++ b/docs/runtime/binary-data.mdx
@@ -64,7 +64,7 @@ dv.setUint16(1, 513);
console.log(dv.getUint16(1)); // => 513
```
-The first three bytes of the underlying `ArrayBuffer` now have values. Even though the second and third bytes were written with `setUint16()`, you can still read each component byte with `getUint8()`.
+The first three bytes of the underlying `ArrayBuffer` now have values. Even though you wrote the second and third bytes with `setUint16()`, you can still read each component byte with `getUint8()`.
```ts
console.log(dv.getUint8(1)); // => 2
@@ -134,7 +134,7 @@ The typed array classes, and how each interprets the bytes in an `ArrayBuffer`:
| [`BigUint64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array) | Every eight (8) bytes are interpreted as an unsigned `BigInt`. Range 0 to 18446744073709551615 (though `BigInt` is capable of representing larger numbers). |
| [`Uint8ClampedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray) | Same as `Uint8Array`, but automatically "clamps" to the range 0-255 when assigning a value to an element. |
-The following table shows how the same bytes in an `ArrayBuffer` are interpreted by different typed array classes.
+The following table shows how different typed array classes interpret the same bytes in an `ArrayBuffer`.
| | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 |
| ---------------- | ------------------- | ---------- | ------------------- | ---------- | -------------------- | ---------- | -------------------- | ---------- |
@@ -194,7 +194,7 @@ const arr2 = new Uint8Array(5);
// => Uint8Array(5) [0, 0, 0, 0, 0]
```
-Typed arrays can also be instantiated directly from an array of numbers, or another typed array:
+You can also instantiate typed arrays directly from an array of numbers, or another typed array:
```ts
// from an array of numbers
@@ -238,7 +238,7 @@ new Uint8Array([255, 254, 253, 252, 251]).toHex(); // "fffefdfcfb"
Uint8Array.fromHex("fffefdfcfb"); // Uint8Array(5) [255, 254, 253, 252, 251]
```
-It is the return value of [`TextEncoder#encode`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder), and the input type of [`TextDecoder#decode`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder), two utility classes that translate between strings and various binary encodings, most notably `"utf-8"`.
+It is the return value of [`TextEncoder#encode`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) and the input type of [`TextDecoder#decode`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder). These two utility classes translate between strings and various binary encodings, most notably `"utf-8"`.
```ts
const encoder = new TextEncoder();
@@ -283,7 +283,7 @@ blob.type; // => text/html
blob.size; // => 18
```
-These parts can be `string`, `ArrayBuffer`, `TypedArray`, `DataView`, or other `Blob` instances. The parts are concatenated in the order they're given.
+These parts can be `string`, `ArrayBuffer`, `TypedArray`, `DataView`, or other `Blob` instances. The constructor concatenates the parts in the order you give them.
```ts
const blob = new Blob([
@@ -830,7 +830,7 @@ new Response(stream).blob();
#### To `ReadableStream`
-To split a `ReadableStream` into two streams that can be consumed independently:
+To split a `ReadableStream` into two streams that you can consume independently:
```ts
const [a, b] = stream.tee();
diff --git a/docs/runtime/bunfig.mdx b/docs/runtime/bunfig.mdx
index 97c76cb5741d..d50a74d1d5d9 100644
--- a/docs/runtime/bunfig.mdx
+++ b/docs/runtime/bunfig.mdx
@@ -107,7 +107,7 @@ Bun supports the following loaders:
### `telemetry`
-The `telemetry` field enables or disables analytics. By default, telemetry is enabled. This is equivalent to the `DO_NOT_TRACK` environment variable.
+The `telemetry` field enables or disables analytics. By default, telemetry is enabled. This setting is equivalent to the `DO_NOT_TRACK` environment variable.
We do not currently collect telemetry; this setting only controls anonymous crash reports. We plan to collect information like which Bun APIs are used most or how long `bun build` takes.
@@ -133,7 +133,7 @@ file = false
Use this in production or CI/CD pipelines where you want to rely solely on system environment variables.
-Files passed explicitly with `--env-file` are still loaded even when default loading is disabled.
+Bun still loads files passed explicitly with `--env-file` even when default loading is disabled.
### `console`
@@ -156,7 +156,7 @@ The `[serve]` section configures `Bun.serve` and `bun run` when serving HTTP.
### `serve.port`
-The default port for `Bun.serve` to listen on. Default `3000`. Can also be set with the `BUN_PORT` or `PORT` environment variables, or the `--port` flag.
+The default port for `Bun.serve` to listen on. Default `3000`. You can also set it with the `BUN_PORT` or `PORT` environment variables, or the `--port` flag.
```toml title="bunfig.toml" icon="settings"
[serve]
@@ -192,7 +192,7 @@ preload = ["./setup.ts"]
### `test.pathIgnorePatterns`
-Exclude files and directories from test discovery using glob patterns. Matched directories are pruned during scanning, so their contents are never traversed. Use this when your project contains submodules or vendored code with `*.test.ts` files that you don't want `bun test` to pick up.
+Exclude files and directories from test discovery using glob patterns. The test runner prunes matched directories during scanning, so it never traverses their contents. Use this when your project contains submodules or vendored code with `*.test.ts` files that you don't want `bun test` to pick up.
```toml title="bunfig.toml" icon="settings"
[test]
@@ -275,7 +275,7 @@ coveragePathIgnorePatterns = [
### `test.coverageReporter`
-By default, coverage reports are printed to the console. For persistent reports that CI and other tools can read, use `lcov`.
+By default, the test runner prints coverage reports to the console. For persistent reports that CI and other tools can read, use `lcov`.
```toml title="bunfig.toml" icon="settings"
[test]
@@ -333,7 +333,7 @@ The `--rerun-each` CLI flag overrides this setting.
### `test.retry`
-Default retry count for all tests. Failed tests are retried up to this many times. Per-test `{ retry: N }` overrides this value. Default `0` (no retries).
+Default retry count for all tests. The test runner retries failed tests up to this many times. Per-test `{ retry: N }` overrides this value. Default `0` (no retries).
```toml title="bunfig.toml" icon="settings"
[test]
@@ -357,7 +357,7 @@ The `--concurrent` CLI flag overrides this setting.
### `test.onlyFailures`
-When enabled, only failed tests are displayed in the output, which reduces noise in large test suites. Default `false`.
+When enabled, the output shows only failed tests, which reduces noise in large test suites. Default `false`.
```toml title="bunfig.toml" icon="settings"
[test]
@@ -430,7 +430,7 @@ peer = true
Whether `bun install` runs in "production mode". Default `false`.
-In production mode, `"devDependencies"` are not installed and the lockfile is frozen (same as [`install.frozenLockfile`](#install-frozenlockfile)). Use the `--production` CLI flag to enable it for a single install. Since it freezes the lockfile, `bun add`, `bun remove`, and `bun update` will fail while it is set.
+In production mode, Bun does not install `"devDependencies"` and freezes the lockfile (same as [`install.frozenLockfile`](#install-frozenlockfile)). Use the `--production` CLI flag to enable it for a single install. Since production mode freezes the lockfile, `bun add`, `bun remove`, and `bun update` fail while it is set.
```toml title="bunfig.toml" icon="settings"
[install]
@@ -441,7 +441,7 @@ production = false
Whether to set an exact version in `package.json`. Default `false`.
-By default Bun uses caret ranges; if the `latest` version of a package is `2.4.1`, Bun writes `^2.4.1` to your `package.json`, which accepts any version from `2.4.1` up to (but not including) `3.0.0`.
+By default Bun uses caret ranges. If the `latest` version of a package is `2.4.1`, Bun writes `^2.4.1` to your `package.json`. That range accepts any version from `2.4.1` up to (but not including) `3.0.0`.
```toml title="bunfig.toml" icon="settings"
[install]
@@ -636,7 +636,7 @@ Whether to generate a lockfile on `bun install`. Default `true`.
save = true
```
-Whether to generate a non-Bun lockfile alongside `bun.lock`. (A `bun.lock` is always created.) `"yarn"` is the only supported value.
+Whether to generate a non-Bun lockfile alongside `bun.lock`. (Bun always creates a `bun.lock`.) `"yarn"` is the only supported value.
```toml title="bunfig.toml" icon="settings"
[install.lockfile]
@@ -663,7 +663,7 @@ Valid values are:
### `install.globalStore`
-When using the `"isolated"` linker, share package installations across projects in a global virtual store at `/links/` and link `node_modules/.bun/@` into it instead of materializing each package into the project. Makes warm installs after `rm -rf node_modules` an order of magnitude faster. Default `false`. Can also be set with the `BUN_INSTALL_GLOBAL_STORE` environment variable.
+When using the `"isolated"` linker, share package installations across projects in a global virtual store at `/links/`. Bun links `node_modules/.bun/@` into the store instead of materializing each package into the project. Makes warm installs after `rm -rf node_modules` an order of magnitude faster. Default `false`. You can also set it with the `BUN_INSTALL_GLOBAL_STORE` environment variable.
See [Global virtual store](/pm/global-store).
@@ -674,7 +674,7 @@ globalStore = true
### `install.publicHoistPattern`
-When using the `"isolated"` linker, packages matching these glob patterns are hoisted to the root `node_modules` directory so they can be resolved by any package in the project. Default `[]`. Similar to pnpm's `public-hoist-pattern`.
+When using the `"isolated"` linker, Bun hoists packages matching these glob patterns to the root `node_modules` directory so any package in the project can resolve them. Default `[]`. Similar to pnpm's `public-hoist-pattern`.
```toml title="bunfig.toml" icon="settings"
[install]
@@ -683,7 +683,7 @@ publicHoistPattern = ["*eslint*", "*prettier*"]
### `install.hoistPattern`
-When using the `"isolated"` linker, packages matching these glob patterns are hoisted to a fallback directory inside the virtual store (`node_modules/.bun/node_modules`) so they can be resolved by other packages in the virtual store. By default every package is hoisted there, equivalent to `["*"]`. Similar to pnpm's `hoist-pattern`.
+When using the `"isolated"` linker, Bun hoists packages matching these glob patterns to a fallback directory inside the virtual store (`node_modules/.bun/node_modules`) so other packages in the virtual store can resolve them. By default Bun hoists every package there, equivalent to `["*"]`. Similar to pnpm's `hoist-pattern`.
```toml title="bunfig.toml" icon="settings"
[install]
@@ -692,7 +692,7 @@ hoistPattern = ["*"]
### `install.hoist`
-When using the `"isolated"` linker, Bun creates `node_modules/.bun/node_modules`, a fallback directory containing a symlink to every installed package (or only the packages matching `install.hoistPattern`, when one is set). It sits on the upward resolution path of every package in the store, so a package can still resolve dependencies it never declared ("phantom dependencies"). Set `hoist = false` to skip creating this directory entirely: an undeclared import from a store package then fails unless that package is linked at the project root `node_modules` (a direct dependency, a `publicHoistPattern` match, or a workspace package), which stays on the resolution path because `.bun` lives inside it. The project's own `node_modules` symlinks are unchanged. Default `true`. Equivalent to pnpm's `hoist` setting, including this root-`node_modules` caveat; takes precedence over `install.hoistPattern`.
+When using the `"isolated"` linker, Bun creates `node_modules/.bun/node_modules`. This fallback directory contains a symlink to every installed package, or only to the packages matching `install.hoistPattern` when one is set. The directory sits on the upward resolution path of every package in the store, so a package can still resolve dependencies it never declared ("phantom dependencies"). Set `hoist = false` to skip creating this directory entirely. An undeclared import from a store package then fails unless the imported package is linked at the project root `node_modules` (a direct dependency, a `publicHoistPattern` match, or a workspace package). The root `node_modules` stays on the resolution path because `.bun` lives inside it. The project's own `node_modules` symlinks are unchanged. Default `true`. Equivalent to pnpm's `hoist` setting, including this root-`node_modules` caveat; takes precedence over `install.hoistPattern`.
This setting only applies to the `"isolated"` linker. Hoisted installs are unaffected: there the flat `node_modules` tree is the layout itself, not something this switch controls.
@@ -744,7 +744,7 @@ Learn more about [using and writing security scanners](/pm/security-scanner-api)
### `install.minimumReleaseAge`
-Configure a minimum age (in seconds) for npm package versions. Package versions published more recently than this threshold are filtered out during installation. Default `null` (disabled).
+Configure a minimum age (in seconds) for npm package versions. During installation, Bun filters out package versions published more recently than this threshold. Default `null` (disabled).
```toml title="bunfig.toml" icon="settings"
[install]
@@ -793,9 +793,9 @@ shell = "bun"
### `run.bun` - auto alias `node` to `bun`
-When `true`, this prepends `$PATH` with a `node` symlink that points to the `bun` binary for all scripts or executables invoked by `bun run` or `bun`.
+When `true`, Bun prepends `$PATH` with a `node` symlink that points to the `bun` binary for all scripts or executables invoked by `bun run` or `bun`.
-A script that runs `node` runs `bun` instead, with no changes to the script. This works recursively, so a script that runs another script that runs `node` also runs `bun`, and it applies to shebangs that point to `node`.
+A script that runs `node` runs `bun` instead, with no changes to the script. This works recursively, so a script that runs another script that runs `node` also runs `bun`. The alias also applies to shebangs that point to `node`.
By default, this is enabled if `node` is not already in your `$PATH`.
@@ -831,7 +831,7 @@ When `true`, `bun run` and `bun` don't print the command being run.
silent = true
```
-Without this option, the command being run is printed to the console:
+Without this option, Bun prints the command being run to the console:
```sh terminal icon="terminal"
bun run dev
@@ -842,7 +842,7 @@ echo "Running \"dev\"..."
Running "dev"...
```
-With this option, the command being run is not printed:
+With this option, Bun does not print the command being run:
```sh
bun run dev
@@ -873,7 +873,7 @@ elide-lines = 10
When `true`, Bun watches the process that spawned it and exits as soon as that parent goes away, even if the parent was force-killed and never got a chance to forward a signal. On its own exit, Bun also terminates every descendant process so nothing it spawned outlives it. Useful when Bun is launched by a supervisor (Electron, a CI runner, a thin shim) that may be force-killed.
-On Linux this uses `prctl(PR_SET_PDEATHSIG)` and a `/proc` descendant walk; on macOS, `EVFILT_PROC`/`NOTE_EXIT` on the event loop's kqueue and a libproc descendant walk; on Windows, a thread-pool wait on the parent's process handle and a kill-on-close Job Object.
+On Linux, Bun uses `prctl(PR_SET_PDEATHSIG)` and a `/proc` descendant walk. On macOS, it uses `EVFILT_PROC`/`NOTE_EXIT` on the event loop's kqueue and a libproc descendant walk. On Windows, it uses a thread-pool wait on the parent's process handle and a kill-on-close Job Object.
Equivalent to the `--no-orphans` CLI flag or the `BUN_FEATURE_FLAG_NO_ORPHANS=1` environment variable.
diff --git a/docs/runtime/child-process.mdx b/docs/runtime/child-process.mdx
index a5c489152c68..23589f7f7876 100644
--- a/docs/runtime/child-process.mdx
+++ b/docs/runtime/child-process.mdx
@@ -170,7 +170,7 @@ console.log(`CPU time (system): ${usage.cpuTime.system} µs`);
## Resource limits with cgroups (Linux)
-On Linux, pass `cgroup` to start the subprocess inside a [control group](https://docs.kernel.org/admin-guide/cgroup-v2.html). The child joins the cgroup before it begins executing, so limits configured on it — memory, pids, CPU — apply from the first instruction and to every process the child spawns in turn. When a memory limit is exceeded the kernel OOM-kills a process _inside_ the cgroup instead of reclaiming memory from the parent.
+On Linux, pass `cgroup` to start the subprocess inside a [control group](https://docs.kernel.org/admin-guide/cgroup-v2.html). The child joins the cgroup before it begins executing, so limits configured on the cgroup (memory, pids, CPU) apply from the first instruction. They also apply to every process the child spawns in turn. When a memory limit is exceeded the kernel OOM-kills a process _inside_ the cgroup instead of reclaiming memory from the parent.
A cgroup is a directory under `/sys/fs/cgroup`; create and configure it with ordinary file operations, then pass its path (or an open directory file descriptor):
@@ -187,7 +187,7 @@ const proc = Bun.spawn({
});
```
-The same directory can be passed to any number of spawns; the limit applies to their combined usage. Both cgroup v1 and v2 hierarchies are supported. Creating cgroups typically requires root or a delegated subtree. On other platforms the option is ignored; on Linux, the spawn fails if the cgroup cannot be joined.
+You can pass the same directory to any number of spawns; the limit applies to their combined usage. Bun supports both cgroup v1 and v2 hierarchies. Creating cgroups typically requires root or a delegated subtree. Bun ignores the option on other platforms; on Linux, the spawn fails if the child cannot join the cgroup.
## Using AbortSignal
@@ -231,7 +231,7 @@ const proc = Bun.spawn({
});
```
-The `killSignal` option also controls which signal is sent when an AbortSignal is aborted.
+The `killSignal` option also controls which signal Bun sends when an AbortSignal is aborted.
## Using maxBuffer
@@ -246,9 +246,10 @@ const result = Bun.spawnSync({
// process exits
```
-Bun stops reading as soon as the limit is passed, so the returned output can
-exceed `maxBuffer` only by the single read that passed it, never by whatever
-the process manages to write before the kill lands. This matches Node.js.
+Bun stops reading as soon as the limit is passed. The returned output can
+therefore exceed `maxBuffer` only by the single read that passed it, never by
+whatever the process manages to write before the kill lands. This matches
+Node.js.
## Inter-process communication (IPC)
@@ -279,7 +280,7 @@ const childProc = Bun.spawn(["bun", "child.ts"], {
childProc.send("I am your father"); // The parent can send messages to the child as well
```
-The child process sends messages to its parent with `process.send()` and receives them with `process.on("message")`. This is the same API used for `child_process.fork()` in Node.js.
+The child process sends messages to its parent with `process.send()` and receives them with `process.on("message")`. Node.js uses the same API for `child_process.fork()`.
```ts child.ts
process.send("Hello from child as string");
@@ -301,8 +302,8 @@ process.send({ message: "Hello from child as object" });
The `serialization` option controls the underlying communication format between the two processes:
-- `advanced`: (default) Messages are serialized using the JSC `serialize` API, which supports cloning [everything `structuredClone` supports](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). This does not support transferring ownership of objects.
-- `json`: Messages are serialized using `JSON.stringify` and `JSON.parse`, which does not support as many object types as `advanced` does.
+- `advanced`: (default) Bun serializes messages using the JSC `serialize` API, which supports cloning [everything `structuredClone` supports](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). This does not support transferring ownership of objects.
+- `json`: Bun serializes messages using `JSON.stringify` and `JSON.parse`, which does not support as many object types as `advanced` does.
To disconnect the IPC channel from the parent process, call:
@@ -366,7 +367,7 @@ await proc.exited;
proc.terminal.close();
```
-When the `terminal` option is provided:
+When you pass the `terminal` option:
- The subprocess sees `process.stdout.isTTY` as `true`
- `stdin`, `stdout`, and `stderr` are all connected to the terminal
@@ -432,21 +433,21 @@ await proc2.exited;
When passing an existing `Terminal` object:
-- The terminal can be reused across multiple spawns
+- You can reuse the terminal across multiple spawns
- You control when to close the terminal
- The `exit` callback fires when you call `terminal.close()`, not when each subprocess exits
- Use `proc.exited` to detect individual subprocess exits
### Platform differences
-`Bun.Terminal` uses `openpty()` on Linux and macOS, and ConPTY (`CreatePseudoConsole`) on Windows. The core behavior — child sees a TTY, `write()` reaches the child's stdin, child output reaches the `data` callback, `resize()` updates the child's view — is the same on every platform. A few details differ:
+`Bun.Terminal` uses `openpty()` on Linux and macOS, and ConPTY (`CreatePseudoConsole`) on Windows. The core behavior is the same on every platform: the child sees a TTY, `write()` reaches the child's stdin, child output reaches the `data` callback, and `resize()` updates the child's view. A few details differ:
- **No termios on Windows.** `inputFlags`, `outputFlags`, `localFlags`, and `controlFlags` always read as `0` and setting them is a no-op. `setRawMode()` records the flag but has no effect on the child; the child controls its own console mode.
-- **No echo without a child process on Windows.** On POSIX, the kernel line discipline echoes `write()` input back to the `data` callback even with no process attached. ConPTY has no line discipline; input is buffered for the next reader. If you need echo, spawn a process that echoes.
-- **ConPTY re-encodes output.** ConPTY renders the child's output to a virtual screen and emits whatever VT sequences describe the result, so the `data` callback receives semantically equivalent — but not byte-identical — escape sequences. Colors and text are preserved; cursor-positioning and reset sequences may be reordered or coalesced. ConPTY also emits a short VT init sequence (`\x1b[?9001h\x1b[?1004h…`) before any child output.
+- **No echo without a child process on Windows.** On POSIX, the kernel line discipline echoes `write()` input back to the `data` callback even with no process attached. ConPTY has no line discipline; it buffers input for the next reader. If you need echo, spawn a process that echoes.
+- **ConPTY re-encodes output.** ConPTY renders the child's output to a virtual screen and emits whatever VT sequences describe the result. The `data` callback therefore receives semantically equivalent, but not byte-identical, escape sequences. ConPTY preserves colors and text; it may reorder or coalesce cursor-positioning and reset sequences. ConPTY also emits a short VT init sequence (`\x1b[?9001h\x1b[?1004h…`) before any child output.
- **Input `\r` is not translated to `\n` on Windows.** POSIX `ICRNL` maps carriage return to newline on input; ConPTY passes `\r` through unchanged.
-- **`process.on('SIGWINCH')` in the child does not fire under ConPTY** unless the child is reading stdin in raw mode. `process.stdout.columns`/`rows` do update after `resize()`. This is a libuv limitation that affects any libuv-based child (Node.js included).
-- On Windows before 11 24H2 (build 26100), `terminal.close()` may not terminate a still-running child promptly because [`ClosePseudoConsole`](https://learn.microsoft.com/en-us/windows/console/closepseudoconsole) blocks until conhost has flushed its output through the pipe on those versions. Kill the attached process first if you need to tear down with a running child.
+- **`process.on('SIGWINCH')` in the child does not fire under ConPTY** unless the child is reading stdin in raw mode. `process.stdout.columns`/`rows` do update after `resize()`. The missing signal is a libuv limitation that affects any libuv-based child (Node.js included).
+- On Windows before 11 24H2 (build 26100), `terminal.close()` may not terminate a still-running child promptly. The delay comes from [`ClosePseudoConsole`](https://learn.microsoft.com/en-us/windows/console/closepseudoconsole), which blocks on those versions until conhost has flushed its output through the pipe. Kill the attached process first if you need to tear down with a running child.
---
@@ -454,7 +455,7 @@ When passing an existing `Terminal` object:
`Bun.spawnSync` is the blocking equivalent of `Bun.spawn`. It supports the same inputs and parameters and returns a `SyncSubprocess` object, which differs from `Subprocess` in a few ways.
-1. It contains a `success` property that indicates whether the process exited with a zero exit code.
+1. The returned object contains a `success` property that indicates whether the process exited with a zero exit code.
2. The `stdout` and `stderr` properties are instances of `Buffer` instead of `ReadableStream`.
3. There is no `stdin` property. Use `Bun.spawn` to incrementally write to the subprocess's input stream.
diff --git a/docs/runtime/color.mdx b/docs/runtime/color.mdx
index 6a859e4d0186..917ec28ad7aa 100644
--- a/docs/runtime/color.mdx
+++ b/docs/runtime/color.mdx
@@ -97,7 +97,7 @@ The `"ansi"` format detects the color depth of stdout from environment variables
The `"ansi-16m"` format outputs 24-bit ANSI colors, which can display 16 million colors but require a modern terminal that supports them.
-It converts the input color to RGBA, then outputs that as an ANSI color.
+Bun converts the input color to RGBA, then outputs that as an ANSI color.
```ts
Bun.color("red", "ansi-16m"); // "\x1b[38;2;255;0;0m"
diff --git a/docs/runtime/console.mdx b/docs/runtime/console.mdx
index 28899373a26e..ead782025e15 100644
--- a/docs/runtime/console.mdx
+++ b/docs/runtime/console.mdx
@@ -16,7 +16,7 @@ You can configure how deeply `console.log()` prints nested objects:
- **CLI flag**: Use `--console-depth ` to set the depth for a single run
- **Configuration**: Set `console.depth` in your `bunfig.toml` to persist it across runs
-- **Default**: Objects are inspected to a depth of `2` levels
+- **Default**: Bun inspects objects to a depth of `2` levels
```js
const nested = { a: { b: { c: { d: "deep" } } } };
diff --git a/docs/runtime/cookies.mdx b/docs/runtime/cookies.mdx
index 09cbb0c9e183..00175c053216 100644
--- a/docs/runtime/cookies.mdx
+++ b/docs/runtime/cookies.mdx
@@ -117,7 +117,7 @@ cookies.set(cookie);
#### `delete(options: CookieStoreDeleteOptions): void`
-Removes a cookie from the map. When applied to a Response, this adds a cookie with an empty string value and an expiry date in the past. The browser only deletes the cookie if the domain and path match the ones it was created with.
+Removes a cookie from the map. When applied to a Response, the deletion adds a cookie with an empty string value and an expiry date in the past. The browser only deletes the cookie if the domain and path match the ones it was created with.
```ts title="delete-cookie.ts" icon="/icons/typescript.svg"
// Delete by name using default domain and path.
@@ -143,7 +143,7 @@ const json = cookies.toJSON();
Returns an array of values for Set-Cookie headers that apply all cookie changes.
-Use this with HTTP servers other than `Bun.serve()`. In `Bun.serve()`, you don't need to call it: any changes made to the `req.cookies` map are automatically applied to the response headers.
+Use this with HTTP servers other than `Bun.serve()`. In `Bun.serve()`, you don't need to call it: Bun automatically applies any changes you make to the `req.cookies` map to the response headers.
```js title="node-server.js" icon="file-code"
import { createServer } from "node:http";
diff --git a/docs/runtime/cron.mdx b/docs/runtime/cron.mdx
index 447902e842f2..6864198d616c 100644
--- a/docs/runtime/cron.mdx
+++ b/docs/runtime/cron.mdx
@@ -119,7 +119,7 @@ console.log(next); // => next local midnight
### Time zone
-Schedules are interpreted in the system's **local time zone** — the same way crontab, launchd, and Windows Task Scheduler read them. The OS-level form and the in-process callback form fire at the same wall-clock time.
+Bun interprets schedules in the system's **local time zone**, the same way crontab, launchd, and Windows Task Scheduler read them. The OS-level form and the in-process callback form fire at the same wall-clock time.
To override, pass an IANA time-zone name as `{ tz }` to `Bun.cron.parse()` or the in-process `Bun.cron(schedule, handler, options)`:
@@ -138,14 +138,14 @@ DST transitions:
### Day-of-month and day-of-week interaction
-When **both** day-of-month and day-of-week are specified (neither is `*`), the expression matches when **either** condition is true. This follows the [POSIX cron](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html) standard.
+When you specify **both** day-of-month and day-of-week (neither is `*`), the expression matches when **either** condition is true. This follows the [POSIX cron](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html) standard.
```ts
// Fires on the 15th of every month OR every Friday
Bun.cron.parse("0 0 15 * FRI");
```
-When only one is specified (the other is `*`), only that field is used for matching.
+When you specify only one (the other is `*`), Bun uses only that field for matching.
---
@@ -159,7 +159,7 @@ const job = Bun.cron("*/5 * * * *", async () => {
});
```
-In-process scheduling is the lightweight option for long-running servers and workers — no system cron daemon required, works the same on every platform, and shares state (database pools, caches, module-level variables) between invocations.
+In-process scheduling is the lightweight option for long-running servers and workers. It requires no system cron daemon, works the same on every platform, and shares state (database pools, caches, module-level variables) between invocations.
| | In-process | [OS-level](#bun-cron-path-schedule-title-os-level) |
| ---------------------------- | -------------------------------- | -------------------------------------------------- |
@@ -174,14 +174,14 @@ In-process scheduling is the lightweight option for long-running servers and wor
| Parameter | Type | Description |
| ---------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schedule` | `string` | A [cron expression](#cron-expression-syntax) or nickname like `"@hourly"`. |
-| `handler` | `(this: CronJob) => unknown` | Called on each fire. May return a Promise — the next fire is not scheduled until it settles. Inside a `function` callback, `this` is the `CronJob` (so `this.stop()` works). |
+| `handler` | `(this: CronJob) => unknown` | Called on each fire. May return a Promise; Bun schedules the next fire only once it settles. Inside a `function` callback, `this` is the `CronJob` (so `this.stop()` works). |
| `options` | `{ tz?: string }` | IANA time-zone name to interpret the schedule in (defaults to the system zone). |
Returns a [`CronJob`](#the-cronjob-handle) synchronously. Throws a `TypeError` if the expression is invalid, the time-zone name is unknown, or the expression has no future occurrences, like `"0 0 30 2 *"` (February 30th).
### No-overlap guarantee
-The next fire time is computed only after the handler — including any returned `Promise` — settles. If your handler takes 90 seconds and the schedule is `* * * * *`, the second fire is the first minute boundary _after_ the handler finishes, not 60 seconds after the first fire. Invocations never stack.
+Bun computes the next fire time only after the handler and any returned `Promise` settle. If your handler takes 90 seconds and the schedule is `* * * * *`, the second fire is the first minute boundary _after_ the handler finishes, not 60 seconds after the first fire. Invocations never stack.
### Error handling
@@ -202,7 +202,7 @@ Bun.cron("* * * * *", async () => {
### `bun --hot`
-Under `bun --hot`, all in-process cron jobs are stopped immediately before the module graph re-evaluates. Every `Bun.cron()` call still in your source then re-registers. Editing the schedule, editing the handler, or deleting the line entirely all take effect on save without leaking timers.
+Under `bun --hot`, Bun stops all in-process cron jobs immediately before the module graph re-evaluates. Every `Bun.cron()` call still in your source then re-registers. Editing the schedule, editing the handler, or deleting the line entirely all take effect on save without leaking timers.
### The `CronJob` handle
@@ -239,7 +239,7 @@ await Bun.cron("./worker.ts", "30 2 * * MON", "weekly-report");
| `schedule` | `string` | Cron expression or nickname |
| `title` | `string` | Unique job identifier (alphanumeric, hyphens, underscores) |
-Re-registering with the same `title` overwrites the existing job in-place — the old schedule is replaced, not duplicated.
+Re-registering with the same `title` overwrites the existing job in-place. Bun replaces the old schedule instead of duplicating it.
```ts
await Bun.cron("./worker.ts", "0 * * * *", "my-job"); // every hour
@@ -268,7 +268,7 @@ The handler can be `async`. Bun waits for the returned promise to settle before
### Linux
-Bun uses [crontab](https://man7.org/linux/man-pages/man5/crontab.5.html) to register jobs. Each job is stored as a line in your user's crontab with a `# bun-cron: ` marker comment above it.
+Bun uses [crontab](https://man7.org/linux/man-pages/man5/crontab.5.html) to register jobs. Bun stores each job as a line in your user's crontab with a `# bun-cron: ` marker comment above it.
The crontab entry looks like:
@@ -310,13 +310,13 @@ crontab -l | grep -v "# bun-cron:" | grep -v "\-\-cron-title=" | crontab -
### macOS
-Bun uses [launchd](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html) to register jobs. Each job is installed as a plist file at:
+Bun uses [launchd](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html) to register jobs. Bun installs each job as a plist file at:
```
~/Library/LaunchAgents/bun.cron..plist
```
-The plist uses `StartCalendarInterval` to define the schedule. Complex patterns with ranges, lists, or steps are supported — Bun expands them into multiple `StartCalendarInterval` dicts as a Cartesian product.
+The plist uses `StartCalendarInterval` to define the schedule. Complex patterns with ranges, lists, or steps are supported. Bun expands them into multiple `StartCalendarInterval` dicts as a Cartesian product.
**Viewing registered jobs:**
@@ -354,17 +354,17 @@ rm ~/Library/LaunchAgents/bun.cron.weekly-report.plist
### Windows
-Bun uses [Windows Task Scheduler](https://learn.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page) with XML-based task definitions. Each job is registered as a scheduled task named `bun-cron-` using [`CalendarTrigger`](https://learn.microsoft.com/en-us/windows/win32/taskschd/taskschedulerschema-calendartrigger-triggergroup-element) elements and [`Repetition`](https://learn.microsoft.com/en-us/windows/win32/taskschd/taskschedulerschema-repetition-triggerbasetype-element) patterns.
+Bun uses [Windows Task Scheduler](https://learn.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page) with XML-based task definitions. Bun registers each job as a scheduled task named `bun-cron-` using [`CalendarTrigger`](https://learn.microsoft.com/en-us/windows/win32/taskschd/taskschedulerschema-calendartrigger-triggergroup-element) elements and [`Repetition`](https://learn.microsoft.com/en-us/windows/win32/taskschd/taskschedulerschema-repetition-triggerbasetype-element) patterns.
Most cron expressions are fully supported, including `@daily`, `@weekly`, `@monthly`, `@yearly`, ranges (`1-5`), lists (`1,15`), named days/months, and day-of-month patterns.
#### User context
-Bun registers tasks with the [`S4U` (Service-for-User)](https://learn.microsoft.com/en-us/windows/win32/taskschd/taskschedulerschema-logontype-simpletype) logon type, which runs jobs as the registering user even when not logged in — matching Linux `crontab` behavior. No password is stored.
+Bun registers tasks with the [`S4U` (Service-for-User)](https://learn.microsoft.com/en-us/windows/win32/taskschd/taskschedulerschema-logontype-simpletype) logon type, which runs jobs as the registering user even when not logged in. Linux `crontab` behaves the same way. No password is stored.
TCP/IP networking (`fetch()`, HTTP, WebSocket, database connections) works normally. The only restriction is that S4U tasks cannot access [Windows-authenticated network resources](https://learn.microsoft.com/en-us/windows/win32/taskschd/security-contexts-for-running-tasks) (SMB file shares, mapped drives, Kerberos/NTLM services).
-On headless servers and CI environments where the current user's [Security Identifier (SID)](https://learn.microsoft.com/en-us/windows/security/identity-protection/access-control/security-identifiers) cannot be resolved — such as service accounts created by [NSSM](https://nssm.cc/) or similar tools — `Bun.cron()` fails with an error explaining the issue. To work around this, either run Bun as a regular user account, or create the scheduled task manually with `schtasks /create /xml /tn /ru SYSTEM /f`.
+On some headless servers and CI environments, the current user's [Security Identifier (SID)](https://learn.microsoft.com/en-us/windows/security/identity-protection/access-control/security-identifiers) cannot be resolved, for example with service accounts created by [NSSM](https://nssm.cc/) or similar tools. In that case, `Bun.cron()` fails with an error explaining the issue. To work around this, either run Bun as a regular user account, or create the scheduled task manually with `schtasks /create /xml /tn /ru SYSTEM /f`.
#### Trigger limit
diff --git a/docs/runtime/csrf.mdx b/docs/runtime/csrf.mdx
index 8ea148c82a5e..517925bef756 100644
--- a/docs/runtime/csrf.mdx
+++ b/docs/runtime/csrf.mdx
@@ -3,7 +3,7 @@ title: CSRF Protection
description: Generate and verify CSRF tokens with Bun's built-in API
---
-`Bun.CSRF` generates and verifies [CSRF (Cross-Site Request Forgery)](https://owasp.org/www-community/attacks/csrf) tokens. Tokens are signed with HMAC and include an expiration timestamp.
+`Bun.CSRF` generates and verifies [CSRF (Cross-Site Request Forgery)](https://owasp.org/www-community/attacks/csrf) tokens. Bun signs tokens with HMAC. Each token includes an expiration timestamp.
```ts title="csrf.ts" icon="/icons/typescript.svg"
// Generate a token bound to the requester's session
@@ -16,8 +16,9 @@ console.log(isValid); // true
Always pass a `sessionId` (the requester's session identifier or user ID) to both `generate()` and `verify()`. Without
- it, a token is only bound to the secret — any token the server has ever issued validates for every user, so an
- attacker can obtain a token in their own session and replay it in a forged cross-site request from a victim's browser.
+ it, a token is only bound to the secret, so any token the server has ever issued validates for every user. An attacker
+ can therefore obtain a token in their own session and replay it in a forged cross-site request from a victim's
+ browser.
---
@@ -35,12 +36,12 @@ const token = Bun.CSRF.generate("my-secret-key");
- `secret` (string, optional) — The secret key used to sign the token. If not provided, Bun generates a random in-memory default secret (unique per thread).
- `options` (object, optional):
-| Option | Type | Default | Description |
-| ----------- | -------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `expiresIn` | `number` | `86400000` | Milliseconds until the token expires. Defaults to 24 hours. |
-| `encoding` | `string` | `"base64url"` | Token encoding format: `"base64"`, `"base64url"`, or `"hex"`. |
-| `algorithm` | `string` | `"sha256"` | HMAC algorithm: `"sha256"`, `"sha384"`, `"sha512"`, `"sha512-256"`, `"blake2b256"`, or `"blake2b512"`. |
-| `sessionId` | `string` | (none) | Binds the token to the requesting principal (session ID, user ID, or equivalent). The token only verifies when the same `sessionId` is passed to `verify()`. |
+| Option | Type | Default | Description |
+| ----------- | -------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `expiresIn` | `number` | `86400000` | Milliseconds until the token expires. Defaults to 24 hours. |
+| `encoding` | `string` | `"base64url"` | Token encoding format: `"base64"`, `"base64url"`, or `"hex"`. |
+| `algorithm` | `string` | `"sha256"` | HMAC algorithm: `"sha256"`, `"sha384"`, `"sha512"`, `"sha512-256"`, `"blake2b256"`, or `"blake2b512"`. |
+| `sessionId` | `string` | (none) | Binds the token to the requesting principal (session ID, user ID, or equivalent). The token only verifies when you pass the same `sessionId` to `verify()`. |
**Returns:** `string` — the encoded token.
@@ -74,13 +75,13 @@ const isValid = Bun.CSRF.verify(token, { secret: "my-secret-key" });
- `token` (string, required) — The token to verify.
- `options` (object, optional):
-| Option | Type | Default | Description |
-| ----------- | -------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `secret` | `string` | (auto) | The secret used to sign the token. If not provided, uses the same in-memory default as `generate()`. |
-| `maxAge` | `number` | `86400000` | Maximum token age in milliseconds, independent of the token's own `expiresIn`. |
-| `encoding` | `string` | `"base64url"` | Must match the encoding used during `generate()`. |
-| `algorithm` | `string` | `"sha256"` | Must match the algorithm used during `generate()`. |
-| `sessionId` | `string` | (none) | Must match the `sessionId` used during `generate()`. A token bound to one principal fails verification for any other principal, and a token generated without a `sessionId` fails verification when one is supplied. |
+| Option | Type | Default | Description |
+| ----------- | -------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `secret` | `string` | (auto) | The secret used to sign the token. If not provided, uses the same in-memory default as `generate()`. |
+| `maxAge` | `number` | `86400000` | Maximum token age in milliseconds, independent of the token's own `expiresIn`. |
+| `encoding` | `string` | `"base64url"` | Must match the encoding used during `generate()`. |
+| `algorithm` | `string` | `"sha256"` | Must match the algorithm used during `generate()`. |
+| `sessionId` | `string` | (none) | Must match the `sessionId` used during `generate()`. A token bound to one principal fails verification for any other principal. A token generated without a `sessionId` fails verification when you supply one. |
**Returns:** `boolean`
@@ -162,7 +163,7 @@ console.log(`Listening on ${server.url}`);
## Default secret
-If you omit the `secret` parameter in both `generate()` and `verify()`, Bun uses a random secret generated once per thread. This is convenient for single-threaded applications, but tokens won't verify across servers or workers, or after a restart.
+If you omit the `secret` parameter in both `generate()` and `verify()`, Bun uses a random secret generated once per thread. The default secret is convenient for single-threaded applications, but tokens don't verify across servers or workers, or after a restart.
```ts title="default-secret.ts" icon="/icons/typescript.svg"
// Both calls use the same per-thread default secret within this runtime context.
diff --git a/docs/runtime/debugger.mdx b/docs/runtime/debugger.mdx
index 022304a8232c..6ebf5ac903e7 100644
--- a/docs/runtime/debugger.mdx
+++ b/docs/runtime/debugger.mdx
@@ -130,7 +130,7 @@ Set the `BUN_CONFIG_VERBOSE_FETCH` environment variable to log network requests
### Print fetch & node:http requests as curl commands
-Set `BUN_CONFIG_VERBOSE_FETCH` to `curl` to print each `fetch()` and `node:http` request as a single-line `curl` command you can copy-paste into your terminal to replicate the request.
+Set `BUN_CONFIG_VERBOSE_FETCH` to `curl` to print each `fetch()` and `node:http` request as a single-line `curl` command. You can copy-paste the command into your terminal to replicate the request.
```ts index.ts icon="/icons/typescript.svg"
process.env.BUN_CONFIG_VERBOSE_FETCH = "curl";
@@ -211,7 +211,7 @@ await fetch("https://example.com", {
Bun transpiles every file, which could leave stack traces pointing at the transpiled output. To avoid this, Bun generates and serves sourcemapped files for every file it transpiles. When you see a stack trace in the console, you can click on the file path and land in the original source code, even though it was written in TypeScript or JSX, or has some other transformation applied.
-Bun loads sourcemaps both at runtime when transpiling files on-demand, and when using `bun build` to precompile files ahead of time.
+Bun loads sourcemaps both at runtime when it transpiles files on-demand, and when you use `bun build` to precompile files ahead of time.
### Syntax-highlighted source code preview
@@ -235,7 +235,7 @@ error: Something went wrong
### V8 Stack Traces
-Bun uses JavaScriptCore as its engine, but much of the Node.js ecosystem and npm expects V8, and JavaScript engines differ in how they format `error.stack`. Because Bun aims to be a drop-in replacement for Node.js, it formats `error.stack` the same way V8 does. This matters most when you use libraries that expect V8 stack traces.
+Bun uses JavaScriptCore as its engine, but much of the Node.js ecosystem and npm expects V8. JavaScript engines differ in how they format `error.stack`. Because Bun aims to be a drop-in replacement for Node.js, it formats `error.stack` the same way V8 does. This matters most when you use libraries that expect V8 stack traces.
#### V8 Stack Trace API
diff --git a/docs/runtime/environment-variables.mdx b/docs/runtime/environment-variables.mdx
index 71a340f0cc7a..e23ab1c4a49a 100644
--- a/docs/runtime/environment-variables.mdx
+++ b/docs/runtime/environment-variables.mdx
@@ -89,7 +89,7 @@ env = false
Files passed with `--env-file` still load even when default loading is disabled.
-When Bun is invoked as `node` (for example via `bun --bun`, `bunx --bun`, or a `node` symlink pointing at Bun), automatic `.env` loading is disabled to match Node.js. This lets tools with their own mode-aware `.env` resolution, such as Vite's `loadEnv`, pick the correct `.env.{mode}` file instead of seeing Bun's pre-populated values as shell-set overrides. Explicit `--env-file` arguments are still honored.
+When invoked as `node` (for example via `bun --bun`, `bunx --bun`, or a `node` symlink pointing at Bun), Bun disables automatic `.env` loading to match Node.js. This lets tools with their own mode-aware `.env` resolution, such as Vite's `loadEnv`, pick the correct `.env.{mode}` file instead of seeing Bun's pre-populated values as shell-set overrides. Bun still honors explicit `--env-file` arguments.
---
@@ -116,7 +116,7 @@ BAR=hello$FOO
process.env.BAR; // => "helloworld"
```
-This is useful for constructing connection strings or other compound values.
+Expansion is useful for constructing connection strings or other compound values.
```ini .env icon="settings"
DB_USER=postgres
@@ -198,20 +198,20 @@ Bun reads these environment variables to configure aspects of its behavior.
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NODE_TLS_REJECT_UNAUTHORIZED` | `NODE_TLS_REJECT_UNAUTHORIZED=0` disables SSL certificate validation. Useful for testing and debugging, but be very hesitant to use it in production. Node.js introduced this variable; Bun keeps the name for compatibility. |
| `BUN_CONFIG_VERBOSE_FETCH` | If `BUN_CONFIG_VERBOSE_FETCH=curl`, then fetch requests log the URL, method, request headers and response headers to the console. This also works with `node:http`. `BUN_CONFIG_VERBOSE_FETCH=1` is equivalent to `BUN_CONFIG_VERBOSE_FETCH=curl` except without the `curl` output. |
-| `BUN_RUNTIME_TRANSPILER_CACHE_PATH` | The runtime transpiler caches the transpiled output of source files larger than 4 KB, which makes CLIs using Bun load faster. If `BUN_RUNTIME_TRANSPILER_CACHE_PATH` is set, the cache is written to that directory. If it is set to an empty string or the string `"0"`, caching is disabled. If it is unset, the cache is written to the platform-specific cache directory. |
+| `BUN_RUNTIME_TRANSPILER_CACHE_PATH` | The runtime transpiler caches the transpiled output of source files larger than 4 KB, which makes CLIs using Bun load faster. If `BUN_RUNTIME_TRANSPILER_CACHE_PATH` is set, Bun writes the cache to that directory. If it is set to an empty string or the string `"0"`, caching is disabled. If it is unset, Bun writes the cache to the platform-specific cache directory. |
| `TMPDIR` | Bun occasionally requires a directory to store intermediate assets during bundling or other operations. If unset, defaults to the platform-specific temporary directory: `/tmp` on Linux, `/private/tmp` on macOS. |
| `NO_COLOR` | If `NO_COLOR=1`, then ANSI color output is [disabled](https://no-color.org/). |
| `FORCE_COLOR` | If `FORCE_COLOR=1`, then ANSI color output is forced on, even if `NO_COLOR` is set. |
| `BUN_CONFIG_MAX_HTTP_REQUESTS` | Sets the maximum number of concurrent HTTP requests sent by fetch and `bun install`. Defaults to `256`. Lower it if you run into rate limits or connection issues. |
| `BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD` | If `BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD=true`, then `bun --watch` does not clear the console on reload |
-| `DO_NOT_TRACK` | Disable uploading crash reports to `bun.report` on crash. On macOS & Windows, crash report uploads are enabled by default. Other telemetry is not sent, though we plan to add some. If `DO_NOT_TRACK=1`, then auto-uploading crash reports and telemetry are both [disabled](https://do-not-track.dev/). |
+| `DO_NOT_TRACK` | Disable uploading crash reports to `bun.report` on crash. On macOS & Windows, crash report uploads are enabled by default. Bun sends no other telemetry, though we plan to add some. If `DO_NOT_TRACK=1`, then auto-uploading crash reports and telemetry are both [disabled](https://do-not-track.dev/). |
| `BUN_OPTIONS` | Prepends command-line arguments to any Bun execution. For example, `BUN_OPTIONS="--hot"` makes `bun run dev` behave like `bun --hot run dev`. |
## Runtime transpiler caching
For files larger than 4 KB, Bun caches transpiled output into `$BUN_RUNTIME_TRANSPILER_CACHE_PATH` or the platform-specific cache directory. This makes CLIs using Bun load faster.
-The cache is global and shared across all projects, and it is content-addressable, so it never contains duplicate entries. It is safe to delete at any time, even while a Bun process is running.
+The cache is global and shared across all projects. It is content-addressable, so it never contains duplicate entries. It is safe to delete at any time, even while a Bun process is running.
Disable this cache when using ephemeral filesystems like Docker. Bun's Docker images disable it automatically.
diff --git a/docs/runtime/ffi.mdx b/docs/runtime/ffi.mdx
index a5fc6766bd5e..159569e60cd8 100644
--- a/docs/runtime/ffi.mdx
+++ b/docs/runtime/ffi.mdx
@@ -4,8 +4,8 @@ description: Use Bun's FFI module to efficiently call native libraries from Java
---
- `bun:ffi` is **experimental**, with known bugs and limitations, and should not be relied on in production. The most
- stable way to interact with native code from Bun is to write a [Node-API module](/runtime/node-api).
+ `bun:ffi` is **experimental**, with known bugs and limitations. Do not rely on it in production. The most stable way
+ to interact with native code from Bun is to write a [Node-API module](/runtime/node-api).
Use the built-in `bun:ffi` module to efficiently call native libraries from JavaScript. It works with any language that supports the C ABI, including Zig, Rust, C/C++, C#, Nim, and Kotlin.
@@ -49,7 +49,7 @@ According to [our benchmark](https://github.com/oven-sh/bun/tree/main/bench/ffi)
-`dlopen`, `linkSymbols`, `CFunction`, and `JSCallback` are implemented natively by Bun's JavaScript engine (JavaScriptCore): argument conversion, arity handling, and result boxing happen in-engine, and hot call sites compile down through the DFG/FTL JIT tiers into direct native calls with no per-argument JavaScript shim. [TinyCC](https://github.com/TinyCC/tinycc), a small and fast C compiler, is embedded only for [`cc()`](/runtime/c-compiler), which compiles C source you provide at runtime.
+Bun's JavaScript engine (JavaScriptCore) implements `dlopen`, `linkSymbols`, `CFunction`, and `JSCallback` natively. Argument conversion, arity handling, and result boxing happen in-engine. Hot call sites compile down through the DFG/FTL JIT tiers into direct native calls with no per-argument JavaScript shim. Bun embeds [TinyCC](https://github.com/TinyCC/tinycc), a small and fast C compiler, only for [`cc()`](/runtime/c-compiler), which compiles C source you provide at runtime.
---
@@ -158,9 +158,9 @@ The following `FFIType` values are supported.
`buffer_length` is `buffer`'s length twin: pass the **same** `TypedArray`/`DataView` you passed
for the `buffer` parameter, and the callee receives that view's **byte length** as an unsigned
64-bit integer. The engine reads the pointer and the length off the same object at the moment of
-the call, so the two always agree — an atomic snapshot you can't get by passing
-`view.byteLength` yourself (a length read in JavaScript beforehand can go stale against a
-resizable, growable, or transferred buffer). It's argument-only and, like the napi types, not
+the call, so the two always agree. You can't get that atomic snapshot by passing `view.byteLength`
+yourself, because a length read in JavaScript beforehand can go stale against a resizable,
+growable, or transferred buffer. `buffer_length` is argument-only and, like the napi types, not
available inside `cc()`.
```ts
@@ -174,10 +174,10 @@ const chunk = new TextEncoder().encode("hello");
write_all(1, chunk, chunk); // buf and len both come from `chunk`
```
-`napi_env` and `napi_value` are only valid in [`cc()`](/runtime/c-compiler) source, where a
-`napi_env` parameter is filled in with the module's environment by the compiled trampoline (the
-JavaScript argument passed at that position is a placeholder and is ignored) and `napi_value`
-passes the JavaScript value through unchanged. Using either type in a `dlopen`, `linkSymbols`, `JSCallback`,
+`napi_env` and `napi_value` are only valid in [`cc()`](/runtime/c-compiler) source. There, the
+compiled trampoline fills in a `napi_env` parameter with the module's environment; the JavaScript
+argument passed at that position is a placeholder and is ignored. `napi_value` passes the
+JavaScript value through unchanged. Using either type in a `dlopen`, `linkSymbols`, `JSCallback`,
or `CFunction` descriptor throws a `TypeError`.
---
@@ -219,7 +219,7 @@ To convert from a pointer with a known length to a JavaScript string:
const myString = new CString(ptr, 0, byteLength);
```
-`new CString()` returns a normal string (`typeof myString === "string"`, `myString === "hello"` works) that is a clone of the C string, so it is safe to continue using it after `ptr` has been freed.
+`new CString()` returns a normal string (`typeof myString === "string"`, `myString === "hello"` works). The string is a clone of the C string, so you can safely keep using it after `ptr` has been freed.
```ts
const myString = new CString(ptr);
@@ -229,20 +229,20 @@ my_library_free(ptr);
console.log(myString);
```
-When used in `returns`, `FFIType.cstring` coerces the pointer to a JavaScript `string`. When used in `args`, `FFIType.cstring` accepts everything `ptr` does **and** additionally accepts a JavaScript string directly — the engine transcodes it to a null-terminated UTF-8 buffer that lives for the duration of the call, so you don't need to encode it into a `Buffer` yourself:
+When used in `returns`, `FFIType.cstring` coerces the pointer to a JavaScript `string`. When used in `args`, `FFIType.cstring` accepts everything `ptr` does **and** additionally accepts a JavaScript string directly. The engine transcodes the string to a null-terminated UTF-8 buffer that lives for the duration of the call, so you don't need to encode it into a `Buffer` yourself:
```ts
symbols.puts("Hello, world!"); // args: ["cstring"] — pass the string directly
```
-**Lifetime of a `cstring` return.** The pointer is whatever the C function returned — memory
-owned by the native side (a static, a buffer it manages, or heap it allocated); the engine copies
-nothing on return, and the JavaScript string is cloned out of it. The one aliasing case is a C
-function that hands back a pointer _derived from a `cstring` argument you passed as a JavaScript
-string_: that argument was transcoded into the engine's call-scoped buffer, so treat such a
-returned pointer as valid only until your next FFI call reuses that buffer (the usual C rule for
-functions that return their input). Clone it (via the returned string, or `new CString`) rather
-than holding the raw address.
+**Lifetime of a `cstring` return.** The pointer is whatever the C function returned: memory
+owned by the native side (a static, a buffer it manages, or heap it allocated). The engine copies
+nothing on return, and the JavaScript string is cloned out of that memory. The one aliasing case is
+a C function that hands back a pointer _derived from a `cstring` argument you passed as a JavaScript
+string_. The engine transcodes that argument into its call-scoped buffer, so treat such a returned
+pointer as valid only until your next FFI call reuses that buffer (the usual C rule for functions
+that return their input). Clone it (via the returned string, or `new CString`) rather than holding
+the raw address.
---
@@ -341,7 +341,7 @@ When you're done with a `JSCallback`, call `close()` to free the memory.
`JSCallback` has experimental support for thread-safe callbacks. You need this if you pass a callback function into a different thread from the one that created it. Enable it with the optional `threadsafe` parameter.
-Thread-safe callbacks can be invoked from **any thread** — including threads spawned by your native library that Bun is not otherwise aware of. The engine copies the C arguments on the calling thread and marshals the invocation onto the JavaScript thread, where the arguments are converted (64-bit integers and pointers arrive as exact BigInts) and your function runs. Because the invocation is asynchronous from C's point of view, the value returned to the C caller is unspecified: you may declare a non-`void` `returns` (the example below uses `"bool"`), but the C side must treat a thread-safe callback as returning `void` and ignore its return value.
+Thread-safe callbacks can be invoked from **any thread** — including threads spawned by your native library that Bun is not otherwise aware of. The engine copies the C arguments on the calling thread and marshals the invocation onto the JavaScript thread. There it converts the arguments (64-bit integers and pointers arrive as exact BigInts) and runs your function. Because the invocation is asynchronous from C's point of view, the value returned to the C caller is unspecified. You may declare a non-`void` `returns` (the example below uses `"bool"`), but the C side must treat a thread-safe callback as returning `void` and ignore its return value.
```ts
const searchIterator = new JSCallback((ptr, length) => /hello/.test(new CString(ptr, length)), {
@@ -384,7 +384,7 @@ Bun represents [pointers]( "my-package"
```
-During bundling, the parsed JSON is inlined into the bundle as a JavaScript object.
+During bundling, Bun inlines the parsed JSON into the bundle as a JavaScript object.
```ts
var pkg = {
@@ -64,7 +64,7 @@ var pkg = {
pkg.name;
```
-If a `.json` file is passed as an entrypoint to the bundler, it is converted to a `.js` module that `export default`s the parsed object.
+If you pass a `.json` file as an entrypoint to the bundler, Bun converts it to a `.js` module that `export default`s the parsed object.
@@ -97,7 +97,7 @@ import config from "./config.jsonc";
console.log(config);
```
-During bundling, the parsed JSONC is inlined into the bundle as a JavaScript object, identical to the `json` loader.
+During bundling, Bun inlines the parsed JSONC into the bundle as a JavaScript object, identical to the `json` loader.
```ts
var config = {
@@ -123,7 +123,7 @@ config.logLevel; // => "debug"
// import myCustomTOML from './my.config' with {type: "toml"};
```
-During bundling, the parsed TOML is inlined into the bundle as a JavaScript object.
+During bundling, Bun inlines the parsed TOML into the bundle as a JavaScript object.
```ts
var config = {
@@ -133,7 +133,7 @@ var config = {
config.logLevel;
```
-If a `.toml` file is passed as an entrypoint, it is converted to a `.js` module that `export default`s the parsed object.
+If you pass a `.toml` file as an entrypoint, Bun converts it to a `.js` module that `export default`s the parsed object.
@@ -167,7 +167,7 @@ console.log(config);
import data from "./data.txt" with { type: "yaml" };
```
-During bundling, the parsed YAML is inlined into the bundle as a JavaScript object.
+During bundling, Bun inlines the parsed YAML into the bundle as a JavaScript object.
```ts
var config = {
@@ -177,7 +177,7 @@ var config = {
};
```
-If a `.yaml` or `.yml` file is passed as an entrypoint, it is converted to a `.js` module that `export default`s the parsed object.
+If you pass a `.yaml` or `.yml` file as an entrypoint, Bun converts it to a `.js` module that `export default`s the parsed object.
@@ -211,7 +211,7 @@ console.log(config);
import data from "./data.txt" with { type: "json5" };
```
-During bundling, the parsed JSON5 is inlined into the bundle as a JavaScript object.
+During bundling, Bun inlines the parsed JSON5 into the bundle as a JavaScript object.
```ts
var config = {
@@ -221,7 +221,7 @@ var config = {
};
```
-If a `.json5` file is passed as an entrypoint, it is converted to a `.js` module that `export default`s the parsed object.
+If you pass a `.json5` file as an entrypoint, Bun converts it to a `.js` module that `export default`s the parsed object.
@@ -248,7 +248,13 @@ export default {
**XML loader**. Default for `.xml`.
-XML files can be directly imported. Bun parses them with its native XML 1.0 parser into the compact object shape of [`Bun.XML.parse`](/runtime/xml): one key for the root element, `"@name"` keys for attributes, arrays for repeated child elements, `"#text"` for text next to attributes or children, and every value a string.
+XML files can be directly imported. Bun parses them with its native XML 1.0 parser into the compact object shape of [`Bun.XML.parse`](/runtime/xml):
+
+- One key for the root element
+- `"@name"` keys for attributes
+- Arrays for repeated child elements
+- `"#text"` for text next to attributes or children
+- Every value is a string
```ts
import doc from "./config.xml";
@@ -258,7 +264,7 @@ console.log(doc.config["@version"]);
import feed from "./export.rss" with { type: "xml" };
```
-During bundling, the parsed XML is inlined into the bundle as a JavaScript object.
+During bundling, Bun inlines the parsed XML into the bundle as a JavaScript object.
```ts
var doc = {
@@ -269,7 +275,7 @@ var doc = {
};
```
-If a `.xml` file is passed as an entrypoint, it is converted to a `.js` module that `export default`s the parsed object.
+If you pass a `.xml` file as an entrypoint, Bun converts it to a `.js` module that `export default`s the parsed object.
@@ -299,7 +305,7 @@ export default {
**Text loader**. Default for `.txt`.
-Text files can be directly imported. The file is read and returned as a string.
+Text files can be directly imported. Bun reads the file and returns it as a string.
```ts
import contents from "./file.txt";
@@ -310,14 +316,14 @@ console.log(contents); // => "Hello, world!"
import html from "./index.html" with { type: "text" };
```
-When referenced during a build, the contents are inlined into the bundle as a string.
+When the file is referenced during a build, Bun inlines the contents into the bundle as a string.
```ts
var contents = `Hello, world!`;
console.log(contents);
```
-If a `.txt` file is passed as an entrypoint, it is converted to a `.js` module that `export default`s the file contents.
+If you pass a `.txt` file as an entrypoint, Bun converts it to a `.js` module that `export default`s the file contents.
@@ -342,21 +348,21 @@ import addon from "./addon.node";
console.log(addon);
```
-In the bundler, `.node` files are handled using the [`file`](#file) loader.
+In the bundler, Bun handles `.node` files using the [`file`](#file) loader.
### `sqlite`
**SQLite loader**. `with { "type": "sqlite" }` import attribute
-In the runtime and bundler, SQLite databases can be directly imported. The database is loaded with [`bun:sqlite`](/runtime/sqlite).
+In the runtime and bundler, SQLite databases can be directly imported. Bun loads the database with [`bun:sqlite`](/runtime/sqlite).
```ts
import db from "./my.db" with { type: "sqlite" };
```
-This is only supported when the `target` is `bun`.
+The `sqlite` loader is only supported when the `target` is `bun`.
-By default, the database is external to the bundle: the on-disk database file isn't bundled into the final output, so you can use a database loaded elsewhere.
+By default, the database is external to the bundle: Bun doesn't bundle the on-disk database file into the final output, so you can use a database loaded elsewhere.
You can change this behavior with the `"embed"` attribute:
@@ -365,7 +371,7 @@ You can change this behavior with the `"embed"` attribute:
import db from "./my.db" with { type: "sqlite", embed: "true" };
```
-When using a [standalone executable](/bundler/executables), the database is embedded into the single-file executable.
+With a [standalone executable](/bundler/executables), Bun embeds the database into the single-file executable.
Otherwise, the database to embed is copied into the `outdir` with a hashed filename.
@@ -487,7 +493,7 @@ bun run logo.ts
/path/to/project/logo.svg
```
-_In the bundler_, the file is copied into `outdir` as-is, and the import resolves to a relative path pointing to the copied file.
+_In the bundler_, Bun copies the file into `outdir` as-is, and the import resolves to a relative path pointing to the copied file.
```ts Output
var logo = "./logo.svg";
@@ -502,9 +508,7 @@ If `publicPath` is set, the import uses its value as a prefix to construct an ab
| `"/assets/"` | `/assets/logo.svg` |
| `"https://cdn.example.com/"` | `https://cdn.example.com/logo.svg` |
-
- The location and file name of the copied file is determined by the value of [`naming.asset`](/bundler#naming).
-
+The value of [`naming.asset`](/bundler#naming) determines the location and file name of the copied file.
If you're using TypeScript, you may get an error like this:
@@ -523,6 +527,6 @@ declare module "*.svg" {
}
```
-This tells TypeScript that any default imports from `.svg` should be treated as a string.
+This tells TypeScript to treat any default import from `.svg` as a string.
diff --git a/docs/runtime/glob.mdx b/docs/runtime/glob.mdx
index 06289b3c8de8..e5b0f22d8212 100644
--- a/docs/runtime/glob.mdx
+++ b/docs/runtime/glob.mdx
@@ -120,7 +120,7 @@ glob.match("baz.ts"); // => true
glob.match("bat.ts"); // => false
```
-You can use character ranges (for example `[0-9]`, `[a-z]`) and the negation operators `^` or `!` to match anything _except_ the characters in the brackets (for example `[^ab]`, `[!a-z]`).
+You can use character ranges (for example `[0-9]`, `[a-z]`). The negation operators `^` or `!` match anything _except_ the characters in the brackets (for example `[^ab]`, `[!a-z]`).
```ts
const glob = new Glob("ba[a-z][0-9][^4-9].ts");
@@ -141,7 +141,7 @@ glob.match("c.ts"); // => true
glob.match("d.ts"); // => false
```
-These patterns can be nested up to 10 levels deep and contain any of the earlier wildcards.
+You can nest these patterns up to 10 levels deep, and they can contain any of the earlier wildcards.
### `!` - Negates the result at the start of a pattern
diff --git a/docs/runtime/hashing.mdx b/docs/runtime/hashing.mdx
index cd72e044099e..8a66a5d5e67b 100644
--- a/docs/runtime/hashing.mdx
+++ b/docs/runtime/hashing.mdx
@@ -43,7 +43,7 @@ const bcryptHash = await Bun.password.hash(password, {
});
```
-The algorithm used to create the hash is stored in the hash itself. When using `bcrypt`, the returned hash is encoded in [Modular Crypt Format](https://passlib.readthedocs.io/en/stable/modular_crypt_format.html) for compatibility with most existing `bcrypt` implementations; with `argon2` the result is encoded in the newer [PHC format](https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md).
+The algorithm used to create the hash is stored in the hash itself. When using `bcrypt`, Bun encodes the returned hash in [Modular Crypt Format](https://passlib.readthedocs.io/en/stable/modular_crypt_format.html) for compatibility with most existing `bcrypt` implementations. With `argon2`, Bun encodes the result in the newer [PHC format](https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md).
The `verify` function detects the algorithm from the input hash, whether PHC- or MCF-encoded, and uses the matching verification method.
@@ -188,7 +188,7 @@ Bun.hash.rapidhash("data", 1234);
## `Bun.CryptoHasher`
-`Bun.CryptoHasher` incrementally computes a hash of string or binary data with a cryptographic hash algorithm. The following algorithms are supported:
+`Bun.CryptoHasher` incrementally computes a hash of string or binary data with a cryptographic hash algorithm. It supports the following algorithms:
- `"blake2b256"`
- `"blake2b512"`
@@ -242,7 +242,7 @@ hasher.update("hello world", "base64");
hasher.update("hello world", "latin1");
```
-Once all the data is fed in, compute the final hash with `.digest()`. By default, this method returns a `Uint8Array` containing the hash.
+Once you have fed in all the data, compute the final hash with `.digest()`. By default, this method returns a `Uint8Array` containing the hash.
```ts
const hasher = new Bun.CryptoHasher("sha256");
@@ -303,9 +303,9 @@ HMAC supports a more limited set of algorithms:
- `"sha3-384"`
- `"sha3-512"`
-Unlike the non-HMAC `Bun.CryptoHasher`, the HMAC `Bun.CryptoHasher` instance is not reset after `.digest()` is called, and using the same instance again throws an error.
+Unlike the non-HMAC `Bun.CryptoHasher`, the HMAC `Bun.CryptoHasher` instance does not reset after you call `.digest()`. Using the same instance again throws an error.
-Other methods like `.copy()` and `.update()` are supported (as long as it's before `.digest()`), but methods like `.digest()` that finalize the hasher are not.
+Other methods like `.copy()` and `.update()` are supported as long as you call them before `.digest()`, but methods like `.digest()` that finalize the hasher are not.
```ts
const hasher = new Bun.CryptoHasher("sha256", "secret-key");
diff --git a/docs/runtime/html-rewriter.mdx b/docs/runtime/html-rewriter.mdx
index 60e31af4dc96..ad70d1ca7f9e 100644
--- a/docs/runtime/html-rewriter.mdx
+++ b/docs/runtime/html-rewriter.mdx
@@ -126,8 +126,8 @@ rewriter.on("div", {
});
```
-`transform(response)` returns immediately; the rewrite continues in the
-background and you read the result off the returned `Response`. Reading it
+`transform(response)` returns immediately. The rewrite continues in the
+background, and you read the result off the returned `Response`. Reading it
paces the rewrite: a streamed input (a file, a `fetch()` response, a
`ReadableStream`) is pulled through only as fast as the returned body is
consumed, so a slow reader does not accumulate the whole document in memory. If
@@ -162,10 +162,10 @@ new HTMLRewriter()
// microtask. Pass a Response instead and await its body
```
-A handler whose Promise settles within a microtask checkpoint (anything that
-does not need the event loop, including `process.nextTick` and already-resolved
-Promises) still works with `transform(string)`. Pass a `Response` whenever a
-handler might await real work.
+A handler whose Promise settles within a microtask checkpoint still works with
+`transform(string)`. Anything that does not need the event loop qualifies,
+including `process.nextTick` and already-resolved Promises. Pass a `Response`
+whenever a handler might await real work.
### CSS Selector Support
@@ -209,7 +209,7 @@ rewriter.on("*", handler);
### Element Operations
-All element modification methods return the element instance, so calls can be chained:
+All element modification methods return the element instance, so you can chain calls:
```ts
rewriter.on("div", {
@@ -339,25 +339,25 @@ rewriter.onDocument({
### Response Handling
-When transforming a Response:
+When transforming a Response, HTMLRewriter:
-- The status code, headers, and other response properties are preserved
-- The body is transformed while maintaining streaming capabilities
-- Content-encoding (like gzip) is handled automatically
-- The original response body is marked as used after transformation
-- Headers are cloned to the new response
+- Preserves the status code, headers, and other response properties
+- Transforms the body while maintaining streaming capabilities
+- Handles content-encoding (like gzip) automatically
+- Marks the original response body as used after transformation
+- Clones headers to the new response
## Error Handling
-Which channel an error takes is decided by the overload you called, never by
-timing. `transform()` itself throws for:
+The overload you called decides which channel an error takes. Timing never
+does. `transform()` itself throws for:
- Invalid selector syntax in the `on()` method
- Invalid input types (for example, passing a Symbol)
- Body already used errors, and input bodies that have already failed or aborted
- Anything a content handler raises on a `string` / `ArrayBuffer` input, since
- those have to produce their result before `transform()` returns — including a
- handler that needs the event loop (see [Element Handlers](#element-handlers))
+ those have to produce their result before `transform()` returns. The same goes
+ for a handler that needs the event loop (see [Element Handlers](#element-handlers))
```ts
try {
@@ -383,10 +383,10 @@ try {
}
```
-A rejection from a Promise a handler creates but neither returns nor awaits
-reaches neither channel: like any detached rejection, it goes to the
-process-global `unhandledRejection` path. Earlier versions of Bun could surface
-it from `transform()` itself.
+If a handler creates a Promise but neither returns nor awaits it, a rejection
+from that Promise reaches neither channel. Like any detached rejection, it goes
+to the process-global `unhandledRejection` path. Earlier versions of Bun could
+surface it from `transform()` itself.
---
diff --git a/docs/runtime/http/routing.mdx b/docs/runtime/http/routing.mdx
index 36295e669f1e..9cc65aed41d8 100644
--- a/docs/runtime/http/routing.mdx
+++ b/docs/runtime/http/routing.mdx
@@ -5,7 +5,7 @@ description: Define routes in `Bun.serve` using static paths, parameters, and wi
Add routes to `Bun.serve()` with the `routes` property (static paths, parameters, and wildcards), or handle unmatched requests with the [`fetch`](#fetch) method.
-`Bun.serve()`'s router builds on top of uWebSocket's [tree-based approach](https://github.com/oven-sh/bun/blob/0d1a00fa0f7830f8ecd99c027fce8096c9d459b6/packages/bun-uws/src/HttpRouter.h#L57-L64) to add [SIMD-accelerated route parameter decoding](https://github.com/oven-sh/bun/blob/main/src/jsc/bindings/decodeURIComponentSIMD.cpp#L21-L271) and [JavaScriptCore structure caching](https://github.com/oven-sh/bun/blob/main/src/jsc/bindings/ServerRouteList.cpp#L100-L101) to push the performance limits of what modern hardware allows.
+`Bun.serve()`'s router builds on top of uWebSocket's [tree-based approach](https://github.com/oven-sh/bun/blob/0d1a00fa0f7830f8ecd99c027fce8096c9d459b6/packages/bun-uws/src/HttpRouter.h#L57-L64). The router adds [SIMD-accelerated route parameter decoding](https://github.com/oven-sh/bun/blob/main/src/jsc/bindings/decodeURIComponentSIMD.cpp#L21-L271) and [JavaScriptCore structure caching](https://github.com/oven-sh/bun/blob/main/src/jsc/bindings/ServerRouteList.cpp#L100-L101) to push the performance limits of what modern hardware allows.
## Basic Setup
@@ -22,7 +22,7 @@ Bun.serve({
});
```
-Routes in `Bun.serve()` receive a `BunRequest` (which extends [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)) and return a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) or `Promise`. This makes it easier to use the same code for both sending & receiving HTTP requests.
+Routes in `Bun.serve()` receive a `BunRequest` (which extends [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)) and return a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) or `Promise`. Because routes use these `Request` and `Response` types, it is easier to use the same code for both sending and receiving HTTP requests.
```ts
// Simplified for brevity
@@ -77,7 +77,7 @@ serve({
## Route precedence
-Routes are matched in order of specificity:
+Bun matches routes in order of specificity:
1. Exact routes (`/users/all`)
2. Parameter routes (`/users/:id`)
@@ -124,7 +124,7 @@ Bun.serve({
});
```
-Bun automatically decodes percent-encoded route parameter values, including Unicode characters. Invalid Unicode is replaced with the Unicode replacement character (`\uFFFD`).
+Bun automatically decodes percent-encoded route parameter values, including Unicode characters. Bun replaces invalid Unicode with the Unicode replacement character (`\uFFFD`).
### Static responses
@@ -156,7 +156,7 @@ Bun.serve({
Static responses do not allocate additional memory after initialization. You can generally expect at least a 15% performance improvement over manually returning a `Response` object.
-Static route responses are cached for the lifetime of the server object. To reload static routes, call `server.reload(options)`.
+Bun caches static route responses for the lifetime of the server object. To reload static routes, call `server.reload(options)`.
### File Responses vs Static Responses
@@ -206,10 +206,10 @@ Bun.serve({
});
```
-The part of the request URL after the prefix is percent-decoded once and opened relative to `dir`. Non-canonical paths (those containing `.`, `..`, empty segments, `%2F`, or a `%XX` sequence encoding a character that may appear literally in a path segment) are rejected with `404`, so the served path is always the path the router matched. On Linux the open uses `openat2(RESOLVE_IN_ROOT)`, so symlinks that would escape `dir` are clamped by the kernel.
+Bun percent-decodes the part of the request URL after the prefix once and opens it relative to `dir`. Bun rejects non-canonical paths with `404`, so the served path is always the path the router matched. A path is non-canonical if it contains `.`, `..`, empty segments, `%2F`, or a `%XX` sequence encoding a character that may appear literally in a path segment. On Linux the open uses `openat2(RESOLVE_IN_ROOT)`, so the kernel clamps symlinks that would escape `dir`.
{% callout %}
-Routing is case-sensitive but filesystems on macOS and Windows are case-insensitive by default, so a case-varied URL (`/static/Admin/secret.txt`) will route to the directory wildcard rather than a sibling `/static/admin/*` handler and still open `admin/secret.txt`. As with nginx, Caddy, and other static file servers, do not place access-controlled content inside `dir` and rely on an overlapping route to gate it.
+Routing is case-sensitive, but filesystems on macOS and Windows are case-insensitive by default. As a result, a case-varied URL (`/static/Admin/secret.txt`) routes to the directory wildcard rather than a sibling `/static/admin/*` handler and still opens `admin/secret.txt`. As with nginx, Caddy, and other static file servers, keep access-controlled content outside `dir` rather than relying on an overlapping route to gate it.
{% /callout %}
Directory routes share the response path with file routes:
@@ -217,7 +217,7 @@ Directory routes share the response path with file routes:
- **Content-Type** is set from the file extension.
- **Last-Modified** and a weak `ETag` (`W/"-"`) are sent on every response, and `If-Modified-Since` / `If-None-Match` are honored with `304 Not Modified`.
- **Range requests** are supported with `Accept-Ranges: bytes` and `Content-Range`.
-- A request that resolves to a directory without a trailing `/` is answered with a `301` redirect to the trailing-slash URL; with the trailing slash, `index.html` from that directory is served.
+- A request that resolves to a directory without a trailing `/` receives a `301` redirect to the trailing-slash URL. With the trailing slash, Bun serves `index.html` from that directory.
- Missing files return `404`.
Pass `statCache: false` to disable the per-path `Last-Modified` cache (saves roughly 20 KB per route).
diff --git a/docs/runtime/http/server.mdx b/docs/runtime/http/server.mdx
index fc935f9b93a6..37c91f8eaa00 100644
--- a/docs/runtime/http/server.mdx
+++ b/docs/runtime/http/server.mdx
@@ -66,7 +66,7 @@ Bun.serve({
});
```
-HTML imports don't just serve HTML: they run Bun's [bundler](/bundler), JavaScript transpiler, and CSS parser, so you can build frontends with React, TypeScript, and Tailwind CSS.
+HTML imports do more than serve HTML: they run Bun's [bundler](/bundler), JavaScript transpiler, and CSS parser, so you can build frontends with React, TypeScript, and Tailwind CSS.
For a complete guide to building full-stack applications with HTML imports, see [fullstack dev server](/bundler/fullstack).
@@ -190,7 +190,7 @@ Bun.serve({
When `http3` is enabled, the server listens on the same port over both TCP (HTTP/1.1) and UDP (HTTP/3). HTTP/1.1 responses include an `Alt-Svc` header advertising the HTTP/3 endpoint so capable clients can upgrade automatically.
-To serve HTTP/3 only — no TCP listener at all — set `http1: false`:
+To serve HTTP/3 only, with no TCP listener at all, set `http1: false`:
```ts
Bun.serve({
@@ -207,14 +207,14 @@ Bun.serve({
```
- `http3` is not supported with unix domain sockets — QUIC requires a UDP port. `http1: false` requires `http3: true`.
+ `http3` is not supported with unix domain sockets: QUIC requires a UDP port. `http1: false` requires `http3: true`.
---
## idleTimeout
-By default, `Bun.serve` closes connections after **10 seconds** of inactivity. A connection is idle when no data is being sent or received, including in-flight requests where your handler is still running but hasn't written any bytes to the response yet. Browsers and `fetch()` clients see this as a connection reset.
+By default, `Bun.serve` closes connections after **10 seconds** of inactivity. A connection is idle when no data is being sent or received. That includes in-flight requests where your handler is still running but hasn't written any bytes to the response yet. Browsers and `fetch()` clients see this as a connection reset.
To configure this, set the `idleTimeout` field (in seconds). The maximum value is `255`, and `0` disables the timeout entirely.
@@ -253,7 +253,7 @@ export default {
The type parameter `` is the WebSocket data type. If you add a `websocket` handler that attaches custom data with `server.upgrade(req, { data: ... })`, replace `undefined` with your data type.
-You can run this file as-is: when Bun sees a file with a `default` export containing a `fetch` handler, it passes it into `Bun.serve`.
+You can run this file as-is: when Bun sees a file with a `default` export containing a `fetch` handler, it passes the export into `Bun.serve`.
---
@@ -298,7 +298,7 @@ await server.stop();
await server.stop(true);
```
-By default, `stop()` allows in-flight requests and WebSocket connections to complete. Idle keep-alive connections are closed immediately, and connections with a request in flight close once their response has been sent. Pass `true` to immediately terminate all connections instead. The returned promise resolves once every connection has closed.
+By default, `stop()` allows in-flight requests and WebSocket connections to complete. The server closes idle keep-alive connections immediately. Connections with a request in flight close once the server has sent their response. Pass `true` to immediately terminate all connections instead. The returned promise resolves once every connection has closed.
### `server.closeIdleConnections()`
@@ -309,7 +309,7 @@ const closed = server.closeIdleConnections();
console.log(`closed ${closed} idle connections`);
```
-It returns the number of connections it closed. Connections with a request in flight and open WebSockets are untouched, and the server keeps accepting new connections. This mirrors `node:http`'s `server.closeIdleConnections()`, which returns nothing.
+It returns the number of connections it closed. Connections with a request in flight and open WebSockets are untouched, and the server keeps accepting new connections. This method mirrors `node:http`'s `server.closeIdleConnections()`, which returns nothing.
### `server.ref()` and `server.unref()`
@@ -348,7 +348,7 @@ server.reload({
});
```
-Use this for development and hot reloading. Only `fetch`, `error`, `routes`, and `websocket` can be updated.
+Use this for development and hot reloading. You can update only `fetch`, `error`, `routes`, and `websocket`.
---
diff --git a/docs/runtime/http/websockets.mdx b/docs/runtime/http/websockets.mdx
index c5a4785f777d..3777a1726909 100644
--- a/docs/runtime/http/websockets.mdx
+++ b/docs/runtime/http/websockets.mdx
@@ -24,7 +24,7 @@ Internally Bun's WebSocket implementation is built on [uWebSockets](https://gith
## Start a WebSocket server
-The following server, built with `Bun.serve`, [upgrades](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) every incoming request to a WebSocket connection in the `fetch` handler. The socket handlers are declared in the `websocket` parameter.
+The following server, built with `Bun.serve`, [upgrades](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) every incoming request to a WebSocket connection in the `fetch` handler. You declare the socket handlers in the `websocket` parameter.
```ts server.ts icon="/icons/typescript.svg"
Bun.serve({
@@ -55,9 +55,9 @@ Bun.serve({
-In Bun, handlers are declared once per server, instead of per socket.
+In Bun, you declare handlers once per server, instead of per socket.
-You pass a single `WebSocketHandler` object to `Bun.serve()` with methods for `open`, `message`, `close`, `drain`, and `error`. This is different from the client-side `WebSocket` class, which extends `EventTarget` (`onmessage`, `onopen`, `onclose`).
+You pass a single `WebSocketHandler` object to `Bun.serve()` with methods for `open`, `message`, `close`, `drain`, and `error`. This design differs from the client-side `WebSocket` class, which extends `EventTarget` (`onmessage`, `onopen`, `onclose`).
Clients tend to have few socket connections open, so an event-based API makes sense there.
@@ -168,7 +168,7 @@ Bun.serve({
```
-Previously, you could specify the type of `ws.data` with a type parameter on `Bun.serve`, like `Bun.serve({...})`. This pattern was removed due to [a limitation in TypeScript](https://github.com/microsoft/TypeScript/issues/26242) in favor of the `data` property.
+Previously, you could specify the type of `ws.data` with a type parameter on `Bun.serve`, like `Bun.serve({...})`. Bun removed this pattern in favor of the `data` property because of [a limitation in TypeScript](https://github.com/microsoft/TypeScript/issues/26242).
To connect to this server from the browser, create a new `WebSocket`.
@@ -184,13 +184,13 @@ socket.addEventListener("message", event => {
**Identifying users**
-Cookies set on the page are sent with the WebSocket upgrade request and available on `req.headers` in the `fetch` handler. Parse them to identify the connecting user and set `data` accordingly.
+The browser sends cookies set on the page along with the WebSocket upgrade request. They are available on `req.headers` in the `fetch` handler. Parse them to identify the connecting user and set `data` accordingly.
### Pub/Sub
-Bun's `ServerWebSocket` includes a native publish-subscribe API for topic-based broadcasting. Individual sockets can `.subscribe()` to a topic (specified with a string identifier) and `.publish()` messages to all other subscribers to that topic (excluding itself). This topic-based broadcast API is similar to [MQTT](https://en.wikipedia.org/wiki/MQTT) and [Redis Pub/Sub](https://redis.io/topics/pubsub).
+Bun's `ServerWebSocket` includes a native publish-subscribe API for topic-based broadcasting. You specify a topic with a string identifier. An individual socket can `.subscribe()` to a topic and `.publish()` messages to all other subscribers to that topic (excluding itself). This topic-based broadcast API is similar to [MQTT](https://en.wikipedia.org/wiki/MQTT) and [Redis Pub/Sub](https://redis.io/topics/pubsub).
```ts server.ts icon="/icons/typescript.svg"
const server = Bun.serve({
@@ -310,7 +310,7 @@ const socket = new WebSocket("ws://localhost:3000");
const socket2 = new WebSocket("ws://localhost:3000", ["soap", "wamp"]);
```
-In browsers, cookies set on the page are sent with the WebSocket upgrade request. This is a standard feature of the `WebSocket` API.
+Browsers send cookies set on the page along with the WebSocket upgrade request. This is a standard feature of the `WebSocket` API.
In Bun, you can also set custom headers directly in the constructor. This is a Bun-specific extension of the `WebSocket` standard. _It does not work in browsers._
diff --git a/docs/runtime/image.mdx b/docs/runtime/image.mdx
index d9dd4d6e02b1..dba2f7d5c1f8 100644
--- a/docs/runtime/image.mdx
+++ b/docs/runtime/image.mdx
@@ -3,13 +3,13 @@ title: Image
description: Decode, transform, and encode images with a fast native pipeline
---
-`Bun.Image` is a chainable image pipeline for decoding, resizing, rotating, and re-encoding JPEG, PNG, WebP, HEIC, and AVIF — built on libjpeg-turbo, spng, libwebp, and SIMD geometry kernels, with zero npm dependencies and no native addon build step.
+`Bun.Image` is a chainable image pipeline for decoding, resizing, rotating, and re-encoding JPEG, PNG, WebP, HEIC, and AVIF. It is built on libjpeg-turbo, spng, libwebp, and SIMD geometry kernels, with zero npm dependencies and no native addon build step.
```ts
await Bun.file("photo.jpg").image().resize(400, 400, { fit: "inside" }).webp({ quality: 80 }).write("thumb.webp");
```
-The API is shaped after [Sharp](https://sharp.pixelplumbing.com/): construct from an input, chain transforms, pick an output format, then `await` a terminal method. Nothing runs until the terminal is awaited, and the work executes off the JavaScript thread.
+The API is shaped after [Sharp](https://sharp.pixelplumbing.com/): construct from an input, chain transforms, pick an output format, then `await` a terminal method. Nothing runs until you await the terminal, and the work executes off the JavaScript thread.
## Input
@@ -23,11 +23,11 @@ Bun.file("photo.jpg").image(); // same as above
Bun.s3.file("bucket/photo.jpg").image(); // S3File
```
-The format is sniffed from the bytes — extensions and `Content-Type` are ignored.
+Bun sniffs the format from the bytes and ignores extensions and `Content-Type`.
**Path strings are filesystem paths.** Don't pass user-controlled strings directly to the constructor — that's an arbitrary-file-read primitive. Read untrusted input into a `Buffer` (with `fetch` or `Bun.file` and your own validation) and pass the bytes.
-When passing a `TypedArray`/`ArrayBuffer`, don't mutate it while a terminal is pending — decode runs off-thread and borrows the bytes. `SharedArrayBuffer` and resizable buffers are refused; use `buf.slice()` to pass a fixed view.
+When passing a `TypedArray`/`ArrayBuffer`, don't mutate it while a terminal is pending — decode runs off-thread and borrows the bytes. Bun refuses `SharedArrayBuffer` and resizable buffers; use `buf.slice()` to pass a fixed view.
A second `options` argument guards against decompression bombs and controls EXIF handling:
@@ -78,7 +78,7 @@ img.resize(800, 600, { filter: "mitchell" });
| `"box"` | Area-average; good for large integer downscales |
| `"nearest"` | Pixel art / hard edges |
-When the source is a JPEG and the target is at most half the source size, decode skips straight to the nearest M/8 IDCT scale, so generating a thumbnail from a 24 MP photo never materializes the full-resolution buffer.
+When the source is a JPEG and the target is at most half the source size, decode skips straight to the nearest M/8 IDCT scale. As a result, generating a thumbnail from a 24 MP photo never materializes the full-resolution buffer.
## Rotate · flip
@@ -99,7 +99,7 @@ img.modulate({
## Output formats
-Calling a format method sets the encode target; without one, the source format is reused.
+Calling a format method sets the encode target; without one, Bun reuses the source format.
```ts
img.jpeg({ quality: 85 }); // 1–100, default 80
@@ -111,11 +111,11 @@ img.heic({ quality: 80 }); // macOS / Windows only
img.avif({ quality: 60 }); // macOS / Windows only
```
-`palette: true` quantizes to a ≤256-color palette and emits an indexed (color-type 3) PNG, optionally with Floyd–Steinberg `dither`. This is typically 3–5× smaller than truecolor for screenshots and UI assets.
+`palette: true` quantizes to a ≤256-color palette and emits an indexed (color-type 3) PNG, optionally with Floyd–Steinberg `dither`. The indexed PNG is typically 3–5× smaller than truecolor for screenshots and UI assets.
## Terminals
-A pipeline does no work until one of these is awaited:
+A pipeline does no work until you await one of these:
```ts
await img.bytes(); // Uint8Array
@@ -131,7 +131,7 @@ await img.write(Bun.s3.file("bucket/out.webp"));
## Placeholders
-For a low-quality placeholder to inline in HTML before the real image loads, `.placeholder()` returns a [ThumbHash](https://evanw.github.io/thumbhash/)-rendered ≤32px blur as a `data:` URL — ~400–700 bytes, no client-side decoder needed:
+`.placeholder()` returns a low-quality placeholder to inline in HTML before the real image loads. The placeholder is a [ThumbHash](https://evanw.github.io/thumbhash/)-rendered ≤32px blur as a `data:` URL. It is ~400–700 bytes and needs no client-side decoder:
```ts
const lqip = await Bun.file("hero.jpg").image().placeholder();
@@ -176,7 +176,7 @@ if (img) {
`fromClipboard()` reads PNG, TIFF, HEIC, JPEG, WebP, GIF, or BMP from the system pasteboard on macOS and Windows; the regular decode pipeline takes it from there. Returns `null` if there's no image, and always `null` on Linux — call `wl-paste`/`xclip` yourself and pass the bytes to the constructor.
-For a passive "image in clipboard, press ⌘V" hint, poll `clipboardChangeCount()` (a single integer read) and call `hasClipboardImage()` only when it moves; macOS has no clipboard-change notification, so this is the documented pattern.
+For a passive "image in clipboard, press ⌘V" hint, poll `clipboardChangeCount()` (a single integer read) and call `hasClipboardImage()` only when it moves. Polling is the documented pattern because macOS has no clipboard-change notification.
## Platform backends
@@ -192,7 +192,7 @@ For a passive "image in clipboard, press ⌘V" hint, poll `clipboardChangeCount(
¹ Windows requires the **HEIF Image Extensions** / **AV1 Video Extension** from the Microsoft Store.
² AVIF _encode_ needs an OS AV1 encoder — Apple Silicon M3+ only. Intel Mac and M1/M2 reject with `ERR_IMAGE_FORMAT_UNSUPPORTED`; AVIF _decode_ works everywhere ImageIO does (macOS 13+).
-When a system-backend format isn't available on the current machine, the terminal rejects with `error.code === "ERR_IMAGE_FORMAT_UNSUPPORTED"` — branch on that to fall back to a portable format:
+When a system-backend format isn't available on the current machine, the terminal rejects with `error.code === "ERR_IMAGE_FORMAT_UNSUPPORTED"`. Branch on that error code to fall back to a portable format:
```ts
const out = await img
@@ -204,7 +204,7 @@ const out = await img
});
```
-Formats handled by the system backend (TIFF, HEIC, AVIF, clipboard) inherit the **OS's** patch level — keep macOS / Windows updated. JPEG, PNG, and WebP go through the same statically-linked codecs on every platform, so encoded output is byte-identical across Linux, macOS, and Windows. To force the portable Highway path for geometry too — e.g. for golden-image tests — set the process-global backend:
+Formats handled by the system backend (TIFF, HEIC, AVIF, clipboard) inherit the **OS's** patch level, so keep macOS / Windows updated. JPEG, PNG, and WebP go through the same statically-linked codecs on every platform, so encoded output is byte-identical across Linux, macOS, and Windows. To force the portable Highway path for geometry too (e.g. for golden-image tests), set the process-global backend:
```ts
Bun.Image.backend = "bun"; // default is "system" on macOS/Windows
diff --git a/docs/runtime/index.mdx b/docs/runtime/index.mdx
index 5333c55d7e2d..e481ba5bde36 100644
--- a/docs/runtime/index.mdx
+++ b/docs/runtime/index.mdx
@@ -7,7 +7,7 @@ import Run from "/snippets/cli/run.mdx";
The Bun Runtime is designed to start fast and run fast.
-Bun uses the [JavaScriptCore engine](https://developer.apple.com/documentation/javascriptcore), developed by Apple for Safari. It usually starts and runs faster than V8, the engine used by Node.js and Chromium-based browsers. Bun's transpiler and runtime are written in Rust. On Linux, Bun starts [4x faster](https://twitter.com/jarredsumner/status/1499225725492076544) than Node.js.
+Bun uses the [JavaScriptCore engine](https://developer.apple.com/documentation/javascriptcore), developed by Apple for Safari. JavaScriptCore usually starts and runs faster than V8, the engine used by Node.js and Chromium-based browsers. Bun's transpiler and runtime are written in Rust. On Linux, Bun starts [4x faster](https://twitter.com/jarredsumner/status/1499225725492076544) than Node.js.
| Command | Time |
| --------------- | -------- |
@@ -56,7 +56,7 @@ bun --watch run dev # ✔️ do this
bun run dev --watch # ❌ don't do this
```
-Flags at the end of the command are ignored by `bun` and passed through to the `"dev"` script itself.
+`bun` ignores flags at the end of the command and passes them through to the `"dev"` script itself.
@@ -144,7 +144,7 @@ bun run --bun vite
In a monorepo, the `--filter` argument runs a script in many packages at once.
-`bun run --filter