Repository navigation
install: consolidate the open package manager pull requests - #44968
Merged
Merged
Conversation
… to the top-level dir (#38850)
… package with the isolated linker (#38856)
…ile: tarballs relative to their folder package (#38867)
…lled from the cache (#38986)
… installed package (#38994)
…ackages installed from the cache (#39023)
…name@version column (#39358)
…inked package.json (#39361)
…_parent_pkg_of_dependency, is_local_package, EscapeControlChars)
…sing a registry manifest (#38857)
…tside its range (#39053)
…r test re-resolves (#39190)
…lassified or re-parsed (#38309)
…s under the isolated linker (#38780)
… name after their source (#38681)
…nd hoisted linkers (#38779)
1 task done
robobun
added a commit
to robobun/soup
that referenced
this pull request
Oct 30, 2026
Bun.escapeHTML() had no inverse. HTMLRewriter returns text and attribute values as they are written in the document, so `&` stays `&`, and a script that wants the text a person reads had to install a package to decode it, although the table of the named character references of HTML is already in the binary for Bun.markdown. Bun.unescapeHTML(input, options?) replaces the character references of a string the way the HTML tokenizer does: the 2,125 names with their `;`, the 106 names of HTML 4 also without it (the longest match wins), and decimal and hexadecimal references, with the replacements HTML gives to zero, to surrogates, to numbers above U+10FFFF and to the Windows-1252 block. Text that is not a reference stays as it is, so no input throws. With `attribute: true` a name without its `;` is left alone when `=`, a letter or a digit follows it. That is the rule of the tokenizer for attribute values, and it keeps the `©=` of a query string intact. A string without a reference is returned as it is. Otherwise the output is allocated once at the length of the input and handed to JavaScriptCore without a copy. Latin-1 input stays Latin-1 until a reference names a character outside of it, and UTF-16 input is read as UTF-16, so a lone surrogate passes through. bun_md::entity::lookup is made public for the names. SOUP.md also records that the patch that merged a bun.lock with git conflict markers was dropped in the rebase onto oven-sh#44968.
robobun
added a commit
to robobun/soup
that referenced
this pull request
Oct 30, 2026
When two branches change dependencies, git leaves conflict markers in bun.lock. The lockfile then failed to parse, bun install ignored it and resolved every dependency again, so every pinned version in the project could move. With --frozen-lockfile the error did not name the cause. The loader now reads both sides of the conflict and merges them. A package that one side locks keeps its version. When the sides put two versions of a package at one path, the higher version keeps the path, what is below the path comes from the side of that version, and a dependency whose range does not take it gets the version of its own side. Every dependency is checked against its range after the merge, with the rules the resolver uses for overrides, catalogs, peers and npm: aliases. bun install then compares the merge with package.json. The packages of both sides stay until that is done, so a range that package.json kept gets the version that its side locked. A dependency with a package on neither side goes to the registry. bun install --frozen-lockfile, bun ci and --production fail with an error that names the conflict. Commands that only read the lockfile read the merge when it is complete. bun dedupe, bun pm trust and bun audit fix ask for a bun install first. When the sides cannot be merged, for example with two integrity hashes for one version, the lockfile is ignored as before and a note says why. A lockfile that parses does no new work: the merge starts from the arm that handles the parse error. This patch left the stack in the rebase onto oven-sh#44968, which rewrote the lockfile loader and the commands it hooks into. It is applied again here on the new code: the hooks are placed by hand, and merge_conflict.rs changed only where upstream now does the work itself (the loader no longer records npm: aliases, and the alias rule of the resolver is asked through Group::is_satisfied_by_a_bound_of).
This was referenced Oct 31, 2026
robobun
added a commit
to robobun/soup
that referenced
this pull request
Oct 31, 2026
Bun.escapeHTML() had no inverse. HTMLRewriter returns text and attribute values as they are written in the document, so `&` stays `&`, and a script that wants the text a person reads had to install a package to decode it, although the table of the named character references of HTML is already in the binary for Bun.markdown. Bun.unescapeHTML(input, options?) replaces the character references of a string the way the HTML tokenizer does: the 2,125 names with their `;`, the 106 names of HTML 4 also without it (the longest match wins), and decimal and hexadecimal references, with the replacements HTML gives to zero, to surrogates, to numbers above U+10FFFF and to the Windows-1252 block. Text that is not a reference stays as it is, so no input throws. With `attribute: true` a name without its `;` is left alone when `=`, a letter or a digit follows it. That is the rule of the tokenizer for attribute values, and it keeps the `©=` of a query string intact. A string without a reference is returned as it is. Otherwise the output is allocated once at the length of the input and handed to JavaScriptCore without a copy. Latin-1 input stays Latin-1 until a reference names a character outside of it, and UTF-16 input is read as UTF-16, so a lone surrogate passes through. bun_md::entity::lookup is made public for the names. SOUP.md also records that the patch that merged a bun.lock with git conflict markers was dropped in the rebase onto oven-sh#44968.
robobun
added a commit
to robobun/soup
that referenced
this pull request
Oct 31, 2026
When two branches change dependencies, git leaves conflict markers in bun.lock. The lockfile then failed to parse, bun install ignored it and resolved every dependency again, so every pinned version in the project could move. With --frozen-lockfile the error did not name the cause. The loader now reads both sides of the conflict and merges them. A package that one side locks keeps its version. When the sides put two versions of a package at one path, the higher version keeps the path, what is below the path comes from the side of that version, and a dependency whose range does not take it gets the version of its own side. Every dependency is checked against its range after the merge, with the rules the resolver uses for overrides, catalogs, peers and npm: aliases. bun install then compares the merge with package.json. The packages of both sides stay until that is done, so a range that package.json kept gets the version that its side locked. A dependency with a package on neither side goes to the registry. bun install --frozen-lockfile, bun ci and --production fail with an error that names the conflict. Commands that only read the lockfile read the merge when it is complete. bun dedupe, bun pm trust and bun audit fix ask for a bun install first. When the sides cannot be merged, for example with two integrity hashes for one version, the lockfile is ignored as before and a note says why. A lockfile that parses does no new work: the merge starts from the arm that handles the parse error. This patch left the stack in the rebase onto oven-sh#44968, which rewrote the lockfile loader and the commands it hooks into. It is applied again here on the new code: the hooks are placed by hand, and merge_conflict.rs changed only where upstream now does the work itself (the loader no longer records npm: aliases, and the alias rule of the resolver is asked through Group::is_satisfied_by_a_bound_of).
robobun
added a commit
to robobun/soup
that referenced
this pull request
Oct 31, 2026
Bun.escapeHTML() had no inverse. HTMLRewriter returns text and attribute values as they are written in the document, so `&` stays `&`, and a script that wants the text a person reads had to install a package to decode it, although the table of the named character references of HTML is already in the binary for Bun.markdown. Bun.unescapeHTML(input, options?) replaces the character references of a string the way the HTML tokenizer does: the 2,125 names with their `;`, the 106 names of HTML 4 also without it (the longest match wins), and decimal and hexadecimal references, with the replacements HTML gives to zero, to surrogates, to numbers above U+10FFFF and to the Windows-1252 block. Text that is not a reference stays as it is, so no input throws. With `attribute: true` a name without its `;` is left alone when `=`, a letter or a digit follows it. That is the rule of the tokenizer for attribute values, and it keeps the `©=` of a query string intact. A string without a reference is returned as it is. Otherwise the output is allocated once at the length of the input and handed to JavaScriptCore without a copy. Latin-1 input stays Latin-1 until a reference names a character outside of it, and UTF-16 input is read as UTF-16, so a lone surrogate passes through. bun_md::entity::lookup is made public for the names. SOUP.md also records that the patch that merged a bun.lock with git conflict markers was dropped in the rebase onto oven-sh#44968.
robobun
added a commit
to robobun/soup
that referenced
this pull request
Oct 31, 2026
When two branches change dependencies, git leaves conflict markers in bun.lock. The lockfile then failed to parse, bun install ignored it and resolved every dependency again, so every pinned version in the project could move. With --frozen-lockfile the error did not name the cause. The loader now reads both sides of the conflict and merges them. A package that one side locks keeps its version. When the sides put two versions of a package at one path, the higher version keeps the path, what is below the path comes from the side of that version, and a dependency whose range does not take it gets the version of its own side. Every dependency is checked against its range after the merge, with the rules the resolver uses for overrides, catalogs, peers and npm: aliases. bun install then compares the merge with package.json. The packages of both sides stay until that is done, so a range that package.json kept gets the version that its side locked. A dependency with a package on neither side goes to the registry. bun install --frozen-lockfile, bun ci and --production fail with an error that names the conflict. Commands that only read the lockfile read the merge when it is complete. bun dedupe, bun pm trust and bun audit fix ask for a bun install first. When the sides cannot be merged, for example with two integrity hashes for one version, the lockfile is ignored as before and a note says why. A lockfile that parses does no new work: the merge starts from the arm that handles the parse error. This patch left the stack in the rebase onto oven-sh#44968, which rewrote the lockfile loader and the commands it hooks into. It is applied again here on the new code: the hooks are placed by hand, and merge_conflict.rs changed only where upstream now does the work itself (the loader no longer records npm: aliases, and the alias rule of the resolver is asked through Group::is_satisfied_by_a_bound_of).
robobun
added a commit
to robobun/soup
that referenced
this pull request
Nov 1, 2026
Bun.escapeHTML() had no inverse. HTMLRewriter returns text and attribute values as they are written in the document, so `&` stays `&`, and a script that wants the text a person reads had to install a package to decode it, although the table of the named character references of HTML is already in the binary for Bun.markdown. Bun.unescapeHTML(input, options?) replaces the character references of a string the way the HTML tokenizer does: the 2,125 names with their `;`, the 106 names of HTML 4 also without it (the longest match wins), and decimal and hexadecimal references, with the replacements HTML gives to zero, to surrogates, to numbers above U+10FFFF and to the Windows-1252 block. Text that is not a reference stays as it is, so no input throws. With `attribute: true` a name without its `;` is left alone when `=`, a letter or a digit follows it. That is the rule of the tokenizer for attribute values, and it keeps the `©=` of a query string intact. A string without a reference is returned as it is. Otherwise the output is allocated once at the length of the input and handed to JavaScriptCore without a copy. Latin-1 input stays Latin-1 until a reference names a character outside of it, and UTF-16 input is read as UTF-16, so a lone surrogate passes through. bun_md::entity::lookup is made public for the names. SOUP.md also records that the patch that merged a bun.lock with git conflict markers was dropped in the rebase onto oven-sh#44968.
robobun
added a commit
to robobun/soup
that referenced
this pull request
Nov 1, 2026
When two branches change dependencies, git leaves conflict markers in bun.lock. The lockfile then failed to parse, bun install ignored it and resolved every dependency again, so every pinned version in the project could move. With --frozen-lockfile the error did not name the cause. The loader now reads both sides of the conflict and merges them. A package that one side locks keeps its version. When the sides put two versions of a package at one path, the higher version keeps the path, what is below the path comes from the side of that version, and a dependency whose range does not take it gets the version of its own side. Every dependency is checked against its range after the merge, with the rules the resolver uses for overrides, catalogs, peers and npm: aliases. bun install then compares the merge with package.json. The packages of both sides stay until that is done, so a range that package.json kept gets the version that its side locked. A dependency with a package on neither side goes to the registry. bun install --frozen-lockfile, bun ci and --production fail with an error that names the conflict. Commands that only read the lockfile read the merge when it is complete. bun dedupe, bun pm trust and bun audit fix ask for a bun install first. When the sides cannot be merged, for example with two integrity hashes for one version, the lockfile is ignored as before and a note says why. A lockfile that parses does no new work: the merge starts from the arm that handles the parse error. This patch left the stack in the rebase onto oven-sh#44968, which rewrote the lockfile loader and the commands it hooks into. It is applied again here on the new code: the hooks are placed by hand, and merge_conflict.rs changed only where upstream now does the work itself (the loader no longer records npm: aliases, and the alias rule of the resolver is asked through Group::is_satisfied_by_a_bound_of).
robobun
added a commit
to robobun/soup
that referenced
this pull request
Nov 2, 2026
Bun.escapeHTML() had no inverse. HTMLRewriter returns text and attribute values as they are written in the document, so `&` stays `&`, and a script that wants the text a person reads had to install a package to decode it, although the table of the named character references of HTML is already in the binary for Bun.markdown. Bun.unescapeHTML(input, options?) replaces the character references of a string the way the HTML tokenizer does: the 2,125 names with their `;`, the 106 names of HTML 4 also without it (the longest match wins), and decimal and hexadecimal references, with the replacements HTML gives to zero, to surrogates, to numbers above U+10FFFF and to the Windows-1252 block. Text that is not a reference stays as it is, so no input throws. With `attribute: true` a name without its `;` is left alone when `=`, a letter or a digit follows it. That is the rule of the tokenizer for attribute values, and it keeps the `©=` of a query string intact. A string without a reference is returned as it is. Otherwise the output is allocated once at the length of the input and handed to JavaScriptCore without a copy. Latin-1 input stays Latin-1 until a reference names a character outside of it, and UTF-16 input is read as UTF-16, so a lone surrogate passes through. bun_md::entity::lookup is made public for the names. SOUP.md also records that the patch that merged a bun.lock with git conflict markers was dropped in the rebase onto oven-sh#44968.
robobun
added a commit
to robobun/soup
that referenced
this pull request
Nov 2, 2026
When two branches change dependencies, git leaves conflict markers in bun.lock. The lockfile then failed to parse, bun install ignored it and resolved every dependency again, so every pinned version in the project could move. With --frozen-lockfile the error did not name the cause. The loader now reads both sides of the conflict and merges them. A package that one side locks keeps its version. When the sides put two versions of a package at one path, the higher version keeps the path, what is below the path comes from the side of that version, and a dependency whose range does not take it gets the version of its own side. Every dependency is checked against its range after the merge, with the rules the resolver uses for overrides, catalogs, peers and npm: aliases. bun install then compares the merge with package.json. The packages of both sides stay until that is done, so a range that package.json kept gets the version that its side locked. A dependency with a package on neither side goes to the registry. bun install --frozen-lockfile, bun ci and --production fail with an error that names the conflict. Commands that only read the lockfile read the merge when it is complete. bun dedupe, bun pm trust and bun audit fix ask for a bun install first. When the sides cannot be merged, for example with two integrity hashes for one version, the lockfile is ignored as before and a note says why. A lockfile that parses does no new work: the merge starts from the arm that handles the parse error. This patch left the stack in the rebase onto oven-sh#44968, which rewrote the lockfile loader and the commands it hooks into. It is applied again here on the new code: the hooks are placed by hand, and merge_conflict.rs changed only where upstream now does the work itself (the loader no longer records npm: aliases, and the alias rule of the resolver is asked through Group::is_satisfied_by_a_bound_of).
dylan-conway
pushed a commit
that referenced
this pull request
Nov 2, 2026
) Lint-ratchet maintenance, no reachable defect: `main` already refuses a graph for a module with more than 65535 requests. #44968 left out #39165 and #40312 of this category ("refactor nothing needs"). Merged #39169 has this shape. @alii decides whether this is wanted. ### Problem - `serialize()` in `src/bundler/prelinked_module_graph.rs` packs a `u32` request index into 16 bits of each import and export entry, bounded there only by a `debug_assert!`. - The release bound is one function up, in `build()`, on the request count. mordant reports `bound_checked_only_in_debug`. ### Fix - `parse_module` converts the request count once (`u16::try_from(count).ok()?`) and takes every index from `0..request_count`, as a `u16`. The const, the count test and the assertion are gone. - Removes `"bound_checked_only_in_debug:src/bundler/prelinked_module_graph.rs" = 1` from `mordant-baseline.toml`. - Verified: `bun run rust:mordant` (564c8da, 3 targets) exits 0. Empty baseline: 0 findings in the file (`main`: 1). `test/bundler/bundler_compile_prelinked.test.ts`: 83 pass (debug build). Same bytes as `main` on 4033 graphs in a model of the file. - Self-reviewed: 9 concerns raised, 9 addressed. ### Background - `bun build --compile --bytecode` embeds a pre-resolved module graph for JavaScriptCore. A request is one module that a chunk imports. `PrelinkedModuleGraph::tryCreate` refuses more than 65535 per module. - Considered an `assert!` at the shift: it costs a compare and a branch per entry. ### Downsides - Two new test cases build executables with 65535 and 65536 imports: 1 to 3 s each on release, 13 to 35 s on debug with ASAN, no CI timing yet. - They pass on `main` too, so they hold the limit and prove no fix. The 65536 case builds the `module_info` fallback executable and does not run it. Behaviour change: none <details><summary>Notes</summary> **Why nothing observable changes** - On `main`, `build()` returns `None` when `module.requests.len() > MAX_REQUESTS_PER_MODULE` (65535), and the only call of `serialize()` comes after that test. So the shift never sees an index above 65534 in any build. - The limit was stated twice. The const and its count test are one statement. The `debug_assert!` on the index is the second, with a `u32` between them, and it is a different inequality (it allows index 65535). - mordant reports the shift because the lint looks at one function, and the release bound is in another one. **Measurements** - mordant (564c8da, 3 targets): `bun run rust:mordant` exits 0 with the baseline line deleted. Findings in `prelinked_module_graph.rs` with an empty baseline: 0 (`main`: 1). The baseline goes from 495 accepted findings to 494. - Limit on a real binary (debug build of this PR, `bun build --compile --bytecode --format=esm --packages=external`): 65535 requests give a graph with `requestCount` 65535 and largest packed index 65534. 65536 requests give exit 0 and no graph. A release build with `main`'s version of the file gives the same two results. - Serialized bytes: a model that compiles the real file text against stubbed dependencies gives byte-identical output for `main`'s file and this PR's file on 33 boundary graphs and 4000 random graphs (0 differences). The model is not a bun build. - Release-profile object of `bun_bundler` (one codegen unit, no LTO), `main` against this PR: `.text` 1,893,032 to 1,892,349 bytes (-683), `.rodata` 83,806 to 83,806. Symbols that name `prelinked_module_graph`: 34,043 to 33,360 bytes. `build` (with `parse_module` and `serialize` inlined): 3267 to 3175 instructions, 336 to 328 conditional branches. Limit compares against `0xffff`: 1 per module and 0 per request in both. - Not measured: the linked release binary. The numbers above are from the crate's object before the link. - Struct sizes (`-Zprint-type-sizes`): `Import` 28 to 24 bytes, `Export` 28 to 24 bytes, a star-export element 4 to 2 bytes. These exist only during the build. The serialized layout does not change, and `VERSION` stays 2. - Over-limit module (65536 requests, `build` returns `None`), same model with a counting allocator: 84 to 11 allocator calls, 14.7 MB to 5.2 MB requested. The refusal now happens before the module's requests are parsed. - Diff: `prelinked_module_graph.rs` +21/-26, `mordant-baseline.toml` +0/-1, test +68/-0. **The new test cases** - `compile/prelinked/RequestLimit65535` and `RequestLimit65536` build an entry with that many external imports and read the executable. They do not run it. - They pass before and after this change (checked on a release build of `367d939d9`, which has `main`'s version of the file). They hold a limit that had no test and that now depends on one line. A build with that line changed to `.unwrap_or(u16::MAX)` fails the 65536 case. - Time per case: 0.5 to 2.6 s on a release build (three runs). On a debug build with ASAN: 12.7 s and 25.1 s in a run of the whole file, 18.0 s and 34.7 s when only these two run. An existing case of this file takes about 30 s on that debug build. - The entry is built as a `Buffer`. `itBundled` sends a string through `dedent()`. With the entry as a string, the two cases took 42 s and 57 s in a run of the whole file on the debug build. - The build subprocess of `itBundled` has a 60 s limit. The build step of these cases takes 12 to 17 s on the debug build. - A module over the limit is the only way an executable gets per-module `module_info` bodies in place of a graph. No other bundler test builds a module over the limit. `RequestLimit65536` has no `run`, so nothing executes that path. **What moved** - The refusal of a module over the limit now happens at the head of `parse_module`'s request section, before the requests are parsed. `build()` interns the same strings before that point as it did before. - The check reads the length of `requested_modules_keys`. The old check read the length of the zipped request list. The three request vectors of a `ModuleInfo` have one writer and the same length, so the two are equal. - The four `as usize` reads of the index became `usize::from`, because the type now allows it. **Left as they are** - `bound_checked_only_in_debug:src/bundler/Chunk.rs = 3` (`Query::new`, `EntryPoint::non_entry_point`, `set_entry_point_id`): same lint and crate, no shared type with this file. - The `kind` and `resolution` fields of the same packed word are `u32` values from a `repr(u8)` enum and a five-value table. mordant does not flag them. - A local export keeps the placeholder `request: 0`. Nothing reads it as an index. </details>
robobun
added a commit
to robobun/soup
that referenced
this pull request
Nov 4, 2026
Bun.escapeHTML() had no inverse. HTMLRewriter returns text and attribute values as they are written in the document, so `&` stays `&`, and a script that wants the text a person reads had to install a package to decode it, although the table of the named character references of HTML is already in the binary for Bun.markdown. Bun.unescapeHTML(input, options?) replaces the character references of a string the way the HTML tokenizer does: the 2,125 names with their `;`, the 106 names of HTML 4 also without it (the longest match wins), and decimal and hexadecimal references, with the replacements HTML gives to zero, to surrogates, to numbers above U+10FFFF and to the Windows-1252 block. Text that is not a reference stays as it is, so no input throws. With `attribute: true` a name without its `;` is left alone when `=`, a letter or a digit follows it. That is the rule of the tokenizer for attribute values, and it keeps the `©=` of a query string intact. A string without a reference is returned as it is. Otherwise the output is allocated once at the length of the input and handed to JavaScriptCore without a copy. Latin-1 input stays Latin-1 until a reference names a character outside of it, and UTF-16 input is read as UTF-16, so a lone surrogate passes through. bun_md::entity::lookup is made public for the names. SOUP.md also records that the patch that merged a bun.lock with git conflict markers was dropped in the rebase onto oven-sh#44968.
5 tasks done
This was referenced Nov 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
It consolidates 597 open package manager pull requests into one reviewed change for Bun 1.5. Each was ported, taken in part, or left out with a reason. This is the complete change log:
X -> Y= symptom on main -> behaviour now; a line without->names a symptom that is gone; dep = dependency.699 files, +150,684 / -85,553 (src 221 files +41,383 / -46,459, net -5,076).
Behaviour changes someone will notice
bun install --network-timeout 100000adds the package100000). Drop the flag; bunx: tool flags go after the tool's name. InBUN_OPTIONSthey are skipped.bun ci/--frozen-lockfilefail (note: dependencies in package.json changed since bun.lock was saved) when a dep of the root or a workspace was added, removed, re-grouped or re-ranged. Equivalent spellings,file:directory rows (warning),trustedDependencies/patchedDependenciesedits pass. Runbun install, commit (install: fail --frozen-lockfile when bun install would rewrite bun.lock for a package.json edit #41931; refs bun install --frozen-lockfile allows bun.lock changes in workspaces #22689).file:folders are hard-linked real files (hoisted: was a symlink per file, their deps did not resolve). The folder's ownnode_modulesis not taken, exceptbundleDependencies. Linked, and trusted scripts run, once, not on every install. Declare or bundle what the folder needs;link:/ a workspace for a live link;--forcereruns scripts (install(hoisted): install file: folders with the default backend instead of one symlink per file #40628, install: don't copy the project's node_modules into a file: dependency on an ancestor directory #37127; fixes [windows]:EPERMwhen installing the package locally at../my-package#13379).bun.lockplaced and the new one does not leavesnode_modules, with what only it needed and its bins; not under--filter, not what no lockfile placed (bun prune) (install: prune stale workspace node_modules in hoisted installs #29794, install: remove nested copies bun.lock no longer places, with the hoisted linker #44406).file:/ URL tarballs, packages of registries on a port, path or http;bunxstarts a new cache.BUN_CONFIG_HTTP_RETRY_BACKOFF=<ms>sets the first wait.node_modulesis replaced by a real directory, like links at@scope,.bin,.bun/**(main wrote and deleted through them). Only the rootnode_moduleslink is followed: link the root, not members (install: do not follow a symlink at a node_modules directory the installer creates #42039)."trustedDependencies": []kept the default trusted list off only on the install that createdbun.lock-> always, also in a checkout without the workspace that declares it; written tobun.lock(one line; Bun 1.4 honours it, drops it at its next save) (install: keep an emptytrustedDependencieslist in effect on installs from bun.lock #44765).!dir/**inworkspacesalso removesdir, as npm; negated entries work in every spelling (!./pkg,!pkg/) and position, add no members;bun run --filter/--workspacesfollow them (install: normalize a negated workspaces entry before it is matched #43401, Apply negatedworkspacesentries inbun run --filterand--workspaces#43400; fixes bun install ignores a negatedworkspacesentry written as!./pathor!path/#43399).bun pm lslists what is on disk, exits 1 withoutnode_modules; isolated: a dep of the root only if its link exists (Make bun pm ls list installed packages instead of lockfile entries #38923; fixes bun pm ls counts lockfile entries but labels them node_modules #38909).node_modules/.bunexists, a command that names no linker keeps the isolated layout (main: lays hoisted over it). Back:--linker=hoistedor deletenode_modules.NODE_ENV=development/test:bun installreads the.envfilesbun runreads, not the production ones.--provenance,--provenance-file,publishConfig.provenance,NPM_CONFIG_PROVENANCE,provenance=truein.npmrcare errors (main: published without it). Unset them or publish with npm (publish: add --provenance (npm provenance via Sigstore keyless signing) #30522; refs Prevent publishing whenpublishConfig.provenanceset totrueinpackage.json#18611).packageExtensionsadds a key tobun.lock, a trailing section tobun.lockb, only when an extension added something; older versions load both.-muslvariants (--libc='*'for both).warn:lines: incorrect peers, deprecated versions, skipped required deps, stale / misplacedpatchedDependencies, unreadable config files, retries, unusable hashes..bin, then the inheritedPATH, then shared.bins, so an untrusted package'snodebin cannot run inside a trusted script. Cost: a globalnode-gypwins over the project's, for dep scripts only (install: scope the lifecycle script PATH to the script owner's own bins #44488).bun installitself is killed while a trusted dep's script runs, the next install calls the package installed (registry packages: as main;file:folders, local tarballs: the rerun on every install hid it).bun install --force(install: list unfinished lifecycle scripts per project, so the install after a killed one runs them #42001).Decisions that need the maintainer's confirmation
bun.lockbfiles from this repo's release tags, 6 that pass on main fail here (4 real drift).bun.lock, no package.json edit, one note (install: write a migrated lockfile and its package.json edits only when the command saves a lockfile #38804). Reverses install: pnpm parity — dedupe, prune, pm licenses, audit fix, add --filter/--catalog, nested overrides, transitive update, and workspace fixes #38333.[]line come and go), 10, 11, 12 (removinglinker = "isolated"no longer switches a tree), 14, 18.packageExtensionslockfile key (15): without it an edited or removed extension cannot be told from the package's own deps. install: do not fail when a bundled dependency cannot be resolved #43104 / install: keep bundledDependencies when migrating pnpm-lock.yaml #43209 are out for needing a key.bunx --trust. No longer: root scripts under--dry-run; under frozen, scripts of an installed package trusted after the lockfile was saved (main: every frozen install; now a plain install once, frozen never, silently).libc; pack/publish: resolve workspace: and catalog: specs from the package.json files, not bun.lock #38813; +1renameatper linked package (install: link packages into a staging directory and rename them into place #38783); a token survives only an http -> https redirect on one hostname (install: keep registry credentials across redirects to the same hostname #38001); install: percent-decode the credentials written into a registry or tarball URL #39603 flips a row of install: bound isolated store entry names; tarball URL credentials; file: tarballs relative to their folder package #38867.bun pm packfollow a symlinkednode_modules(it does, pack, publish: open packed files and directories without following symlinks #41925).New features and flags
bun.lockand only if that is saved;link:stayslink:;libcis honoured (install: migrate Yarn Berry (v2+) yarn.lock to bun.lock #40057)link:./dir,link:../x, absolute paths: symlink to that directory, relative to the declaring package.json, as Yarn / pnpm;link:foostays abun linkname (install: support path-stylelink:./dirdependencies (yarn/pnpm semantics) #40025; fixes Bun link works but installs says package is not linked #4719, Install package from local directory #5045)packageExtensionsin the root package.json adds missing deps / peers to registry packages, can mark a peer optional (install: supportpackageExtensions(yarn/pnpm compatibility) #40053)bun pm cache pack <file>/unpack <file>: the cache entries a lockfile needs as one file (CI); unpack adds what is missing, refuses a damaged pack whole (pm: addbun pm cache pack/bun pm cache unpack(single-file cache snapshot for CI) #40012)bun pm cache prune [--max-age <days>] [--dry-run]: drops cache entries older than 30 days, not the global store (pm: add bun pm cache prune to remove cache entries by age #42592)bun pm fetch: downloads all deps (pinned git too) into the cache; nonode_modules, scripts or lockfile write (install: addbun pm fetch#29509)bun publish --recursive/--filter <pattern>: packs the selected workspaces, publishes deps first, skips private and already published ones; the first failure ends the run (publish: add --recursive and --filter for workspace packages #41292)bun update --depth 0: direct deps only, transitive ones stay locked (install: addbun update --depth 0to keep transitive dependencies locked #41720; fixes Add a flag forbun updateto only update direct dependencies and keep transitive ones at their locked versions #41625)bun prune --check: exit 1 when anything would be removed (prune: add --check, a CI gate that exits 1 when anything would be removed #39216)minimumReleaseAgeExcludestakesname@version; new--minimum-release-age-excludes a,b@1.2.3;bun audit fixhonours the list (install: support name@version entries in minimumReleaseAgeExcludes #37909, install: add --minimum-release-age-excludes flag #42803; fixes Feaure Request: Support version-specific entries in minimumReleaseAgeExcludes for safer package whitelisting #28967, Support providing package age exclusion as a command line parameter #42799)minimumReleaseAgeaccepts90m,12 hours,3d,2 weeks;nan/infare config errors;0no longer fetches full manifests (install: accept ms-style duration strings for minimumReleaseAge #30529)libc: on Linux only the glibc or musl variant of an optional native package is installed; recorded inbun.lock;--libcoverrides (install: filter optionalDependencies by libc and record it in the lockfile #38797; fixes Add support for libc in package.json to filter package to download #16282)bunx --cwd <dir>(Add --cwd support to bunx #28669; fixes Add support forcwdargument inbunx#28668)bunx --env-file <file>(main:unrecognised dependency format: @.env); notbun --env-file F x pkg(bunx: parse --env-file #35599; fixesbunxcannot use--env-file#12430, Allow bunx to load .env files #17759)bunx --minimum-release-age, part of its cache key (bunx: honor --minimum-release-age by keying the install cache on it #30751; fixes bunx silently accepts --minimum-release-age flag but does not enforce it (no-op) #30748)--env-file,--no-env-file, bunfigenv = falsefor all package manager commands (main:--env-file ci.envadded the packageci.env) (install: honor --env-file, --no-env-file and NODE_ENV=development for .env loading #36672; fixes Bun install - .env.development environment variables file is not recognised and defaults to .env.production or .env #12011,bun installignores--no-env-file,--env-file, andbunfig env = false#31450)npm-shrinkwrap.jsonis migrated like package-lock.json (install: migrate npm-shrinkwrap.json like package-lock.json #38861)--verbose(install: warn when resolving deprecated npm packages #35457; fixes Bun doesn't warn when installing deprecated packages #6883)bun add -d | --optional | --peer <pkg>moves a package another group lists;--save-prodmoves it back (install: move existing dependency whenbun addis given an explicit group flag #36284; fixes Bun install --dev doesn't change dependency type of preexisting dependency #4852, Runbun add -d <pkg>to move an existing dep to devDependencies #5714)bun audit fixreports and rewrites theoverridesrule that holds a vulnerable version (audit fix: report and rewrite the overrides rule that holds a vulnerable version #38785)bun pm pkg set/delete: arrays (append, index), bracketed names (exports[./package.json]) (pm pkg: support arrays in set and delete #38025; fixesbun pm pkg setdoesn't support arrays #22035)--dry-runforbun link,unlink,pm version,pm cache rm,pm trust,pm pkg,pm migrate(main: they wrote) (pm: honour --dry-run in link, unlink, pm version, pm cache rm, pm trust, pm pkg, and pm migrate #41690)bun publishhonourspublishConfig.registryandpublishConfig["@scope:registry"](publish: honor publishConfig.registry and publishConfig["@scope:registry"] #38322; fixes Cannot publish to custom registry, npm can with the same config #18670).npmrc(main:@corp/xcame from the default registry) (auto-install: read the registries and credentials of .npmrc #44754)NPM_CONFIG_USERCONFIGnames the user.npmrc, asactions/setup-nodesets it (install: read NPM_CONFIG_USERCONFIG as the user-level .npmrc path #38047)Cache-Control: no-cache,no-store,max-ageunder 300 s on registry metadata (install: honour Cache-Control on packument responses #41644)workspace:*, also for pack / publish (install: resolve workspace: deps on the root package #36594; fixes workspace member package can't depend on workspace root #2773, Circular dependency does not resolve using workspaces #5176)"gypfile": falseturns off the implicitnode-gyp rebuild; a trusted tarball, git,file:package withbinding.gypand no scripts is built like a registry one (install: skip the default node-gyp rebuild when package.json sets gypfile: false #43913; fixesbun installrunsnode-gyp rebuildeven when package.json has"gypfile": false#43903)npm_package_name/version/jsonof their own package,npm_config_local_prefix,npm_package_config_*,npm_lifecycle_event,npm_lifecycle_script; inheritednpm_package_*are dropped (install: set npm_package_name/version/json for dependency lifecycle scripts #36690, install: set npm_config_local_prefix and npm_package_config_* for lifecycle scripts #38110; fixes Bun refers to a differentprocess.env.npm_package_versionthan npm or yarn in postInstall.js #12752)node_modulesholds (nothing, a part, other versions,--force) and after a download; not on Windows;file:/link:/ workspace / trusted deps, bins and scripts stay on the main thread; everything in today's order, scripts too; a cold install is no faster (install: parallelize hoisted package linking on fresh node_modules #29496)Security
htps://, no scheme):pm view,whoami,publish,audit,pm diffsent the token there over plain TCP; a badBUN_CONFIG_REGISTRYfell back to another registry with the token -> error, no request, on every route (http: refuse a blocking request whose URL is not http or https #41662, install: never drop BUN_CONFIG_REGISTRY / NPM_CONFIG_REGISTRY silently #41773)_auth, user or password with CR, LF, NUL could inject headers, as could--otp,--user-agent-> refused (install: reject registry auth tokens containing CR, LF or NUL #37458, cli: reject --user-agent and publish --otp values containing CR, LF or NUL #41661)token = "$NPM_TOKEN"with the variable unset sent the literal text -> warning, nothing sent (install: treat an unset $VAR credential reference in bunfig as no credential #41049)--verbose,pm ls/why/view/diff,audit, migrations -> masked (install: redact credentials in tarball and git URLs printed as resolutions and specifiers #38977, install: redact the registry URL in the manifest, tarball and pnpm named registry messages #38997, install: redact secrets in the URLs printed for failed manifest and tarball downloads #38817)pm view,licenses,untrusted,audit, resolutions, bin names, errors) -> escaped (pm view: escape control characters coming from the registry #38536, install: escape control characters in resolutions, specifiers, bin names and registry error text #38631, pm licenses: escape control characters in text output (adds bun_core::fmt::escape_control_chars) #39358, install: escape control characters in the bun pm untrusted/trust script listing #38525).bin,node_modules, a bare@scope, with control characters, empty or scope-only were installed or requested -> refused; skipped with a warning in a third party's manifest (install: refuse a dependency name that node_modules cannot hold (.bin, node_modules, bare @scope) #44439, install: reject dependency names containing control characters #38615, install: do not resolve a registry dependency with an empty name #43331, install: reject an empty or scope-only package name before it reaches the registry #41735)link:a directory, use a local.tgzof the project or afile:path that leaves it -> refused at resolve and install time (install: refuse link: targets that leave the project when a package from the cache declares them, at resolve and install time #39023, install: refuse local tarball dependencies declared by packages installed from the cache #38986, install: refuse a registry package's escaping file: folder in the isolated linker #43101)npm:alias could replace a same-named direct dep of the root; an alias in a refused or stale lockfile kept redirecting -> the root's dep wins; loaders register no aliases (install: do not redirect the root package's own dependency to another manifest's npm: alias #44412, install: lockfile loaders do not register npm: aliases #44294)bun.locklinks to a workspace missing on disk (dependency confusion) -> fails without a request (install: fail --frozen-lockfile when a range links a workspace that is missing on disk #43471)pm trustran scripts in the store, saw nothing under isolated -> its closure is project-local (install: keep the dependency closure of a trusted package out of the global store #40239, install: makebun pm untrusted/trustsee packages under the isolated linker #35106)link:dep deleted that folder -> only what the linker placed goes (install: remove a failed optional dependency by what the linker placed, not by the script cwd #43226)bun prune,update,deduperemoved packages behind a symlinked workspacenode_modules-> prune refuses (exit 1), the others skip it (install: stopbun pruneandbun updateremoving packages behind a symlinked workspace node_modules #44484; fixesbun pruneandbun updateremove packages behind a workspacenode_modulesthat is a symlink #44669)bun add -g,link,unlinkreplaced or deleted foreign files in the global bin directory -> only what Bun made (install: only replace or remove a package's own bins in the global bin dir #39745)bun pm cache rmcould delete the project, home or Bun's directory (an emptyBUN_INSTALL_CACHE_DIRmeant the project) -> refused (pm: confine what bun pm cache rm deletes #39750)globalDir/globalBinDiranywhere -> ignored with a warning (install: take bunfig globalDir and globalBinDir from the user config only #41775)bunxinstalled into the shared temp directory, where another user'snode_modules/ package.json was loaded -><cache>/.bunx-<uid>, 0700 (bunx: root the package cache in a per-user directory #44393, bunx: store the package cache under the per-user bun cache directory #31447)minimumReleaseAge: an exact version passed when only the abbreviated manifest was cached; so did a publish time that is not a date (install: do not resolve an exact version from an abbreviated manifest under minimum-release-age #38659, install: block a version whose publish time is not a date under minimum-release-age #43307)BUN_CONFIG_NO_VERIFY=0turned verification off,=1did not -> off only for1ortrue(install: make BUN_CONFIG_NO_VERIFY disable integrity verification when set #38310)bun audittook a malformed registry answer for a clean audit -> failed audit; a scoped registry is named and skipped (audit: fail closed on malformed registry responses and escape control characters in the report #38557)**/a/bpatterns, a BOM, trailing spaces disabled ignore rules; a symlinked README was sent (pack: look up a dependency of a bundled dependency only by package name #44902, pack: match multi-segment **/ patterns at any depth and trim trailing spaces #41531, pack: skip a UTF-8 BOM at the start of .npmignore and .gitignore #41871)bun publishpublished with"private": "true"or1-> any truthy value is private (Treat any truthy package.json "private" value as private in bun publish #41664)Install core
node_modulestoo (install: reinstall node_modules when switching between the isolated and hoisted linkers #38779)node_modules/.old_modules-<hex>-> deleted after the install;bun prunecollects old ones--dry-runran root lifecycle scripts and wroteyarn.lock(install: clear SAVE_YARN_LOCK for --dry-run in Options::load and start no root lifecycle script #38830)[install] dryRun = truewas ignored -> honoured, also by publish,pm pack, prune, dedupe,audit fix(install: honor[install] dryRunfrom bunfig.toml #34979)error: failed to download <name>@<version>, exit 1 (install: fail when an optional and a required dependency share a failed download #43197, install: fail a download that a non-optional peer needs, whichever dependency owns the slot #43219; fixes bun install: whether a failed download of a peer dependency's package is an error depends on the name order of its parents #43214)--offlinefailed on what is below an optional dep it had skipped -> only misses still reachable are errors (install: --offline does not fail on what is below a skipped optional dependency #43185)--offlineexited 0 without a required dep of an optional package installed in one place, missing in anotheros/cpuwas silent -> warning;bun addreports it as skipped (install: warn when a required dependency is skipped for the target os/cpu #41956)--production/--omit=devfailed on aworkspace:,link:,file:devDependency whose target is missing -> left out (install: don't fail --production on a missing workspace: devDependency #36704; fixes "workspace:*" errors even if devDependency using --production #14134,bun install --productionproduce errors with devDependencies: "workspace: *" #15102)--productioninstalled, and ran scripts of, a devDependency that is also an optional peer;bun prune --productionremoves it (install: stop --production from installing a devDependency that is also an optional peerDependency #42040)$TMPDIRon another file system than the cache kept a probe file per install (install: remove the same-filesystem probe from $TMPDIR when the rename into the cache fails #38859)bun add,remove,update,patchrewrote a package.json in which nothing changedInfinity/NaNwere written into package.json ->null(js_printer: write null for non-finite numbers in JSON output #41743)--cwdwas applied twice bybun update,link,unlink,why(install: make--cwdchdir idempotent across re-parses #31154; fixesbun --cwd=<dir> update -idoes not work likebun --cwd=<dir> add <package>#31152)bun --bun runworked only for the first user of a shared machine ->nodeshim directory per user (run: scope the --bun node shim directory by uid #35565; fixes using/tmp/bun-nodefor overriding node.js is incorrect #7504)Resolver and version picking
@types/*stubs under*) -> prefers one that is not, like npm; a locked version stays untilbun update(install: prefer non-deprecated versions when resolving a semver range #36630; fixes tsc failing due to missing second order type definition file, works with npm #7571, Bun does not respect deprecated on npm with "bad publish" #25314)bun add pkginstalled a deprecatedlatest-> highest release that is not deprecated;pkg@lateststill follows the tag (install: skip a deprecated latest dist-tag like npm does #40317; fixes bun add follows a deprecated latest dist-tag that npm skips, installing a bin-less placeholder #40310)dist-tags.latestfailed add, install,update --latest, hid the package fromoutdated/update -i-> highest release, with a warning (install: fall back to highest version when dist-tags.latest does not resolve #36671; fixes Bun update --latest sometimes cannot get tag from npm (mirrors?) #12041)*,npm:pkg,npm:pkg@*skipped a prereleaselatest-> follow the tag, like npm (install: resolve a bare npm: alias to the latest dist-tag #39774)*,>=1) nested the newest version -> shares a resolved version that satisfies it;bun updateno longer re-adds whatbun deduperemoved (install: keep one copy of a package when one version satisfies every range #38770)1.0.0 || ^2.0.0,=2.5.0 <2resolved as their first exact version (install: consider OR'd alternatives in find_best_version fast path #30076)>=0.5 0was read as alternatives -> intersection, like npm (semver: intersect space-separated comparators when both are valid #32993)^,~, x-ranges admitted prereleases of their upper bound (pkg@>=5.0.0-alpha.0 <8overrode^4) -> end at<X.Y.Z-0(semver: end ^, ~ and x-range upper bounds at <X.Y.Z-0 like node-semver #43102; fixes bun install applies anoverridesentry to versions outside its range when the selector has a prerelease bound #41371)overrideskeys) (semver: an exact version intersects a range only when it satisfies it #43150; fixes bun install applies a rangedoverrideskey to a dependency pinned at a prerelease that the range does not admit #43138)>1meant>1.0.0->>=2.0.0;>1.2->>=1.3.0, like node-semver (semver: read>1as>=2.0.0like node-semver #43253)<x,<*,>x,>*matched everything -> nothing;^x,~xmatch any version (semver: read a wildcard major after < or > as the empty set #43258)Bun.semver.satisfies()was true for"",1.2.3.4,1.2.3 junk(semver: return false from satisfies() for an invalid version argument #32998)satisfies("1.0.0+build-1", "^1.0.0")was false;< =1was misreadpkg@2x,1beta,1.2xwere read as ranges -> dist-tags (install: classify digit-prefixed dist-tags like2xas tags, not ranges #35259; fixes No version matching "" found for specifier "" (but package exists) #19065)ssh/httpwere read as URLs -> dist-tags (install: classify bare words starting with ssh or http as dist-tags #37133)npm:,catalog:) broke the install, lost a pin inbun add --catalog, or panicked (install: ignore leading whitespace in dist-tag, bare folder and local tarball versions #38331, install: ignore leading whitespace wherever a dependency literal is classified or re-parsed #38309)npm:alias) under a range the old version fits kept the old package (install: re-resolve a root dependency edited to name a different package under a range the old version fits #41774)npm:alias's package outside its range (install: bind a same-named npm alias's package only when it is in the dependency's range #43267)npm:alias asked for a garbled name (GET /1.0.0no-deps - 404) (install: fix garbage package name when a dependency follows an npm: alias #43515; fixes Bun returns random string artifacts in a 404 GET url #23264)file:andlink:on one directory became one package (install: resolve file: and link: dependencies on the same directory as separate packages #38114)file:folder in bothdependenciesanddevDependencieswas duplicated (install: dedupe a file: folder listed in both dependencies and devDependencies #41833)bun.lockresolves (install: reuse lockfile-resolved git/github/tarball packages when re-resolving #37142)file:../xdep failed to install (install: resolve a bundled file: dependency whose path leaves the package #43125)bundleDependencieswere kept from one dependency group of a manifest only (install: keep bundleDependencies from every dependency group when parsing a registry manifest #38857)nameis invalid was installed under it -> refused (install: reject tarball, folder and git packages whose package.json name is invalid #38633)namegot broken lockfile rows -> named after their source (install: name folder, tarball and git packages without a package.json name after their source #38681; fixes bun.lock failed on file:../xxx #17060)node_modulessymlink was a resolver error -> missing module; isolated: a failed optional dep goes with the links to it (Resolve dangling node_modules symlinks as missing modules; isolated installs unlink failed optional dependencies #38756)Hoisted linker
node_moduleshung when the OS refused the pool's first threadbun pruneleft the.old-<random>of a killed installbun install#20376)bundledDependenciesresolved the bundled version (install: keep the rows a tarball ships in the hoisted install tree #43377; fixes Hoisted install places a dependency above a bundled copy that shadows it #43351)file:override on a registry package's or transitive dep was not linked or resolved from the wrong directory -> from the project root, linked with its deps (install(hoisted): link a file: override applied to a dependency of an installed package #38994, install: resolve and link a file: override applied to a transitive dependency from the top-level dir #44339; fixes bun install: a file: directory used as an override for a transitive dependency is installed without its dependencies #44299)file:folder that cannot be hard-linked (EXDEV) switched all later packages to copyfile (install: keep the copyfile fallback of a folder dependency from emptying its source #40663).binlinks (install: remove the bin links of a failed optional dependency (hoisted linker) #44931)Isolated linker, global store
node_modulesor entry that is a mount point (Docker volume) is kept and reset in placebun add -gnever linked bins into the global bin directory (install(isolated): link bins of -g update requests into $BUN_INSTALL/bin #30451; fixes Bun fails to create global shims (package:sudocode) #28597, bun add -g fails to link binary to ~/.bun/bin when linker = "isolated" is set in $HOME/.bunfig.toml #30450)--offline(install: do not fail the isolated install when an optional dependency's tarball cannot be downloaded #43229; fixes bun install with the isolated linker exits 1 when an optional dependency's tarball cannot be downloaded #43212)node_modules/.bun/node_moduleschanged between installs of one lockfile withnpm:aliases -> links named by the dep, one layout (install: name the isolated linker's fallback link by the dependency name #40356; fixes isolated linker: shared fallback.bun/node_modules/<name>link races between versions across identical installs (npm: alias installing a second copy; breaks TypeScript 6/7 coexistence) #40355)link:target was accepted -> install fails, as hoisted (install: fail the isolated install when a link: dependency's target is missing #38045)file:./vendor/xdep failed -> linked from inside the package (install: link file: dependencies of registry packages from inside the package with the isolated linker #38856)--omit=peerdropped a peer an ancestor provides;bun prune --omit=peeragrees (install: link ancestor-provided peers in the isolated store with --omit=peer #41416)file:folder stayed in its copy (install: remove files deleted from a folder dependency on reinstall with the isolated linker #37138)node_modules(both linkers) (install: remove the node_modules links of a renamed or removed workspace #38511)bun pruneleft dangling links to removed optional / peer deps in store entries (prune: unlink a removed dependency from the store entries that depend on it #42625)bun linkregistrations (install: keep bun link registrations when the isolated linker resets the global node_modules #43582)@types/*was shared, TypeScript missed the types -> project-local (install: keep global-store entries with @types-typed peers project-local #36796; fixes globalStore = true makes @types/* unreachable to TypeScript from inside the link store #36795)Bins
--silenta failed bin link exited 0 and saved the lockfile -> 1, nothing saved (install: record a failed bin link at every log level, so --silent exits 1 #44997, install: record a failed bin link or git clone at every log level #44958).binlinks, failed withEEXISTon a read-only up-to-date tree -> a right link is left alone (per bin: chmod 1 -> 0, unlink 1 -> 0, symlink 2 -> 1) (install: skip .bin link recreate when the existing symlink already matches #36701, Avoid the copy up when installing symlinks #20067; fixes Bun fails to use dependencies when mounted :ro in Docker #14736)file:,link:packages, then by name (install: prefer direct dependencies when linking conflicting bin names #31416, install: link registry packages' bins before the bins of workspace and other local directory packages #41867)0777 & ~umask(install: bin.rs owns the bin mode, read once in CommandLineArguments::parse #41509)binentry whose value is not a string dropped the package's other commands (install: skip bin entries whose value is not a string, keep the others #43159; fixesbun installlinks no bin when one value of abinobject is not a string #43148).binlinks (install: fall back to copying the bin target when symlink fails with EACCES or EPERM #40161)bun update -g/bun ci -glinked no bins; a reinstalled global package gets its command back (install: link bins into the global bin dir on a barebun update -g#43384)bun pm migrate/pm trustwrote uninitialised bin bytes intobun.lockb(install: store all 16 bytes of bin::Value at every Bin construction #37581)bun.lock, bun.lockb
bun.lockbcrashed or hung install,pm ls,why,outdated-> validated at load, refused with a warning; files by Bun 0.x are normalised and keep loading (install: validate bun.lockb slice descriptors and ids at load time #32753, install: reject bun.lockb packages whose resolution tag no writer produces #39720)file:or registry dep named like a workspace made Bun write a lockfile it refuses (Duplicate package path) -> the member beatsfile:; a registry range it does not satisfy gets the root slot (install: fix duplicate bun.lock keys when a file: dependency shares a workspace member's name #37245, install: fix npm dependencies that share a workspace member's name #37248)file:paths from the lockfile were resolved from the wrong directory afterbun updateor an override removal (install: resolve file: dependencies loaded from the lockfile relative to the top-level dir #38850)bun.lockholds only through optional peers (install: keep the packages bun.lock holds only through optional peers when an install re-resolves #39002)bun addinstalled nothing whenbun.lockrepeats a dependency line (install: do not miss a new dependency when the lockfile repeats a dependency line #43271)--lockfile-onlysaved after an error;--silentfailed on abun.lockit ignores (install: fail --lockfile-only on an error logged while resolving #44364)bun.lock,bun.lockb,yarn.lockignored the umask (yarn.lock0666) (install: honor umask when writing bun.lock, bun.lockb and yarn.lock #41808)bun <path>.lockbprinted the directory's lockfile, read.env, needed package.json -> prints the given file (git textconv works) (install: read only the lockfile forbun <path>.lockband--hash#44870)git+git://resolutions did not load back frombun.lock(install: read git+git:// resolutions back from bun.lock #38798)bun pm migrate/pm trustwrote anotherconfigVersion/patchedDependenciesthan install; yarn entries without#sha1(install: lockfile parity forbun pm migrate,bun pm trustand yarn.lock entries without#sha1#41908; fixes trustedDependencies has no effect after bun pm migrate from pnpm-lock.yaml; scripts silently blocked #38193)bun.lock/ pnpm-lock.yaml crashed or counted as pruned (install: resolve a lockfile's workspace path when reading its package.json #43405)bun install --yarnrewrote an unchangedyarn.lockevery time--frozen-lockfile, bun ci
bun cifailed in a workspace when nothing changed (member with a script and alatestrow); one member's edit moved pins elsewhere -> members are compared row by row, keep their pins (install: diff and apply workspace members row by row, like the root package #44919; fixes Unnecessary HTTP requests when workspace package has lifecycle scripts #26070)minimumReleaseAge(install: keep versions already pinned in the lockfile installable under minimum-release-age #40032; fixesbun cire-appliesminimumReleaseAgeto dependency already pinned inbun.lockon Bun 1.4.0 #40031)pm migratetobun.lockb, the next frozen install failed on the meta hash (install: derive the bun.lockb meta hash in the writer #44501)overridesandresolutionsonlyoverridesapplied -> both; a lockfile without those rules fails frozen, 1.4's too (install: apply both "overrides" and "resolutions" when package.json has both #38811)packagesresolution outranks package.json:bun installand--frozen-lockfileboth install a version the manifest denies #43795)Migrations (npm, pnpm, Yarn v1)
yarn.lockhas no key for is the workspace that satisfies it; Yarn's ownpackageExtensions;resolutionskeyed on an alias; a name in several groups of a workspace;catalog:in an extension;%xxin a folder name; clearer refusals (install: follow-ups to the Yarn Berry migration #45052)InvalidNPMLockfile(Fix package-lock.json migration for scope registries configured without a trailing slash #38698)file:directory was resolved from the project; one that climbs out is skipped (install: migrate a package's own file: directories from package-lock.json relative to the package #38990)file:directory copied by--install-linksfailed with EISDIR (install: migrate a file: directory that npm --install-links copied as a folder #43103)pnpm-workspace.yamlwas read only with a migratable lockfile;bun patch --committhen segfaulted (install: import pnpm-workspace.yaml even without a migratable pnpm-lock.yaml #38754)pnpmblock was removed from package.json -> kept, fields copied (install: keep the pnpm block in package.json when migrating from pnpm #38775)onlyBuiltDependencieswas lost ->trustedDependencies; isolated reports blocked scripts (install: migrate pnpm onlyBuiltDependencies and report blocked scripts under the isolated linker #38780)bun.lock(install: migrate pnpm importer rows in the shape package.json parsing produces #38791)pnpm-workspace.yamlcrashed -> scalars copied as written (react: 18->"18") (install: only move string entries of pnpm-workspace.yaml into package.json #44470)npm:alias packages were named after the tarball URL (install: name yarn.lock npm: alias packages after the alias spec, not the tarball URL #38948)resolvedfailed (install: migrate yarn.lock registry entries that have no resolved field #38985)Workspaces, catalogs
namefailed the install -> skipped (install: skip workspace members whose package.json has no name #38781; fixes Ignore package.json files without "name" property #6317)latest,"") on an unpublished sibling got a 404 -> links the workspace (install: link a workspace for a dist-tag the registry cannot satisfy #35468; fixes bun i in workspaces doesn't symlink local dependencies when using latest #4830)workspace:spec linked the project into itself -> error; a glob like../*made the root its own member (install: fail to resolve an empty workspace: spec, and never make the root its own workspace member #41653)/(orC:\) were not found (install: resolve the workspaces of a package.json in the filesystem root #43404)workspacesglob that fails named no path (install: name the path a workspaces glob fails on #43517)bun update <name>rewroteworkspace:^,file:and other non-registry entries (install: keep workspace and other non-registry entries as written on bun update <name> #38847)bun add member@workspace:^lost the spelling (install: keep the workspace: spelling given to bun add <member>@workspace:<range> #38866)bun updateprinted no row, or another catalog's version, for a moved catalog entry (install: print the update row for catalog entries moved by bun update #38763)bun update -iupdated one of several catalogs defining a package -> a row per catalog (cli(update -i): update every catalog that defines the same package, not just one #36446)catalog:dep always suggestedbun add --catalog-> only where that works (install: advisebun add --catalogonly for a row that command can add #44638)npm run <script> -w <ws>/--workspacesinside a script recursed forever -> runs the workspace's script (run: translate npm workspace flags when rewriting npm run scripts #33800; fixes bun and npm behaves differently for parameter "--ws". #22719, Bun install unexpectedly replacingnpm runcommand in npm workspaces monorepo lifecycle scripts #24838, bun run recursively re-enters npm workspace script using -w #33796,bun runrewritesnpm runand drops--workspace, recursing forever when root and workspace share a script name #41543)Overrides, peers
*peers resolved by manifest arrival order -> after their siblings; unprovided peers in a fixed order (install: resolve deferred*peers after their siblings, not on arrival order #37713)incorrect peer dependencywas checked against any copy in the tree, hoisted only, every install -> the copy each dependent resolves, both linkers, only when something was resolved (install: check peer ranges in the linkers, against the copy each dependent resolves #41936; fixes Add package info for peer dependency warnings #7403)file:,link:, tarball or git dep provides was looked up in the registry (install: resolve peers provided by file: dependencies #33156)bundownloaded thebunnpm package (80-170 MB) -> optional (install: do not auto-install a transitive peer dependency on bun #39757)minimumReleaseAgeblocking a peer or optional pin failed the install -> warning (install: warn instead of failing when minimum-release-age blocks a peer dependency #38995)bun update name@versionbound a peer row to a stale lockfile leftover (install: re-resolve a peer row instead of binding it to a stale bun.lock leftover #38901)overrides/ catalogs re-resolved rows a package no longer owns (install: only re-resolve rows a package still owns when overrides or catalogs change #38849)bun update <name>left rows of a$nameoverride stale, frozen passed (install: re-resolve rows whose override or catalog value changed in the package.json write-back #43981; fixesbun update <pkg>does not re-resolve packages whose$pkgoverride changed; lock stays stale andbun install/--frozen-lockfiledo not notice #43975)bun update --latestmoved versions an override selects;-rwrote"latest"into package.json (install: resolvebun update --latestat the row, so package.json never holds a temporarylatest#44485)overrides/resolutionsthat are not objects were ignored -> error (install: fail when "overrides" or "resolutions" is not an object #40729)foo@catalog:,my-foo@npm:foo[@^1](Yarn) were ignored with a warningRegistries, credentials, .npmrc, bunfig
.npmrccredentials follow npm's key walk://host/covers paths below it,_authsent as written, a line reaches registries named by--registry/ env /$VAR, a tarball on another https host gets that host's line; a sibling or look-alike path inherits nothing;:443/:80keys keep working (install: .npmrc credentials the npm way — verbatim _auth, no URL-embedded credential in the request path, key-walk lookup for registries and tarballs #40423; fixes _authToken with with scopes stopped working in bun v1.3.11 works in 1.3.10 #28233, Private GitLab npm registry: scoped package resolution fails (401/404) since 1.3.x #30311, auth tokens not sent to scoped-registry tarball URLs when token path diverges from registry URL path (regression from 1.3.10) #30513, private package installation seems to be broken via .npmrc in bun 1.4 #40549)/#5368, Custom registry url gets trimmed if trailing forward slash is missing #14017, Bun doesn't handle custom.npmrcregistries the same way as npm for paths #16185,bun installwith private repository for a scoped package uses wrong URL path #20373)user:pass@in a registry URL from an env var was dropped (install: send credentials embedded in a registry URL that comes from an env var #38834)user@/user:@sent nothing -> Basic auth with an empty password (install: send Basic auth for a registry URL with a username but no password #39063)p%40sssendsp@ss) (install: percent-decode the credentials written into a registry or tarball URL #39603).npmrc:;/#comments were part of the value;ignore-scripts=1,=True, empty did nothing -> as npm (npmrc: cut inline comments and read boolean options (ignore-scripts=1 etc.) the way npm does #41916).npmrckeys and values kept Unicode whitespace (install: trim .npmrc keys and values with the JS whitespace set, like npm #41714)--ca/--cafilewere shadowed by a config file'scafile-> one setting, a later layer replaces both (install: let --ca / --cafile replace the config file's CA settings instead of being shadowed by its cafile #41898)-gcommands ignoredglobalDirfrom--configand~/.bunfig.toml(install: load bunfig before resolving globalDir for -g installs #35454; fixes Global configuration from a config file does not seem to apply #4553; refs.bunfig.tomlglobalDirandglobalBinDiris not respected on Windows 11 #12886)~incache.dir,globalDir,globalBinDir,cafilewas a literal directory (install: expand leading ~ in bunfig [install] path values #36667; fixes BUN_HOME env variable for bun #10152, Bun does not expand the~symbol into the full path to the home directory on macOS #26715)[install.cache] disable = truestill cached manifests (install: propagate[install.cache] disable = trueto the manifest cache #36219).npmrc/ bunfig.toml was skipped silently -> warning (install: warn when a present .npmrc or bunfig.toml cannot be read #41693)bun publish,pm whoami,audit,pm view,pm diffignoredhttp_proxyandNODE_TLS_REJECT_UNAUTHORIZED=0(publish, pm whoami: route registry requests through http_proxy #38075, publish, pm whoami, audit: honor NODE_TLS_REJECT_UNAUTHORIZED=0 like bun install #38093)bun pm whoamisent a stub CI name -> the real vendor (ci/github-actions) (install: send the real CI vendor name in the whoami user-agent #40805)Network, retries
Retry-After, 429 / 408 retried, certificate errors not (install: back off between retries (honour Retry-After), don't halve concurrency on the first network error #40009)BUN_CONFIG_MAX_HTTP_REQUESTSwas ignored (install: honor BUN_CONFIG_MAX_HTTP_REQUESTS #38744)--network-concurrencydefaults to 48 -> 64 (install: say --network-concurrency defaults to 64 in help and docs #38759)Cache, tarballs, extraction
--offlineright after an install could failfile:/ URL tarballs were cached by path or URL, two projects or branches got each other's package -> by the integrity inbun.lock(install: cache file: tarballs under the integrity the lockfile pins #39016)file:/ URL tarball deps were reinstalled on every run (hoisted) -> verified by a.bun-tag(install: skip reinstalling file:/URL tarball deps on second install #35576; fixes bun install always reinstalls vendored packages #8260)--force,bun update, re-adding the same path / URL kept old bytes or the old hash -> direct tarball deps are fetched again (install: refresh URL/local tarballs under --force and explicit re-add #31868; fixes Rust rewrite: installing tarballs from URLs using --force does not refresh them #31864)$TMPDIR(install: stop leaking the displaced cache folder when a tarball is re-extracted #38702, install: don't leak package extraction temp directories into $TMPDIR #33979; fixes bun install leaks package-extraction temp directories (.{16hex}-{8digits}.tmp) into $TMPDIR; filled 25 GB on a long-lived CI container #33977; refs Windows: parallel bun install --no-cache with shared BUN_INSTALL_CACHE_DIR can fail with ENOENT opening cache/package/version dir #28062)RENAME_EXCHANGE, NFS) -> first writer wins (install: fix ENOENT race on shared cache when RENAME_EXCHANGE is unsupported (NFS) #36229; fixesrenameat_concurrently_without_fallbackdelete+rename fallback causes ENOENT for concurrent readers on NFS #36227)Bun.Archive(install: verify the gzip trailer of package tarballs #41503)github:tarballs extracted symlink entries -> skipped, like npm (install: skip symlink entries in extracted tarballs (matching npm) and harden extraction with O_NOFOLLOW #31481)--no-verifycached unchecked bytes under the lockfile's hash, for all projects -> own key; isolated kept such a copygit, GitHub, folder deps
bun updatefollows moved refs (install: fetch new tags/branches when updating a cached git dependency #35566; fixes bun requires clearing cache when updating package that is using a git tag #11548, Bun installs the first commit of a library that's not on NPM.JS #13769, Usingbun installon a package with a git address will 100% not pull it. #15336, bun requires clearing cache when updating package that is using a git tag #18947)git+ssh://git@host:2222/..lost its port in the ssh fallback (install: keep non-default ssh port in the ssh fallback clone URL #36932; fixes git+ssh dependency with non-default SSH port fails to resolve #36931)deploy@host:org/repo.gitbecamegit@deploy@host(install: fix try_ssh scp-style fallback URL truncation #35026)bun add name@git+ssh://host/repo.gitlost the alias for hosts other than GitHub (install: accept aliased non-GitHub git URLs in bun add / bunx #36687; fixesbunxdoesn't work with packages in a git repo #13891)github:o/r#feat%2Fxsilently installed the default branch; an escaped owner / repo is an error (install: keep percent-escapes in a GitHub dependency's owner, repo and committish #43172)github:/ git refs on one commit, or afile:folder declared by several packages, failed with EEXIST or wrote a lockfile Bun refuses -> one package (install: reuse the existing package when a git, github: or tarball extract resolves to it #42640, install: reuse the package of a file: folder that several packages declare #42545)GIT_DIR(git hooks) made install andbun creatework on another repository -> 13 git variables are not passed on (Do not pass GIT_DIR to the git that bun install and bun create spawn #41715)file:folders declared by git / tarball packages were resolved from the project (install: keep file: folder paths declared by git and tarball packages relative to the package #38816)git@host/pathwas handed togit cloneas a local path; a URL could be read as an optionbun link -g/unlink -gfailed;bun link -g <name>removed the registration ->-gis ignored (link: don't chdir into the global dir forbun link -g/bun unlink -g#33142; fixesbun link -gshould either throw and warn orignore -gand not link the global install into itself #21397, bun link -g fails on WSL2 with package.json missing name error #33141)Lifecycle scripts, trust
--concurrent-scripts, the next script waited for all running ones to end -> for onetrustedDependenciesentry added to a member after the first install did nothing -> its blocked scripts run once (install: pick up trustedDependencies added to a workspace member's package.json on a repeat install #41803)npm:alias could not be trusted consistently -> by its real name in package.json,--trust,pm trust,bun.lock(install: trust npm: aliased packages by the same name in bun pm trust, the installer and bun.lock #39443)bun pm untrusted/trust/ls --trustedfollowed a stale lockfile, knew nothing withbun.lockb-> read package.json (install: makebun pm untrusted,trust, andls --trustedread trustedDependencies from package.json #41933, install: honor bun.lockb trustedDependencies sentinel inbun pm untrusted/trust#34327)pm untrusted/trustlisted and reran workspace members (install: stop bun pm untrusted and bun pm trust from treating workspace members as blocked #41513)--concurrent-scriptsdid not apply to the isolated linker (install: apply --concurrent-scripts to the isolated linker #42445; fixes install: the isolated linker ignores --concurrent-scripts and [install] concurrentScripts #42421)concurrentScriptsbeat--concurrent-scripts(install: let --concurrent-scripts override bunfig concurrentScripts #42422)--helpsaid dep scripts never run ->--ignore-scriptsalso skips trusted ones (install: say --ignore-scripts also skips trusted dependencies' scripts in --help #38893)bun pm -g untrustedsuggestedbun pm trust->bun pm -g trust(pm untrusted: printbun pm -g trustin the hint for a global install #44663)Security scanner, audit
file:,link:), URLs without userinfo / query; a fatal advisory stops them before install and scripts (install: pass every resolved dependency to the configured security scanner #37590)--production/--omit=devfailed when the scanner is a devDependency (install: allow dev security scanner under--production#31031; fixes bun install --production + security scanner fails #31028).envitself -> gets the install's environment, follows--env-file,--no-env-file,env = false(install: make the security scanner child follow --no-env-file, --env-file and bunfig env = false #44845)bun audit fixcould downgrade into an older major (audit fix: never downgrade a package out of its installed release line #39317)bun audit --helplacked--production,--omit,--dry-run,--cwd(audit: list --production, --omit, --dry-run and --cwd in bun audit --help #38836)add, remove, update, outdated, link
bun remove foodeleted thenode_modules/fooanother workspace has for"foo": "npm:bar"link,install <pkg>,install --analyze,update -i,removeexited 0 afterFailed to install N packages-> 1 (install: exit 1 from the shared add/link/update helper when a package fails to install #44989)bun add ../pathfrom below package.json,bun install -g .resolved from the wrong directory -> the cwd (install: resolve a relative local path given to bun add against the cwd #43348, install: resolve relative folder positionals against cwd forbun install -g .#36623; fixesbun install .not working #5682)bun add <folder | tarball | git url>of a declared package wrote it twice (install: replace the declared dependency whenbun addgets a spec without a name #43380; fixes DependencyLoop with github dependencies #8031)bun add -gused a package.json above the global directory (~/package.json) (install: stopbun add -gfrom walking above the global install dir #30659; fixes package.json in $HOME is treated strangely #40683)bun add --dry-runin an empty directory created package.json (install: don't create package.json forbun add --dry-runin an empty directory #36694; fixes--dry-runshould not create apackage.jsonfile #13244)bun removereordered package.json keys -> order, tabs, final newline kept (install: preserve package.json key order when bun remove empties a dependency group #35586; fixesbun removeenforcingpackage.jsonstyling #7215, Bun mangles package.json formatting in some cases #9351)bun remove -gleft dangling links in the globalnode_modules/.bin, deleted foreign dangling links in the bin directory (install: remove dangling links from the global node_modules/.bin onbun remove -g#43403)bun update <name>blanked an optional / peer range no version satisfies (install: leave an entry that bun update <name> could not resolve as written #43195)bun updatethat only rewrites package.json said(no changes)(Report package.json sync in the bun update summary instead of (no changes) #38918; fixes bun update reports (no changes) while rewriting package.json #38908)bun update -irepeated its prompt on mouse clicks and in short terminals (Keep the update -i prompt frame on screen and skip no-op redraws #39683)update -i/outdatedtables were sized in bytes -> display width (CJK, emoji), cells escaped (cli(update -i): measure table cells in terminal columns, not bytes #43333, bun outdated: size and pad the name cells by display width #43236)outdated/update -iused the lockfile's ranges -> package.json's (install: makebun outdatedandbun update -iread dependency ranges from package.json #42005)bun outdated a bneeded all patterns to match -> union;!patternremoves (outdated: list a dependency that matches any of the name patterns #43309)outdated/whyknew an aliased dep by one name -> either, both printed (outdated, why: match an aliased dependency by either name and print both #43338)rc.1->rc.10) dim; long prerelease tags were garbled (semver: fix the colored version diff for build tags and appended tag suffixes #38688, install: format versions with the string buffer they were parsed from (bun outdated table, minimum-release-age messages) #38649)minimumReleaseAgeerror swapped range and name (install: print version before package name in the minimum-release-age error #37895)bun link/unlink --helpdescribed a wrong--savedefault (install: describe the real --save default in bun link and bun unlink --help #38915)bun pm, why, info
bun pm pkg getof several values altered one with a}in it, left a key unescaped (pm pkg get: print several values as one object through print_json #45061)bun pm cache rm --cache-dir <dir>cleared the global cache -><dir>; project config still cannot redirect itbun pm bin -g/pm ls -gfailed before any global install -> work, write nothing (pm: letbun pm -g bin/bun pm ls -gwork before any global install #36622; fixesbun pm cache rm -gisn't working on Linux system #6382,bun pm -g binthrowsMissingPackageJSON#7501,bun pm ls -gdoes not work #13019)bun pm cache rmsays when the global store goes with the cache; docs name--forcefor stale manifests (pm: warn when bun pm cache rm removes the global store, document --force for stale manifests #43667)bun pm versionsaw no repository in worktrees / submodules (.gitis a file) (pm version: detect a git repository when .git is a file #41754)bun pm version prereleaseoverflowed above 2^32; versions npm calls invalid were accepted (bun pm version: read the prerelease number as a u64, reject a version npm calls invalid #42459)bun pm view pkg@tag/pm diffusedlatestfor an unknown dist-tag -> error listing the tags (pm view, pm diff: an unknown dist-tag is an error, notlatest#42063)bun pm,pm --help, an unknown subcommand printed three help texts, unindented, withoutpm diffandls -g-> one (cli: listbun pm diffinbun pm --help, keep one copy of the pm help text, restore the indent of the\-continued messages #39487, bun why: keep the indent in the no-argument usage text #43314, pm: document -g for bun pm ls in the help text and docs #43098; fixes bun why: the no-argument usage text loses its indentation #43310)bun pm bin -gsaidnot in $PATHfor an entry with a trailing slash (Fix global bin path warnings: double backslashes on Windows and trailing slash PATH detection #28773; refs Issues with global bin paths on Windows #28771)bun whyprinted a package's dependents at every occurrence (exponential) -> once, repeats*deduped(bun why: print a package's dependents once, mark repeats as *deduped #37922)bun why name@versionfailed for exact / scoped names, ignored a workspace's version (bun why: match an exact name that has a version #43284, bun why: compare the query version with the workspace version #43290; fixesbun why <workspace>@<version>finds nothing: the version part is compared withworkspace:<path>#43288)bun why: no-match error on stdout -> stderr (bun why: print the no-match error to stderr #43287; fixesbun whyprints the "No packages matching" error to stdout #43285)bun why: branch left open after the last visible workspace dependent (bun why: close the branch after the last visible workspace dependent #43292; fixes bun why: branch markers count a dependent that the workspace filter hides #43291)bun why: a cycle back to the queried package is*circularat its first repeat (bun why: mark a cycle back to the queried package as circular at its first repeat #43300; fixes bun why: a cycle through the queried package prints one extra round before *circular #43295)bun why: a root package.json withoutnamewas missing as a dependent (why: list a root package.json with no name as a dependent #43277)bun why: a peer dependent was shown under the wrong copy (why: show a peer dependency under the copy its owner resolves #41941)pack, publish
workspace:/catalog:versions came frombun.lock(stale, or failed without one) -> the package.json files; no lockfile is read (pack/publish: resolve workspace: and catalog: specs from the package.json files, not bun.lock #38813; fixesbun pm packreplaces workspace packages with bun.lock version, not their package.json one #20477,bun pm pack,workspace:*resolve version frombun.lockinstead from linked packagepackage.json#20829)workspace:aliases and directories were published as written ->npm:aliases; the workspace's version (pack: publish workspace: aliases as npm: aliases #38788, pack: publish workspace: directory dependencies as the workspace's version #38835)!patternin.npmignorecould not re-include files below an excluded directory (pack: honor .npmignore negation patterns that reach inside an excluded directory #36699; fixesbun pm packdoes not handle.npmignorecorrectly #15602)--destination,--filename,bun publish ./x.tgzwere resolved from the package root -> the cwd (pm pack: resolve relative --destination and --filename against the invoking directory #38739, publish: resolve a relative tarball path against the invoking directory #38704)publish --dry-rundeleted an existing one, a FIFO--filenamehung -> built in memory, written once complete (pack: build the tarball in memory and write it out only once it is complete #38721)DT_UNKNOWNlost entries (pack, publish: include entries on filesystems whose readdir reports DT_UNKNOWN #38716)binpaths naming no file,.., a trailing slash indirectories.binbroke pack; an emptybinhiddirectories.bin(pack: ignore bin paths that name no file, strip the trailing slash from directories.bin #38720)binnames was packed several times (fix(pack): Now dedups paths in package.json bin field #41291)bun publishaccepted aversionthat is not semver -> refused after the scripts;v1.0.0goes out as1.0.0(publish: reject a package.json version that is not semver #43142)bun patch
--commitfrom a workspace member flipped the root package.json's final newline to the member'sEACCESfor non-root users (install: create patch parent dirs with directory mode, not file mode #33573; fixesbun patchfails with EACCES Permission denied mkdir() when adding new files in nested directory #13330, bun install: applying a patch that adds files in a new (non-existent) directory fails with "EACCES: Permission denied (mkdir())" — still reproduces on 1.4.0 #33571, bun patch: adding a file in a new directory fails with EACCES (remaining half of #13330) #38569)-U0hunks landed a line off -> placed by context likegit apply;hunk #N does not applyfails the install (patch: locate hunks by their context and fail when a hunk does not apply #41733, patch: position zero-length hunk ranges after the named line #41695; fixes bun patch silently applies hunks at stale + offsets, corrupting patched files #38879)-- a/were misparsed (patch: detect legacy-diff file boundaries inline instead of reparsing #34870)git diffleft a partial or nonode_modules/<pkg>-> staged copy, everything put back (bun patch: copy the package into a staging folder before it replaces node_modules/<pkg> #43188, bun patch: keep node_modules intact when the cache entry is missing or git diff fails #43160; fixesbun patch <pkg>leaves a partial package in node_modules when the copy from the cache fails part-way #43164)bun patchdropped the package's bundled and nested deps,--commitrecorded them as deleted (bun patch: keep the bundled and nested dependencies of the patched package #43178)bun patchof a bundled dep deleted the bundled copy -> refused (bun patch: refuse a bundled dependency as the target #43199)bun patchof afile:folder,link:or workspace package damagednode_modules-> refused (bun patch: refuse a file: folder or workspace package as the target #43223; fixesbun patchreads thefile:dependency of a registry package from the project root #43200)bun patch <path>replaced a folder with another version -> refused when the version is not locked (bun patch: refuse a folder whose version is not in the lockfile #43367; fixes #43353)npm:alias could not be named by its package; a secondbun patch <alias>lost the first patch (#43382)bun patch <transitive>made a straynode_modules/<name>-> edits the store entry (#43383)bun patchreplaced workspace links and symlinks above the package, could write into the global store -> links stay; anything under the cache is refused (#43406)--commiton a patched package wrote.bun-tagfiles and cache paths into the patch, read/etc/gitconfig(#44349; fixes #19327, #44320)--commit: a new binary file became empty, a changed one was skipped, exit 0 -> commit failspatchedDependencieswere ignored silently -> warning (#39869)bun patchreplacednode_modules/<pkg>by a directory that could not load its deps -> edits a copy in the store entry, the link stays; global store:--commitrestores the link (#43365)bunx
bunx pkg@latestrelinked everything each run, a day-old tree was refreshed every run -> once; served with a warning when the registry gives no answer (#41226; fixes #23597, #41211)bunx <pkg>at once corrupted the shared install -> file lock (#37851; fixes #37830)directories.binwas resolved from the wrong directory (#39096)bun create
--forcereplaces the linkbun create <local template> <dir>deleted an existing directory -> merges, lists conflicts (links in the destination too), overwrites only with--force(#36351; fixes #27948, #36341)git init/ commit raced tasks and install -> finished first; the first commit holds exactly the template (#41342; fixes #36953)bun-createtasks ran without a shell, not at all without deps, failures ignored -> run like package.json scripts, withnpm_package_name; a failing task stops with its exit code (#41598, #38955)bun installwas ignored -> exits with its code (#38485).,.., an empty name are refused (#41770)bun create ./Component.tsxoverwrote your stylesheet,styles/globals.css,lib/utils.ts-> kept and listed;--forceoverwrites (#44668){{}}(#37847)Auto-install
require(),import(),Bun.resolveSync()while a package was fetched -> the thread waits; git deps are no longer cloned at run time (#36159; fixes #43842)pkg@2afterpkg@1) with the manifest in the disk cache failed withUnexpected(#36659; fixes #10411)Cache-Control); any other failure is asked again once the event loop has gone around (#42883)fetch()to 4 concurrent requests for the rest of the process (#42945)--prefer-offlinestill asked the registry -> cached manifest (#36776)install.preferworked forbun run <file>only -> alsobun <file>,bun testlatestCompletions, installer
.zshrcthat already sources~/.bun/_bun(#35561; fixes #10897)/etcwhen~/.config/fish/completionsis missing (#41659)another bun.exe is already in PATHcheck never fired (#38567)Crashes, panics, hangs
dependency cycle cannot be laid out in node_modules, exit 1; a bundled back-edge installs (#38976)node_modules/@scopesymlink (hoisted)--concurrent-scripts=0-> the default (#42379)ftp://) (#39672)overridesor spelled differently inbun.lock; after a failed clone / checkout (#44448, #37156, #43075)link:targets, long linked names,<alias>/package.json->ENAMETOOLONG/Invalid dependency name(#37424, #37400, #38369, #38571, #38575)--cafile,--cwd,add --filter;$BUN_INSTALL/$HOMEwith-g; bunx bin names; pnpmlink:/file:paths; progress bar names (#41702, #38413, #37394, #43537, #38558)bun run,bunxin a cwd within 13 bytes ofPATH_MAX->ENAMETOOLONG, or the file counts as unreadable (#42502)outdated,update,update -ion abun.lockwithoutpackages(#41647)bunxinstall flags at oncebun add --trustand at the end of an isolated install (#31245, #39640)Bad system callon Android / Termux (openat2,fchmodat2under seccomp) (#39084)Windows
env -Sdid not run (#31391; refs #22422, #27924, #31387).bunxshims broke when the package is on another drive (#38018)bun removeleft.bunx/.exeshims (#35585; refs #11970)bun installfailed in the root of a drive (#43437; refs #29273)EINVAL: Failed to replace old lockfileon exFAT / FAT32 (#33029; refs #10169)BUN_INSTALL_WINDOWS_RENAME_RETRY_MS) (#33884, #35568; refs #11250, #28062)bunx.exewas written when the hard link fails ->bunx.cmd(#35686)Performance
User-space instructions of
bun install(perf stat, median of 3, millions), generated project of N packages, loopback registry; warm = nothing to do, relink = cache + lockfile, nonode_modules.System calls per package (
strace -c, slope between 100 and 300 packages of 31 files). Per run: a few morelstat(change 8), one read perfile:directory dep.Quadratic scans in the
bun.lockworkspace parse,yarn.lockprinter (#34537) and Yarn v1 migration (#37244) are gone; no allocator heap per cached package.json (#44610).bun pm hash, 32,000 workspaces (ms)bun pm migrate, Yarn v1, 20,000 entries (ms)bun install, 2,100 workspaces (KB)node_modules, 8 cores, wall (ms)node_moduleslacks half, 8 coresnode_modules, 8 coresInternal, no user-visible effect
--cwd, cache publish, specifier -> version (was 4), lockfile writer, registry requestpack()consumes its context; onebinding.gyppredicate; Yarn v1 migrator 2,032 -> 888 lines, nounsafe(#37670, #43912, #37539, #38303, #37865)nodeshim directory kept, manifest cross-check deleted (#37335, #37384, #43170)unsafein src/install: 585 -> 540Test-only PRs
Bun.serve(on main since): #44055..tarball entries, Bun's ownbun.lockpasses frozen, hoisted layouts, what a linker verifies): #37431 #42384 #43878 #41864isRoot, peak RSS,pm trustCPU test on Windows): #39232 #43192 #35626 (refs #11252)Taken in part (what is in. Not: what is not)
bunx --trust(runs the scripts of the package and its whole closure). Not: trust by defaultbun prune)bun.lockis an error. Not: pinning a computed sha512; refusing an empty integrity under frozenbun createwithout a git identity stages the files, says the commit was skipped. Not: a made-up authornpm_config_ignore_scripts=trueturns scripts off. Not:false/0turning them on over a config fileWorkspace name already existson a hash collision. Not: the same fortrustedDependenciesnames (one stays blocked) (refs #36386)pack, lockfile read. Not: typed arrays, coveragenpm:alias. Not: a peer without an override shares the alias (react->npm:@preact/compat)[install.packageExtensions]in bunfig;pnpm.packageExtensionswithout a migration;catalog:values (ignored, warning)patchedDependencieskey that matches no installed version. Not: failing frozen/tmp) is ignored, with a warning. Not: the owner rulepm pkg set --jsonwith a value that is not JSON is an error, nothing is written. Not: strict JSON (0x10,.5are normalised)directories.binare neither packed nor listed. Not: race-proof opener, Windows, a symlinkednode_modulesfile:directory's package.json. Not: failing frozen (warns)bun prunerefuses a linked.bun. Not: the root link (on purpose); a package's own directory on a warm runbun pm view/info: no project needed, several fields, npm paths (maintainers[0].name), semver-sortedversions,bin:,DEPRECATED. Not: new--jsonerrors (refs #20673)pm diffcompares a folder of this project as pack publishes it. Not: folders of other projects.envreaches install'sgit. Not: proxy / TLS variables from.envbun patchnever writes into the global store. Not: a project-local copy that installs keep@scopehang. Not: materialising symlinked cache entries; #43788 unverifiednodeshim next to bun.exe when%TEMP%is on another drive (never run). Not: a missing%TEMP%(warning) (refs #44033).bin, thenPATH, then shared.bin: another package's bin cannot shadownode/sh. Not: bins per script owner.cmd/.batbin (any case) refuses cmd.exe special characters. Not:bun run <bin>.cmdnode_modulesa git dep commits is dropped unless it bundles (then without links). Not: bundled closure onlynode_modulesis reset when only the root's was deleted. Not: moving stale directories aside; a markerNot taken
bun pm sbom: own PRbun linkname would override every projectbun upgradeBun.Archivebun run,Bun.write, pipe reader, repo-wide)bun.lockkeyObsolete (on main via the PR in brackets, or moot): #18582 (the Zig build is gone) #35542 (#38333) #35603 (#41854) #36352 (verdaccio is gone) #36670 (#38269) #36977 (#40010;
--tarball-dir, #7956 stay open) #37126 (#40734) #37137 (#38269) #37250 (#38333) #37616 (#40734) #37925 (#37426) #38648 (#40063) #38671 (#39229) #38687 (#40406) #38776 (#38796) #38869 (#41302) #38891 (verdaccio is gone) #40766 (verdaccio is gone) #41667 (#42881)Fixed issues
Fixes #2773. Fixes #4553. Fixes #4719. Fixes #4830. Fixes #4852. Fixes #5045. Fixes #5176. Fixes #5368. Fixes #5682. Fixes #5714. Fixes #6317.
Fixes #6382. Fixes #6883. Fixes #7215. Fixes #7403. Fixes #7501. Fixes #7504. Fixes #7571. Fixes #8031. Fixes #8260. Fixes #8292. Fixes #9351.
Fixes #10152. Fixes #10411. Fixes #10897. Fixes #11548. Fixes #12011. Fixes #12041. Fixes #12430. Fixes #12752. Fixes #13019. Fixes #13244. Fixes #13330.
Fixes #13379. Fixes #13769. Fixes #13891. Fixes #14017. Fixes #14134. Fixes #14736. Fixes #15102. Fixes #15336. Fixes #15602. Fixes #15711. Fixes #16185.
Fixes #16282. Fixes #17060. Fixes #17759. Fixes #18670. Fixes #18947. Fixes #19065. Fixes #19327. Fixes #20373. Fixes #20376. Fixes #20477. Fixes #20829.
Fixes #21397. Fixes #22035. Fixes #22719. Fixes #23264. Fixes #23597. Fixes #24838. Fixes #25314. Fixes #26070. Fixes #26715. Fixes #27948. Fixes #28233.
Fixes #28597. Fixes #28668. Fixes #28967. Fixes #29263. Fixes #29343. Fixes #30311. Fixes #30450. Fixes #30513. Fixes #30748. Fixes #31028. Fixes #31152.
Fixes #31450. Fixes #31864. Fixes #33141. Fixes #33571. Fixes #33796. Fixes #33977. Fixes #36227. Fixes #36341. Fixes #36795. Fixes #36931. Fixes #36953.
Fixes #36964. Fixes #37830. Fixes #38079. Fixes #38193. Fixes #38569. Fixes #38879. Fixes #38908. Fixes #38909. Fixes #40031. Fixes #40310. Fixes #40355.
Fixes #40549. Fixes #40683. Fixes #41211. Fixes #41371. Fixes #41543. Fixes #41625. Fixes #42421. Fixes #42799. Fixes #42989. Fixes #43138. Fixes #43148.
Fixes #43164. Fixes #43200. Fixes #43211. Fixes #43212. Fixes #43214. Fixes #43256. Fixes #43285. Fixes #43286. Fixes #43288. Fixes #43291. Fixes #43295.
Fixes #43310. Fixes #43350. Fixes #43351. Fixes #43353. Fixes #43399. Fixes #43795. Fixes #43842. Fixes #43903. Fixes #43975. Fixes #44299. Fixes #44320.
Fixes #44669.
Expected fixed, first exercised by CI (Windows; close by hand): #28062 #11250 #11970 #29273 #10169 #22422 #27924 #31387 #11252 #28771 #44033 #12886
Covered by a PR taken in part (close by hand): #18611 #36386 #15516 #22689 #20673
Credits
How did you verify your code works?
bun.lockbx 7 commands: no crash or hang.latestand^1.0.0-a.0.Not verified:
--trustwith an alias, an aborted upgrade leaves no holes, two installs at once. overlayfs: a simulatedEXDEV.Co-authored-by: nikelborm 29357907+nikelborm@users.noreply.github.com