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 performance comparison](https://github.com/user-attachments/assets/dc8913db-01d2-48f8-a8ef-ac4e984f9763) -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: <title>` 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.<title>.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-<title>` 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-<title>` 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 <file> /tn <name> /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 <file> /tn <name> /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 <Callout type="warning"> 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. </Callout> --- @@ -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 --- <Warning> - `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). </Warning> 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) <Image src="/images/ffi.png" height="400" /> -`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](<https://en.wikipedia.org/wiki/Pointer_(computer_progr 64-bit processors support up to [52 bits of addressable space](https://en.wikipedia.org/wiki/64-bit_computing#Limits_of_processors). [JavaScript numbers](https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64) support 53 bits of usable space, which leaves about 11 bits of extra space. -**Why not `BigInt`?** `BigInt` is slower. JavaScript engines allocate `BigInt`s separately, so they can't fit into a regular JavaScript value. If you pass a `BigInt` to a function, it is converted to a `number`. +**Why not `BigInt`?** `BigInt` is slower. JavaScript engines allocate `BigInt`s separately, so they can't fit into a regular JavaScript value. If you pass a `BigInt` to a function, Bun converts it to a `number`. **Windows Note**: The Windows API type HANDLE does not represent a virtual address, and using `ptr` for it does _not_ work as expected. Use `u64` to safely represent HANDLE values. @@ -464,7 +464,7 @@ To track when a `TypedArray` is no longer in use from JavaScript, use a [Finaliz #### From C, Rust, Zig, etc -To track when a `TypedArray` is no longer in use from C or FFI, pass a callback and an optional context pointer to `toArrayBuffer` or `toBuffer`. The callback is called later, once the garbage collector frees the underlying `ArrayBuffer` JavaScript object. +To track when a `TypedArray` is no longer in use from C or FFI, pass a callback and an optional context pointer to `toArrayBuffer` or `toBuffer`. Bun calls the callback later, once the garbage collector frees the underlying `ArrayBuffer` JavaScript object. The expected signature is the same as in [JavaScriptCore's C API](https://developer.apple.com/documentation/javascriptcore/jstypedarraybytesdeallocator?language=objc): diff --git a/docs/runtime/file-io.mdx b/docs/runtime/file-io.mdx index 8f9abd4c4cd4..9ce3a55f9727 100644 --- a/docs/runtime/file-io.mdx +++ b/docs/runtime/file-io.mdx @@ -23,7 +23,7 @@ foo.size; // number of bytes foo.type; // MIME type ``` -The reference conforms to the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) interface, so the contents can be read in various formats. +The reference conforms to the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) interface, so you can read the contents in various formats. ```ts const foo = Bun.file("foo.txt"); @@ -173,7 +173,7 @@ writer.write("it was the best of times\n"); writer.write("it was the worst of times\n"); ``` -These chunks are buffered internally. To flush the buffer to disk, use `.flush()`. This returns the number of flushed bytes. +The `FileSink` buffers these chunks internally. To flush the buffer to disk, use `.flush()`. This returns the number of flushed bytes. ```ts writer.flush(); // write buffer to disk @@ -192,7 +192,7 @@ To flush the buffer and close the file: writer.end(); ``` -By default, the `bun` process stays alive until this `FileSink` is explicitly closed with `.end()`. To opt out of this behavior, "unref" the instance. +By default, the `bun` process stays alive until you explicitly close this `FileSink` with `.end()`. To opt out of this behavior, "unref" the instance. ```ts writer.unref(); diff --git a/docs/runtime/file-system-router.mdx b/docs/runtime/file-system-router.mdx index 4c40461bee41..0254882c6ad2 100644 --- a/docs/runtime/file-system-router.mdx +++ b/docs/runtime/file-system-router.mdx @@ -41,7 +41,7 @@ router.match("/"); } ``` -Query parameters are parsed and returned in the `query` property. +The router parses query parameters and returns them in the `query` property. ```ts router.match("/settings?foo=bar"); @@ -77,7 +77,7 @@ router.match("/blog/my-cool-post"); } ``` -The `.match()` method also accepts `Request` and `Response` objects; their `url` property is used to resolve the route. +The `.match()` method also accepts `Request` and `Response` objects; the router uses their `url` property to resolve the route. ```ts router.match(new Request("https://example.com/blog/my-cool-post")); diff --git a/docs/runtime/file-types.mdx b/docs/runtime/file-types.mdx index 19e04e182760..128f8e91197d 100644 --- a/docs/runtime/file-types.mdx +++ b/docs/runtime/file-types.mdx @@ -31,7 +31,7 @@ Parses the code and applies a set of default transforms like dead-code eliminati **JavaScript + JSX**. Default for `.js` and `.jsx`. -Same as the `js` loader, but JSX syntax is supported. By default, JSX is down-converted to plain JavaScript; the details depend on the `jsx*` compiler options in your `tsconfig.json`. Refer to the TypeScript documentation [on JSX](https://www.typescriptlang.org/docs/handbook/jsx.html). +Same as the `js` loader, but JSX syntax is supported. By default, Bun down-converts JSX to plain JavaScript; the details depend on the `jsx*` compiler options in your `tsconfig.json`. Refer to the TypeScript documentation [on JSX](https://www.typescriptlang.org/docs/handbook/jsx.html). ### `ts` @@ -54,7 +54,7 @@ import pkg from "./package.json"; pkg.name; // => "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. <CodeGroup> @@ -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. <CodeGroup> @@ -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. <CodeGroup> @@ -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. <CodeGroup> @@ -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. <CodeGroup> @@ -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. <CodeGroup> @@ -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` | -<Note> - The location and file name of the copied file is determined by the value of [`naming.asset`](/bundler#naming). -</Note> +<Note>The value of [`naming.asset`](/bundler#naming) determines the location and file name of the copied file.</Note> <Accordion title="Fixing TypeScript import errors"> 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. </Accordion> 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<Response>`. 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<Response>`. 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/"<size>-<mtime>"`) 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({ ``` <Note> - `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`. </Note> --- ## 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 `<undefined>` 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({ <Accordion title="An API designed for speed"> -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({ ``` <Info> -Previously, you could specify the type of `ws.data` with a type parameter on `Bun.serve`, like `Bun.serve<MyData>({...})`. 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<MyData>({...})`. Bun removed this pattern in favor of the `data` property because of [a limitation in TypeScript](https://github.com/microsoft/TypeScript/issues/26242). </Info> To connect to this server from the browser, create a new `WebSocket`. @@ -184,13 +184,13 @@ socket.addEventListener("message", event => { <Info> **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. </Info> ### 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. </Note> @@ -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 <pattern> <script>` executes `<script>` in every package selected by `<pattern>`, which can be a package name glob, a `./path`, a `{dir}` directory or a dependency relation like `foo...`. +`bun run --filter <pattern> <script>` executes `<script>` in every package selected by `<pattern>`. The pattern can be a package name glob, a `./path`, a `{dir}` directory or a dependency relation like `foo...`. For example, if you have subdirectories containing packages named `foo`, `bar` and `baz`, running ```bash terminal icon="terminal" @@ -188,7 +188,7 @@ Control the depth of object inspection in console output with the `--console-dep bun --console-depth 5 run index.tsx ``` -`--console-depth` sets how deeply nested objects are displayed in `console.log()` output. The default depth is `2`. Higher values show more nested properties but may produce verbose output for complex objects. +`--console-depth` sets how deeply Bun displays nested objects in `console.log()` output. The default depth is `2`. Higher values show more nested properties but may produce verbose output for complex objects. ```ts console.ts icon="/icons/typescript.svg" const nested = { a: { b: { c: { d: "deep" } } } }; @@ -205,11 +205,11 @@ In memory-constrained environments, use the `--smol` flag to reduce memory usage bun --smol run index.tsx ``` -`--smol` makes the garbage collector run more frequently, which can slow down execution. Bun adjusts the garbage collector's heap size based on the available memory (accounting for cgroups and other memory limits) with and without the `--smol` flag, so the flag is mostly useful when you want the heap to grow more slowly. +`--smol` makes the garbage collector run more frequently, which can slow down execution. Bun adjusts the garbage collector's heap size based on the available memory (accounting for cgroups and other memory limits) with and without the `--smol` flag. The flag is therefore mostly useful when you want the heap to grow more slowly. ## Resolution order -Absolute paths and paths starting with `./` or `.\\` are always executed as source files. Unless you use `bun run`, a name with an allowed extension resolves to the file rather than a `package.json` script. +Bun always executes absolute paths and paths starting with `./` or `.\\` as source files. Unless you use `bun run`, a name with an allowed extension resolves to the file rather than a `package.json` script. When a `package.json` script and a file have the same name, `bun run` prefers the script. The full resolution order is: diff --git a/docs/runtime/json5.mdx b/docs/runtime/json5.mdx index 62acebf9a41a..88a7b093fbad 100644 --- a/docs/runtime/json5.mdx +++ b/docs/runtime/json5.mdx @@ -250,7 +250,7 @@ bun --hot server.ts ## Bundler Integration -When you bundle with Bun, imported JSON5 files are parsed at build time and included as JavaScript modules: +When you bundle with Bun, the bundler parses imported JSON5 files at build time and includes them as JavaScript modules: ```bash terminal icon="terminal" bun build app.ts --outdir=dist @@ -264,7 +264,7 @@ Parsing at build time means: ### Dynamic Imports -JSON5 files can be dynamically imported: +You can import JSON5 files dynamically: ```ts const { default: config } = await import("./config.json5"); diff --git a/docs/runtime/jsonl.mdx b/docs/runtime/jsonl.mdx index 2beaebf2ac5c..734e8711d86c 100644 --- a/docs/runtime/jsonl.mdx +++ b/docs/runtime/jsonl.mdx @@ -41,7 +41,7 @@ With `Uint8Array` input, Bun skips a UTF-8 BOM at the start of the buffer. ### Error handling -If the input contains invalid JSON and no values were successfully parsed, `Bun.JSONL.parse()` throws a `SyntaxError`. If at least one value was parsed before the error, the parsed values are returned without throwing. +If the input contains invalid JSON and no values were successfully parsed, `Bun.JSONL.parse()` throws a `SyntaxError`. If at least one value was parsed before the error, it returns the parsed values without throwing. ```ts try { @@ -55,7 +55,7 @@ try { ## `Bun.JSONL.parseChunk()` -For streaming, `parseChunk` parses as many complete values as it can from the input and reports how far it got, so you know where to resume when data arrives incrementally (for example, from a network stream). +For streaming, `parseChunk` parses as many complete values as it can from the input and reports how far it got. That way you know where to resume when data arrives incrementally (for example, from a network stream). ```ts const chunk = '{"id":1}\n{"id":2}\n{"id":3'; @@ -182,7 +182,7 @@ const values = Bun.JSONL.parse(input); ## Performance notes -- **ASCII fast path**: Pure ASCII input is parsed directly without copying, using a zero-allocation `StringView`. -- **UTF-8 support**: Non-ASCII `Uint8Array` input is decoded to UTF-16 using SIMD-accelerated conversion. -- **BOM handling**: UTF-8 BOM (`0xEF 0xBB 0xBF`) at the start of a `Uint8Array` is automatically skipped. +- **ASCII fast path**: Bun parses pure ASCII input directly without copying, using a zero-allocation `StringView`. +- **UTF-8 support**: Bun decodes non-ASCII `Uint8Array` input to UTF-16 using SIMD-accelerated conversion. +- **BOM handling**: Bun automatically skips a UTF-8 BOM (`0xEF 0xBB 0xBF`) at the start of a `Uint8Array`. - **Pre-built object shape**: The result object from `parseChunk` uses a cached structure for fast property access. diff --git a/docs/runtime/jsx.mdx b/docs/runtime/jsx.mdx index af3f4882ca1c..96f1da24ebc1 100644 --- a/docs/runtime/jsx.mdx +++ b/docs/runtime/jsx.mdx @@ -25,7 +25,7 @@ Bun respects the following compiler options. ### [`jsx`](https://www.typescriptlang.org/tsconfig#jsx) -How JSX constructs are transformed into vanilla JavaScript internally. The following table lists the possible values of `jsx`, along with how each transpiles this JSX component: +How Bun transforms JSX constructs into vanilla JavaScript internally. The following table lists the possible values of `jsx`, along with how each transpiles this JSX component: ```tsx <Box width={5}>Hello</Box> @@ -62,7 +62,7 @@ The function name used to represent [JSX fragments](https://react.dev/reference/ <Note>Only applicable when `jsx` is `react-jsx` or `react-jsxdev`.</Note> -The module the component factory function (such as `createElement`, `jsx`, or `jsxDEV`) is imported from. Default value is `"react"`. You'll typically need this when using a component library like Preact. +The module the component factory function (such as `createElement`, `jsx`, or `jsxDEV`) is imported from. Default value is `"react"`. You typically need this when using a component library like Preact. | Compiler options | Transpiled output | | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -82,7 +82,7 @@ You can set any of these values per file with a _pragma_, a comment that sets a ## Logging -Bun implements special logging for JSX to make debugging easier. Given the following file: +Bun implements special logging for JSX to help with debugging. Given the following file: ```tsx index.tsx icon="/icons/typescript.svg" import { Stack, UserCard } from "./components"; diff --git a/docs/runtime/markdown.mdx b/docs/runtime/markdown.mdx index 84506170575a..7ed7ee4df6f3 100644 --- a/docs/runtime/markdown.mdx +++ b/docs/runtime/markdown.mdx @@ -102,7 +102,7 @@ Bun.markdown.html("## Hello World", { headings: { ids: true } }); ## `Bun.markdown.render()` -Parse Markdown and render it using custom JavaScript callbacks. This gives you full control over the output format — you can generate HTML with custom classes, React elements, ANSI terminal output, or any other string format. +Parse Markdown and render it using custom JavaScript callbacks. The callbacks give you full control over the output format. You can generate HTML with custom classes, React elements, ANSI terminal output, or any other string format. ```ts const result = Bun.markdown.render("# Hello **world**", { @@ -120,7 +120,7 @@ Each callback receives: 1. **`children`** — the accumulated content of the element as a string 2. **`meta`** (optional) — an object with element-specific metadata -Return a string to replace the element's rendering. Return `null` or `undefined` to omit the element from the output entirely. If no callback is registered for an element, its children pass through unchanged. +Return a string to replace the element's rendering. Return `null` or `undefined` to omit the element from the output entirely. If an element has no callback, its children pass through unchanged. ### Block callbacks @@ -333,7 +333,7 @@ const el = Bun.markdown.react( #### Available overrides -Every HTML tag produced by the parser can be overridden: +You can override every HTML tag the parser produces: | Option | Props | Description | | ------------ | ---------------------------- | --------------------------------------------------------------- | diff --git a/docs/runtime/module-resolution.mdx b/docs/runtime/module-resolution.mdx index a603a8fca383..be473d4bf738 100644 --- a/docs/runtime/module-resolution.mdx +++ b/docs/runtime/module-resolution.mdx @@ -3,7 +3,7 @@ title: "Module Resolution" description: "How Bun resolves modules and handles imports in JavaScript and TypeScript" --- -The JavaScript ecosystem is in a years-long transition from CommonJS modules to native ES modules (ESM), and different runtimes and build tools have historically disagreed on how import specifiers map to files on disk. Bun aims to provide a consistent and predictable module resolution system that works without configuration. +The JavaScript ecosystem is in a years-long transition from CommonJS modules to native ES modules (ESM). Different runtimes and build tools have historically disagreed on how import specifiers map to files on disk. Bun aims to provide a consistent and predictable module resolution system that works without configuration. ## Syntax @@ -55,7 +55,7 @@ Here `./hello` is a relative path with no extension. **Extensioned imports are o <Note> The exact order varies by context: `require()` tries CommonJS extensions (`.cts`, `.cjs`) before ESM ones (`.mts`, - `.mjs`), and imports inside `node_modules` try JavaScript extensions before TypeScript ones. The list above shows the + `.mjs`). Imports inside `node_modules` try JavaScript extensions before TypeScript ones. The list above shows the order for a local ESM `import`. </Note> @@ -66,7 +66,7 @@ import { hello } from "./hello"; import { hello } from "./hello.ts"; // this works ``` -There's one additional rule for TypeScript compatibility: if you import `from "*.js"` or `from "*.jsx"`, Bun also checks for a matching `*.ts` or `*.tsx` file, and outside `node_modules` `from "*.mjs"` also matches `*.mts`. This follows the TypeScript compiler's [file extension substitution](https://www.typescriptlang.org/docs/handbook/modules/reference.html#file-extension-substitution), which lets source files reference each other by their compiled output paths. Note that unlike TypeScript, Bun doesn't rewrite `.cjs` to `.cts`. +There's one additional rule for TypeScript compatibility. If you import `from "*.js"` or `from "*.jsx"`, Bun also checks for a matching `*.ts` or `*.tsx` file. Outside `node_modules`, `from "*.mjs"` also matches `*.mts`. This rule follows the TypeScript compiler's [file extension substitution](https://www.typescriptlang.org/docs/handbook/modules/reference.html#file-extension-substitution), which lets source files reference each other by their compiled output paths. Unlike TypeScript, Bun doesn't rewrite `.cjs` to `.cts`. ```ts index.ts icon="/icons/typescript.svg" import { hello } from "./hello"; @@ -136,7 +136,7 @@ The biggest difference between CommonJS and ES modules is that CommonJS modules - ES modules are always in [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode), while CommonJS modules are not. - Browsers do not have native support for CommonJS modules, but they do have native support for ES modules through `<script type="module">`. - CommonJS modules are not statically analyzable, while ES modules only allow static imports and exports. -- Static `import` statements run synchronously, just like CommonJS `require`. ES modules can also be loaded on the fly with the asynchronous `import()` function, called a "dynamic import". +- Static `import` statements run synchronously, just like CommonJS `require`. You can also load ES modules on the fly with the asynchronous `import()` function, called a "dynamic import". </Accordion> @@ -231,7 +231,7 @@ Bun respects subpath [`"exports"`](https://nodejs.org/api/packages.html#subpath- } ``` -Subpath imports and conditional imports work in conjunction with each other. +Subpath imports and conditional imports work together. ```json package.json icon="file-json" { @@ -245,7 +245,7 @@ Subpath imports and conditional imports work in conjunction with each other. } ``` -As in Node.js, specifying any subpath in the `"exports"` map prevents other subpaths from being importable; you can only import files that are explicitly exported. Given the preceding `package.json`: +As in Node.js, specifying any subpath in the `"exports"` map prevents other subpaths from being importable. You can only import files that are explicitly exported. Given the preceding `package.json`: ```ts index.ts icon="/icons/typescript.svg" import stuff from "foo"; // this works @@ -330,9 +330,9 @@ Bun's JavaScript runtime has native support for CommonJS. When Bun's JavaScript })(module, exports, require); ``` -`module`, `exports`, and `require` are very much like the `module`, `exports`, and `require` in Node.js. These are assigned through a [`with scope`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with) in C++. An internal `Map` stores the `exports` object to handle cyclical `require` calls before the module is fully loaded. +`module`, `exports`, and `require` are very much like the `module`, `exports`, and `require` in Node.js. Bun assigns these through a [`with scope`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with) in C++. An internal `Map` stores the `exports` object to handle cyclical `require` calls before the module is fully loaded. -Once the CommonJS module is successfully evaluated, a Synthetic Module Record is created with the `default` ES Module [export set to `module.exports`](https://github.com/oven-sh/bun/blob/9b6913e1a674ceb7f670f917fc355bb8758c6c72/src/bun.js/bindings/CommonJSModuleRecord.cpp#L212-L213) and keys of the `module.exports` object are re-exported as named exports (if the `module.exports` object is an object). +Once the CommonJS module is successfully evaluated, Bun creates a Synthetic Module Record with the `default` ES Module [export set to `module.exports`](https://github.com/oven-sh/bun/blob/9b6913e1a674ceb7f670f917fc355bb8758c6c72/src/bun.js/bindings/CommonJSModuleRecord.cpp#L212-L213). If `module.exports` is an object, Bun also re-exports its keys as named exports. Bun's bundler works differently: it wraps the CommonJS module in a `require_${moduleName}` function which returns the `module.exports` object. @@ -366,6 +366,6 @@ import.meta.resolve("zod"); // => "file:///path/to/project/node_modules/zod/inde | `import.meta.file` | The name of the current file, e.g. `index.tsx` | | `import.meta.path` | Absolute path to the current file, e.g. `/path/to/project/index.ts`. Equivalent to `__filename` in CommonJS modules (and Node.js) | | `import.meta.filename` | An alias to `import.meta.path`, for Node.js compatibility | -| `import.meta.main` | Indicates whether the current file is the entrypoint to the current `bun` process: `true` if it's executed directly by `bun run`, `false` if it's imported | +| `import.meta.main` | Indicates whether the current file is the entrypoint to the current `bun` process: `true` if `bun run` executes it directly, `false` if it's imported | | `import.meta.resolve` | Resolve a module specifier (e.g. `"zod"` or `"./file.tsx"`) to a url. Equivalent to [`import.meta.resolve` in browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta#resolve). Example: `import.meta.resolve("zod")` returns `"file:///path/to/project/node_modules/zod/index.ts"` | | `import.meta.url` | A `string` url to the current file, e.g. `file:///path/to/project/index.ts`. Equivalent to [`import.meta.url` in browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta#url) | diff --git a/docs/runtime/networking/dns.mdx b/docs/runtime/networking/dns.mdx index 439a962e6f0e..5a5888ed9040 100644 --- a/docs/runtime/networking/dns.mdx +++ b/docs/runtime/networking/dns.mdx @@ -25,8 +25,8 @@ dns.prefetch("bun.com", 443); `Bun.dns.lookup()` accepts a `backend` option that selects the resolver implementation: -- `"c-ares"`: the [c-ares](https://c-ares.org/) asynchronous resolver. It reads `/etc/resolv.conf` directly, so it does not consult NSS modules such as `systemd-resolved`. This is the default for `Bun.dns.lookup()` on Linux. -- `"system"`: the platform's own resolver (the non-blocking system API on macOS, `getaddrinfo` on a thread pool everywhere else). This is the default on macOS, Windows, and Android. +- `"c-ares"`: the [c-ares](https://c-ares.org/) asynchronous resolver. It reads `/etc/resolv.conf` directly, so it does not consult NSS modules such as `systemd-resolved`. This backend is the default for `Bun.dns.lookup()` on Linux. +- `"system"`: the platform's own resolver (the non-blocking system API on macOS, `getaddrinfo` on a thread pool everywhere else). This backend is the default on macOS, Windows, and Android. - `"getaddrinfo"` (alias `"libc"`): the POSIX `getaddrinfo(3)` function on a thread pool. ```ts @@ -43,7 +43,7 @@ Bun caches DNS lookups, which makes repeated connections to the same hosts faste The cache holds up to 256 entries for a maximum of 30 seconds each. If a connection to a host fails, Bun removes that host's entry from the cache. Simultaneous connections to the same host share one DNS lookup. -This cache is automatically used by: +Bun uses this cache automatically in: - `bun install` - `fetch()` @@ -62,7 +62,7 @@ import { dns } from "bun"; dns.prefetch("my.database-host.com", 5432); ``` -A database driver is a good example: prefetch the database host's DNS entry when your application starts, and by the time the rest of the application has loaded, the lookup may already be complete. +A database driver is a good example: prefetch the database host's DNS entry when your application starts. By the time the rest of the application has loaded, the lookup may already be complete. ### `dns.prefetch` diff --git a/docs/runtime/networking/fetch.mdx b/docs/runtime/networking/fetch.mdx index afe904b75e09..d55fab0c69c9 100644 --- a/docs/runtime/networking/fetch.mdx +++ b/docs/runtime/networking/fetch.mdx @@ -5,7 +5,7 @@ description: Send HTTP requests with Bun's fetch API Bun implements the WHATWG `fetch` standard, with some extensions to meet the needs of server-side JavaScript. -Bun also implements `node:http`, but `fetch` is generally recommended instead. +Bun also implements `node:http`, but we generally recommend `fetch` instead. ## Sending an HTTP request @@ -73,7 +73,7 @@ const response = await fetch("http://example.com", { }); ``` -The `headers` are sent directly to the proxy in `CONNECT` requests (for HTTPS targets) or in the proxy request (for HTTP targets). If you provide a `Proxy-Authorization` header, it overrides any credentials in the proxy URL. +Bun sends the `headers` directly to the proxy in `CONNECT` requests (for HTTPS targets) or in the proxy request (for HTTP targets). If you provide a `Proxy-Authorization` header, it overrides any credentials in the proxy URL. ### Custom headers @@ -154,15 +154,15 @@ const response = await fetch("http://example.com", { When using streams with HTTP(S): -- The data is streamed directly to the network without buffering the entire body in memory -- If the connection is lost, the stream is canceled -- The `Content-Length` header is not automatically set unless the stream has a known size +- Bun streams the data directly to the network without buffering the entire body in memory +- If the connection is lost, Bun cancels the stream +- Bun sets the `Content-Length` header automatically only when the stream has a known size When using streams with S3: - For PUT/POST requests, Bun automatically uses multipart upload -- The stream is consumed in chunks and uploaded in parallel -- Progress can be monitored through the S3 options +- Bun consumes the stream in chunks and uploads the chunks in parallel +- You can monitor progress through the S3 options ### Fetching a URL with a timeout @@ -300,7 +300,7 @@ const response = await fetch("file:///path/to/file.txt"); const text = await response.text(); ``` -On Windows, paths are automatically normalized: +On Windows, Bun normalizes paths automatically: ```ts // Both work on Windows @@ -435,7 +435,7 @@ By default, Bun limits the number of simultaneous `fetch` requests to 256, for t - It improves overall system stability. Operating systems have an upper limit on the number of simultaneous open TCP sockets, usually in the low thousands. Nearing this limit causes your entire computer to behave strangely. Applications hang and crash. - It encourages HTTP Keep-Alive connection reuse. For short-lived HTTP requests, the slowest step is often the initial connection setup. Reusing connections can save a lot of time. -When the limit is exceeded, requests are queued and sent as soon as the next request ends. +When the limit is exceeded, Bun queues requests and sends them as soon as the next request ends. To raise the limit, set the `BUN_CONFIG_MAX_HTTP_REQUESTS` environment variable: @@ -466,8 +466,8 @@ await write("output.txt", response); ### Implementation details -- Connection pooling is enabled by default but can be disabled per-request with `keepalive: false` or the `"Connection: close"` header. -- Large file uploads are optimized using the operating system's `sendfile` syscall under specific conditions: +- Connection pooling is enabled by default. You can disable it per-request with `keepalive: false` or the `"Connection: close"` header. +- Bun optimizes large file uploads using the operating system's `sendfile` syscall under specific conditions: - The file must be larger than 32KB - The request must not be using a proxy - On macOS, only regular files (not pipes, sockets, or devices) can use `sendfile` diff --git a/docs/runtime/networking/tcp.mdx b/docs/runtime/networking/tcp.mdx index 98e7bc0871c6..e5bfdf50743d 100644 --- a/docs/runtime/networking/tcp.mdx +++ b/docs/runtime/networking/tcp.mdx @@ -151,7 +151,7 @@ const socket = await Bun.connect({ ## Hot reloading -Both TCP servers and sockets can be hot reloaded with new handlers. +You can hot reload both TCP servers and sockets with new handlers. <CodeGroup> @@ -234,6 +234,6 @@ queueMicrotask(() => { <Note> **Corking** -Support for corking is planned, but in the meantime backpressure must be managed manually with the `drain` handler. +Support for corking is planned. In the meantime, you must manage backpressure manually with the `drain` handler. </Note> diff --git a/docs/runtime/nodejs-compat.mdx b/docs/runtime/nodejs-compat.mdx index d955c82ebc08..114339a97717 100644 --- a/docs/runtime/nodejs-compat.mdx +++ b/docs/runtime/nodejs-compat.mdx @@ -7,7 +7,7 @@ Every day, Bun gets closer to 100% Node.js API compatibility. Popular frameworks **If a package works in Node.js but doesn't work in Bun, we consider it a bug in Bun.** [Open an issue](https://bun.com/issues) and we'll fix it. -This page is updated regularly and reflects the latest version of Bun's compatibility with _Node.js v26_. +We update this page regularly. It reflects the latest version of Bun's compatibility with _Node.js v26_. ## Built-in Node.js modules @@ -21,7 +21,7 @@ This page is updated regularly and reflects the latest version of Bun's compatib ### [`node:console`](https://nodejs.org/api/console.html) -🟢 Fully implemented. Output is written directly to the stdout/stderr file descriptors and formatted with Bun's own inspector, so replacing `process.stdout.write` does not capture it and object layout differs from `util.inspect`; `console.trace()` writes to stdout and `console.time*()` to stderr. +🟢 Fully implemented. Bun writes console output directly to the stdout/stderr file descriptors and formats it with its own inspector. As a result, replacing `process.stdout.write` does not capture the output, and object layout differs from `util.inspect`. `console.trace()` writes to stdout and `console.time*()` to stderr. ### [`node:dgram`](https://nodejs.org/api/dgram.html) @@ -29,11 +29,11 @@ This page is updated regularly and reflects the latest version of Bun's compatib ### [`node:diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html) -🟡 `channel()`, `subscribe()`, `tracingChannel()` and the `http` client, `http2` and `dgram` built-in channels are implemented. Missing `boundedChannel()` and the `http.server.*`, `net`, `module`, `console`, `child_process` and `worker_threads` built-in channels; a `Channel` is not kept alive by its subscribers, so hold a reference to it. +🟡 `channel()`, `subscribe()`, `tracingChannel()` and the `http` client, `http2` and `dgram` built-in channels are implemented. Missing `boundedChannel()` and the `http.server.*`, `net`, `module`, `console`, `child_process` and `worker_threads` built-in channels. Subscribers do not keep a `Channel` alive, so hold a reference to it. ### [`node:dns`](https://nodejs.org/api/dns.html) -🟢 Fully implemented. Missing `resolveTlsa`; the `Resolver` `maxTimeout` option is ignored, and the callback-style `Resolver` class cannot be subclassed (`dns.promises.Resolver` can). +🟢 Fully implemented. Missing `resolveTlsa`. Bun ignores the `Resolver` `maxTimeout` option, and the callback-style `Resolver` class cannot be subclassed (`dns.promises.Resolver` can). ### [`node:events`](https://nodejs.org/api/events.html) @@ -45,23 +45,23 @@ This page is updated regularly and reflects the latest version of Bun's compatib ### [`node:http`](https://nodejs.org/api/http.html) -🟢 Fully implemented. `http.Server` does not extend `net.Server`; `listen(handle)` and the `fd`, `ipv6Only` and `signal` options of `listen()` are ignored, and `keepAlive`/`keepAliveInitialDelay` on the server are no-ops. +🟢 Fully implemented. `http.Server` does not extend `net.Server`. Bun ignores `listen(handle)` and the `fd`, `ipv6Only` and `signal` options of `listen()`. `keepAlive`/`keepAliveInitialDelay` on the server are no-ops. ### [`node:https`](https://nodejs.org/api/https.html) -🟡 `request`, `get`, `Agent` and `globalAgent` are implemented, including connection pooling. `https.Server` is `http.Server` with TLS options rather than a `tls.Server`: request sockets are not `tls.TLSSocket`s (`encrypted`, `authorized` and `servername` work; `getPeerCertificate()` and `getCipher()` are missing), and `setSecureContext()`, `addContext()`, `SNICallback` and `handshakeTimeout` are not supported. +🟡 `request`, `get`, `Agent` and `globalAgent` are implemented, including connection pooling. `https.Server` is `http.Server` with TLS options rather than a `tls.Server`. Request sockets are not `tls.TLSSocket`s: `encrypted`, `authorized` and `servername` work, but `getPeerCertificate()` and `getCipher()` are missing. `setSecureContext()`, `addContext()`, `SNICallback` and `handshakeTimeout` are not supported. ### [`node:os`](https://nodejs.org/api/os.html) -🟢 Fully implemented. `userInfo()` reads `username`, `shell` and `homedir` from the environment (`USER`, `SHELL`, `HOME`) rather than the passwd database, and `machine()` returns `"arm64"` instead of `"aarch64"` on Linux arm64. +🟢 Fully implemented. `userInfo()` reads `username`, `shell` and `homedir` from the environment (`USER`, `SHELL`, `HOME`) rather than the passwd database. `machine()` returns `"arm64"` instead of `"aarch64"` on Linux arm64. ### [`node:path`](https://nodejs.org/api/path.html) -🟢 Fully implemented. `matchesGlob()` uses `Bun.Glob` semantics rather than minimatch (`*` matches dotfiles, no extglobs), and `path.win32` differs from Node in a few edge cases involving device paths and reserved names. +🟢 Fully implemented. `matchesGlob()` uses `Bun.Glob` semantics rather than minimatch (`*` matches dotfiles, no extglobs). `path.win32` differs from Node in a few edge cases involving device paths and reserved names. ### [`node:punycode`](https://nodejs.org/api/punycode.html) -🟢 Fully implemented. 100% of Node.js's test suite passes, _deprecated by Node.js_. +🟢 Fully implemented. 100% of Node.js's test suite passes. _Deprecated by Node.js_. ### [`node:querystring`](https://nodejs.org/api/querystring.html) @@ -81,7 +81,7 @@ This page is updated regularly and reflects the latest version of Bun's compatib ### [`node:timers`](https://nodejs.org/api/timers.html) -🟢 Fully implemented. The exports are the same functions as the globals; `node:timers/promises` (including `scheduler.wait()` and `scheduler.yield()`) is also implemented. +🟢 Fully implemented. The exports are the same functions as the globals. `node:timers/promises` (including `scheduler.wait()` and `scheduler.yield()`) is also implemented. ### [`node:tty`](https://nodejs.org/api/tty.html) @@ -97,11 +97,11 @@ This page is updated regularly and reflects the latest version of Bun's compatib ### [`node:async_hooks`](https://nodejs.org/api/async_hooks.html) -🟡 `AsyncLocalStorage` and `AsyncResource` are implemented. `createHook`, `executionAsyncId`, `triggerAsyncId` and `executionAsyncResource` are stubs (hooks are not invoked, apart from `init` for `process.nextTick`, and async ids are always `0`); Node.js [strongly discourages](https://nodejs.org/docs/latest/api/async_hooks.html#async-hooks) these APIs in favor of `AsyncLocalStorage`. `AsyncLocalStorage` context is not propagated into `MessagePort`, `BroadcastChannel` or `Worker` events. +🟡 `AsyncLocalStorage` and `AsyncResource` are implemented. `createHook`, `executionAsyncId`, `triggerAsyncId` and `executionAsyncResource` are stubs: Bun does not invoke hooks, apart from `init` for `process.nextTick`, and async ids are always `0`. Node.js [strongly discourages](https://nodejs.org/docs/latest/api/async_hooks.html#async-hooks) these APIs in favor of `AsyncLocalStorage`. Bun does not propagate `AsyncLocalStorage` context into `MessagePort`, `BroadcastChannel` or `Worker` events. ### [`node:child_process`](https://nodejs.org/api/child_process.html) -🟡 IPC can send `net.Socket`, `net.Server` and `dgram.Socket` handles (including to and from Node.js processes), but not `http` server sockets; `serialization: "advanced"` only works between Bun processes, so use JSON serialization for Node.js ↔ Bun IPC. Missing `subprocess.channel.ref()`/`unref()`; a child's `stdout`/`stderr` cannot be passed as another child's `stdio`, and `spawnSync` does not return extra `stdio` pipes in `output`. +🟡 IPC can send `net.Socket`, `net.Server` and `dgram.Socket` handles (including to and from Node.js processes), but not `http` server sockets. `serialization: "advanced"` only works between Bun processes, so use JSON serialization for Node.js ↔ Bun IPC. Missing `subprocess.channel.ref()`/`unref()`. You cannot pass a child's `stdout`/`stderr` as another child's `stdio`, and `spawnSync` does not return extra `stdio` pipes in `output`. ### [`node:cluster`](https://nodejs.org/api/cluster.html) @@ -109,11 +109,11 @@ This page is updated regularly and reflects the latest version of Bun's compatib ### [`node:crypto`](https://nodejs.org/api/crypto.html) -🟡 Missing `encapsulate`/`decapsulate` (ML-KEM keys can be used through `crypto.subtle`); `argon2()` and custom engines (`setEngine()`) throw, `setFips()` is a no-op and `secureHeapUsed()` returns `undefined`. Bun's crypto is backed by BoringSSL, which lacks the `ed448`, `x448`, `rsa-pss`, `dsa` and `dh` key types, EC curves other than P-224/256/384/521 (no `secp256k1`), and the CCM, OCB, XTS and `chacha20-poly1305` ciphers. +🟡 Missing `encapsulate`/`decapsulate` (you can use ML-KEM keys through `crypto.subtle`). `argon2()` and custom engines (`setEngine()`) throw, `setFips()` is a no-op and `secureHeapUsed()` returns `undefined`. Bun's crypto is backed by BoringSSL, which lacks the `ed448`, `x448`, `rsa-pss`, `dsa` and `dh` key types, EC curves other than P-224/256/384/521 (no `secp256k1`), and the CCM, OCB, XTS and `chacha20-poly1305` ciphers. ### [`node:domain`](https://nodejs.org/api/domain.html) -🟡 Missing `Domain` `members`. A domain only catches errors thrown synchronously inside `run()`/`bind()` or emitted by emitters passed to `add()`; errors from timers, `process.nextTick`, promises and other async callbacks are not routed to it. +🟡 Missing `Domain` `members`. A domain only catches errors thrown synchronously inside `run()`/`bind()` or emitted by emitters passed to `add()`. Bun does not route errors from timers, `process.nextTick`, promises and other async callbacks to the domain. ### [`node:http2`](https://nodejs.org/api/http2.html) @@ -121,15 +121,15 @@ This page is updated regularly and reflects the latest version of Bun's compatib ### [`node:module`](https://nodejs.org/api/module.html) -🟡 Missing `Module#load()`, `registerHooks`, `findPackageJSON`, `stripTypeScriptTypes`, `getSourceMapsSupport`/`setSourceMapsSupport`. Overriding `require.cache`, `require.extensions` and `module._resolveFilename` is supported. `syncBuiltinESMExports`, `module._load`, `module._pathCache` and `module.register` are no-ops (we recommend [`Bun.plugin`](/runtime/plugins) instead), and `findSourceMap` always returns `undefined`. +🟡 Missing `Module#load()`, `registerHooks`, `findPackageJSON`, `stripTypeScriptTypes`, `getSourceMapsSupport`/`setSourceMapsSupport`. Overriding `require.cache`, `require.extensions` and `module._resolveFilename` is supported. `syncBuiltinESMExports`, `module._load`, `module._pathCache` and `module.register` are no-ops (we recommend [`Bun.plugin`](/runtime/plugins) instead). `findSourceMap` always returns `undefined`. ### [`node:net`](https://nodejs.org/api/net.html) -🟢 Fully implemented, including `BlockList`, `SocketAddress`, `autoSelectFamily`, Unix domain sockets and `server.listen({ fd })`. `new net.Socket({ fd })` cannot read from an existing file descriptor (only write-only wrapping works), `server.listen(handle)` only accepts `{ fd }`, and `blockList.toJSON()`/`fromJSON()` are missing. +🟢 Fully implemented, including `BlockList`, `SocketAddress`, `autoSelectFamily`, Unix domain sockets and `server.listen({ fd })`. `new net.Socket({ fd })` cannot read from an existing file descriptor (only write-only wrapping works). `server.listen(handle)` only accepts `{ fd }`. Missing `blockList.toJSON()`/`fromJSON()`. ### [`node:perf_hooks`](https://nodejs.org/api/perf_hooks.html) -🟡 `monitorEventLoopDelay()`, `createHistogram()`, `timerify()` and `PerformanceObserver` (`mark`, `measure`, `function`, `net`, `http` and `http2` entries) are implemented. No `gc`, `dns` or `resource` entries are ever emitted, `eventLoopUtilization()` always returns zeros, and `performance.nodeTiming` holds placeholder values. The Node-specific additions to the global `performance` object only appear once `node:perf_hooks` has been imported. +🟡 `monitorEventLoopDelay()`, `createHistogram()`, `timerify()` and `PerformanceObserver` (`mark`, `measure`, `function`, `net`, `http` and `http2` entries) are implemented. Bun never emits `gc`, `dns` or `resource` entries. `eventLoopUtilization()` always returns zeros, and `performance.nodeTiming` holds placeholder values. The Node-specific additions to the global `performance` object only appear once `node:perf_hooks` has been imported. ### [`node:process`](https://nodejs.org/api/process.html) @@ -141,47 +141,47 @@ This page is updated regularly and reflects the latest version of Bun's compatib ### [`node:tls`](https://nodejs.org/api/tls.html) -🟡 Missing `pskCallback`, OCSP stapling (`requestOCSP`), the server `'newSession'`/`'resumeSession'` events and session ticket keys (`ticketKeys` is ignored), so session resumption does not work across processes. Bun uses BoringSSL, so `tlsSocket.renegotiate()` always fails and `getEphemeralKeyInfo()`/`getSharedSigalgs()` return no information. +🟡 Missing `pskCallback`, OCSP stapling (`requestOCSP`), the server `'newSession'`/`'resumeSession'` events and session ticket keys (`ticketKeys` is ignored). As a result, session resumption does not work across processes. Bun uses BoringSSL, so `tlsSocket.renegotiate()` always fails and `getEphemeralKeyInfo()`/`getSharedSigalgs()` return no information. ### [`node:util`](https://nodejs.org/api/util.html) -🟡 Missing `diff` `transferableAbortSignal` `transferableAbortController`. `debuglog()` ignores its `callback` argument and the returned function has no `enabled` property. +🟡 Missing `diff`, `transferableAbortSignal` and `transferableAbortController`. `debuglog()` ignores its `callback` argument and the returned function has no `enabled` property. ### [`node:v8`](https://nodejs.org/api/v8.html) -🟡 `writeHeapSnapshot`, `getHeapSnapshot`, `getHeapStatistics`, `getHeapSpaceStatistics`, `GCProfiler` and `startupSnapshot` are implemented; the heap statistics describe JavaScriptCore's single heap, and `setFlagsFromString` ignores the flags it is given. `serialize` and `deserialize` use JavaScriptCore's wire format instead of V8's. Missing `queryObjects`, `startCpuProfile`, `startHeapProfile`, `Serializer`/`Deserializer`, `takeCoverage`/`stopCoverage` and `promiseHooks`. For profiling, use [`bun:jsc`](/project/benchmarking#javascript-heap-stats) instead. +🟡 `writeHeapSnapshot`, `getHeapSnapshot`, `getHeapStatistics`, `getHeapSpaceStatistics`, `GCProfiler` and `startupSnapshot` are implemented. The heap statistics describe JavaScriptCore's single heap, and `setFlagsFromString` ignores the flags it is given. `serialize` and `deserialize` use JavaScriptCore's wire format instead of V8's. Missing `queryObjects`, `startCpuProfile`, `startHeapProfile`, `Serializer`/`Deserializer`, `takeCoverage`/`stopCoverage` and `promiseHooks`. For profiling, use [`bun:jsc`](/project/benchmarking#javascript-heap-stats) instead. ### [`node:vm`](https://nodejs.org/api/vm.html) -🟡 Core functionality and ES modules are implemented, including `vm.Script`, `vm.createContext`, `vm.runInContext`, `vm.runInNewContext`, `vm.runInThisContext`, `vm.compileFunction`, `vm.isContext`, `vm.Module`, `vm.SourceTextModule`, `vm.SyntheticModule` (exported without `--experimental-vm-modules`), and `importModuleDynamically` support. The `timeout`, `breakOnSigint`, `cachedData`, `microtaskMode` and `codeGeneration` options are supported. An `importModuleDynamically` callback that returns a promise for a `vm.Module` resolves `import()` to the module object rather than its namespace, and `vm.measureMemory()` reports whole-heap figures for every context. +🟡 Core functionality and ES modules are implemented, including `vm.Script`, `vm.createContext`, `vm.runInContext`, `vm.runInNewContext`, `vm.runInThisContext`, `vm.compileFunction`, `vm.isContext`, `vm.Module`, `vm.SourceTextModule`, `vm.SyntheticModule` (exported without `--experimental-vm-modules`), and `importModuleDynamically` support. The `timeout`, `breakOnSigint`, `cachedData`, `microtaskMode` and `codeGeneration` options are supported. An `importModuleDynamically` callback that returns a promise for a `vm.Module` resolves `import()` to the module object rather than its namespace. `vm.measureMemory()` reports whole-heap figures for every context. ### [`node:wasi`](https://nodejs.org/api/wasi.html) -🟡 Partially implemented. `WASI` supports `args`, `env`, `preopens`, `wasiImport` and `start()`, and `bun ./program.wasm` runs a WASI command directly. Missing `getImportObject()` (use `wasiImport`), `initialize()` and the `sock_accept` import; the `version`, `returnOnExit`, `stdin`, `stdout` and `stderr` options are ignored, so `proc_exit` exits the Bun process. +🟡 Partially implemented. `WASI` supports `args`, `env`, `preopens`, `wasiImport` and `start()`, and `bun ./program.wasm` runs a WASI command directly. Missing `getImportObject()` (use `wasiImport`), `initialize()` and the `sock_accept` import. Bun ignores the `version`, `returnOnExit`, `stdin`, `stdout` and `stderr` options, so `proc_exit` exits the Bun process. ### [`node:worker_threads`](https://nodejs.org/api/worker_threads.html) -🟡 `Worker` ignores the `resourceLimits` and `trackUnmanagedFds` options, and `execArgv` only sets `process.execArgv` in the worker. `worker.performance.eventLoopUtilization()` is a stub. Missing `moveMessagePortToContext` `locks`. +🟡 `Worker` ignores the `resourceLimits` and `trackUnmanagedFds` options, and `execArgv` only sets `process.execArgv` in the worker. `worker.performance.eventLoopUtilization()` is a stub. Missing `moveMessagePortToContext` and `locks`. ### [`node:inspector`](https://nodejs.org/api/inspector.html) -🟡 Partially implemented. `Session` supports the `Profiler` domain (including precise coverage), `Runtime.enable` and `NodeTracing`, from both `node:inspector` and `node:inspector/promises`; other `Session` commands such as `Runtime.evaluate` and the `HeapProfiler` domain are not implemented. `open()`, `url()`, `close()` and `waitForDebugger()` are implemented; `open()` serves the `Debugger` and `Runtime` domains and throws in workers. Missing `Network`. +🟡 Partially implemented. `Session` supports the `Profiler` domain (including precise coverage), `Runtime.enable` and `NodeTracing`, from both `node:inspector` and `node:inspector/promises`. Other `Session` commands such as `Runtime.evaluate` and the `HeapProfiler` domain are not implemented. `open()`, `url()`, `close()` and `waitForDebugger()` are implemented. `open()` serves the `Debugger` and `Runtime` domains and throws in workers. Missing `Network`. ### [`node:repl`](https://nodejs.org/api/repl.html) -🟡 Mostly implemented. `bun --interactive` starts a Node.js-compatible REPL. Result previews (which need V8's inspector-based side-effect-free eval) are not shown, tab-completion skips `let`/`const`/`class` bindings, and some V8-specific error-message and stack-frame wording differs. +🟡 Mostly implemented. `bun --interactive` starts a Node.js-compatible REPL. The REPL does not show result previews (they need V8's inspector-based side-effect-free eval). Tab-completion skips `let`/`const`/`class` bindings, and some V8-specific error-message and stack-frame wording differs. ### [`node:sqlite`](https://nodejs.org/api/sqlite.html) -🟢 Fully implemented. `backup()` runs synchronously and blocks the event loop for the duration of the copy (Node runs it on a worker thread). A `Buffer`/`Uint8Array` database path must be valid UTF-8 (Node passes the raw bytes through; Bun rejects non-UTF-8 with `ERR_INVALID_ARG_VALUE`). On macOS, Bun uses the system `libsqlite3.dylib`; `loadExtension()` (and, on older macOS releases, `createSession()`/`applyChangeset()`) require a full SQLite build — call `require("bun:sqlite").Database.setCustomSQLite(path)` before opening a database. +🟢 Fully implemented. `backup()` runs synchronously and blocks the event loop for the duration of the copy (Node runs it on a worker thread). A `Buffer`/`Uint8Array` database path must be valid UTF-8 (Node passes the raw bytes through; Bun rejects non-UTF-8 with `ERR_INVALID_ARG_VALUE`). On macOS, Bun uses the system `libsqlite3.dylib`. `loadExtension()` requires a full SQLite build, and so do `createSession()`/`applyChangeset()` on older macOS releases. To use a full SQLite build, call `require("bun:sqlite").Database.setCustomSQLite(path)` before opening a database. ### [`node:test`](https://nodejs.org/api/test.html) -🟡 Partially implemented. The in-process API works when test files run under `bun test`: tests, suites, subtests, hooks, `t.plan()`, `t.assert`, `assert.register()`, `t.waitFor()`, `getTestContext()`, `expectFailure`, and `t.mock` (function/method/getter/setter/property mocks and mock timers). `run()` requires an explicit `files` list and runs each file in a `bun test` child process; most of its options (`globPatterns`, `watch`, `coverage`, `shard`, `only`, `testNamePatterns`, ...) throw `ERR_NOT_IMPLEMENTED`. Missing `node:test/reporters`, snapshot testing, `mock.module()`, `t.runOnly()`, code coverage, `--test-only`, test-level `signal` abort, and Node's `--test` CLI runner mode. `test.only()` / `{only: true}` are accepted but do not filter. `concurrency` is validated but subtests always run serially. Use [`bun:test`](/test) instead. +🟡 Partially implemented. The in-process API works when test files run under `bun test`: tests, suites, subtests, hooks, `t.plan()`, `t.assert`, `assert.register()`, `t.waitFor()`, `getTestContext()`, `expectFailure`, and `t.mock` (function/method/getter/setter/property mocks and mock timers). `run()` requires an explicit `files` list and runs each file in a `bun test` child process. Most of its options (`globPatterns`, `watch`, `coverage`, `shard`, `only`, `testNamePatterns`, ...) throw `ERR_NOT_IMPLEMENTED`. Missing `node:test/reporters`, snapshot testing, `mock.module()`, `t.runOnly()`, code coverage, `--test-only`, test-level `signal` abort, and Node's `--test` CLI runner mode. `test.only()` / `{only: true}` are accepted but do not filter. `concurrency` is validated but subtests always run serially. Use [`bun:test`](/test) instead. ### [`node:trace_events`](https://nodejs.org/api/tracing.html) -🟢 Fully implemented. `createTracing()`, `getEnabledCategories()` and the `--trace-events-enabled`, `--trace-event-categories` and `--trace-event-file-pattern` flags are supported; the trace is written at exit. Some categories record less than in Node.js (for example `node.async_hooks` only records timers, and the `v8` category is a placeholder, since JavaScriptCore has no V8 GC or compile events). +🟢 Fully implemented. `createTracing()`, `getEnabledCategories()` and the `--trace-events-enabled`, `--trace-event-categories` and `--trace-event-file-pattern` flags are supported. Bun writes the trace at exit. Some categories record less than in Node.js. For example, `node.async_hooks` only records timers, and the `v8` category is a placeholder, since JavaScriptCore has no V8 GC or compile events. ### [`node:quic`](https://github.com/nodejs/node/blob/main/doc/api/quic.md) @@ -345,7 +345,7 @@ The following list covers the globals implemented by Node.js and Bun's compatibi ### [`PerformanceObserver`](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver) -🟡 Observing `mark` and `measure` entries works. Node-only entry types (`function`, `http`, `net`, ...) are only delivered to the `node:perf_hooks` `PerformanceObserver`, and `gc`, `dns` and `resource` entries are never emitted. +🟡 Observing `mark` and `measure` entries works. Bun only delivers Node-only entry types (`function`, `http`, `net`, ...) to the `node:perf_hooks` `PerformanceObserver`, and never emits `gc`, `dns` or `resource` entries. ### [`PerformanceObserverEntryList`](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserverEntryList) @@ -357,11 +357,11 @@ The following list covers the globals implemented by Node.js and Bun's compatibi ### [`performance`](https://developer.mozilla.org/en-US/docs/Web/API/performance) -🟡 `now()`, `timeOrigin`, `mark()`, `measure()` and `getEntries()` are implemented. The Node.js additions (`eventLoopUtilization()`, `nodeTiming`, `timerify()`) only exist once `node:perf_hooks` has been loaded; `eventLoopUtilization()` always returns zeros and `nodeTiming` holds placeholder values. +🟡 `now()`, `timeOrigin`, `mark()`, `measure()` and `getEntries()` are implemented. The Node.js additions (`eventLoopUtilization()`, `nodeTiming`, `timerify()`) only exist once `node:perf_hooks` has been loaded. `eventLoopUtilization()` always returns zeros and `nodeTiming` holds placeholder values. ### [`process`](https://nodejs.org/api/process.html) -🟡 Mostly implemented. `process.binding` (internal Node.js bindings some packages rely on) is partially implemented: `buffer`, `config`, `constants`, `fs`, `natives`, `tty_wrap`, `util` and `uv` are available, the rest throw. Setting `process.title` is a no-op on macOS & Linux. `getActiveResourcesInfo()`, `_getActiveHandles()` and `_getActiveRequests()` always return an empty array, `setSourceMapsEnabled()` is a no-op, and `process.report.writeReport()` writes nothing. Missing `sourceMapsEnabled` `addUncaughtExceptionCaptureCallback`. +🟡 Mostly implemented. `process.binding` (internal Node.js bindings some packages rely on) is partially implemented: `buffer`, `config`, `constants`, `fs`, `natives`, `tty_wrap`, `util` and `uv` are available, the rest throw. Setting `process.title` is a no-op on macOS & Linux. `getActiveResourcesInfo()`, `_getActiveHandles()` and `_getActiveRequests()` always return an empty array, `setSourceMapsEnabled()` is a no-op, and `process.report.writeReport()` writes nothing. Missing `sourceMapsEnabled` and `addUncaughtExceptionCaptureCallback`. ### [`queueMicrotask()`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask) diff --git a/docs/runtime/plugins.mdx b/docs/runtime/plugins.mdx index 350de036de6e..7453e77efc31 100644 --- a/docs/runtime/plugins.mdx +++ b/docs/runtime/plugins.mdx @@ -12,9 +12,9 @@ Plugins intercept imports and perform custom loading logic, like reading files o Plugins register callbacks that run at various points in the lifecycle of a bundle: - [`onStart()`](#onstart): Run once the bundler has started a bundle -- [`onResolve()`](#onresolve): Run before a module is resolved -- [`onLoad()`](#onload): Run before a module is loaded -- [`onBeforeParse()`](#onbeforeparse): Run zero-copy native addons in the parser thread before a file is parsed +- [`onResolve()`](#onresolve): Run before the bundler resolves a module +- [`onLoad()`](#onload): Run before the bundler loads a module +- [`onBeforeParse()`](#onbeforeparse): Run zero-copy native addons in the parser thread before the bundler parses a file ### Reference @@ -60,7 +60,7 @@ type Loader = ## Usage -A plugin is defined as a JavaScript object containing a `name` property and a `setup` function. +A plugin is a JavaScript object containing a `name` property and a `setup` function. ```tsx myPlugin.ts icon="/icons/typescript.svg" import type { BunPlugin } from "bun"; @@ -87,7 +87,7 @@ await Bun.build({ ### Namespaces -`onLoad` and `onResolve` accept an optional `namespace` string. Every module has a namespace, which prefixes the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` transforms an import from `./myfile.yaml` into `yaml:./myfile.yaml`. +`onLoad` and `onResolve` accept an optional `namespace` string. Every module has a namespace, which prefixes the import in transpiled code. For instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` transforms an import from `./myfile.yaml` into `yaml:./myfile.yaml`. The default namespace is `"file"` and you don't need to specify it: `import myModule from "./my-module.ts"` is the same as `import myModule from "file:./my-module.ts"`. @@ -167,7 +167,7 @@ onResolve( To bundle your project, Bun walks down the dependency tree of all modules in your project. For each imported module, Bun has to find and read that module. The "finding" part is known as "resolving" a module. -The `onResolve()` lifecycle callback customizes how a module is resolved. +The `onResolve()` lifecycle callback customizes how Bun resolves a module. The first argument to `onResolve()` is an object with a `filter` and [`namespace`](#what-is-a-namespace) property. The filter is a regular expression run on the import string. Together they determine which modules your custom resolution logic applies to. @@ -250,7 +250,7 @@ This plugin transforms all imports of the form `import env from "env"` into a Ja #### `.defer()` -The `onLoad` callback receives a `defer` function, which returns a `Promise` that resolves once all _other_ modules have been loaded. Await it when a module's contents depend on other modules. +The `onLoad` callback receives a `defer` function, which returns a `Promise` that resolves once Bun has loaded all _other_ modules. Await it when a module's contents depend on other modules. ##### Example: tracking and reporting unused exports @@ -294,7 +294,7 @@ plugin({ }); ``` -The `.defer()` function can only be called once per `onLoad` callback. +You can call the `.defer()` function only once per `onLoad` callback. ## Native plugins @@ -304,7 +304,7 @@ Native plugins are written as [NAPI](/runtime/node-api) modules and can run on m The following lifecycle hooks are available to native plugins: -- [`onBeforeParse()`](#onbeforeparse): Called on any thread before a file is parsed by Bun's bundler. +- [`onBeforeParse()`](#onbeforeparse): Called on any thread before Bun's bundler parses a file. Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions. To create one, export a C ABI function that matches the signature of the lifecycle hook you want to implement. @@ -397,4 +397,4 @@ This lifecycle callback runs immediately before Bun's bundler parses a file. It receives the file's contents and can return new source code. -The callback can be called from any thread, so the NAPI module implementation must be thread-safe. +Bun can call the callback from any thread, so the NAPI module implementation must be thread-safe. diff --git a/docs/runtime/redis.mdx b/docs/runtime/redis.mdx index 6e81051bf893..628fb996ffd0 100644 --- a/docs/runtime/redis.mdx +++ b/docs/runtime/redis.mdx @@ -413,11 +413,11 @@ const client = new RedisClient("redis://localhost:6379", { When a connection is lost, the client automatically attempts to reconnect with exponential backoff: 1. The client starts with a small delay (50ms) and doubles it with each attempt -2. Reconnection delay is capped at 2000ms (2 seconds) +2. The client caps the reconnection delay at 2000ms (2 seconds) 3. The client attempts to reconnect up to `maxRetries` times (default: 20) -4. Commands executed during disconnection are: - - Queued if `enableOfflineQueue` is true (default) - - Rejected immediately if `enableOfflineQueue` is false +4. While disconnected, the client: + - Queues commands if `enableOfflineQueue` is true (default) + - Rejects commands immediately if `enableOfflineQueue` is false --- @@ -561,13 +561,13 @@ async function getSession(sessionId) { ## Implementation Notes -Bun's Redis client is implemented in Rust and uses the Redis Serialization Protocol (RESP3). It reconnects automatically with exponential backoff and pipelines commands, so multiple commands can be sent without waiting for replies to previous ones. +Bun's Redis client is implemented in Rust and uses the Redis Serialization Protocol (RESP3). It reconnects automatically with exponential backoff. It also pipelines commands, so it can send multiple commands without waiting for replies to previous ones. ## Limitations and Future Plans Limitations we plan to address in future versions: -- Transactions (MULTI/EXEC) must be done through raw commands +- Transactions (MULTI/EXEC) require raw commands Unsupported features: diff --git a/docs/runtime/repl.mdx b/docs/runtime/repl.mdx index 848f9ecdcb22..a2d2b995d8bc 100644 --- a/docs/runtime/repl.mdx +++ b/docs/runtime/repl.mdx @@ -27,8 +27,8 @@ undefined - **TypeScript & JSX** — Write TypeScript and JSX directly. Bun transpiles everything on the fly. - **Top-level `await`** — Await promises directly at the prompt without wrapping in an async function. -- **Syntax highlighting** — Input is highlighted as you type. -- **Persistent history** — History is saved to `~/.bun_repl_history` and persists across sessions. +- **Syntax highlighting** — The REPL highlights input as you type. +- **Persistent history** — The REPL saves history to `~/.bun_repl_history`. History persists across sessions. - **Tab completion** — Press `Tab` to complete property names and REPL commands. - **Multi-line input** — Unclosed brackets, braces, and parentheses automatically continue on the next line. - **Node.js globals** — `require`, `module`, `__dirname`, and `__filename` are available, resolved relative to your current working directory. @@ -74,7 +74,7 @@ undefined ## Importing modules -Just like Bun's runtime, the REPL accepts both `require` and `import`: mix ES modules and CommonJS freely at the prompt. Module resolution uses the same rules as `bun run`, so you can import from `node_modules`, relative paths, or `node:` builtins. +Like Bun's runtime, the REPL accepts both `require` and `import`: mix ES modules and CommonJS freely at the prompt. Module resolution uses the same rules as `bun run`, so you can import from `node_modules`, relative paths, or `node:` builtins. ```txt > import { z } from "zod" @@ -85,7 +85,7 @@ undefined '/tmp/file.txt' ``` -Declarations persist for the rest of the session, and `const`/`let` can be redeclared across evaluations (unlike in regular scripts), so you can re-run `import` and `require` statements while iterating. +Declarations persist for the rest of the session. Unlike in regular scripts, you can redeclare `const`/`let` across evaluations, so you can re-run `import` and `require` statements while iterating. --- @@ -148,7 +148,7 @@ The REPL supports Emacs-style line editing. ## History -REPL history is automatically saved to `~/.bun_repl_history` (up to 1000 entries) and loaded at the start of each session. Use `Up`/`Down` to navigate. +The REPL automatically saves history to `~/.bun_repl_history` (up to 1000 entries) and loads it at the start of each session. Use `Up`/`Down` to navigate. To export your history to a different file, use `.save`: @@ -173,4 +173,4 @@ bun repl -p "{ a: 1, b: 2 }" # { a: 1, b: 2 } ``` -Both flags use the same transforms as the interactive REPL, so a bare object literal like `{ a: 1 }` is treated as an object expression instead of a block statement. The process exits after the event loop drains (pending timers and I/O complete first). On error, the process exits with code `1`. +Both flags use the same transforms as the interactive REPL, so Bun treats a bare object literal like `{ a: 1 }` as an object expression instead of a block statement. The process exits after the event loop drains (pending timers and I/O complete first). On error, the process exits with code `1`. diff --git a/docs/runtime/s3.mdx b/docs/runtime/s3.mdx index d2293961874d..9dfc38a99580 100644 --- a/docs/runtime/s3.mdx +++ b/docs/runtime/s3.mdx @@ -183,7 +183,7 @@ await writer.end(); When your production service needs to let users upload files to your server, it's often more reliable for the user to upload directly to S3 instead of your server acting as an intermediary. -To do this, presign URLs for S3 files. Presigning generates a URL with a signature that lets a user upload that specific file to S3, without exposing your credentials or granting them unnecessary access to your bucket. +To let users upload directly to S3, presign URLs for S3 files. Presigning generates a URL with a signature that lets a user upload that specific file to S3, without exposing your credentials or granting them unnecessary access to your bucket. By default, Bun generates a `GET` URL that expires in 24 hours. @@ -273,7 +273,7 @@ const url = s3file.presign({ To redirect users to a presigned URL for an S3 file, pass an `S3File` instance to a `Response` object as the body. -The response redirects the user to a presigned URL for the S3 file, saving you the memory, time, and bandwidth cost of downloading the file to your server and sending it back to the user. +The response redirects the user to a presigned URL for the S3 file. The redirect saves you the memory, time, and bandwidth cost of downloading the file to your server and sending it back to the user. ```ts s3.ts icon="/icons/typescript.svg" const response = new Response(s3file); @@ -404,9 +404,9 @@ const supabase = new S3Client({ When using a virtual hosted-style endpoint, set the `virtualHostedStyle` option to `true`. <Note> - - If you don't specify an endpoint, Bun determines the AWS S3 endpoint from the provided region and bucket. - If no - region is specified, Bun defaults to `us-east-1`. - If you explicitly provide an endpoint, you don't need to specify a - bucket name. + - If you don't specify an endpoint, Bun determines the AWS S3 endpoint from the provided region and bucket. - If you + don't specify a region, Bun defaults to `us-east-1`. - If you explicitly provide an endpoint, you don't need to + specify a bucket name. </Note> ```ts s3.ts icon="/icons/typescript.svg" highlight={17, 25} @@ -465,7 +465,7 @@ For each option, if the `S3_*` environment variable is not set, Bun falls back t | `bucket` | `AWS_BUCKET` | | `sessionToken` | `AWS_SESSION_TOKEN` | -Bun reads these environment variables from [`.env` files](/runtime/environment-variables) or from the process environment at initialization time (`process.env` is not used for this). +Bun reads these environment variables from [`.env` files](/runtime/environment-variables) or from the process environment at initialization time (Bun does not use `process.env` for this). Options you pass to `s3.file(credentials)`, `new Bun.S3Client(credentials)`, or any of the methods that accept credentials override these defaults. So if you use the same credentials for different buckets, you can set the credentials once in your `.env` file and pass only `bucket: "my-bucket"` to `s3.file()`. @@ -597,7 +597,7 @@ Like `Bun.file()`, `S3File` extends [`Blob`](https://developer.mozilla.org/en-US | `await s3File.stream()` | `ReadableStream` | | `await s3File.arrayBuffer()` | `ArrayBuffer` | -That means `S3File` instances work with `fetch()`, `Response`, and other web APIs that accept `Blob` instances. +Because `S3File` extends `Blob`, `S3File` instances work with `fetch()`, `Response`, and other web APIs that accept `Blob` instances. ### Partial reads with `slice` @@ -637,7 +637,7 @@ When Bun's S3 API throws an error, the error has a `code` property with one of t - `ERR_S3_INVALID_SIGNATURE` - `ERR_S3_INVALID_SESSION_TOKEN` -When the S3 service itself returns an error (that is, not Bun), it is an `S3Error` instance (an `Error` instance with the name `"S3Error"`). +When the S3 service itself returns an error (that is, not Bun), the error is an `S3Error` instance: an `Error` instance with the name `"S3Error"`. ## `S3Client` static methods diff --git a/docs/runtime/secrets.mdx b/docs/runtime/secrets.mdx index 01103f5a0a9b..f86388d504cb 100644 --- a/docs/runtime/secrets.mdx +++ b/docs/runtime/secrets.mdx @@ -103,8 +103,8 @@ await secrets.set({ **Notes:** -- If a credential already exists for the given service/name combination, it is replaced -- The stored value is encrypted by the operating system +- If a credential already exists for the given service/name combination, Bun replaces it +- The operating system encrypts the stored value ### `Bun.secrets.delete(options)` @@ -241,7 +241,7 @@ await Bun.secrets.set({ ### macOS (Keychain) -- Credentials are stored in the user's login keychain +- Bun stores credentials in the user's login keychain - The keychain may prompt for access permission on first use - Credentials persist across system restarts - Accessible by the user who stored them @@ -249,20 +249,20 @@ await Bun.secrets.set({ ### Linux (libsecret) - Requires a secret service daemon such as GNOME Keyring or KWallet -- Credentials are stored in the default collection +- Bun stores credentials in the default collection - May prompt for unlock if the keyring is locked - The secret service must be running ### Windows (Credential Manager) -- Credentials are stored in Windows Credential Manager +- Bun stores credentials in Windows Credential Manager - Visible in Control Panel → Credential Manager → Windows Credentials - Persisted with the `CRED_PERSIST_ENTERPRISE` flag, so they're scoped per user - Encrypted using Windows Data Protection API ## Security Considerations -1. **Encryption**: Credentials are encrypted by the operating system's credential manager +1. **Encryption**: The operating system's credential manager encrypts credentials 2. **Access Control**: Only the user who stored the credential can retrieve it 3. **No Plain Text**: Passwords are never stored in plain text 4. **Memory Safety**: Bun zeros out password memory after use @@ -285,7 +285,7 @@ await Bun.secrets.set({ Unlike environment variables, `Bun.secrets`: - ✅ Encrypts credentials at rest (thanks to the operating system) -- ✅ Avoids exposing secrets in process memory dumps (memory is zeroed after it's no longer needed) +- ✅ Avoids exposing secrets in process memory dumps (Bun zeros the memory after it's no longer needed) - ✅ Survives application restarts - ✅ Can be updated without restarting the application - ✅ Provides user-level access control diff --git a/docs/runtime/semver.mdx b/docs/runtime/semver.mdx index da76b767981d..5b7fba5a298a 100644 --- a/docs/runtime/semver.mdx +++ b/docs/runtime/semver.mdx @@ -5,7 +5,7 @@ description: Use Bun's semantic versioning API `Bun.semver` compares semantic versions and checks whether a version is compatible with a range of versions. Versions and ranges are designed to be compatible with `node-semver`, which npm clients use. -It's about 20x faster than `node-semver`. +`Bun.semver` is about 20x faster than `node-semver`. <Frame>![Benchmark](https://github.com/oven-sh/bun/assets/709451/94746adc-8aba-4baf-a143-3c355f8e0f78)</Frame> @@ -54,4 +54,4 @@ unsorted.sort(semver.order); // ["1.0.0-alpha", "1.0.0-beta", "1.0.0-rc", "1.0.0 console.log(unsorted); ``` -If you need other semver functions, feel free to open an issue or pull request. +If you need other semver functions, open an issue or pull request. diff --git a/docs/runtime/shell.mdx b/docs/runtime/shell.mdx index 153df9860f9a..a5b1749d612a 100644 --- a/docs/runtime/shell.mdx +++ b/docs/runtime/shell.mdx @@ -20,9 +20,9 @@ await $`cat < ${response} | wc -c`; // 1256 ## Features -- **Cross-platform**: works on Windows, Linux & macOS. Instead of installing `rimraf` or `cross-env`, you can use Bun Shell. Common shell commands like `ls`, `cd`, and `rm` are implemented natively. +- **Cross-platform**: works on Windows, Linux & macOS. Instead of installing `rimraf` or `cross-env`, you can use Bun Shell. It implements common shell commands like `ls`, `cd`, and `rm` natively. - **Familiar**: Bun Shell is a bash-like shell that supports redirection, pipes, and environment variables. -- **Globs**: Glob patterns are supported natively, including `**`, `*`, and `{expansion}`. +- **Globs**: Bun Shell supports glob patterns natively, including `**`, `*`, and `{expansion}`. - **Template literals**: Template literals execute shell commands and interpolate variables and expressions. - **Safety**: Bun Shell escapes all strings by default, preventing shell injection attacks. - **JavaScript interop**: Use `Response`, `ArrayBuffer`, `Blob`, `Bun.file(path)` and other JavaScript objects as stdin, stdout, and stderr. @@ -33,7 +33,7 @@ await $`cat < ${response} | wc -c`; // 1256 ## Getting started -The simplest shell command is `echo`. To run it, use the `$` template literal tag: +Start with `echo`. To run it, use the `$` template literal tag: ```js import { $ } from "bun"; @@ -257,7 +257,7 @@ import { $ } from "bun"; await $`echo Hash of current commit: $(git rev-parse HEAD)`; ``` -The output is inserted as text, so you can use it to declare a shell variable: +Bun Shell inserts the output as text, so you can use it to declare a shell variable: ```js import { $ } from "bun"; @@ -271,7 +271,7 @@ await $` <Note> -Because Bun internally uses the special [`raw`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#raw_strings) property on the input template literal, using the backtick syntax for command substitution won't work: +Because Bun internally uses the special [`raw`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#raw_strings) property on the input template literal, using the backtick syntax for command substitution doesn't work: ```ts icon="file-code" import { $ } from "bun"; @@ -317,7 +317,7 @@ const foo = "bar123"; await $`FOO=${foo + "456"} bun -e 'console.log(process.env.FOO)'`; // bar123456\n ``` -Input is escaped by default, preventing shell injection attacks: +Bun Shell escapes input by default, preventing shell injection attacks: ```js import { $ } from "bun"; @@ -566,7 +566,7 @@ Bun Shell is a small programming language implemented in Rust, with a handwritte By design, Bun Shell _does not invoke a system shell_ like `/bin/sh`. It's a re-implementation of bash that runs in the same Bun process. -When parsing command arguments, it treats all _interpolated variables_ as single, literal strings. +When parsing command arguments, Bun Shell treats all _interpolated variables_ as single, literal strings. This protects against **command injection**: @@ -579,12 +579,12 @@ const userInput = "my-file.txt; rm -rf /"; await $`ls ${userInput}`; ``` -Here, `userInput` is treated as a single string, so `ls` tries to read the +Here, Bun Shell treats `userInput` as a single string, so `ls` tries to read the contents of a single directory named `my-file.txt; rm -rf /`. ### Security considerations -While command injection is prevented by default, you are still +While Bun Shell prevents command injection by default, you are still responsible for security in certain scenarios. Similar to the `Bun.spawn` or `node:child_process.exec()` APIs, you can intentionally diff --git a/docs/runtime/sql.mdx b/docs/runtime/sql.mdx index 1a9eb1552b54..a71d0c016099 100644 --- a/docs/runtime/sql.mdx +++ b/docs/runtime/sql.mdx @@ -3,7 +3,7 @@ title: SQL description: Bun provides native bindings for working with SQL databases through a unified Promise-based API that supports PostgreSQL, MySQL, and SQLite. --- -Queries are written as tagged template literals, and the client supports connection pooling, transactions, and prepared statements. +You write queries as tagged template literals, and the client supports connection pooling, transactions, and prepared statements. ```ts title="db.ts" icon="/icons/typescript.svg" import { sql, SQL } from "bun"; @@ -53,7 +53,7 @@ const sqliteResults = await sqlite` ### PostgreSQL -PostgreSQL is used when: +Bun uses PostgreSQL when: - The connection string doesn't match SQLite or MySQL patterns (it's the fallback adapter) - The connection string explicitly uses `postgres://` or `postgresql://` protocols @@ -240,7 +240,7 @@ const sql = new SQL({ }); ``` -Query parameters in the URL are parsed to set these options: +Bun parses query parameters in the URL to set these options: - `?mode=ro` → `readonly: true` - `?mode=rw` → `readonly: false, create: false` @@ -375,7 +375,7 @@ await sql` ### Dynamic columns in updates -Use `sql(object, ...string)` to pick which columns to update. Each column must be defined on the object. If you don't list any columns, all keys on the object are used. +Use `sql(object, ...string)` to pick which columns to update. Each column must be defined on the object. If you don't list any columns, Bun uses all keys on the object. ```ts await sql`UPDATE users SET ${sql(user, "name", "email")} WHERE id = ${user.id}`; @@ -489,7 +489,7 @@ When you use `Bun.sql()` without arguments, or `new SQL()` with a connection str #### MySQL Auto-Detection -MySQL is selected when the connection string matches these patterns: +Bun selects MySQL when the connection string matches these patterns: - `mysql://...` - MySQL protocol URLs - `mysql2://...` - MySQL2 protocol URLs (compatibility alias) @@ -506,7 +506,7 @@ DATABASE_URL="mysql2://user:pass@localhost:3306/mydb" bun run app.js #### SQLite Auto-Detection -SQLite is selected when the connection string matches these patterns: +Bun selects SQLite when the connection string matches these patterns: - `:memory:` - In-memory database - `sqlite://...` - SQLite protocol URLs @@ -541,7 +541,7 @@ DATABASE_URL="localhost:5432/mydb" bun run app.js ### MySQL Environment Variables -MySQL connections can be configured with environment variables: +You can configure MySQL connections with environment variables: ```bash # Primary connection URL (checked first) @@ -552,7 +552,7 @@ DATABASE_URL="mysql://user:pass@localhost:3306/mydb" DATABASE_URL="mysql2://user:pass@localhost:3306/mydb" ``` -If no connection URL is provided, Bun checks these individual parameters: +Without a connection URL, Bun checks these individual parameters: | Environment Variable | Default Value | Description | | ------------------------ | ------------- | -------------------------------- | @@ -577,7 +577,7 @@ These environment variables define the PostgreSQL connection: | `TLS_POSTGRES_DATABASE_URL` | SSL/TLS-enabled connection URL | | `TLS_DATABASE_URL` | Alternative SSL/TLS-enabled connection URL | -If no connection URL is provided, Bun checks these individual parameters: +Without a connection URL, Bun checks these individual parameters: | Environment Variable | Fallback Variables | Default Value | Description | | -------------------- | ---------------------------- | ------------- | ------------------------------------------------------------------------------ | @@ -590,7 +590,7 @@ If no connection URL is provided, Bun checks these individual parameters: ### SQLite Environment Variables -SQLite connections can be configured with `DATABASE_URL` when it contains a SQLite-compatible URL: +You can configure SQLite connections with `DATABASE_URL` when it contains a SQLite-compatible URL: ```bash # These are all recognized as SQLite @@ -599,7 +599,7 @@ DATABASE_URL="sqlite://./app.db" DATABASE_URL="file:///absolute/path/to/db.sqlite" ``` -**Note:** PostgreSQL-specific environment variables such as `POSTGRES_URL` and `PGHOST` are ignored when using SQLite. +**Note:** Bun ignores PostgreSQL-specific environment variables such as `POSTGRES_URL` and `PGHOST` when you use SQLite. --- @@ -618,7 +618,7 @@ DATABASE_URL=postgres://user:pass@localhost:5432/db bun --sql-preconnect index.j bun --sql-preconnect --hot index.js ``` -The `--sql-preconnect` flag establishes a PostgreSQL connection at startup using your configured environment variables. If the connection fails, the error is handled without crashing your application. +The `--sql-preconnect` flag establishes a PostgreSQL connection at startup using your configured environment variables. If the connection fails, Bun handles the error without crashing your application. --- @@ -837,7 +837,7 @@ await sqlite`INSERT INTO flexible VALUES (${1}, ${"text"}, ${123.45}, ${Buffer.f To start a new transaction, use `sql.begin`. This method works for both PostgreSQL and SQLite. For PostgreSQL, it reserves a dedicated connection from the pool. For SQLite, it begins a transaction on the single connection. -The `BEGIN` command is sent automatically, including any optional configurations you specify. If an error occurs during the transaction, Bun issues a `ROLLBACK`. +Bun sends the `BEGIN` command automatically, including any optional configurations you specify. If an error occurs during the transaction, Bun issues a `ROLLBACK`. ### Basic Transactions @@ -886,7 +886,7 @@ await sql.begin(async tx => { ### Distributed Transactions -Two-Phase Commit (2PC) is a distributed transaction protocol: in phase 1 the coordinator prepares each node, making sure its data is written and ready to commit, and in phase 2 the nodes commit or roll back based on the coordinator's decision. +Two-Phase Commit (2PC) is a distributed transaction protocol. In phase 1, the coordinator prepares each node, making sure its data is written and ready to commit. In phase 2, the nodes commit or roll back based on the coordinator's decision. In PostgreSQL and MySQL, distributed transactions persist beyond their original session, so privileged users or coordinators can commit or roll them back later. PostgreSQL implements them as prepared transactions; MySQL uses XA Transactions. @@ -908,7 +908,7 @@ await sql.rollbackDistributed("tx1"); ## Authentication -Bun supports SCRAM-SHA-256 (SASL), MD5, and Clear Text authentication. SASL is recommended for better security. See [Postgres SASL Authentication](https://www.postgresql.org/docs/current/sasl-authentication.html). +Bun supports SCRAM-SHA-256 (SASL), MD5, and Clear Text authentication. We recommend SASL for better security. See [Postgres SASL Authentication](https://www.postgresql.org/docs/current/sasl-authentication.html). ### SSL Modes Overview @@ -947,7 +947,7 @@ const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=verify-full ## Connection Pooling -Bun's SQL client manages a connection pool: database connections are reused across queries instead of being opened and closed for each one, and the pool caps the number of concurrent connections. +Bun's SQL client manages a connection pool. The pool reuses database connections across queries instead of opening and closing one for each query, and it caps the number of concurrent connections. ```ts const sql = new SQL({ @@ -959,7 +959,7 @@ const sql = new SQL({ }); ``` -No connection is made until you run a query. +Bun doesn't open a connection until you run a query. ```ts const sql = Bun.SQL(); // no connection are created @@ -1031,9 +1031,9 @@ await subscription.unlisten(); ### How it works -- All subscriptions on a client share one dedicated connection. The first `listen()` opens it and removing the last subscription closes it, so a client that never listens never pays for it, and unlistening everything lets the process exit without `sql.close()`. +- All subscriptions on a client share one dedicated connection. The first `listen()` opens it, and removing the last subscription closes it. As a result, a client that never listens never pays for the connection, and unlistening everything lets the process exit without `sql.close()`. - While anything is subscribed, that connection keeps the process alive, like a listening server. -- If the connection drops, it is re-established with exponential backoff (250ms doubling to 32s, with jitter) and every channel is re-subscribed. PostgreSQL only delivers to connected listeners, so notifications sent in between are lost; the optional third argument to `listen()` runs on the initial subscribe and after every reconnect, which is the place to catch up: +- If the connection drops, Bun re-establishes it with exponential backoff (250ms doubling to 32s, with jitter) and re-subscribes every channel. PostgreSQL only delivers to connected listeners, so notifications sent in between are lost. The optional third argument to `listen()` runs on the initial subscribe and after every reconnect, which is the place to catch up: ```ts await sql.listen("orders", handleOrder, async () => { @@ -1041,12 +1041,12 @@ await sql.listen("orders", handleOrder, async () => { }); ``` -- Every `listen()` call is its own subscription. Several on one channel share a single server-side `LISTEN`, each callback receives every notification, and each handle's `unlisten()` removes only what its own call registered. A callback (either argument) that throws is reported as an uncaught exception and stays subscribed. -- Channel names are quoted as identifiers for you; like any PostgreSQL identifier they are limited to 63 bytes, and longer names are rejected rather than silently truncated. PostgreSQL limits payloads to 8000 bytes by default. +- Every `listen()` call is its own subscription. Several on one channel share a single server-side `LISTEN`. Each callback receives every notification, and each handle's `unlisten()` removes only what its own call registered. A callback (either argument) that throws is reported as an uncaught exception and stays subscribed. +- Bun quotes channel names as identifiers for you. Like any PostgreSQL identifier, they are limited to 63 bytes, and Bun rejects longer names rather than silently truncating them. PostgreSQL limits payloads to 8000 bytes by default. ### `notify()` -`notify()` is an ordinary query (`SELECT pg_notify($1, $2)`) on whichever handle you call it through. On `sql` it uses the pool; inside `sql.begin()` it runs in the transaction, so PostgreSQL delivers it on `COMMIT` and drops it on `ROLLBACK`, which is how to announce a change only once it is visible: +`notify()` is an ordinary query (`SELECT pg_notify($1, $2)`) on whichever handle you call it through. On `sql` it uses the pool. Inside `sql.begin()` it runs in the transaction, so PostgreSQL delivers it on `COMMIT` and drops it on `ROLLBACK`. Calling `notify()` inside a transaction is how to announce a change only once it is visible: ```ts await sql.begin(async tx => { @@ -1063,7 +1063,7 @@ The payload is optional: `sql.notify("cache-invalidated")` is PostgreSQL's bare ## Prepared Statements -By default, Bun's SQL client creates named prepared statements for queries it can infer are static, which is faster. To disable this, set `prepare: false` in the connection options: +By default, Bun's SQL client creates named prepared statements for queries it can infer are static, which is faster. To disable named prepared statements, set `prepare: false` in the connection options: ```ts const sql = new SQL({ @@ -1077,7 +1077,7 @@ When `prepare: false` is set: Queries still use the "extended" protocol, but run as [unnamed prepared statements](https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY). An unnamed prepared statement lasts only until the next Parse statement specifying the unnamed statement as destination is issued. - Parameter binding is still safe against SQL injection -- Each query is parsed and planned from scratch by the server +- The server parses and plans each query from scratch - Queries are not [pipelined](https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-PIPELINING) You might want to use `prepare: false` when: @@ -1238,7 +1238,7 @@ try { ## Numbers and BigInt -Numbers that exceed the range of a 53-bit integer are returned as strings: +Bun returns numbers that exceed the range of a 53-bit integer as strings: ```ts import { sql } from "bun"; @@ -1281,13 +1281,13 @@ Things we haven't finished yet: #### Authentication Methods -MySQL supports multiple authentication plugins that are automatically negotiated: +MySQL supports multiple authentication plugins, which the client negotiates automatically: - **`mysql_native_password`** - Traditional MySQL authentication, widely compatible - **`caching_sha2_password`** - Default in MySQL 8.0+, more secure with RSA key exchange - **`sha256_password`** - SHA-256 based authentication -The client automatically handles authentication plugin switching when requested by the server, including secure password exchange over non-SSL connections. +The client automatically handles authentication plugin switching when the server requests it, including secure password exchange over non-SSL connections. #### Prepared Statements & Performance @@ -1342,7 +1342,7 @@ Bun sends client information to MySQL for monitoring: #### Type Handling -MySQL types are converted to JavaScript types: +Bun converts MySQL types to JavaScript types: | MySQL Type | JavaScript Type | Notes | | --------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------ | @@ -1361,7 +1361,7 @@ MySQL types are converted to JavaScript types: | BIT(1) | boolean | BIT(1) in MySQL | | GEOMETRY | string | Geometry data | -`DATETIME` and `TIMESTAMP` values have no timezone on the wire, so Bun reads them back as **UTC** — the `Date` you get has the same UTC wall-clock that was stored, regardless of the machine's timezone. This matches how values are written (a bound `Date` stores its UTC components). The same applies to PostgreSQL's `timestamp` (without time zone); `timestamptz` carries an explicit offset and is unaffected. +`DATETIME` and `TIMESTAMP` values have no timezone on the wire, so Bun reads them back as **UTC**. The `Date` you get has the same UTC wall-clock that was stored, regardless of the machine's timezone. Reading as UTC matches how Bun writes values (a bound `Date` stores its UTC components). The same applies to PostgreSQL's `timestamp` (without time zone); `timestamptz` carries an explicit offset and is unaffected. #### Differences from PostgreSQL @@ -1442,7 +1442,7 @@ try { The plan was to add more database drivers. The unified API now supports PostgreSQL, MySQL, and SQLite. </Accordion> <Accordion title="How do I know which database adapter is being used?"> - The adapter is automatically detected from the connection string: + Bun detects the adapter automatically from the connection string: - URLs starting with `mysql://` or `mysql2://` use MySQL - URLs matching SQLite patterns (`:memory:`, `sqlite://`, `file://`) use SQLite diff --git a/docs/runtime/sqlite.mdx b/docs/runtime/sqlite.mdx index 6f420cde81e4..34a0060dbdab 100644 --- a/docs/runtime/sqlite.mdx +++ b/docs/runtime/sqlite.mdx @@ -120,7 +120,7 @@ const db = new Database(); db.close(false); ``` -Statements created with `.query()` are owned by the `Database` and are finalized immediately either way. The underlying connection (and the database file handle) is released once the last outstanding `.prepare()` statement is finalized. +The `Database` owns statements created with `.query()` and finalizes them immediately either way. Bun releases the underlying connection (and the database file handle) once the last outstanding `.prepare()` statement is finalized. To finalize **every** outstanding statement, release the connection immediately, and throw if SQLite reports an error while closing, call `.close(true)`: @@ -130,12 +130,12 @@ const db = new Database(); db.close(true); ``` -Using a statement that was finalized by `close()` throws `Database has closed`, except `toString()`, which returns an empty string, and `finalize()`, which stays safe to call. +Using a statement that `close()` finalized throws `Database has closed`. Two exceptions: `toString()` returns an empty string, and `finalize()` stays safe to call. <Note> - `close()` is safe to call multiple times but has no effect after the first (except that `close(true)` after - `close(false)` still finalizes any remaining `.prepare()` statements). If a `Database` is garbage collected without - being closed, the connection is released once every statement created from it has also been finalized or collected. + `close()` is safe to call multiple times but has no effect after the first. One exception: `close(true)` after + `close(false)` still finalizes any remaining `.prepare()` statements. If a `Database` is garbage collected without + being closed, Bun releases the connection once every statement created from it has also been finalized or collected. The `using` statement calls `close(true)`. </Note> @@ -180,7 +180,7 @@ const query = db.query(`select "Hello world" as message`); <Note> **What does "cached" mean?** -The caching refers to the **compiled prepared statement** (the SQL bytecode), not the query results. When you call `db.query()` with the same SQL string multiple times, Bun returns the same cached `Statement` object instead of recompiling the SQL. The cache holds the `Database.MAX_QUERY_CACHE_SIZE` (default 20) most recently used SQL strings; evicted statements keep working but a later `db.query()` with the same string compiles a new one. +The caching refers to the **compiled prepared statement** (the SQL bytecode), not the query results. When you call `db.query()` with the same SQL string multiple times, Bun returns the same cached `Statement` object instead of recompiling the SQL. The cache holds the `Database.MAX_QUERY_CACHE_SIZE` (default 20) most recently used SQL strings. Evicted statements keep working, but a later `db.query()` with the same string compiles a new one. It is safe to reuse a cached statement with different parameter values: @@ -204,7 +204,7 @@ const query = db.prepare("SELECT * FROM foo WHERE bar = ?"); ## WAL mode -SQLite supports [write-ahead log mode](https://www.sqlite.org/wal.html) (WAL), which dramatically improves performance, especially with many concurrent readers and a single writer. Enabling WAL mode is recommended for most applications. +SQLite supports [write-ahead log mode](https://www.sqlite.org/wal.html) (WAL), which dramatically improves performance, especially with many concurrent readers and a single writer. We recommend enabling WAL mode for most applications. To enable WAL mode, run this pragma query at the beginning of your application: @@ -213,18 +213,18 @@ db.run("PRAGMA journal_mode = WAL;"); ``` <Accordion title="What is WAL mode?"> - In WAL mode, writes to the database are written directly to a separate file called the "WAL file" (`-wal`). A - shared-memory index file (`-shm`) is also created for read coordination. The WAL file is later integrated into the - main database file. Think of it as a buffer for pending writes. Refer to the [SQLite + In WAL mode, writes to the database go directly to a separate file called the "WAL file" (`-wal`). SQLite also creates + a shared-memory index file (`-shm`) for read coordination. SQLite later integrates the WAL file into the main database + file. Think of the WAL file as a buffer for pending writes. Refer to the [SQLite docs](https://www.sqlite.org/wal.html) for a more detailed overview. </Accordion> ### WAL sidecar file cleanup -When using WAL mode with a file-based database, SQLite creates two sidecar files alongside your database: a write-ahead log (`-wal`) and a shared-memory index (`-shm`). Whether these files are automatically removed after `.close()` depends on your platform: +When using WAL mode with a file-based database, SQLite creates two sidecar files alongside your database: a write-ahead log (`-wal`) and a shared-memory index (`-shm`). Whether SQLite removes these files automatically after `.close()` depends on your platform: - **macOS**: Bun uses the system-provided SQLite, which Apple builds with persistent WAL enabled. The `-wal` and `-shm` files **persist** after close. This is not a bug — it is how Apple configured the system SQLite. -- **Linux** and **Windows**: Bun statically links its own SQLite build, which follows upstream defaults. The sidecar files are **typically removed** after close when no other connections are open. +- **Linux** and **Windows**: Bun statically links its own SQLite build, which follows upstream defaults. SQLite **typically removes** the sidecar files after close when no other connections are open. To ensure sidecar files are cleaned up on all platforms, disable WAL persistence and run a truncating checkpoint before closing: @@ -263,7 +263,7 @@ const query = db.query(`SELECT ?1, ?2;`); const query = db.query(`SELECT $param1, $param2;`); ``` -Values are bound to these parameters when the query is executed. A `Statement` can be executed with several different methods, each returning the results in a different form. +You bind values to these parameters when you execute the query. You can execute a `Statement` with several different methods, each returning the results in a different form. ### Binding values @@ -330,7 +330,7 @@ query.get({ $message: "Hello world" }); { $message: "Hello world" } ``` -Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sqlite3_reset) followed by [`sqlite3_step`](https://www.sqlite.org/capi3ref.html#sqlite3_step) until it no longer returns `SQLITE_ROW`. If the query returns no rows, `null` is returned. +Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sqlite3_reset) followed by [`sqlite3_step`](https://www.sqlite.org/capi3ref.html#sqlite3_step) until it no longer returns `SQLITE_ROW`. If the query returns no rows, the result is `null`. ### `.run()` @@ -379,9 +379,9 @@ true true ``` -As a performance optimization, the class constructor is not called, default initializers are not run, and private fields are not accessible. This is more like `Object.create` than `new`: the class's prototype is assigned to the object, so its methods, getters, and setters work. +As a performance optimization, Bun does not call the class constructor or run default initializers, and private fields are not accessible. This is more like `Object.create` than `new`: Bun assigns the class's prototype to the object, so its methods, getters, and setters work. -The database columns are set as properties on the class instance. +Bun sets the database columns as properties on the class instance. ### `.iterate()` (`@@iterator`) @@ -492,7 +492,7 @@ const results = query.all("hello", "goodbye"); SQLite supports signed 64-bit integers, but JavaScript only supports signed 52-bit integers or arbitrary-precision integers with `bigint`. -`bigint` input is supported everywhere, but by default `bun:sqlite` returns integers as `number` types. If you need to handle integers larger than 2^53, set the `safeIntegers` option to `true` when creating a `Database` instance. This also validates that `bigint` values passed to `bun:sqlite` do not exceed 64 bits. +`bigint` input is supported everywhere, but by default `bun:sqlite` returns integers as `number` types. If you need to handle integers larger than 2^53, set the `safeIntegers` option to `true` when creating a `Database` instance. The option also validates that `bigint` values passed to `bun:sqlite` do not exceed 64 bits. ### `safeIntegers: true` @@ -565,7 +565,7 @@ const insertCats = db.transaction(cats => { No cats have been inserted yet. `db.transaction()` returns a new function (`insertCats`) that _wraps_ the function that executes the queries. -To execute the transaction, call this function. Arguments are passed through to the wrapped function, and the wrapped function's return value is returned by the transaction function. The wrapped function also has access to the `this` context as defined where the transaction is executed. +To execute the transaction, call this function. The transaction function passes its arguments through to the wrapped function and returns the wrapped function's return value. The wrapped function also has access to the `this` context as defined where the transaction is executed. ```ts db.ts icon="/icons/typescript.svg" highlight={3} const insert = db.prepare("INSERT INTO cats (name) VALUES ($name)"); @@ -579,10 +579,10 @@ const count = insertCats([{ $name: "Keanu" }, { $name: "Salem" }, { $name: "Croo console.log(`Inserted ${count} cats`); ``` -The driver automatically [begins](https://www.sqlite.org/lang_transaction.html) a transaction when `insertCats` is called and commits it when the wrapped function returns. If an exception is thrown, the transaction is rolled back. The exception propagates as usual; it is not caught. +The driver automatically [begins](https://www.sqlite.org/lang_transaction.html) a transaction when you call `insertCats` and commits it when the wrapped function returns. If an exception is thrown, the driver rolls back the transaction. The exception propagates as usual; the driver does not catch it. <Note> -**Nested transactions** — Transaction functions can be called from inside other transaction functions. When doing so, the inner transaction becomes a [savepoint](https://www.sqlite.org/lang_savepoint.html). +**Nested transactions** — You can call transaction functions from inside other transaction functions. When you do, the inner transaction becomes a [savepoint](https://www.sqlite.org/lang_savepoint.html). <Accordion title="View nested transaction example"> diff --git a/docs/runtime/streams.mdx b/docs/runtime/streams.mdx index 76c6dd25c5bc..c9b731a07194 100644 --- a/docs/runtime/streams.mdx +++ b/docs/runtime/streams.mdx @@ -27,7 +27,7 @@ const stream = new ReadableStream({ }); ``` -The contents of a `ReadableStream` can be read chunk-by-chunk with `for await` syntax. +You can read the contents of a `ReadableStream` chunk-by-chunk with `for await` syntax. ```ts for await (const chunk of stream) { @@ -44,7 +44,7 @@ for await (const chunk of stream) { Bun implements an optimized version of `ReadableStream` that avoids unnecessary data copying and queue management. -With a traditional `ReadableStream`, chunks of data are _enqueued_. Each chunk is copied into a queue, where it sits until the stream is ready to send more data. +With a traditional `ReadableStream`, you _enqueue_ chunks of data. The stream copies each chunk into a queue, where it sits until the stream is ready to send more data. ```ts const stream = new ReadableStream({ @@ -56,7 +56,7 @@ const stream = new ReadableStream({ }); ``` -With a direct `ReadableStream`, chunks of data are written directly to the stream. No queueing happens, and there's no need to clone the chunk data into memory. The `controller` API reflects this: you call `.write()` instead of `.enqueue()`. +With a direct `ReadableStream`, you write chunks of data directly to the stream. No queueing happens, and there's no need to clone the chunk data into memory. The `controller` API reflects this: you call `.write()` instead of `.enqueue()`. ```ts const stream = new ReadableStream({ @@ -72,7 +72,7 @@ When using a direct `ReadableStream`, the destination handles all chunk queueing ### Handling backpressure -`controller.write()` returns the number of bytes written, or a **pending `Promise<number>`** when the destination's internal buffer is full (for example, a slow HTTP client). The chunk is accepted either way; the promise resolves once the destination has drained, so `await`ing the result is enough: +`controller.write()` returns the number of bytes written, or a **pending `Promise<number>`** when the destination's internal buffer is full (for example, a slow HTTP client). The chunk is accepted either way. The promise resolves once the destination has drained, so `await`ing the result is enough: ```ts const stream = new ReadableStream({ @@ -86,9 +86,9 @@ const stream = new ReadableStream({ }); ``` -`await controller.flush(true)` is equivalent and can be used after a write returns a `Promise`. +`await controller.flush(true)` is equivalent, and you can use it after a write returns a `Promise`. -For default (non-`direct`) `ReadableStream`s and async-generator response bodies, Bun applies this backpressure automatically — the producer is paused while the destination is backed up. +For default (non-`direct`) `ReadableStream`s and async-generator response bodies, Bun applies this backpressure automatically: it pauses the producer while the destination is backed up. --- @@ -180,7 +180,7 @@ sink.write(Buffer.from("lo").buffer); sink.end(); ``` -Once `.end()` is called, no more data can be written to the `ArrayBufferSink`. However, when buffering a stream you may want to keep writing data and periodically `.flush()` the contents (say, into a `WritableStream`). To support this, pass `stream: true` to the `start` method. +Once you call `.end()`, you can't write any more data to the `ArrayBufferSink`. However, when buffering a stream you may want to keep writing data and periodically `.flush()` the contents (say, into a `WritableStream`). To support this, pass `stream: true` to the `start` method. ```ts const sink = new Bun.ArrayBufferSink(); diff --git a/docs/runtime/templating/create.mdx b/docs/runtime/templating/create.mdx index 01183102846d..44930a186c48 100644 --- a/docs/runtime/templating/create.mdx +++ b/docs/runtime/templating/create.mdx @@ -60,7 +60,7 @@ When you run `bun create <component>`, Bun: [TailwindCSS](https://tailwindcss.com/) is a utility-first CSS framework for styling web applications. -When you run `bun create <component>`, Bun scans your JSX/TSX file for TailwindCSS class names (and any files it imports). If it detects TailwindCSS class names, it adds the following dependencies to your `package.json`: +When you run `bun create <component>`, Bun scans your JSX/TSX file (and any files it imports) for TailwindCSS class names. If Bun detects TailwindCSS class names, it adds the following dependencies to your `package.json`: ```json package.json icon="file-json" { @@ -78,7 +78,7 @@ Bun also configures `bunfig.toml` to use its TailwindCSS plugin with `Bun.serve( plugins = ["bun-plugin-tailwind"] ``` -And writes a `${component}.css` file with `@import "tailwindcss";` at the top: +Bun also writes a `${component}.css` file with `@import "tailwindcss";` at the top: ```css MyComponent.css icon="file-code" @import "tailwindcss"; @@ -226,7 +226,7 @@ After cloning a template, `bun create` removes the `"bun-create"` section from ` | Name | Description | | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `GITHUB_API_DOMAIN` | The GitHub domain Bun downloads from. Set this if you use GitHub Enterprise or a proxy | -| `GITHUB_TOKEN` (or `GITHUB_ACCESS_TOKEN`) | Lets `bun create` access private repositories and avoid rate limits. `GITHUB_TOKEN` is chosen over `GITHUB_ACCESS_TOKEN` if both exist. | +| `GITHUB_TOKEN` (or `GITHUB_ACCESS_TOKEN`) | Lets `bun create` access private repositories and avoid rate limits. Bun picks `GITHUB_TOKEN` over `GITHUB_ACCESS_TOKEN` if both exist. | <Accordion title={<span>How <code>bun create</code> works</span>}> @@ -237,13 +237,13 @@ IF remote template 1. GET `registry.npmjs.org/@bun-examples/${template}/latest` and parse it 2. GET `registry.npmjs.org/@bun-examples/${template}/-/${template}-${latestVersion}.tgz` 3. Decompress & extract `${template}-${latestVersion}.tgz` into `${destination}` - - If files would be overwritten, warn and exit unless `--force` is passed + - If files would be overwritten, warn and exit unless you pass `--force` IF GitHub repo 1. Download the tarball from GitHub’s API 2. Decompress & extract into `${destination}` - - If files would be overwritten, warn and exit unless `--force` is passed + - If files would be overwritten, warn and exit unless you pass `--force` ELSE IF local template @@ -253,11 +253,11 @@ ELSE IF local template 4. Parse the `package.json` (again!), update `name` to be `${basename(destination)}`, remove the `bun-create` section from the `package.json` and save the updated `package.json` to disk. 5. Run any tasks defined in `"bun-create": { "preinstall" }` -6. Run `bun install` unless `--no-install` is passed OR no dependencies are in package.json +6. Run `bun install` unless you pass `--no-install` OR no dependencies are in package.json 7. Run any tasks defined in `"bun-create": { "postinstall" }` 8. Run `git init; git add -A .; git commit -am "Initial Commit";` - Rename `gitignore` to `.gitignore`. npm strips `.gitignore` files from published packages. - - If there are dependencies, this runs in a separate thread concurrently while node_modules are being installed - - Using libgit2 if available was tested and performed 3x slower in microbenchmarks + - If there are dependencies, this step runs in a separate thread concurrently while Bun installs node_modules + - We tested using libgit2 if available, and it performed 3x slower in microbenchmarks </Accordion> diff --git a/docs/runtime/templating/init.mdx b/docs/runtime/templating/init.mdx index 07463ec6e465..be0de6faa62f 100644 --- a/docs/runtime/templating/init.mdx +++ b/docs/runtime/templating/init.mdx @@ -46,8 +46,8 @@ It creates: AI Agent rules (disable with `$BUN_AGENT_RULE_DISABLED=1`): -- a `CLAUDE.md` file when Claude CLI is detected (disable with `CLAUDE_CODE_AGENT_RULE_DISABLED` env var) -- a `.cursor/rules/*.mdc` file when Cursor is detected, which tells [Cursor AI](https://cursor.sh) to use Bun instead of Node.js and npm +- a `CLAUDE.md` file when `bun init` detects Claude CLI (disable with `CLAUDE_CODE_AGENT_RULE_DISABLED` env var) +- a `.cursor/rules/*.mdc` file when `bun init` detects Cursor; the file tells [Cursor AI](https://cursor.sh) to use Bun instead of Node.js and npm Pass `-y` or `--yes` to accept the defaults without prompting. diff --git a/docs/runtime/toml.mdx b/docs/runtime/toml.mdx index c0a85b2ea23d..5139ef473b7e 100644 --- a/docs/runtime/toml.mdx +++ b/docs/runtime/toml.mdx @@ -48,7 +48,7 @@ console.log(data); Bun's TOML parser implements the full [TOML v1.1.0 specification](https://github.com/toml-lang/toml/releases/tag/1.1.0) and passes the complete official [toml-test](https://github.com/toml-lang/toml-test) conformance suite. - **Strings**: basic (`"..."`) and literal (`'...'`), including multi-line, with all escapes (`\uHHHH`, `\UHHHHHHHH`, and TOML 1.1's `\xHH` and `\e`) -- **Integers**: decimal, hex (`0x`), octal (`0o`), and binary (`0b`). Integers that cannot be represented losslessly as a JavaScript number — outside ±(2^53 - 1) — throw +- **Integers**: decimal, hex (`0x`), octal (`0o`), and binary (`0b`). Integers outside ±(2^53 - 1) throw, because a JavaScript number cannot represent them losslessly - **Floats**: including `inf` and `nan` - **Booleans**: `true` and `false` - **Date/times**: returned as [Temporal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal) objects — offset date-time as `Temporal.Instant`, local date-time as `Temporal.PlainDateTime`, local date as `Temporal.PlainDate`, and local time as `Temporal.PlainTime` diff --git a/docs/runtime/transpiler.mdx b/docs/runtime/transpiler.mdx index 02a3b3fccb08..0364354d50b0 100644 --- a/docs/runtime/transpiler.mdx +++ b/docs/runtime/transpiler.mdx @@ -15,7 +15,7 @@ const transpiler = new Bun.Transpiler({ ## `.transformSync()` -Transpile code synchronously with the `.transformSync()` method. Modules are not resolved and the code is not executed. The result is a string of vanilla JavaScript code. +Transpile code synchronously with the `.transformSync()` method. The transpiler does not resolve modules or execute the code. The result is a string of vanilla JavaScript code. <CodeGroup> ```ts transpile.ts icon="/icons/typescript.svg" @@ -80,7 +80,7 @@ await transpiler.transform("<div>hi!</div>", "tsx"); The `.transform()` method runs the transpiler in Bun's worker threadpool, so running it 100 times spreads the work across `Math.floor($cpu_count * 0.8)` threads without blocking the main JavaScript thread. -If your code uses a macro, it may spawn a new copy of Bun's JavaScript runtime environment in that new thread. +If your code uses a macro, the transpiler may spawn a new copy of Bun's JavaScript runtime environment in that new thread. </Accordion> diff --git a/docs/runtime/typescript.mdx b/docs/runtime/typescript.mdx index 8c40b210d14f..deda4a90c61b 100644 --- a/docs/runtime/typescript.mdx +++ b/docs/runtime/typescript.mdx @@ -17,7 +17,7 @@ console.log(Bun.version); ## Suggested `compilerOptions` -Bun supports top-level await, JSX, and imports with `.ts` extensions, which TypeScript doesn't allow by default. The following `compilerOptions` are recommended for a Bun project, so you can use these features without compiler warnings from TypeScript. +Bun supports top-level await, JSX, and imports with `.ts` extensions, which TypeScript doesn't allow by default. We recommend the following `compilerOptions` for a Bun project so you can use these features without compiler warnings from TypeScript. ```jsonc { diff --git a/docs/runtime/utils.mdx b/docs/runtime/utils.mdx index c268b11418bc..c34d6d388f5b 100644 --- a/docs/runtime/utils.mdx +++ b/docs/runtime/utils.mdx @@ -27,14 +27,14 @@ An alias for `process.env`. ## `Bun.main` -An absolute path to the entrypoint of the current program (the file that was executed with `bun run`). +An absolute path to the entrypoint of the current program (the file you executed with `bun run`). ```ts script.ts Bun.main; // /path/to/script.ts ``` -Use this to determine whether a script is being executed directly, as opposed to being imported by another script. +Use this to determine whether a script is running directly or another script is importing it. ```ts if (import.meta.path === Bun.main) { @@ -44,7 +44,7 @@ if (import.meta.path === Bun.main) { } ``` -This is analogous to the [`require.main = module` trick](https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line) in Node.js. +This check is analogous to the [`require.main = module` trick](https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line) in Node.js. ## `Bun.sleep()` @@ -126,11 +126,11 @@ const id = randomUUIDv7(); A UUID v7 is a 128-bit value that encodes the current timestamp, a random value, and a counter. The timestamp is encoded using the lowest 48 bits, and the random value and counter are encoded using the remaining bits. -The `timestamp` parameter defaults to the current time in milliseconds. When the clock moves forward, the counter is reseeded to a new pseudo-random integer (the high bit of the 12-bit counter is kept clear so at least 2048 values remain before rollover). If the clock has not advanced past the last emitted timestamp, Bun reuses the last emitted timestamp and increments the counter. If that counter rolls over, Bun bumps the emitted timestamp forward instead of wrapping the counter, so the returned UUIDs stay strictly increasing (RFC 9562 §6.2). The counter is atomic and threadsafe, so calls to `Bun.randomUUIDv7()` from many Workers in the same process at the same timestamp don't produce colliding counter values. +The `timestamp` parameter defaults to the current time in milliseconds. When the clock moves forward, Bun reseeds the counter to a new pseudo-random integer. Bun leaves the high bit of the 12-bit counter clear when reseeding, so at least 2048 values remain before rollover. If the clock has not advanced past the last emitted timestamp, Bun reuses the last emitted timestamp and increments the counter. If that counter rolls over, Bun bumps the emitted timestamp forward instead of wrapping the counter, so the returned UUIDs stay strictly increasing (RFC 9562 §6.2). The counter is atomic and threadsafe, so calls to `Bun.randomUUIDv7()` from many Workers in the same process at the same timestamp don't produce colliding counter values. -When you pass an explicit `timestamp`, Bun encodes that value verbatim and tracks a separate counter for it, so explicit-timestamp calls do not observe or alter the monotonic state used by the default path. Repeated calls with the same explicit timestamp increment that separate counter (and bump the emitted timestamp on rollover) so they stay sortable; a call with a different explicit timestamp reseeds it. +When you pass an explicit `timestamp`, Bun encodes that value verbatim and tracks a separate counter for it, so explicit-timestamp calls do not observe or alter the monotonic state used by the default path. Repeated calls with the same explicit timestamp increment that separate counter, and bump the emitted timestamp on rollover, so they stay sortable. A call with a different explicit timestamp reseeds that counter. -The final 8 bytes of the UUID are a cryptographically secure random value. It uses the same random number generator used by `crypto.randomUUID()` (which comes from BoringSSL, which in turn comes from the platform-specific system random number generator usually provided by the underlying hardware). +The final 8 bytes of the UUID are a cryptographically secure random value. `Bun.randomUUIDv7()` uses the same random number generator as `crypto.randomUUID()`. That generator comes from BoringSSL. BoringSSL's randomness in turn comes from the platform-specific system random number generator, which the underlying hardware usually provides. ```ts namespace Bun { @@ -274,7 +274,7 @@ Bun.deepEquals(a, b); // => true Bun.deepEquals(a, b, true); // => false ``` -In strict mode, the following are considered unequal: +In strict mode, Bun considers the following unequal: ```ts // undefined values @@ -307,13 +307,13 @@ Escapes the following characters from an input string: This function is optimized for large input. On an M1X, it processes 480 MB/s - 20 GB/s, depending on how much data is being escaped and whether there is non-ASCII -text. Non-string types are converted to a string before escaping. +text. Bun converts non-string types to a string before escaping. ## `Bun.stringWidth()` <Note>~6,756x faster `string-width` alternative</Note> -Get the column count of a string as it would be displayed in a terminal. +Get the column count of a string as a terminal would display it. Supports ANSI escape codes, emoji, and wide characters. Example usage: @@ -326,7 +326,7 @@ Bun.stringWidth("\u001b[31mhello\u001b[0m", { countAnsiEscapeCodes: true }); // Use it to align text in a terminal or to check whether a string contains ANSI escape codes. -The API matches the "string-width" npm package, so existing code can be ported to Bun and vice versa. +The API matches the "string-width" npm package, so you can port existing code to Bun and vice versa. [In this benchmark](https://github.com/oven-sh/bun/blob/5147c0ba7379d85d4d1ed0714b84d6544af917eb/bench/snippets/string-width.mjs#L13), `Bun.stringWidth` is ~6,756x faster than the `string-width` npm package for input larger than about 500 characters. Big thanks to [sindresorhus](https://github.com/sindresorhus) for their work on `string-width`. @@ -646,7 +646,7 @@ dec.decode(decompressedSync); ## `Bun.inspect()` -Serializes an object to a `string` exactly as it would be printed by `console.log`. +Serializes an object to a `string` exactly as `console.log` would print it. ```ts const obj = { foo: "bar" }; @@ -660,7 +660,7 @@ const str = Bun.inspect(arr); ### `Bun.inspect.custom` -The symbol Bun uses to implement `Bun.inspect`. Override it to customize how your objects are printed. It is identical to `util.inspect.custom` in Node.js. +The symbol Bun uses to implement `Bun.inspect`. Override it to customize how Bun prints your objects. It is identical to `util.inspect.custom` in Node.js. ```ts class Foo { @@ -783,7 +783,7 @@ await Bun.readableStreamToFormData(stream, multipartFormBoundary); ## `Bun.resolveSync()` -Resolves a file path or module specifier using Bun's internal [module resolution](/runtime/module-resolution) algorithm. The first argument is the path to resolve, and the second argument is the "root". If no match is found, it throws an `Error`. +Resolves a file path or module specifier using Bun's internal [module resolution](/runtime/module-resolution) algorithm. The first argument is the path to resolve, and the second argument is the "root". If nothing matches, it throws an `Error`. ```ts Bun.resolveSync("./foo.ts", "/path/to/project"); @@ -979,7 +979,7 @@ const obj = deserialize(buf); console.log(obj); // => { foo: "bar" } ``` -Internally, [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) and [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) serialize and deserialize the same way. This exposes the underlying [HTML Structured Clone Algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) to JavaScript as a SharedArrayBuffer. +Internally, [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) and [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) serialize and deserialize the same way. `serialize` and `deserialize` expose the underlying [HTML Structured Clone Algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) to JavaScript as a SharedArrayBuffer. --- diff --git a/docs/runtime/watch-mode.mdx b/docs/runtime/watch-mode.mdx index 27d2d8d617ba..a65b9efbddca 100644 --- a/docs/runtime/watch-mode.mdx +++ b/docs/runtime/watch-mode.mdx @@ -36,7 +36,7 @@ Instead, Bun uses the operating system's native filesystem watcher APIs, like kq </Note> -The following examples show Bun live-reloading a file as it is edited, with VSCode configured to save the file [on each keystroke](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save). +The following examples show Bun live-reloading a file as you edit it, with VSCode configured to save the file [on each keystroke](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save). ```sh terminal icon="terminal" bun run --watch watchy.tsx @@ -77,8 +77,8 @@ bun --watch test </Note> <Note> - Before each restart, `bun run --watch` runs the handlers your script registered for the kill signal (default - `SIGTERM`, matching the signal Node.js sends its watched process). Use **`--watch-kill-signal`** to pick a different + Before each restart, `bun run --watch` runs the handlers your script registered for the kill signal. The default is + `SIGTERM`, matching the signal Node.js sends its watched process. Use **`--watch-kill-signal`** to pick a different signal, e.g. `bun --watch --watch-kill-signal SIGINT index.ts`. </Note> @@ -89,17 +89,17 @@ bun --watch test Use `bun --hot` to enable hot reloading when executing code with Bun. Unlike `--watch` mode, Bun doesn't hard-restart the entire process. It detects code changes and updates its internal module cache with the new code. <Note> - This is not the same as hot reloading in the browser. Many frameworks provide a "hot reloading" experience, where you - can edit & save your frontend code (say, a React component) and see the changes reflected in the browser without - refreshing the page. Bun's `--hot` is the server-side equivalent of this experience. To get hot reloading in the - browser, use a framework like [Vite](https://vite.dev). + Bun's `--hot` is not the same as hot reloading in the browser. Many frameworks provide a "hot reloading" experience, + where you can edit & save your frontend code (say, a React component) and see the changes reflected in the browser + without refreshing the page. Bun's `--hot` is the server-side equivalent of this experience. To get hot reloading in + the browser, use a framework like [Vite](https://vite.dev). </Note> ```bash terminal icon="terminal" bun --hot server.ts ``` -Starting from the entrypoint (`server.ts` in this example), Bun builds a registry of all imported source files (excluding those in `node_modules`) and watches them for changes. When a file changes, Bun performs a "soft reload". All files are re-evaluated, but global state (notably, the `globalThis` object) persists. +Starting from the entrypoint (`server.ts` in this example), Bun builds a registry of all imported source files (excluding those in `node_modules`) and watches them for changes. When a file changes, Bun performs a "soft reload". Bun re-evaluates all files, but global state (notably, the `globalThis` object) persists. ```ts title="server.ts" icon="/icons/typescript.svg" // make TypeScript happy diff --git a/docs/runtime/web-apis.mdx b/docs/runtime/web-apis.mdx index 48930ec70cb3..9aeced07991a 100644 --- a/docs/runtime/web-apis.mdx +++ b/docs/runtime/web-apis.mdx @@ -6,7 +6,7 @@ mode: center Some Web APIs, like the [DOM API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API#html_dom_api_interfaces) and [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API), aren't relevant in a server-first runtime like Bun. Many others are broadly useful outside the browser; when possible, Bun implements these Web-standard APIs instead of introducing new ones. -The following Web APIs are partially or completely supported. +Bun partially or completely supports the following Web APIs. | Category | APIs | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/docs/runtime/webview.mdx b/docs/runtime/webview.mdx index 6e3a2f7f2fbf..5265f0d44f9d 100644 --- a/docs/runtime/webview.mdx +++ b/docs/runtime/webview.mdx @@ -35,7 +35,7 @@ const view = new Bun.WebView({ The constructor is synchronous — it returns immediately and spawns the browser subprocess in the background. The first operation you `await` (such as `navigate()` or `evaluate()`) waits for the browser to be ready. -If you pass `url`, the view begins navigating before the constructor returns. This is equivalent to calling `view.navigate(url)` on the next line. +If you pass `url`, the view begins navigating before the constructor returns. Passing `url` is equivalent to calling `view.navigate(url)` on the next line. ### Automatic cleanup with `using` @@ -63,7 +63,7 @@ Views that share the same `directory` share cookies and storage. Pass `dataStore <Note> With the Chrome backend, `dataStore.directory` maps to `--user-data-dir` and applies to the **entire Chrome process**, - not per-view. Since Chrome is spawned once per Bun process, the first view's directory wins for all subsequent views. + not per-view. Bun spawns Chrome once per Bun process, so the first view's directory wins for all subsequent views. </Note> <Note> @@ -91,13 +91,13 @@ const view = new Bun.WebView({ backend: "chrome" }); ### How the WebKit backend works -Bun spawns a lightweight host subprocess (the `bun` binary itself, re-executed in a special mode) that owns the `WKWebView` on its main thread. Your Bun process talks to it over a Unix socket using a compact binary protocol. The host process is spawned once and shared by every `"webkit"` view in your program. +Bun spawns a lightweight host subprocess (the `bun` binary itself, re-executed in a special mode) that owns the `WKWebView` on its main thread. Your Bun process talks to it over a Unix socket using a compact binary protocol. Bun spawns the host process once, and every `"webkit"` view in your program shares it. ### How the Chrome backend works Bun either **connects** to an already-running Chrome over a WebSocket, or **spawns** a headless Chrome subprocess and talks to it over a pipe (`--remote-debugging-pipe`). Either way, communication uses the [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/). -Chrome is spawned (or connected) once per Bun process. Each `new Bun.WebView({ backend: "chrome" })` creates a new tab with `Target.createTarget` in that single Chrome instance. +Bun spawns (or connects to) Chrome once per Bun process. Each `new Bun.WebView({ backend: "chrome" })` creates a new tab with `Target.createTarget` in that single Chrome instance. #### Finding the Chrome executable @@ -109,11 +109,11 @@ When Bun needs to spawn Chrome, it searches in this order: 4. Standard install locations (`/Applications/Google Chrome.app`, `~/Applications/...`, `/usr/bin/...`, `/snap/bin/...`) 5. Playwright's cache (`~/Library/Caches/ms-playwright` or `~/.cache/ms-playwright`) for `chrome-headless-shell` -If none is found, the constructor throws. +If Bun finds none, the constructor throws. #### Connecting to an already-running Chrome {#existing-chrome} -By default, before spawning, Bun checks whether a Chrome-family browser is **already running** with remote debugging enabled by reading the `DevToolsActivePort` file from standard profile directories. If found, Bun connects to that browser over WebSocket instead of spawning a new one — your views open as tabs in your existing browser. +By default, before spawning, Bun reads the `DevToolsActivePort` file from standard profile directories to check whether a Chrome-family browser is **already running** with remote debugging enabled. If found, Bun connects to that browser over WebSocket instead of spawning a new one — your views open as tabs in your existing browser. To enable remote debugging in a running Chrome, visit `chrome://inspect/#remote-debugging` and flip the toggle, or launch Chrome with `--remote-debugging-port=9222`. Chrome prompts for permission on each new connection when you use the `chrome://inspect` toggle. @@ -230,10 +230,10 @@ const items = await view.evaluate("[...document.querySelectorAll('li')].map(li = const user = await view.evaluate("({ name: 'bun', ok: true })"); ``` -The script is wrapped as `await (<your script>)`, so: +Bun wraps the script as `await (<your script>)`, so: - It must be an **expression**, not a statement sequence. For multiple statements, wrap in an IIFE: `evaluate("(() => { let x = foo(); return x + 1 })()")`. -- If it evaluates to a `Promise`, the promise is awaited and its resolved value is returned. +- If it evaluates to a `Promise`, `evaluate()` awaits the promise and returns its resolved value. The result round-trips through `JSON.stringify` in the page and `JSON.parse` in Bun. Arrays and plain objects come back as real structures; `undefined`, functions, and symbols resolve to `undefined`; circular references reject. @@ -272,7 +272,7 @@ await view.screenshot({ format: "webp", quality: 75 }); // Chrome backend only ### Return type -The `encoding` option controls how the image bytes are handed back: +The `encoding` option controls how Bun hands back the image bytes: | `encoding` | Returns | Notes | | -------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------- | @@ -291,7 +291,7 @@ console.log(`<img src="data:image/png;base64,${b64}">`); #### Shared memory for terminal graphics -`encoding: "shmem"` is designed for Kitty's [terminal graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) `t=s` transmission mode — Bun writes the image to a POSIX shared-memory segment and returns its name; the terminal reads it directly and unlinks it when done. No copying through the pipe. +`encoding: "shmem"` is designed for Kitty's [terminal graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) `t=s` transmission mode. Bun writes the image to a POSIX shared-memory segment and returns its name. The terminal reads it directly and unlinks it when done. No copying through the pipe. ```ts const { name, size } = await view.screenshot({ encoding: "shmem" }); @@ -305,7 +305,7 @@ On WebKit, the shm name looks like `/bun-webview-<pid>-<seq>`; on Chrome, `/bun- ## Input simulation -All input methods dispatch **native** browser events. The page receives `pointerdown`/`mousedown`/`keydown`/`wheel` events with `isTrusted: true`, CSS `:active` and `:hover` states apply, and default actions (form submission, link navigation, text selection) fire exactly as if a user performed them. +All input methods dispatch **native** browser events. The page receives `pointerdown`/`mousedown`/`keydown`/`wheel` events with `isTrusted: true`. CSS `:active` and `:hover` states apply. Default actions (form submission, link navigation, text selection) fire exactly as if a user performed them. ### Clicking @@ -339,7 +339,7 @@ An element is actionable when it: The check runs page-side at `requestAnimationFrame` rate. If the element never becomes actionable within `timeout` milliseconds (default `30000`), the promise rejects with an error like `timeout waiting for '#submit' to be actionable`. -The selector is passed as data, not interpolated into a script, so selectors containing quotes or JavaScript syntax are safe. +Bun passes the selector as data instead of interpolating it into a script, so selectors containing quotes or JavaScript syntax are safe. ### Typing text @@ -365,7 +365,7 @@ Named virtual keys: `Enter`, `Tab`, `Space`, `Backspace`, `Delete`, `Escape`, `A Any single character (for example, `"a"`) combined with `modifiers` sends a keyboard chord. -On the WebKit backend, most named keys (without modifiers) map to editing commands such as `DeleteBackward`, `MoveLeft`, and `InsertNewline`, and resolve after the page has applied them. `Escape`, `Space`, and any key with modifiers fall back to raw `keydown`/`keyup` events — these fire a `keydown` the page can observe, but there's no completion barrier, so follow with an `evaluate()` if you need to observe the effect. +On the WebKit backend, most named keys (without modifiers) map to editing commands such as `DeleteBackward`, `MoveLeft`, and `InsertNewline`, and resolve after the page has applied them. `Escape`, `Space`, and any key with modifiers fall back to raw `keydown`/`keyup` events. These keys fire a `keydown` the page can observe, but there's no completion barrier. Follow with an `evaluate()` if you need to observe the effect. Modifier names: `"Shift"`, `"Control"` (or `"Ctrl"`), `"Alt"` (or `"Option"`), `"Meta"` (or `"Cmd"` / `"Command"`). @@ -406,7 +406,7 @@ Forward `console.*` calls from the page to your Bun process by passing the `cons ### Mirror to Bun's console -Pass `globalThis.console` (the actual object, by reference) and page-side `console.log("hi")` prints `hi` to your stdout with Bun's formatter; `console.error` goes to stderr. This path dispatches directly through Bun's console implementation with no per-call JavaScript overhead. +Pass `globalThis.console` (the actual object, by reference). Page-side `console.log("hi")` then prints `hi` to your stdout with Bun's formatter, and `console.error` goes to stderr. This path dispatches directly through Bun's console implementation with no per-call JavaScript overhead. ```ts const view = new Bun.WebView({ @@ -432,7 +432,7 @@ Primitive arguments (strings, numbers, booleans, `null`, `undefined`) unwrap to - **Chrome backend**: the raw CDP [`RemoteObject`](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject) — an object with `type`, `className`, `description`, and (when available) a `preview.properties` array. - **WebKit backend**: the `JSON.stringify` round-trip of the object. Functions, circular references, and other non-serializable values fall back to their `String(...)` coercion. -If you don't pass `console`, page-side console output is dropped. +If you don't pass `console`, Bun drops page-side console output. <Note> Ordering guarantee: a `console.log(...)` inside a script you pass to `evaluate()` reaches your handler **before** that @@ -471,7 +471,7 @@ Commands are scoped to this view's session (they target this tab). You must `awa ### Subscribing to events -`Bun.WebView` extends `EventTarget`. With the Chrome backend, CDP events are dispatched as DOM events whose `type` is the CDP method name and whose `data` is the parsed `params` object: +`Bun.WebView` extends `EventTarget`. With the Chrome backend, the view dispatches CDP events as DOM events whose `type` is the CDP method name and whose `data` is the parsed `params` object: ```ts await view.navigate("about:blank"); @@ -484,7 +484,7 @@ view.addEventListener("Network.responseReceived", event => { await view.navigate("https://example.com"); ``` -Events for which no listener is registered are dropped before the JSON `params` are even parsed, so enabling a chatty domain (like `Network`) is cheap if you only listen for one or two event types. +Bun drops events that have no registered listener before it even parses their JSON `params`, so enabling a chatty domain (like `Network`) is cheap if you only listen for one or two event types. On the WebKit backend, `cdp()` throws `ERR_METHOD_NOT_IMPLEMENTED` — there is no DevTools Protocol bridge. The `EventTarget` interface still works for your own `dispatchEvent()` calls. @@ -518,7 +518,7 @@ The browser subprocess does **not** keep Bun's event loop alive on its own. An o ### Subprocess death -If the browser subprocess dies unexpectedly (crash, OOM-kill, `SIGKILL`), every pending promise on every view rejects with an error describing how it died (`"Chrome killed by signal 9"`, `"WebView host process died"`), and further operations on those views throw. +If the browser subprocess dies unexpectedly (crash, OOM-kill, `SIGKILL`), every pending promise on every view rejects with an error describing how it died (`"Chrome killed by signal 9"`, `"WebView host process died"`). Further operations on those views throw. --- diff --git a/docs/runtime/workers.mdx b/docs/runtime/workers.mdx index 7b1b6734574f..b9e9261d0ba9 100644 --- a/docs/runtime/workers.mdx +++ b/docs/runtime/workers.mdx @@ -47,7 +47,7 @@ declare var self: Worker; You can use `import` and `export` syntax in your worker code. Unlike in browsers, you don't need to pass `{type: "module"}` to use ES modules. -If the worker's script fails to resolve, an `"error"` event is emitted on the `Worker` object. +If the worker's script fails to resolve, Bun emits an `"error"` event on the `Worker` object. ```js const worker = new Worker("/not-found.js"); @@ -56,7 +56,7 @@ worker.addEventListener("error", event => { }); ``` -The specifier passed to `Worker` is resolved relative to the project root (like typing `bun ./path/to/file.js`). +Bun resolves the specifier passed to `Worker` relative to the project root (like typing `bun ./path/to/file.js`). ### `preload` - load modules before the worker starts @@ -98,7 +98,7 @@ const worker = new Worker(url); ### `"open"` -The `"open"` event is emitted when a worker is created and ready to receive messages. (This event does not exist in browsers.) +Bun emits the `"open"` event when a worker is created and ready to receive messages. (This event does not exist in browsers.) ```ts index.ts icon="/icons/typescript.svg" const worker = new Worker(new URL("worker.ts", import.meta.url).href); @@ -112,7 +112,7 @@ Bun enqueues messages until the worker is ready, so you don't need to wait for t ## Messages with `postMessage` -To send messages, use [`worker.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) and [`self.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). Messages are serialized with the [HTML Structured Clone Algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). +To send messages, use [`worker.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) and [`self.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). Bun serializes messages with the [HTML Structured Clone Algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). ### Performance optimizations @@ -208,11 +208,11 @@ Calling `worker.terminate()` makes the worker exit as soon as possible. ### `process.exit()` -A worker can terminate itself with `process.exit()`. This does not terminate the main process. Like in Node.js, `process.on('beforeExit', callback)` and `process.on('exit', callback)` are emitted on the worker thread (and not on the main thread), and the exit code is passed to the `"close"` event. +A worker can terminate itself with `process.exit()`. This does not terminate the main process. Like in Node.js, `process.on('beforeExit', callback)` and `process.on('exit', callback)` are emitted on the worker thread, not on the main thread. Bun passes the exit code to the `"close"` event. ### `"close"` -The `"close"` event is emitted when a worker has been marked as terminated; the worker itself can take some time to fully exit. The `CloseEvent` contains the exit code passed to `process.exit()`, or 0 if it closed for another reason. +Bun emits the `"close"` event when a worker has been marked as terminated. The worker itself can take some time to fully exit. The `CloseEvent` contains the exit code passed to `process.exit()`, or 0 if it closed for another reason. ```ts index.ts icon="/icons/typescript.svg" const worker = new Worker(new URL("worker.ts", import.meta.url).href); diff --git a/docs/runtime/xml.mdx b/docs/runtime/xml.mdx index 8368b24fd2fd..8c72e6dbc580 100644 --- a/docs/runtime/xml.mdx +++ b/docs/runtime/xml.mdx @@ -50,7 +50,7 @@ By default the result is a **compact object** keyed by element name — the shap - The result has one key, the root element's name. - An element with no attributes and no child elements becomes its text content, trimmed of surrounding whitespace (`""` when empty). -- Any other element becomes an object with a `"@name"` key per attribute, one key per distinct child element name — an **array** when that name repeats, in document order — and `"#text"` for its trimmed character data, if any. +- Any other element becomes an object. It has a `"@name"` key per attribute, one key per distinct child element name, and `"#text"` for its trimmed character data, if any. When a child element name repeats, its key holds an **array** in document order. - CDATA sections and entity references are already expanded into the text. Comments and processing instructions are dropped. - All values are strings. Nothing is coerced to numbers, booleans, or `null`. @@ -71,7 +71,7 @@ console.log(p); // } ``` -Every element is `{ name, attributes, children }`; `children` holds child elements and strings, and text is passed through exactly (including whitespace-only runs between elements). +Every element is `{ name, attributes, children }`. `children` holds child elements and strings. The parser passes text through exactly, including whitespace-only runs between elements. #### Input types and encodings @@ -97,7 +97,7 @@ try { ### `Bun.XML.stringify()` -Serialize either shape back to XML. The output has no XML declaration and is always well-formed: `&`, `<`, `>` (and, in attributes, quotes, tabs and newlines) are escaped, and element or attribute names that are not XML names throw. +Serialize either shape back to XML. The output has no XML declaration and is always well-formed. Bun escapes `&`, `<` and `>`. In attributes it also escapes quotes, tabs and newlines. Element or attribute names that are not XML names throw. ```ts import { XML } from "bun"; @@ -120,7 +120,7 @@ XML.stringify({ // '<p class="lead">Hello <b>world</b>!</p>' ``` -A value with a string `name` and a `children` or `attributes` property is written as a node; anything else is a compact object and must have exactly one key naming the root element. Strings, numbers, booleans, bigints and `Date`s (as ISO strings) become text, `null` becomes an empty element, and `undefined`, functions and symbols are skipped like `JSON.stringify` skips them (unlike `JSON.stringify`, a bigint is written as its decimal digits rather than rejected). +Bun writes a value as a node when it has a string `name` and a `children` or `attributes` property. Anything else is a compact object and must have exactly one key naming the root element. Strings, numbers, booleans, bigints and `Date`s (as ISO strings) become text. `null` becomes an empty element. Bun skips `undefined`, functions and symbols, as `JSON.stringify` does. Unlike `JSON.stringify`, Bun writes a bigint as its decimal digits rather than rejecting it. #### Pretty printing @@ -144,7 +144,7 @@ console.log(XML.stringify(data, null, 2)); ### ES Modules -You can import XML files directly. Files are decoded like bytes passed to `XML.parse` (UTF-8, UTF-16, or ISO-8859-1 per the byte-order mark or declaration), and the module's value is the compact object described above: +You can import XML files directly. Bun decodes the file like bytes passed to `XML.parse` (UTF-8, UTF-16, or ISO-8859-1 per the byte-order mark or declaration). The module's value is the compact object described above: ```xml config.xml <?xml version="1.0" encoding="UTF-8"?> @@ -215,7 +215,7 @@ bun --hot server.ts ## Bundler Integration -When you bundle with Bun, imported XML files are parsed at build time and inlined as JavaScript objects: +When you bundle with Bun, the bundler parses imported XML files at build time and inlines them as JavaScript objects: ```bash terminal icon="terminal" bun build app.ts --outdir=dist @@ -229,7 +229,7 @@ Parsing at build time means: ### Dynamic Imports -XML files can be dynamically imported: +You can import XML files dynamically: ```ts const { default: doc } = await import("./config.xml"); @@ -242,17 +242,17 @@ const { default: doc } = await import("./config.xml"); Bun's XML parser is written in Rust and implements [XML 1.0 (Fifth Edition)](https://www.w3.org/TR/2008/REC-xml-20081126/) as a **non-validating processor that does not read external entities**: - The whole document, including the internal DTD subset, must be well-formed — anything else throws a `SyntaxError`. -- Internal entities declared in the document are expanded (with expansion limits, so "billion laughs" payloads fail instead of exhausting memory), attribute values are normalized, and attribute defaults declared in the internal subset are applied. -- External DTDs and external entities are never fetched or read, so there is no XXE surface. In a document with no DTD, a reference to an undeclared entity is an error; when the DOCTYPE points at an external subset (or uses parameter entities) that could have declared it, the reference is kept as written (` ` stays ` `), unless the document says `standalone="yes"`. -- Nothing is validated against the DTD, namespaces are not resolved (prefixed names are kept verbatim), and comments and processing instructions are skipped. +- The parser expands internal entities declared in the document, with expansion limits so "billion laughs" payloads fail instead of exhausting memory. It normalizes attribute values and applies attribute defaults declared in the internal subset. +- The parser never fetches or reads external DTDs or external entities, so there is no XXE surface. In a document with no DTD, a reference to an undeclared entity is an error. When the DOCTYPE points at an external subset (or uses parameter entities) that could have declared the entity, the parser keeps the reference as written (` ` stays ` `), unless the document says `standalone="yes"`. +- The parser validates nothing against the DTD. It does not resolve namespaces and keeps prefixed names verbatim. It skips comments and processing instructions. -It is run against the [W3C XML Conformance Test Suite](https://www.w3.org/XML/Test/): all 1,679 cases that have a required outcome for this class of processor pass — not-well-formed documents are rejected, well-formed ones are accepted and, where the suite gives one, their element tree matches its canonical output byte for byte. The [translated test suite](https://github.com/oven-sh/bun/blob/main/test/js/bun/xml/xml-test-suite.test.ts) lists every case, including the ones whose outcome legitimately depends on not reading external entities. +The parser is run against the [W3C XML Conformance Test Suite](https://www.w3.org/XML/Test/). All 1,679 cases that have a required outcome for this class of processor pass: the parser rejects not-well-formed documents and accepts well-formed ones. Where the suite gives a canonical output, the element tree of a well-formed document matches it byte for byte. The [translated test suite](https://github.com/oven-sh/bun/blob/main/test/js/bun/xml/xml-test-suite.test.ts) lists every case, including the ones whose outcome legitimately depends on not reading external entities. --- ## Performance -The parser works in two stages, like Bun's JSON parser: a SIMD pass (runtime-dispatched AVX2/AVX-512/NEON/SVE kernels) finds the bytes that can change the parse, so character data, attribute values, comments and CDATA sections are never scanned a byte at a time, and element and attribute names reuse JavaScriptCore's atom-string cache the same way `JSON.parse` does. +The parser works in two stages, like Bun's JSON parser. A SIMD pass (runtime-dispatched AVX2/AVX-512/NEON/SVE kernels) finds the bytes that can change the parse, so the parser never scans character data, attribute values, comments and CDATA sections a byte at a time. Element and attribute names reuse JavaScriptCore's atom-string cache the same way `JSON.parse` does. [`bench/xml/xml.mjs`](https://github.com/oven-sh/bun/blob/main/bench/xml/xml.mjs) compares `Bun.XML.parse` with popular npm parsers on the same documents (lower is better; Linux x64, one core): diff --git a/docs/runtime/yaml.mdx b/docs/runtime/yaml.mdx index 0ff3e59542c9..5a28023c83e7 100644 --- a/docs/runtime/yaml.mdx +++ b/docs/runtime/yaml.mdx @@ -74,7 +74,7 @@ Bun's YAML parser supports the full YAML 1.2 specification, including: - **Scalars**: strings, numbers, booleans, null values - **Collections**: sequences (arrays) and mappings (objects) -- **Anchors and Aliases**: reusable nodes with `&` and `*`. Aliased collections share identity, and an alias may refer to a collection that contains it, so `Bun.YAML.parse` can return cyclic objects (YAML imported as a module cannot be cyclic). +- **Anchors and Aliases**: reusable nodes with `&` and `*`. Aliased collections share identity. An alias may refer to a collection that contains it, so `Bun.YAML.parse` can return cyclic objects. YAML imported as a module cannot be cyclic. - **Tags**: type hints like `!!str`, `!!int`, `!!float`, `!!bool`, `!!null` - **Multi-line strings**: literal (`|`) and folded (`>`) scalars - **Comments**: using `#` diff --git a/docs/snippets/cli/build.mdx b/docs/snippets/cli/build.mdx index a6065f5c4081..e9b2f50a9b51 100644 --- a/docs/snippets/cli/build.mdx +++ b/docs/snippets/cli/build.mdx @@ -79,7 +79,7 @@ bun build <entry points> </ParamField> <ParamField path="--public-path" type="string"> - Prefix to be added to import paths in bundled code + Prefix the bundler adds to import paths in bundled code </ParamField> <ParamField path="--external" type="string"> @@ -139,8 +139,8 @@ bun build <entry points> </ParamField> <ParamField path="--react-compiler" type="boolean"> - Run the React Compiler over `.jsx`/`.tsx` files, automatically memoizing components and hooks. Output mode is derived - from `--target` (`browser` → client, `bun`/`node` → ssr). Experimental. + Run the React Compiler over `.jsx`/`.tsx` files, automatically memoizing components and hooks. The bundler derives the + output mode from `--target` (`browser` → client, `bun`/`node` → ssr). Experimental. </ParamField> ### Standalone Executables diff --git a/docs/snippets/cli/install.mdx b/docs/snippets/cli/install.mdx index b0083696f758..3b4c93cb9f05 100644 --- a/docs/snippets/cli/install.mdx +++ b/docs/snippets/cli/install.mdx @@ -163,7 +163,7 @@ bun install <name>@<version> ### Lifecycle Script Management <ParamField path="--ignore-scripts" type="boolean"> - Skip lifecycle scripts in the project's package.json (dependency scripts are never run) + Skip lifecycle scripts in the project's package.json (Bun never runs dependency scripts) </ParamField> ### Help Information diff --git a/docs/snippets/cli/outdated.mdx b/docs/snippets/cli/outdated.mdx index f1813d331f48..a80ae6bb75e5 100644 --- a/docs/snippets/cli/outdated.mdx +++ b/docs/snippets/cli/outdated.mdx @@ -127,7 +127,7 @@ bun outdated <filter> </ParamField> <ParamField path="--ignore-scripts" type="boolean"> - Skip lifecycle scripts in the project's <code>package.json</code> (dependency scripts are never run) + Skip lifecycle scripts in the project's <code>package.json</code> (Bun never runs dependency scripts) </ParamField> <ParamField path="--backend" type="string" default="clonefile"> diff --git a/docs/snippets/cli/publish.mdx b/docs/snippets/cli/publish.mdx index 478c7fe30c8a..a6ba5083955f 100644 --- a/docs/snippets/cli/publish.mdx +++ b/docs/snippets/cli/publish.mdx @@ -13,7 +13,7 @@ bun publish dist 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" { @@ -32,7 +32,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/snippets/cli/run.mdx b/docs/snippets/cli/run.mdx index 440f46fff00f..43f7ea4985e9 100644 --- a/docs/snippets/cli/run.mdx +++ b/docs/snippets/cli/run.mdx @@ -65,7 +65,7 @@ bun run <file or script> <ParamField path="--interactive" type="boolean"> Open the Node.js-compatible REPL (<code>node:repl</code>). When combined with <code>-e</code>, starts the REPL and then evaluates the script. Under <code>--interactive</code>, <code>-e</code> is raw JavaScript (matching{" "} - <code>node -i -e</code>); use <code>bun repl</code> for TypeScript. Distinct from <code>bun repl</code>, which is + <code>node -i -e</code>). Use <code>bun repl</code> for TypeScript. Distinct from <code>bun repl</code>, which is Bun's native REPL. </ParamField> @@ -141,7 +141,7 @@ bun run <file or script> ### Dependency & Module Resolution <ParamField path="--preload" type="string"> - Import a module before other modules are loaded. Alias: <code>-r</code> + Import a module before Bun loads other modules. Alias: <code>-r</code> </ParamField> <ParamField path="--require" type="string"> @@ -200,7 +200,7 @@ bun run <file or script> </ParamField> <ParamField path="--define" type="string"> - Substitute K:V while parsing, e.g. <code>--define process.env.NODE_ENV:"development"</code>. Values are parsed as + Substitute K:V while parsing, e.g. <code>--define process.env.NODE_ENV:"development"</code>. Bun parses values as JSON. Alias: <code>-d</code> </ParamField> @@ -215,7 +215,7 @@ bun run <file or script> </ParamField> <ParamField path="--no-macros" type="boolean"> - Disable macros from being executed in the bundler, transpiler and runtime + Disable macro execution in the bundler, transpiler and runtime </ParamField> <ParamField path="--jsx-factory" type="string"> @@ -292,7 +292,7 @@ bun run <file or script> </ParamField> <ParamField path="--cwd" type="string"> - Absolute path to resolve files & entrypoints from. This just changes the process' cwd + Absolute path to resolve files & entrypoints from. This only changes the process' cwd </ParamField> <ParamField path="--config" type="string"> diff --git a/docs/snippets/cli/test.mdx b/docs/snippets/cli/test.mdx index 90e265079cf2..ce25fc998130 100644 --- a/docs/snippets/cli/test.mdx +++ b/docs/snippets/cli/test.mdx @@ -15,7 +15,7 @@ bun test <patterns> </ParamField> <ParamField path="--retry" type="number"> - Retry failed tests up to <code>NUMBER</code> times. Overridden by per-test <code>{`{ retry: N }`}</code> + Retry failed tests up to <code>NUMBER</code> times. Per-test <code>{`{ retry: N }`}</code> overrides this flag </ParamField> <ParamField path="--concurrent" type="boolean"> diff --git a/docs/snippets/cli/update.mdx b/docs/snippets/cli/update.mdx index f840a6e3d6f4..ba4ce28d371a 100644 --- a/docs/snippets/cli/update.mdx +++ b/docs/snippets/cli/update.mdx @@ -113,7 +113,7 @@ bun up ### Script Execution <ParamField path="--ignore-scripts" type="boolean"> - Skip lifecycle scripts in the project's <code>package.json</code> (dependency scripts are never run) + Skip lifecycle scripts in the project's <code>package.json</code> (Bun never runs dependency scripts) </ParamField> <ParamField path="--concurrent-scripts" type="number"> diff --git a/docs/test/code-coverage.mdx b/docs/test/code-coverage.mdx index c0ab8a3f92e9..d06196a96ac8 100644 --- a/docs/test/code-coverage.mdx +++ b/docs/test/code-coverage.mdx @@ -141,7 +141,7 @@ Coverage reports exclude test files by default. To include them: coverageSkipTestFiles = false # default true ``` -When `coverageSkipTestFiles` is `true` (the default), files matching test patterns (for example `*.test.ts`, `*.spec.js`) are excluded from the coverage report. +When `coverageSkipTestFiles` is `true` (the default), the coverage report excludes files matching test patterns (for example `*.test.ts`, `*.spec.js`). ### Ignore Specific Paths and Patterns @@ -161,7 +161,7 @@ coveragePathIgnorePatterns = [ ] ``` -The option accepts glob patterns and works like Jest's `collectCoverageFrom` ignore patterns. Files matching any of the patterns are excluded from coverage calculation and reporting in both text and LCOV output. +The option accepts glob patterns and works like Jest's `collectCoverageFrom` ignore patterns. Bun excludes files matching any of the patterns from coverage calculation and reporting in both text and LCOV output. #### Common Use Cases @@ -196,7 +196,7 @@ coveragePathIgnorePatterns = [ ## Sourcemaps -Bun transpiles all files by default, generating an internal source map that maps lines of your original source code onto Bun's internal representation. To disable this, set `test.coverageIgnoreSourcemaps` to `true`; you rarely want this outside of advanced use cases. +Bun transpiles all files by default, generating an internal source map that maps lines of your original source code onto Bun's internal representation. To make coverage reports ignore this source map, set `test.coverageIgnoreSourcemaps` to `true`. You rarely want this outside of advanced use cases. ```toml title="bunfig.toml" icon="settings" [test] @@ -213,8 +213,8 @@ coverageIgnoreSourcemaps = true # default false By default, coverage reports: - **Exclude** `node_modules` directories -- **Exclude** files loaded with non-JS/TS loaders (for example `.css`, `.txt`) unless a custom JS loader is specified -- **Exclude** test files themselves (can be included with `coverageSkipTestFiles = false`) +- **Exclude** files loaded with non-JS/TS loaders (for example `.css`, `.txt`) unless you specify a custom JS loader +- **Exclude** test files themselves (include them with `coverageSkipTestFiles = false`) - Can exclude additional files with `coveragePathIgnorePatterns` ## Advanced Configuration @@ -314,7 +314,7 @@ All files | 85.71 | 90.48 | - **80%+ overall coverage**: Generally considered good - **90%+ critical paths**: Important business logic should be well-tested -- **100% utility functions**: Pure functions and utilities are easy to test completely +- **100% utility functions**: Pure functions and utilities can be tested completely - **Lower coverage for UI components**: Often acceptable as they may require integration tests ## Best Practices @@ -361,7 +361,7 @@ bun test --coverage src/critical-module.ts ### Combine with Other Quality Metrics -Coverage is just one metric. Also consider: +Coverage is only one metric. Also consider: - **Code review quality** - **Integration test coverage** @@ -390,7 +390,7 @@ If you see coverage reports that don't match your expectations: 1. Check if source maps are working correctly 2. Verify file patterns in `coveragePathIgnorePatterns` -3. Ensure test files are actually importing the code to test +3. Ensure test files import the code to test ### Performance Issues with Large Codebases diff --git a/docs/test/configuration.mdx b/docs/test/configuration.mdx index 80cd603d51ab..62c03cbec046 100644 --- a/docs/test/configuration.mdx +++ b/docs/test/configuration.mdx @@ -87,7 +87,7 @@ mock.module("./external-api", () => ({ ### Path Ignore Patterns -`pathIgnorePatterns` excludes files and directories from test discovery entirely, using glob patterns. Unlike `coveragePathIgnorePatterns`, which only affects coverage reports, `pathIgnorePatterns` prevents matching paths from being discovered and run as tests. +`pathIgnorePatterns` excludes files and directories from test discovery entirely, using glob patterns. Unlike `coveragePathIgnorePatterns`, which only affects coverage reports, `pathIgnorePatterns` prevents Bun from discovering matching paths and running them as tests. Use it when your project contains submodules, vendored code, or other directories with `*.test.ts` files that you don't want `bun test` to pick up. @@ -135,7 +135,7 @@ pathIgnorePatterns = [ ``` <Note> - Command-line `--path-ignore-patterns` flags override the `bunfig.toml` value entirely -- the two are not merged. + Command-line `--path-ignore-patterns` flags override the `bunfig.toml` value entirely. Bun does not merge the two. </Note> ## Reporters @@ -213,7 +213,7 @@ Run test files matching a glob pattern with concurrent test execution enabled. concurrentTestGlob = "**/concurrent-*.test.ts" # Run files matching this pattern concurrently ``` -Test files matching the pattern behave as if the `--concurrent` flag was passed: every test in those files runs concurrently. Use this to migrate a test suite to concurrent execution gradually, or to run one kind of test (say, integration tests) concurrently while the rest stay sequential. +Test files matching the pattern behave as if you passed the `--concurrent` flag: every test in those files runs concurrently. Use this to migrate a test suite to concurrent execution gradually, or to run one kind of test (say, integration tests) concurrently while the rest stay sequential. The `--concurrent` CLI flag overrides this setting, forcing all tests to run concurrently regardless of the glob pattern. @@ -331,7 +331,7 @@ coveragePathIgnorePatterns = [ ] ``` -Files matching any of these patterns are excluded from coverage calculation and reporting. See [Code coverage](/test/code-coverage). +Bun excludes files matching any of these patterns from coverage calculation and reporting. See [Code coverage](/test/code-coverage). #### Common Ignore Patterns @@ -370,7 +370,7 @@ coveragePathIgnorePatterns = [ ### Sourcemap Handling -Bun transpiles every file, so coverage results pass through sourcemaps before they're reported. `coverageIgnoreSourcemaps` opts out of this, but the results will be confusing: during transpilation, Bun may move code around and rename variables. The option is mostly useful for debugging coverage issues. +Bun transpiles every file, so coverage results pass through sourcemaps before they're reported. `coverageIgnoreSourcemaps` opts out of this, but the results are confusing: during transpilation, Bun may move code around and rename variables. The option is mostly useful for debugging coverage issues. ```toml title="bunfig.toml" icon="settings" [test] diff --git a/docs/test/dates-times.mdx b/docs/test/dates-times.mdx index 47978cf7aa65..e567dd65e510 100644 --- a/docs/test/dates-times.mdx +++ b/docs/test/dates-times.mdx @@ -27,7 +27,7 @@ test("it is 2020", () => { }); ``` -Jest's `useFakeTimers` and `useRealTimers` are also supported, so existing tests that use them keep working: +`bun:test` also supports Jest's `useFakeTimers` and `useRealTimers`, so existing tests that use them keep working: ```ts title="test.ts" icon="/icons/typescript.svg" test("just like in jest", () => { diff --git a/docs/test/discovery.mdx b/docs/test/discovery.mdx index b8893b4363eb..f135e1cf88e7 100644 --- a/docs/test/discovery.mdx +++ b/docs/test/discovery.mdx @@ -59,7 +59,7 @@ To filter tests by name rather than file path, use the `-t`/`--test-name-pattern bun test --test-name-pattern addition ``` -The pattern is matched against the test name prefixed with the labels of all its parent `describe` blocks, separated by spaces. For example, a test defined as: +`bun test` matches the pattern against the test name prefixed with the labels of all its parent `describe` blocks, separated by spaces. For example, a test defined as: ```ts title="math.test.ts" icon="/icons/typescript.svg" describe("Math", () => { @@ -71,7 +71,7 @@ describe("Math", () => { }); ``` -This test is matched against the string "Math operations should add correctly". +For this test, `bun test` matches the pattern against the string "Math operations should add correctly". ### Changing the Root Directory diff --git a/docs/test/index.mdx b/docs/test/index.mdx index 62a31931ba83..e9b67f22611f 100644 --- a/docs/test/index.mdx +++ b/docs/test/index.mdx @@ -25,7 +25,7 @@ Bun ships with a fast, built-in, Jest-compatible test runner. Tests run in the B bun test ``` -Tests are written in JavaScript or TypeScript with a Jest-like API. See [Writing tests](/test/writing-tests). +You write tests in JavaScript or TypeScript with a Jest-like API. See [Writing tests](/test/writing-tests). ```ts math.test.ts icon="/icons/typescript.svg" import { expect, test } from "bun:test"; @@ -107,7 +107,7 @@ JUnit XML is a popular format for reporting test results in CI/CD pipelines. ## Timeouts -Use the `--timeout` flag to specify a _per-test_ timeout in milliseconds. If a test times out, it is marked as failed. The default value is `5000`. +Use the `--timeout` flag to specify a _per-test_ timeout in milliseconds. If a test times out, Bun marks it as failed. The default value is `5000`. ```bash terminal icon="terminal" # default value is 5000 @@ -142,7 +142,7 @@ bun test --concurrent --max-concurrency 4 bun test --concurrent ``` -This helps prevent resource exhaustion when running many concurrent tests. The default value is 20. +The limit helps prevent resource exhaustion when running many concurrent tests. The default value is 20. ### `test.concurrent` @@ -202,7 +202,7 @@ test.failing.each([1, 2, 3])("chained qualifiers %d", input => { ## Retry failed tests -Use the `--retry` flag to automatically retry failed tests up to a given number of times. If a test fails and then passes on a subsequent attempt, it is reported as passing. +Use the `--retry` flag to automatically retry failed tests up to a given number of times. If a test fails and then passes on a subsequent attempt, Bun reports it as passing. ```sh terminal icon="terminal" bun test --retry 3 @@ -245,7 +245,7 @@ Use the `--randomize` flag to run tests in a random order. This helps detect tes bun test --randomize ``` -With `--randomize`, the seed used for randomization is displayed in the test summary: +With `--randomize`, Bun displays the seed used for randomization in the test summary: ```sh terminal icon="terminal" bun test --randomize @@ -370,15 +370,15 @@ See [DOM testing](/test/dom). ## Large codebases -For a suite with thousands of test files, `bun test` has several knobs that stack — worker processes, isolation level, sharding across machines, and duration-aware scheduling. Each is covered in depth on [Parallel & isolated test runs](/test/parallel); here is how they fit together, roughly in order of payoff: +For a suite with thousands of test files, `bun test` has several knobs that stack: worker processes, isolation level, sharding across machines, and duration-aware scheduling. [Parallel & isolated test runs](/test/parallel) covers each in depth. Here is how they fit together, roughly in order of payoff: **1. Use every core: [`--parallel`](/test/parallel#--parallel).** One worker per core, files handed out one at a time. -**2. Decide how much isolation you need.** `--parallel` gives every file a fresh global, which is the safe default and what Jest/Vitest do. If your files don't leak state into each other (they already pass under plain `bun test`, which shares one global), [`--parallel --no-isolate`](/test/parallel#every-file-is-isolated-unless-you-opt-out) lets each worker evaluate your imports and preloads once instead of once per file. On suites made of many small files that is the single biggest win — see [how it compares](/test/parallel#how-it-compares). +**2. Decide how much isolation you need.** `--parallel` gives every file a fresh global, which is the safe default and what Jest/Vitest do. If your files don't leak state into each other (they already pass under plain `bun test`, which shares one global), [`--parallel --no-isolate`](/test/parallel#every-file-is-isolated-unless-you-opt-out) lets each worker evaluate your imports and preloads once instead of once per file. On suites made of many small files, that is the single biggest win. See [how it compares](/test/parallel#how-it-compares). -**3. Split across machines: [`--shard=i/n`](/test/parallel#splitting-a-suite-across-ci-machines-with---shard).** Deterministic, no coordinator; each CI job runs one slice, and each slice still uses `--parallel` locally. +**3. Split across machines: [`--shard=i/n`](/test/parallel#splitting-a-suite-across-ci-machines-with---shard).** Deterministic, no coordinator. Each CI job runs one slice, and each slice still uses `--parallel` locally. -**4. Balance by time, not count: [`--timings`](/test/parallel#balancing-with---timings).** With recorded durations, shards are cut so each gets about the same total time (longest-processing-time style, but keeping path-neighbours together so a worker's module cache stays warm), each worker starts its slowest file first, and idle workers steal the slowest remaining file — so the run isn't held up by one long file that happened to start last. +**4. Balance by time, not count: [`--timings`](/test/parallel#balancing-with---timings).** With recorded durations, Bun cuts shards so each gets about the same total time. The split is longest-processing-time style, but keeps path-neighbours together so a worker's module cache stays warm. Each worker starts its slowest file first, and idle workers steal the slowest remaining file. That way, one long file that happened to start last doesn't hold up the run. **5. Keep the timings fresh automatically: `--update-timings`.** Each shard writes the durations of the files it ran; the next run reads all of them. In GitHub Actions that looks like: @@ -421,7 +421,7 @@ jobs: key: bun-test-timings-${{ github.run_id }} ``` -Every shard must read the _same set_ of timings files for the shards to add up to the whole suite, which is why a run reads the previous run's files (restored from the cache) and writes its own where sibling shards still in flight won't pick them up (`next/` above). Add `--no-isolate` to the `bun test` line if step 2 applies to you. +Every shard must read the _same set_ of timings files for the shards to add up to the whole suite. That is why a run reads the previous run's files (restored from the cache), and why it writes its own where sibling shards still in flight won't pick them up (`next/` above). Add `--no-isolate` to the `bun test` line if step 2 applies to you. **6. Within a file: [`test.concurrent`](#concurrent-test-execution)** for I/O-bound tests that spend their time awaiting. @@ -445,7 +445,7 @@ Set any of the following environment variables to enable AI-friendly output: ### Behavior -When an AI agent environment is detected: +When Bun detects an AI agent environment: - Only test failures are displayed in detail - Passing, skipped, and todo test indicators are hidden diff --git a/docs/test/lifecycle.mdx b/docs/test/lifecycle.mdx index 31c95bbcc0b8..a3897e8799b0 100644 --- a/docs/test/lifecycle.mdx +++ b/docs/test/lifecycle.mdx @@ -37,7 +37,7 @@ test("example test", () => { ## Per-Scope Setup and Teardown -Perform per-scope setup and teardown logic with `beforeAll` and `afterAll`. The scope is determined by where the hook is defined. +Perform per-scope setup and teardown logic with `beforeAll` and `afterAll`. Where you define the hook determines its scope. ### Scoped to a Describe Block @@ -93,7 +93,7 @@ describe("test group", () => { ### `onTestFinished` -Use `onTestFinished` to run a callback after a single test completes. It runs after all `afterEach` hooks. +Use `onTestFinished` to run a callback after a single test completes. The callback runs after all `afterEach` hooks. ```ts title="test.ts" icon="/icons/typescript.svg" import { test, onTestFinished } from "bun:test"; @@ -239,7 +239,7 @@ test("async test", async () => { ## Nested Hooks -Hooks can be nested. They run in the following order: +You can nest hooks. They run in the following order: ```ts title="test.ts" icon="/icons/typescript.svg" import { describe, beforeAll, beforeEach, afterEach, afterAll, test } from "bun:test"; @@ -283,7 +283,7 @@ describe("outer describe", () => { ## Error Handling -If a `beforeAll` hook throws, every test in its scope is skipped: +If a `beforeAll` hook throws, the test runner skips every test in the hook's scope: ```ts title="test.ts" icon="/icons/typescript.svg" import { beforeAll, test } from "bun:test"; diff --git a/docs/test/mocks.mdx b/docs/test/mocks.mdx index 0ba3f17d19fa..aee75d8c270a 100644 --- a/docs/test/mocks.mdx +++ b/docs/test/mocks.mdx @@ -165,7 +165,7 @@ test("async mock functions", async () => { ## Spies with spyOn() -Use `spyOn()` to track calls to a function without replacing it with a mock. Spies can be passed to `.toHaveBeenCalled()` and `.toHaveBeenCalledTimes()`. +Use `spyOn()` to track calls to a function without replacing it with a mock. You can pass spies to `.toHaveBeenCalled()` and `.toHaveBeenCalledTimes()`. ```ts title="test.ts" icon="/icons/typescript.svg" import { test, expect, spyOn } from "bun:test"; @@ -322,7 +322,7 @@ preload = ["./my-preload"] #### When to Use Preload -Mocking a module that's already been imported updates the module cache, so anything that imports it gets the mocked version. The original module has already been evaluated, though, so its side effects have already happened. +Mocking a module that's already been imported updates the module cache, so anything that imports it gets the mocked version. Bun has already evaluated the original module, though, so its side effects have already happened. To prevent the original module from being evaluated at all, use `--preload` to load your mocks before your tests run. @@ -410,7 +410,7 @@ test("clearing all mocks", () => { ### Reset All Mocks -`jest.resetAllMocks()` (and its `vi.resetAllMocks()` alias) calls `mockFn.mockReset()` on every mock: on top of what `clearAllMocks()` does, it drops the implementations set by `mockImplementation()`, `mockReturnValue()` and friends. It does not restore the original implementation of a spy: +`jest.resetAllMocks()` (and its `vi.resetAllMocks()` alias) calls `mockFn.mockReset()` on every mock. On top of what `clearAllMocks()` does, it drops the implementations set by `mockImplementation()`, `mockReturnValue()` and friends. It does not restore the original implementation of a spy: ```ts title="test.ts" icon="/icons/typescript.svg" import { expect, jest, test } from "bun:test"; @@ -504,7 +504,7 @@ Module mocks interact with both ESM and CommonJS module caches. ### Lazy Evaluation -The mock factory callback is only evaluated when the module is imported or required. +Bun evaluates the mock factory callback only when the module is imported or required. ### Path Resolution diff --git a/docs/test/parallel.mdx b/docs/test/parallel.mdx index 5112849a3353..2db038b107ca 100644 --- a/docs/test/parallel.mdx +++ b/docs/test/parallel.mdx @@ -20,7 +20,7 @@ bun test --parallel # one worker per CPU core bun test --parallel=4 # exactly 4 workers ``` -The main `bun test` process becomes a coordinator. It discovers test files as usual, then starts worker processes and hands each one file at a time. Results stream back as each test finishes, so the output looks the same as a serial run — each file's results are printed together under its filename, and `console.log` output from a test is never interleaved with another file's. +The main `bun test` process becomes a coordinator. It discovers test files as usual, then starts worker processes and hands each one file at a time. Results stream back as each test finishes, so the output looks the same as a serial run. The coordinator prints each file's results together under its filename, and never interleaves `console.log` output from a test with another file's. ```txt bun test v1.4.0 8x PARALLEL @@ -35,17 +35,17 @@ src/db.test.ts: ... ``` -Workers start lazily. The first worker starts immediately; the rest are only spawned once every running worker has been busy for a few milliseconds (`--parallel-delay=<ms>`, default `5`). A suite of tiny files therefore runs on a single worker with no process-spawn overhead, while the first slow file triggers full fan-out. +Workers start lazily. The first worker starts immediately; the coordinator spawns the rest only once every running worker has been busy for a few milliseconds (`--parallel-delay=<ms>`, default `5`). A suite of tiny files therefore runs on a single worker with no process-spawn overhead, while the first slow file triggers full fan-out. ### How files are distributed -Files are sorted by path and split into one contiguous chunk per worker, so files in the same directory — which usually import the same modules — mostly land in the same process (a chunk boundary can fall inside a directory, and stolen files move). When a worker drains its chunk it steals the back half of the largest remaining chunk from another worker. With [`--timings`](#balancing-with---timings) the chunks are cut by recorded duration instead of file count, each worker starts its slowest file first, and an idle worker steals the slowest not-yet-started file from whichever chunk has the most time left. +The coordinator sorts files by path and splits them into one contiguous chunk per worker, so files in the same directory, which usually import the same modules, mostly land in the same process (a chunk boundary can fall inside a directory, and stolen files move). When a worker drains its chunk it steals the back half of the largest remaining chunk from another worker. With [`--timings`](#balancing-with---timings) the coordinator cuts the chunks by recorded duration instead of file count, each worker starts its slowest file first, and an idle worker steals the slowest not-yet-started file from whichever chunk has the most time left. ### Every file is isolated (unless you opt out) `--parallel` implies [`--isolate`](#--isolate): each file runs in a fresh global object even when two files land on the same worker. Tests that pass with `--parallel` don't depend on state leaked by an earlier file. -`--parallel --no-isolate` turns that off: each worker keeps a single global and module registry for all the files it is handed, exactly like a serial `bun test` does for the whole suite. Imports (and `--preload` modules) are evaluated once per worker instead of once per file, which is the fastest way to run a large suite of small files — at the price that a file can observe whatever an earlier file on the same worker left behind. Preload-level `beforeAll`/`afterAll` hooks still wrap every file, since a worker never knows which file is its last. +`--parallel --no-isolate` turns that off: each worker keeps a single global and module registry for all the files it is handed, exactly like a serial `bun test` does for the whole suite. Each worker evaluates imports (and `--preload` modules) once instead of once per file, which is the fastest way to run a large suite of small files. The price is that a file can observe whatever an earlier file on the same worker left behind. Preload-level `beforeAll`/`afterAll` hooks still wrap every file, since a worker never knows which file is its last. ### Worker environment @@ -55,15 +55,15 @@ Each worker gets `BUN_TEST_WORKER_ID` and `JEST_WORKER_ID` set to its 1-based in const dbName = `app_test_${process.env.BUN_TEST_WORKER_ID ?? "1"}`; ``` -Flags that affect how tests execute (`--timeout`, `--preload`, `--define`, `--coverage`, `--update-snapshots`, `-t`, `--retry`, `--rerun-each`, `--concurrent`, `--randomize`/`--seed`, …) are forwarded to workers. `--bail` is handled by the coordinator at file granularity: once the failure threshold is reached no new files are started, but files already running finish. +Flags that affect how tests execute (`--timeout`, `--preload`, `--define`, `--coverage`, `--update-snapshots`, `-t`, `--retry`, `--rerun-each`, `--concurrent`, `--randomize`/`--seed`, …) are forwarded to workers. The coordinator handles `--bail` at file granularity: once the failure threshold is reached it starts no new files, but files already running finish. -Coverage, JUnit XML and snapshot writes are merged by the coordinator, so `--parallel --coverage --reporter=junit --reporter-outfile=junit.xml` produces one report. +The coordinator merges coverage, JUnit XML and snapshot writes, so `--parallel --coverage --reporter=junit --reporter-outfile=junit.xml` produces one report. -If a worker crashes (a native addon segfaults, or a test calls `process.exit`) the file it was running is reported as failed and a replacement worker picks up the remaining files. A crash from a fatal signal aborts the whole run so it can't be masked by later passing files. +If a worker crashes (a native addon segfaults, or a test calls `process.exit`), the coordinator reports the file that worker was running as failed, and a replacement worker picks up the remaining files. A crash from a fatal signal aborts the whole run, so later passing files can't mask it. ### When `--parallel` helps, and when it doesn't -`--parallel` pays off when the suite is dominated by test execution — I/O waits, real computation, subprocesses, many files. It costs something too: every file re-evaluates its imports in a fresh global (see [`--isolate`](#--isolate)), and each worker is a separate process with its own JIT warm-up. For a suite of very fast files that all import the same large module graph, plain `bun test` (one process, one shared module registry) can be faster. Try both; the numbers are printed at the end of every run. +`--parallel` pays off when the suite is dominated by test execution — I/O waits, real computation, subprocesses, many files. It costs something too: every file re-evaluates its imports in a fresh global (see [`--isolate`](#--isolate)), and each worker is a separate process with its own JIT warm-up. For a suite of very fast files that all import the same large module graph, plain `bun test` (one process, one shared module registry) can be faster. Try both; Bun prints the numbers at the end of every run. ## `--isolate` @@ -78,9 +78,9 @@ Runs each test file in a fresh JavaScript global object inside the same process. - closes servers, sockets, file watchers and subprocesses the file left open, cancels its timers, and restores fake timers, - re-runs `--preload` scripts in the new global. -This is how Jest and Vitest behave by default. It makes "passes alone, fails in the full suite" bugs go away at the cost of re-evaluating imports per file. +Isolating every file is how Jest and Vitest behave by default. It makes "passes alone, fails in the full suite" bugs go away at the cost of re-evaluating imports per file. -To keep that cost low, transpiled source and bytecode are cached at the process level and shared across globals: the second file to import a module skips reading, transpiling and parsing it and goes straight to evaluation. Only the module's top-level code runs again. +To keep that cost low, Bun caches transpiled source and bytecode at the process level and shares them across globals. The second file to import a module skips reading, transpiling and parsing it and goes straight to evaluation. Only the module's top-level code runs again. Without `--isolate` (the default), all files share one global and one module registry. That is the fastest mode and is fine for suites whose files don't leak state into each other. @@ -126,7 +126,7 @@ Every machine sorts the discovered test files by path and takes a deterministic ### Balancing with `--timings` -File count is a poor proxy for duration: one shard can end up with all the slow integration tests. Give `bun test` a record of how long each file takes and it will cut shards by total time instead, keeping neighbouring files (which share imports) together: +File count is a poor proxy for duration: one shard can end up with all the slow integration tests. Give `bun test` a record of how long each file takes and it cuts shards by total time instead, keeping neighbouring files (which share imports) together: ```sh terminal icon="terminal" # Record durations (any run can do this; --parallel is fine) @@ -152,12 +152,12 @@ The file is plain JSON, slowest first, so it doubles as a "what's slow" report: - Paths are relative to the project root; values are wall-clock milliseconds for the whole file. - Without `--shard`, `--update-timings` merges into what it read, so re-running part of the suite locally refreshes those entries and keeps the rest. Entries for files that no longer exist are left alone; delete the file to start over. - With `--shard`, `--update-timings` writes **only the files that shard ran** — see below. -- Files with no entry are assumed to take the median time when cutting shards, and are started first under `--parallel`. -- With `--timings`, `--parallel` also uses the durations: worker chunks are cut by time and each worker starts its slowest file first. +- Bun assumes files with no entry take the median time when cutting shards, and starts them first under `--parallel`. +- With `--timings`, `--parallel` also uses the durations: the coordinator cuts worker chunks by time and each worker starts its slowest file first. #### One timings file per shard -`--timings` can be passed more than once; the files are read as one table (paths that don't exist yet are skipped), and `--update-timings` writes to the **first** path. Under `--shard` that output contains just the files the shard ran, so the shards' outputs are disjoint and, read together on the next run, add up to the whole suite — no merge step. [Large codebases](/test/index#large-codebases) on the main page has the full CI workflow. +You can pass `--timings` more than once. Bun reads the files as one table and skips paths that don't exist yet. `--update-timings` writes to the **first** path. Under `--shard` that output contains only the files the shard ran, so the shards' outputs are disjoint. Read together on the next run, they add up to the whole suite with no merge step. [Large codebases](/test/index#large-codebases) on the main page has the full CI workflow. ## How it compares @@ -172,8 +172,8 @@ The file is plain JSON, slowest first, so it doubles as a "what's slow" report: <Note> Wall-clock, `hyperfine --warmup 1`, Bun 1.4, Node.js 25.6, each runner's stock config plus its setup-file option (`bunfig.toml` `test.preload`, `setupFilesAfterEnv`, `setupFiles`). The generator and configs are in the repository so - you can rerun it; the ratios move with what your tests actually do — this suite is deliberately dominated by per-file - overhead rather than test bodies. + you can rerun it. The ratios move with what your tests do: this suite is deliberately dominated by per-file overhead + rather than test bodies. </Note> -Where the time goes: with a fresh global per file, every runner re-evaluates the imports and setup file 2 000 times. Bun shares transpiled source and bytecode across those globals so nothing is re-parsed, but module evaluation and JIT warm-up still repeat per file — which is why, on this shape, one shared global (`bun test`) beats sixteen isolated workers, and sixteen shared globals (`--parallel --no-isolate`) beat both. +Where the time goes: with a fresh global per file, every runner re-evaluates the imports and setup file 2 000 times. Bun shares transpiled source and bytecode across those globals so nothing is re-parsed, but module evaluation and JIT warm-up still repeat per file. That repeated work is why, on this shape, one shared global (`bun test`) beats sixteen isolated workers, and sixteen shared globals (`--parallel --no-isolate`) beat both. diff --git a/docs/test/reporters.mdx b/docs/test/reporters.mdx index c49e5036484b..cd3bb607a253 100644 --- a/docs/test/reporters.mdx +++ b/docs/test/reporters.mdx @@ -52,7 +52,7 @@ bun test --reporter=dots ### JUnit XML Reporter -For CI/CD environments, Bun can generate JUnit XML reports, a widely-adopted test result format that many CI/CD systems can parse, including GitLab and Jenkins. +For CI/CD environments, Bun can generate JUnit XML reports. JUnit XML is a widely-adopted test result format that many CI/CD systems can parse, including GitLab and Jenkins. #### Using the JUnit Reporter diff --git a/docs/test/runtime-behavior.mdx b/docs/test/runtime-behavior.mdx index 462a9f99bb4d..d7d41c084710 100644 --- a/docs/test/runtime-behavior.mdx +++ b/docs/test/runtime-behavior.mdx @@ -115,7 +115,7 @@ test("test 2", () => { ### Promise Rejections -Unhandled promise rejections are also caught: +The test runner also catches unhandled promise rejections: ```ts title="test.ts" icon="/icons/typescript.svg" import { test } from "bun:test"; @@ -291,7 +291,7 @@ The test runner runs all tests in a single process by default. This provides: - **Faster startup** - No need to spawn multiple processes - **Shared memory** - Efficient resource usage -- **Simple debugging** - All tests in one process +- **Simpler debugging** - All tests in one process However, this means: diff --git a/docs/test/snapshots.mdx b/docs/test/snapshots.mdx index f1549e17abf4..059efe5472fb 100644 --- a/docs/test/snapshots.mdx +++ b/docs/test/snapshots.mdx @@ -7,7 +7,7 @@ Snapshot testing saves the output of a value and compares it against future test ## Basic Snapshots -Snapshot tests are written using the `.toMatchSnapshot()` matcher: +Write snapshot tests with the `.toMatchSnapshot()` matcher: ```ts title="test.ts" icon="/icons/typescript.svg" import { test, expect } from "bun:test"; @@ -52,7 +52,7 @@ Do this when you've intentionally changed the output or added new snapshot tests ## Inline Snapshots -For smaller values, use `.toMatchInlineSnapshot()`. Inline snapshots are stored directly in your test file: +For smaller values, use `.toMatchInlineSnapshot()`. Bun stores inline snapshots directly in your test file: ```ts title="test.ts" icon="/icons/typescript.svg" import { test, expect } from "bun:test"; @@ -358,7 +358,7 @@ tests/ ### Snapshot Failures -When snapshots fail, you'll see a diff: +When snapshots fail, Bun shows a diff: ```diff title="diff" icon="file-code" - Expected diff --git a/docs/test/writing-tests.mdx b/docs/test/writing-tests.mdx index 1cf09456393f..c77a223027de 100644 --- a/docs/test/writing-tests.mdx +++ b/docs/test/writing-tests.mdx @@ -3,7 +3,7 @@ title: "Writing tests" description: "Write tests with Bun's Jest-compatible API, including async tests, timeouts, and test modifiers" --- -Define tests with a Jest-like API imported from the built-in `bun:test` module. Long term, Bun aims for complete Jest compatibility; a limited set of `expect` matchers is supported. +Define tests with a Jest-like API imported from the built-in `bun:test` module. Long term, Bun aims for complete Jest compatibility; for now, it supports a limited set of `expect` matchers. ## Basic Usage @@ -76,7 +76,7 @@ test("wat", async () => { In `bun:test`, a timeout throws an uncatchable exception to force the test to stop running and fail. Bun also kills any child processes spawned in the test, so they don't linger as zombie processes. -The default timeout for each test is 5000ms (5 seconds) if not overridden by this timeout option or `jest.setTimeout()`. +The default timeout for each test is 5000ms (5 seconds) unless you override it with this timeout option or `jest.setTimeout()`. ## Retries and Repeats @@ -117,13 +117,13 @@ test( ### 🧟 Zombie Process Killer -When a test times out, Bun kills any processes spawned in it with `Bun.spawn`, `Bun.spawnSync`, or `node:child_process` that are still running, and logs a message to the console. This prevents zombie processes from lingering after timed-out tests. +When a test times out, Bun kills any still-running processes that the test spawned with `Bun.spawn`, `Bun.spawnSync`, or `node:child_process`, and logs a message to the console. This prevents zombie processes from lingering after timed-out tests. ## Test Modifiers ### test.skip -Skip individual tests with `test.skip`. These tests are not run. +Skip individual tests with `test.skip`. Bun does not run these tests. ```ts title="math.test.ts" icon="/icons/typescript.svg" import { expect, test } from "bun:test"; @@ -136,7 +136,7 @@ test.skip("wat", () => { ### test.todo -Mark a test as a todo with `test.todo`. These tests are not run. +Mark a test as a todo with `test.todo`. Bun does not run these tests. ```ts title="math.test.ts" icon="/icons/typescript.svg" import { expect, test } from "bun:test"; @@ -162,7 +162,7 @@ my.test.ts: 1 expect() calls ``` -With this flag, failing todo tests do not cause an error, but todo tests that pass are marked as failing so you can remove the todo mark or fix the test. +With this flag, failing todo tests do not cause an error, but Bun marks todo tests that pass as failing so you can remove the todo mark or fix the test. ### test.only @@ -323,10 +323,10 @@ describe.each([ ### Argument Passing -How arguments are passed to your test function depends on the structure of your test cases: +How Bun passes arguments to your test function depends on the structure of your test cases: -- If a table row is an array (like `[1, 2, 3]`), each element is passed as an individual argument -- If a row is not an array (like an object), it's passed as a single argument +- If a table row is an array (like `[1, 2, 3]`), Bun passes each element as an individual argument +- If a row is not an array (like an object), Bun passes it as a single argument ```ts title="example.test.ts" icon="/icons/typescript.svg" // Array items passed as individual arguments @@ -407,7 +407,7 @@ test("async work calls assertions", async () => { }); ``` -This is especially useful in async tests, to make sure your assertions run. +`expect.hasAssertions()` is especially useful in async tests, to make sure your assertions run. ### expect.assertions(count) @@ -422,7 +422,7 @@ test("exactly two assertions", () => { }); ``` -This helps ensure all your assertions run, especially in complex async code with multiple code paths. +`expect.assertions(count)` helps ensure all your assertions run, especially in complex async code with multiple code paths. ## Type Testing @@ -432,7 +432,7 @@ Bun includes `expectTypeOf` for testing TypeScript types, compatible with Vitest <Warning>These functions are no-ops at runtime. Run TypeScript separately to verify the type checks.</Warning> -The `expectTypeOf` function provides type-level assertions that are checked by TypeScript's type checker. To test your types: +The `expectTypeOf` function provides type-level assertions that TypeScript's type checker verifies. To test your types: 1. Write your type assertions using `expectTypeOf` 2. Run `bunx tsc --noEmit` to check that your types are correct diff --git a/docs/typescript-6.mdx b/docs/typescript-6.mdx index 366c233f0757..2a24f9b76f25 100644 --- a/docs/typescript-6.mdx +++ b/docs/typescript-6.mdx @@ -3,7 +3,7 @@ title: TypeScript 6 and 7 description: "How to configure Bun's type definitions for TypeScript 6.0 and 7.0, which no longer auto-discover @types packages. Fix 'Cannot find name Bun' and other missing type errors after upgrading TypeScript." --- -TypeScript 6.0 changed how type definitions are discovered. If you've upgraded TypeScript and your editor no longer recognizes `Bun`, `Request`, or other globals from `@types/bun`, here's how to fix it. +TypeScript 6.0 changed how it discovers type definitions. If you've upgraded TypeScript and your editor no longer recognizes `Bun`, `Request`, or other globals from `@types/bun`, here's how to fix it. ## What changed @@ -31,7 +31,7 @@ The `types` array tells TypeScript to load type definitions from `@types/bun`. I } ``` -You still need `@types/bun` installed — the `types` option tells TypeScript _which_ packages to include, but the package itself must exist in `node_modules`: +You still need `@types/bun` installed. The `types` option tells TypeScript _which_ packages to include, but the package itself must exist in `node_modules`: ```sh terminal icon="terminal" bun add -d @types/bun