SIGN IN SIGN UP
oven-sh / bun UNCLAIMED

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one

0 0 150 Rust

install: make lockfileVersion 2 stamp independent of the writer's registry config (#31556)

Follow-up to #31539 (review
[thread](https://github.com/oven-sh/bun/pull/31539#discussion_r2213321779)).
Jarred asked for this as a separate PR.

## What

`version_to_write` picks whether a re-saved text lockfile can be stamped
`lockfileVersion: 2`. For an npm package without a supported integrity
hash, it treated the entry as "v2-clean" if the tarball URL was under
**either** the writer's scope-configured registry
(`scope_for_package_name(...)`) **or** the default registry. The
`scope_for_package_name` half is config-dependent and undermines the
cross-machine round-trip guarantee the function exists for. This PR
drops it, keeping only the config-independent default-registry check.

## The problem

A lockfile is committed and shared, so whether the **reader** accepts it
must not depend on the **writer's** registry config. But:

1. A dev has `@myorg → http://internal.example.com` configured locally
(`~/.npmrc` / `bunfig.toml`, not committed).
2. The repo has a v1 `bun.lock` with `"@myorg/foo": ["@myorg/foo@1.0.0",
"http://internal.example.com/@myorg/foo/-/foo-1.0.0.tgz", {}, ""]` —
off-default-registry, empty integrity (e.g. from a migration or older
Bun).
3. The dev re-saves (`bun add …`, `--lockfile-only`, …).
`version_to_write` sees the entry: integrity unsupported, but the URL
**is** under `scope_for_package_name("@myorg/foo")` (their `@myorg`
scope) → treated as v2-clean → whole file stamped **v2**.
4. The writer only blanks a URL to `""` when it's under the **default**
registry, so this URL is written verbatim and integrity stays `""`.
5. A teammate / CI with **no** `@myorg` scope runs `bun install`. The
parser's `npm_url_needs_integrity` is evaluated against the *reader's*
config → the URL is under neither the reader's scope nor the default →
at v2, parse **fails** with *"Missing integrity hash for npm package
resolved to a tarball URL outside the configured registry"*.

So a v1 lockfile that (post-#31539) loads fine becomes unloadable for a
teammate after the first dev re-saves it — the exact load→save→load
failure `version_to_write` was added to prevent, just split across two
machines' configs.

## The fix

In `version_to_write`'s `Npm` arm, drop the `scope_for_package_name`
check and only treat an integrity-less entry as v2-clean when the URL is
under `Npm::Registry::DEFAULT_URL`:

```rust
let url = res.npm().url.slice(buf);
if !url_is_under_registry(url, Npm::Registry::DEFAULT_URL.as_bytes()) {
    return Version::V1;
}
```

The default-registry case is the only normalization the writer actually
performs (it blanks those URLs to `""`, and an empty URL never sets
`npm_url_needs_integrity`), so it round-trips for any reader regardless
of config. Packages freshly resolved from a configured registry carry
integrity and pass the `is_supported()` early-continue before this
branch, so the only cost is keeping legacy
private-registry-without-integrity lockfiles at v1 a little longer for
single-config teams. `options` is no longer needed, so it's dropped from
the signature.

## Verification

New test in `test/cli/install/lockfile-version-2.test.ts` — *"re-saving
keeps v1 for a tarball under a writer-only scoped registry"* — runs
fully offline:

- **Writer** has `@myorg` scoped to a loopback registry and re-saves
(`--lockfile-only`) a v1 lockfile with an `@myorg/foo` tarball under
that registry and empty integrity. The re-saved lockfile must stay
`"lockfileVersion": 1`.
- **Reader** (fresh dir, no `@myorg` scope) loads the re-saved lockfile
and must **not** hit the integrity error — proving the round-trip holds
across config boundaries.

Fail-before/pass-after confirmed by building with the `src/` change
stashed: the writer stamps `"lockfileVersion": 2` and the assertion
fails; with the change applied it stays v1 and the reader loads it.
R
robobun committed
051f78227a5702889a6c43411c4954ae07452663
Parent: 9d00056
Committed by GitHub <noreply@github.com> on 5/29/2026, 5:47:23 PM