Latest Results
chore(deps): update pnpm to v11.9.0 (#1256)
This PR contains the following updates:
| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Adoption](https://docs.renovatebot.com/merge-confidence/) |
[Passing](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| [pnpm](https://pnpm.io)
([source](https://redirect.github.com/pnpm/pnpm/tree/HEAD/pnpm)) |
[`11.4.0` β
`11.9.0`](https://renovatebot.com/diffs/npm/pnpm/11.4.0/11.9.0) |

|

|

|

|
---
### Release Notes
<details>
<summary>pnpm/pnpm (pnpm)</summary>
###
[`v11.9.0`](https://redirect.github.com/pnpm/pnpm/compare/v11.8.0...v11.9.0)
[Compare
Source](https://redirect.github.com/pnpm/pnpm/compare/v11.8.0...v11.9.0)
###
[`v11.8.0`](https://redirect.github.com/pnpm/pnpm/blob/HEAD/pnpm/CHANGELOG.md#1180)
[Compare
Source](https://redirect.github.com/pnpm/pnpm/compare/v11.7.0...v11.8.0)
##### Minor Changes
- [`c112b61`](https://redirect.github.com/pnpm/pnpm/commit/c112b61):
Added a `--dry-run` option to `pnpm install`. It runs a full dependency
resolution and reports what an install would change, but writes nothing
to disk (no lockfile, no `node_modules`) and always exits with code 0.
This mirrors the preview semantics of `npm install --dry-run`
[#​7340](https://redirect.github.com/pnpm/pnpm/issues/7340).
- [`179ebc4`](https://redirect.github.com/pnpm/pnpm/commit/179ebc4):
`pnpm run --no-bail` now exits with a non-zero exit code when any of the
executed scripts fail, while still running every matched script to
completion. This makes the exit-code behavior of `--no-bail` consistent
between recursive and non-recursive runs (recursive runs already failed
at the end). Previously, a non-recursive `pnpm run --no-bail` always
exited with code 0, even when a script failed
[#​8013](https://redirect.github.com/pnpm/pnpm/issues/8013).
- [`0474a9c`](https://redirect.github.com/pnpm/pnpm/commit/0474a9c):
Added support for generating Node.js package maps at
`node_modules/.package-map.json` during isolated and hoisted installs.
Added the `node-experimental-package-map` setting to inject the
generated map into pnpm-managed Node.js script environments, and the
`node-package-map-type` setting to choose between `standard` and `loose`
package maps.
- [`dcededc`](https://redirect.github.com/pnpm/pnpm/commit/dcededc):
`pnpm sbom` now marks components reachable only through
`devDependencies` with CycloneDX `scope: "excluded"` and the
`cdx:npm:package:development` property. The `excluded` scope documents
"component usage for test and other non-runtime purposes", which matches
the semantics of a devDependency; the property is the CycloneDX
npm-taxonomy marker emitted by `@cyclonedx/cyclonedx-npm`, so both
modern (scope) and existing (property) consumers are covered. Components
reachable at runtime (including installed `optionalDependencies`) omit
`scope` and default to `required`.
- [`1495cb0`](https://redirect.github.com/pnpm/pnpm/commit/1495cb0):
Added per-package SBOM generation with `--out` and `--split` flags. Use
`--out out/%s.cdx.json` to write one SBOM per workspace package to
individual files, or `--split` for NDJSON output to stdout. When
`--filter` selects a single package, the SBOM root component now uses
that package's metadata. Workspace inter-dependencies (`workspace:`
protocol) and their transitive dependencies are included. Author,
repository, and license fall back to the root manifest when the package
doesn't define them.
- [`293921a`](https://redirect.github.com/pnpm/pnpm/commit/293921a):
feat(view): support searching project manifest upward when package name
is omitted
When running `pnpm view` without a package name, the command now
searches
upward for the nearest project manifest (`package.json`, `package.yaml`,
or `package.json5`) and uses its `name` field.
If the manifest exists but lacks a `name` field, an error is thrown.
This change also replaces the `find-up` dependency with `empathic` for
improved performance and consistency across workspace tools.
##### Patch Changes
- [`29ab905`](https://redirect.github.com/pnpm/pnpm/commit/29ab905):
Fixed `pnpm update` overriding the version range policy of a named
catalog whose name parses as a version (e.g. `catalog:express4-21`). The
`catalog:` reference carries no pinning of its own, so the prefix from
the catalog entry (such as `~`) is now preserved instead of being
widened to `^`
[#​10321](https://redirect.github.com/pnpm/pnpm/issues/10321).
- [`bee4bf4`](https://redirect.github.com/pnpm/pnpm/commit/bee4bf4):
Security: validate config dependency names and versions from the env
lockfile (`pnpm-lock.yaml`) before using them to build filesystem paths.
A committed lockfile with a traversal-shaped `configDependencies` name
(such as `../../PWNED`) or version (such as `../../../PWNED`) could
previously cause `pnpm install` to create symlinks or write package
files outside `node_modules/.pnpm-config` and the store. Names must now
be valid npm package names and versions must be exact semver versions;
the same validation is applied to optional subdependencies of config
dependencies, and to the legacy workspace-manifest format before any
lockfile is written. See
[GHSA-qrv3-253h-g69c](https://redirect.github.com/pnpm/pnpm/security/advisories/GHSA-qrv3-253h-g69c).
- [`96bdd57`](https://redirect.github.com/pnpm/pnpm/commit/96bdd57): Fix
`link:` workspace protocol switching to `file:` after `pnpm rm` is run
from inside a workspace package whose target workspace dependency has
its own dependencies, when `injectWorkspacePackages: true` is set.
Follow-up to
[#​10575](https://redirect.github.com/pnpm/pnpm/pull/10575), which
fixed the same symptom for workspace packages without dependencies.
- [`302a2f7`](https://redirect.github.com/pnpm/pnpm/commit/302a2f7): No
longer warn about using both `packageManager` and
`devEngines.packageManager` when the two fields pin the same package
manager at the same version with the same integrity hash (e.g. both
`pnpm@11.5.1+sha512.β¦`). Previously the hash was stripped from the
legacy `packageManager` field but not from `devEngines.packageManager`,
so even identical specifications looked like a mismatch
[#​12028](https://redirect.github.com/pnpm/pnpm/issues/12028).
The warning still fires on any genuine divergence, and several cases now
state the specific reason instead of a single generic message: a
different package manager, a different version, or contradictory
integrity hashes for the same version.
- [`3f0fb21`](https://redirect.github.com/pnpm/pnpm/commit/3f0fb21):
Fixed the progress line showing leftover characters from external
processes that write to the terminal between progress updates (e.g. an
SSH passphrase prompt would leave a fragment like `added 0sa':`). The
interactive reporter now redraws each frame in place, erasing to the end
of the display before reprinting, so any such remnants are cleared
[#​12350](https://redirect.github.com/pnpm/pnpm/issues/12350).
- [`564619f`](https://redirect.github.com/pnpm/pnpm/commit/564619f):
Fixed `pnpm approve-builds` reporting "no packages awaiting approval"
when a build-script dependency whose approval was revoked (e.g. after
`git stash` drops the `allowBuilds` from `pnpm-workspace.yaml`) is
re-added. The revoked packages are now correctly recorded in
`.modules.yaml` so `approve-builds` can find them.
[#​12221](https://redirect.github.com/pnpm/pnpm/issues/12221)
- [`3d1fd20`](https://redirect.github.com/pnpm/pnpm/commit/3d1fd20):
Skip the redundant "target bin directory already contains an exe called
node" warning on Windows when the existing `node.exe` already matches
the target (same hard link or identical content)
[pnpm/pnpm#12203](https://redirect.github.com/pnpm/pnpm/issues/12203).
- [`1b02b47`](https://redirect.github.com/pnpm/pnpm/commit/1b02b47): Fix
macOS Gatekeeper blocking native binaries (`.node`, `.dylib`, `.so`) by
removing the `com.apple.quarantine` extended attribute after importing
them from the store.
When pnpm imports files from its content-addressable store into
`node_modules`, macOS preserves extended attributes, including
`com.apple.quarantine`. If this xattr is present on a store blob (e.g.
it was first written under a Gatekeeper-enabled app such as a Git
client), it propagates to `node_modules`, and Gatekeeper blocks the
native binary from loading even though pnpm already verified the file's
integrity against the lockfile.
After importing a package, pnpm now strips `com.apple.quarantine` from
its native binaries, matching Homebrew's behaviour of dropping
quarantine from verified downloads. The cleanup is macOS-only, runs in a
single batched `xattr` call per package, is restricted to native
binaries (other files are untouched), and is non-fatal (it logs a
warning on unexpected errors).
Fixes
[#​11056](https://redirect.github.com/pnpm/pnpm/issues/11056)
- [`61969fb`](https://redirect.github.com/pnpm/pnpm/commit/61969fb): Fix
`pnpm install` with `optimisticRepeatInstall` incorrectly reporting
`Already up to date` when `pnpm-lock.yaml` changed but project manifests
did not. This affected workflows such as checking out or restoring only
the lockfile
[#​12100](https://redirect.github.com/pnpm/pnpm/issues/12100).
Also fixes `checkDepsStatus` to use the correct lockfile path when
`useGitBranchLockfile` is enabled, so the optimistic fast-path and
lockfile modification detection work with `pnpm-lock.<branch>.yaml`
files instead of always stat'ing `pnpm-lock.yaml`. Merge-conflict
detection now reads the resolved lockfile name as well, and with
`mergeGitBranchLockfiles` enabled every `pnpm-lock.*.yaml` is scanned
for modifications and conflicts. The git branch is now resolved by
reading `.git/HEAD` directly (no process spawn) and uses the workspace
directory rather than `process.cwd()`.
- [`5c12968`](https://redirect.github.com/pnpm/pnpm/commit/5c12968): Fix
recursive updates of transitive dependencies when the update command
mixes transitive dependency patterns with direct dependency selectors.
For example, `pnpm up -r "@​babel/core" uuid` now updates matching
transitive `@babel/core` dependencies even when `uuid` is a direct
dependency selector
[#​12103](https://redirect.github.com/pnpm/pnpm/issues/12103).
- [`9d79ba1`](https://redirect.github.com/pnpm/pnpm/commit/9d79ba1):
Register the `pnpm update --no-save` flag in the CLI help and option
parser.
- [`0474a9c`](https://redirect.github.com/pnpm/pnpm/commit/0474a9c):
Fixed `pnpm import` for Yarn v2 lockfiles when `js-yaml` v4 is
installed.
- [`9e0c375`](https://redirect.github.com/pnpm/pnpm/commit/9e0c375):
Fixed `pnpm install` repeatedly prompting to remove and reinstall
`node_modules` in a workspace package when `enableGlobalVirtualStore` is
enabled. The post-install build step recorded a per-project
`node_modules/.pnpm` virtual store directory in
`node_modules/.modules.yaml`, overwriting the global `<storeDir>/links`
value the install step had written. The next install then detected a
virtual-store mismatch (`ERR_PNPM_UNEXPECTED_VIRTUAL_STORE`). The build
step now derives the same global virtual store directory as the install
step
[#​12307](https://redirect.github.com/pnpm/pnpm/issues/12307).
- [`223d060`](https://redirect.github.com/pnpm/pnpm/commit/223d060):
Document the `--cpu`, `--os` and `--libc` flags in the output of `pnpm
install --help`. These flags were already supported but were only
documented on the website
[#​12359](https://redirect.github.com/pnpm/pnpm/issues/12359).
- [`e85aea2`](https://redirect.github.com/pnpm/pnpm/commit/e85aea2):
Avoid reading `README.md` from disk when publishing if the publish
manifest already provides a `readme` field. The README is now only read
lazily, inside `createExportableManifest`, when it is actually needed.
- [`3188ae7`](https://redirect.github.com/pnpm/pnpm/commit/3188ae7):
Fixed `pnpm peers check` to accept loose peer dependency ranges such as
`>=3.16.0 || >=4.0.0-` when the installed peer version satisfies the
range
[#​12149](https://redirect.github.com/pnpm/pnpm/issues/12149).
- [`531f2a3`](https://redirect.github.com/pnpm/pnpm/commit/531f2a3):
Fixed `pnpm update` rewriting a `workspace:` dependency that points at a
local path (e.g. `workspace:../packages/foo/dist`) into a normalized
`link:` or version-range specifier. Such specifiers are now preserved
verbatim when the workspace protocol is preserved
[#​3902](https://redirect.github.com/pnpm/pnpm/issues/3902).
- [`fe66535`](https://redirect.github.com/pnpm/pnpm/commit/fe66535):
Fixed a lockfile non-convergence bug where an incremental install kept a
duplicate transitive dependency that a fresh install would not produce.
When a package is reused from the lockfile, its child edges are taken
verbatim and bypass the preferred-versions walk, so a transitive
dependency could stay pinned to an older version even after a direct
dependency resolved to a higher version that satisfies the same range.
The resolver now refreshes such a stale pin to the higher
direct-dependency version during resolution β so the older version is
never resolved or fetched, and the incremental result converges to the
fresh one.
- [`6d35338`](https://redirect.github.com/pnpm/pnpm/commit/6d35338):
`pnpm install` detects changes inside local file dependencies again. The
optimistic repeat-install fast path only tracks manifest and lockfile
modification times, so edits inside a local dependency's directory (or a
repacked local tarball) were reported as "Already up to date". Projects
with local file dependencies (`file:` and bare local path or tarball
specifiers, declared directly or through `pnpm.overrides`) now always
run a full install, which refetches those dependencies, matching pnpm
v10 behavior
[#​11795](https://redirect.github.com/pnpm/pnpm/issues/11795).
- [`4ca9247`](https://redirect.github.com/pnpm/pnpm/commit/4ca9247):
Preserve the existing Node.js runtime version prefix when resolving
`node@runtime:<range>` to a concrete version.
- [`30c7590`](https://redirect.github.com/pnpm/pnpm/commit/30c7590):
Create shorter CAFS temporary package directories to leave room for
lifecycle scripts that create IPC socket paths under TMPDIR.
- [`13815ad`](https://redirect.github.com/pnpm/pnpm/commit/13815ad):
Reporter output (warnings, progress) for `pnpm store` and `pnpm config`
subcommands now goes to stderr instead of stdout. This fixes scripts
that capture their stdout (e.g. `PNPM_STORE=$(pnpm store path)`, `pnpm
config list --json | jq`) from getting warnings mixed into the result.
- [`1c05876`](https://redirect.github.com/pnpm/pnpm/commit/1c05876):
Avoid relinking unchanged child dependencies and remove stale child
links during warm installs.
- [`817f99d`](https://redirect.github.com/pnpm/pnpm/commit/817f99d):
Fixed lockfile churn where a package's `transitivePeerDependencies`
could be dropped (and shift between packages) when the package
participates in a dependency cycle. A cycle re-entry resolves against
truncated children, so it must not be cached as "pure"; otherwise
sibling occurrences of the same package short-circuit and lose
transitive peers depending on traversal order
[#​5108](https://redirect.github.com/pnpm/pnpm/issues/5108).
- [`eba03e0`](https://redirect.github.com/pnpm/pnpm/commit/eba03e0): Fix
`pnpm install` reporting "Already up to date" after a catalog entry in
`pnpm-workspace.yaml` was reverted to a previous version. After an
update modified a catalog, the workspace state cache stored the
pre-update catalog versions, so reverting the entry back to its original
version was not detected as an outdated state
[#​12418](https://redirect.github.com/pnpm/pnpm/issues/12418).
- [`3b54d79`](https://redirect.github.com/pnpm/pnpm/commit/3b54d79):
`pnpm update` now keeps lockfile `overrides` that resolve through a
catalog in sync with the catalog. Previously, when an override
referenced a catalog (e.g. `overrides: { foo: 'catalog:' }`) and `pnpm
update` bumped that catalog entry, the lockfile's `catalogs` advanced
while the resolved `overrides` kept the old version. The resulting
lockfile was internally inconsistent, so a later `pnpm install
--frozen-lockfile` failed with `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH`.
- [`9d0a300`](https://redirect.github.com/pnpm/pnpm/commit/9d0a300):
Fixed `pnpm version --recursive` so it honors the workspace selection.
In recursive mode the version bump now applies to the packages resolved
from the workspace filter (`selectedProjectsGraph`), matching the
behavior of `pnpm publish --recursive`, instead of always bumping every
workspace package
[#​11348](https://redirect.github.com/pnpm/pnpm/issues/11348).
###
[`v11.7.0`](https://redirect.github.com/pnpm/pnpm/blob/HEAD/pnpm/CHANGELOG.md#1170)
[Compare
Source](https://redirect.github.com/pnpm/pnpm/compare/v11.6.0...v11.7.0)
##### Minor Changes
- Added a new setting `frozenStore` (`--frozen-store`) that lets `pnpm
install` run against a package store on a read-only filesystem (e.g. a
Nix store, a read-only bind mount, an OCI layer). When enabled, pnpm
opens the store's SQLite `index.db` through the `immutable=1` URI β
bypassing the WAL/`-shm` sidecar creation that otherwise fails on a
read-only directory β and suppresses every store-write path (the
`index.db` writer and the project-registry write). Pair it with
`--offline --frozen-lockfile` against a fully-populated store. Under the
global virtual store, package directories live inside the store, so if
the store is missing the build output of a package whose lifecycle
scripts are approved (or that has a patch), pnpm fails up front with
`ERR_PNPM_FROZEN_STORE_NEEDS_BUILD` rather than crashing mid-build on a
read-only write β seed the store with those builds first. Incompatible
with `--force` and with a configured pnpr server, since both write into
the store; the side-effects cache is likewise not written under
`frozenStore`. If the store is missing its content directory, the
install fails fast with `ERR_PNPM_FROZEN_STORE_INCOMPLETE` rather than
attempting to initialize it. The read-only `immutable=1` open requires
Node.js >=22.15.0, >=23.11.0, or >=24.0.0; on older runtimes
`--frozen-store` fails with a clear
`ERR_PNPM_FROZEN_STORE_UNSUPPORTED_NODE` error. Bin-linking also
tolerates a read-only store: under the global virtual store a package's
bin source lives inside the store, so the `chmod` that makes it
executable would be refused β with `EPERM`/`EACCES`, or with `EROFS` on
a genuinely read-only filesystem. That `chmod` is redundant when the
seed already ships its bins executable with a normalized shebang, so it
is now skipped in that case, while a non-executable bin (or one still
carrying a Windows CRLF shebang) on a read-only store still errors.
- When
[`pacquet`](https://redirect.github.com/pnpm/pnpm/tree/main/pacquet)
(the Rust port of pnpm) is declared in `configDependencies`, pnpm now
delegates dependency **resolution** to it too β not just materialization
β provided the installed pacquet is new enough to support full resolving
installs (>= 0.11.7).
Previously pacquet only ran in frozen-install mode: pnpm always resolved
the dependency graph itself (writing `pnpm-lock.yaml`) and handed
pacquet a finished lockfile to fetch / import / link. With pacquet >=
0.11.7, a non-frozen `pnpm install` (default isolated `nodeLinker`,
plain install) is delegated to pacquet end-to-end in a single pass β
pacquet resolves the manifests, writes the lockfile, and materializes
`node_modules`. pnpm detects the capability from the installed pacquet's
version; older pacquet releases keep the resolve-then-materialize split,
and `add` / `update` / `remove` still resolve in pnpm (it has to mutate
the manifests first). This remains an opt-in preview of the Rust install
engine
[#​11723](https://redirect.github.com/pnpm/pnpm/issues/11723).
- Added a new opt-in `--batch` flag to `pnpm publish --recursive` that
sends all selected packages to the registry in a single `PUT
/-/pnpm/v1/publish` request instead of one request per package. The
target registry has to implement the batch publish endpoint (pnpr does);
registries that don't are reported with a clear
`ERR_PNPM_BATCH_PUBLISH_UNSUPPORTED` error. The batch is processed
all-or-nothing by pnpr: if any package in the batch fails validation,
none of the packages are published.
##### Patch Changes
- Reject path-traversal and reserved dependency aliases (such as
`../../../escape`, `.bin`, `.pnpm`, or `node_modules`) that come from a
lockfile rather than a freshly resolved manifest. A crafted lockfile
alias could otherwise be joined directly under a hoisted `node_modules`
directory, letting package files be written outside the intended install
root or overwrite pnpm-owned layout.
The fix adds two layers:
- The `nodeLinker: hoisted` graph builder now validates each alias at
the directory sink (`safeJoinModulesDir`), matching the validation pnpm
already performs when resolving aliases from manifests.
- The lockfile verification gate (`verifyLockfileResolutions`) now runs
an always-on, policy-independent check that rejects any importer or
snapshot dependency alias that is not a valid package name, failing the
install early β before any fetch or filesystem work β for every node
linker at once.
- Made shared package child resolution deterministic when the same
package is reached through multiple contexts. pnpm now chooses the
shallowest occurrence, then importer order, then parent path, instead of
letting request timing decide the child context and missing-peer report
[pnpm/pnpm#12358](https://redirect.github.com/pnpm/pnpm/issues/12358).
- Fix garbled summary line after submitting `pnpm update -i` and `pnpm
audit --fix -i`. The interactive checkbox prompt previously printed
every selected choice's full table row (label, current/target versions,
workspace, URL) joined by commas, producing a wall of text after
pressing Enter. The summary now lists only the selected package names
(or vulnerability keys) by setting an explicit `short` per choice; the
in-progress selection UI is unchanged.
- Prevent `pnpm patch-remove` from removing files outside the configured
patches directory.
- Fixed `pnpm publish` ignoring `strictSsl: false` when publishing to
registries with self-signed certificates. The `strictSSL` option is now
forwarded to `libnpmpublish` / `npm-registry-fetch` so that
`strict-ssl=false` in `.npmrc` or `strictSsl: false` in
`pnpm-workspace.yaml` is respected during publish, the same way it is
for `pnpm install`
[pnpm/pnpm#12012](https://redirect.github.com/pnpm/pnpm/issues/12012).
- Fixed `Cannot destructure property 'manifest' of
'manifestsByPath[rootDir]' as it is undefined` regression introduced in
11.6.0 when running `pnpm add <pkg>` outside a workspace on Windows.
`selectProjectByDir` was keying the resulting `ProjectsGraph` by
`opts.dir` instead of `project.rootDir`, so downstream `manifestsByPath`
lookups missed when the two paths normalized differently (typically
drive-letter casing).
[pnpm/pnpm#12379](https://redirect.github.com/pnpm/pnpm/issues/12379)
- Git dependencies that point to a subdirectory of a repository
(`repo#commit&path:/sub/dir`) keep their `path` in the lockfile again.
Since the integrity of git-hosted tarballs started being pinned in the
lockfile, any install that actually downloaded the tarball rebuilt the
lockfile resolution as `{ integrity, tarball, gitHosted }` and dropped
the `path` field, while installs served from the store kept it β so the
field disappeared seemingly at random. Without `path`, later installs
from that lockfile silently unpacked the repository root instead of the
subdirectory
[#​12304](https://redirect.github.com/pnpm/pnpm/issues/12304).
- Fixed nondeterministic lockfile output that made `pnpm dedupe --check`
fail intermittently in CI. When a locked peer provider was pinned for a
dependency that has no child dependencies of its own, the pinned
provider leaked into the shared parent scope, so siblings resolved after
it could pick up an optional peer they should not see. Which siblings
were affected depended on resolution order, which varies with network
timing.
- Sped up `pnpm install` with a frozen lockfile by running lockfile
verification (the policy revalidation gate added for
`minimumReleaseAge`/`trustPolicy` and the tarball-URL anti-tamper check)
concurrently with fetching and linking instead of blocking the whole
install on it. Dependency lifecycle scripts are still held back until
verification succeeds, so no script runs on an unverified lockfile: if
verification fails the install aborts before any dependency build, and
if linking finishes first the install waits for the verification verdict
before completing.
- User-defined `npm_config_*` environment variables are now preserved
during lifecycle script execution. Previously, all `npm_`-prefixed env
vars were stripped, which caused user-set variables like
`npm_config_platform_arch` to be lost
[pnpm/pnpm#12399](https://redirect.github.com/pnpm/pnpm/issues/12399).
- pnpm can now use different auth tokens for different package scopes,
even when those scopes use the same registry URL.
Previously, auth was selected only by registry URL. If `@org-a` and
`@org-b` both used `https://npm.pkg.github.com/`, they had to share the
same token. This caused problems for registries that issue tokens per
organization or per scope.
Configure a scope-specific token by adding the package scope after the
registry URL in the auth key:
```ini
@​org-a:registry=https://npm.pkg.github.com/
@​org-b:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:@​org-a:_authToken=${ORG_A_TOKEN}
//npm.pkg.github.com/:@​org-b:_authToken=${ORG_B_TOKEN}
//npm.pkg.github.com/:_authToken=${FALLBACK_TOKEN}
```
`pnpm login --registry=https://npm.pkg.github.com --scope=@​org-a`
writes the token to the same scope-specific auth key.
When installing or publishing `@org-a/*`, pnpm uses `ORG_A_TOKEN`. For
`@org-b/*`, pnpm uses `ORG_B_TOKEN`. Packages without a matching scope
continue to use the registry-wide fallback token.
- `pnpm setup` no longer prompts to approve build scripts for
`@pnpm/exe` when installing the standalone executable. pnpm links the
platform-specific binary itself, so the package's install scripts are
skipped during the global self-install
[#​12377](https://redirect.github.com/pnpm/pnpm/issues/12377).
- Close lockfile reads deterministically before rewriting lockfiles and
keep pacquet's virtual store directory length aligned with pnpm on
Windows.
- A `304 Not Modified` answer from the registry now renews the cached
metadata file's mtime, so the `minimumReleaseAge` freshness shortcut
keeps serving resolutions from the cache. Previously, once a cached
packument grew older than `minimumReleaseAge`, every subsequent install
re-validated it against the registry forever, because a 304 never
rewrites the file.
- Updated dependency ranges. Notably:
- `@pnpm/logger` peer dependency range moved to `^1100.0.0`.
- `msgpackr` 1.11.8 β 2.0.4 (store index files remain byte-compatible in
both directions).
- `open` ^7.4.2 β ^11.0.0, `memoize` ^10 β ^11, `cli-truncate` ^5 β ^6,
`pidtree` ^0.6 β ^1.
- `@yarnpkg/core` 4.5.0 β 4.8.0, `@rushstack/worker-pool` 0.7.7 β
0.7.18, `@cyclonedx/cyclonedx-library` 10.0.0 β 10.1.0,
`@pnpm/config.nerf-dart` ^1 β ^2, `@pnpm/log.group` 3.0.2 β 4.0.1,
`@pnpm/util.lex-comparator` ^3 β ^4.
- Updated `@zkochan/cmd-shim` to v9.0.6.
- Fixed a Windows-only hang where a failed command could take 20β46
seconds to exit. On error, pnpm enumerates descendant processes (via
`pidtree`) to terminate them, which on Windows shells out to
`wmic`/PowerShell `Get-CimInstance Win32_Process` β a lookup that is
extremely slow on some machines. The lookup is now bounded by a short
timeout so it can no longer stall the process exit.
###
[`v11.6.0`](https://redirect.github.com/pnpm/pnpm/blob/HEAD/pnpm/CHANGELOG.md#1160)
[Compare
Source](https://redirect.github.com/pnpm/pnpm/compare/v11.5.3...v11.6.0)
##### Minor Changes
- `pnpm install` completes without re-resolving when `pnpm-lock.yaml`
was deleted but `node_modules` is intact: the up-to-date check now
treats the current lockfile (`node_modules/.pnpm/lock.yaml`) β the
record of what the previous install materialized β as the wanted
lockfile, verifies the manifests still match it, restores
`pnpm-lock.yaml` from it, and reports "Already up to date". Previously
this scenario triggered a full resolution and a re-verification of every
locked package against the registry.
- [`615c669`](https://redirect.github.com/pnpm/pnpm/commit/615c669):
Added support for configuring URL-scoped registry settings through
`npm_config_//β¦` and `pnpm_config_//β¦` environment variables, for
example:
```text
npm_config_//registry.npmjs.org/:_authToken=<token>
pnpm_config_//registry.npmjs.org/:_authToken=<token>
```
This provides a file-free way to supply registry authentication. Because
the registry a value applies to is encoded in the (trusted) environment
variable name, it is host-scoped by construction and cannot be
redirected to another registry by repository-controlled config. The
environment value is treated as trusted config: it takes precedence over
a project/workspace `.npmrc` but is still overridden by command-line
options. When the same key is provided through both prefixes,
`pnpm_config_` wins.
- Raised the default network concurrency from `min(64, max(cpuCores * 3,
16))` to `min(96, max(cpuCores * 3, 64))`. Package downloads are
I/O-bound, not CPU-bound, so deriving the floor from the core count left
machines with few cores (for example 4-vCPU CI runners) downloading only
16 tarballs at a time and unable to saturate a low-latency registry. The
`networkConcurrency` setting still overrides the default.
##### Patch Changes
- Improved the warning printed when a project `.npmrc` uses an
environment variable in a registry/proxy URL or in registry credentials.
The message now explains why the setting was ignored and how to migrate
it to a trusted source β for example by moving the line to the
user-level `~/.npmrc` or running `pnpm config set "<key>" <value>` β
with a link to <https://pnpm.io/npmrc>. The `pnpm config set` example is
only suggested when the key has no `${...}` placeholder, so the snippet
is always safe to copy-paste.
- Print a "Lockfile passes supply-chain policies (verified 2h ago)"
message when lockfile verification is skipped because a cached verdict
for the same lockfile content and policy is reused. Previously the
cached short-circuit was completely silent, which made it look like the
policy gate never ran
[#​12324](https://redirect.github.com/pnpm/pnpm/issues/12324).
- Platform-specific optional dependencies are now skipped even when
their `os`/`cpu`/`libc` fields are missing from the registry metadata or
the lockfile. Some registries strip these fields from the package
metadata, which made pnpm download and install the binaries of every
platform regardless of `supportedArchitectures`. The missing platform
fields of an optional dependency are now inferred from its name (e.g.
`@nx/nx-win32-arm64-msvc` β `os: win32`, `cpu: arm64`), so
foreign-platform binaries are skipped without even downloading them
[#​11702](https://redirect.github.com/pnpm/pnpm/issues/11702).
###
[`v11.5.3`](https://redirect.github.com/pnpm/pnpm/blob/HEAD/pnpm/CHANGELOG.md#1153)
[Compare
Source](https://redirect.github.com/pnpm/pnpm/compare/v11.5.2...v11.5.3)
##### Patch Changes
- Stopped expanding environment variables in repository-controlled
registry/proxy request destinations and registry credential values from
`.npmrc`, and in workspace registry URLs from `pnpm-workspace.yaml`.
Move dynamic registry URL and token configuration to trusted user,
global, CLI, or environment config.
- Resolve package-manager bootstrap dependencies with trusted user or
CLI registry and network config, and reject package-manager env-lockfile
records that do not use registry package paths with integrity-only
resolutions before auto-switch execution.
- Avoid writing `packageManagerDependencies` to `pnpm-lock.yaml` when
package manager policy is set to `onFail: ignore` or `pmOnFail: ignore`
[#​12228](https://redirect.github.com/pnpm/pnpm/issues/12228).
- Avoid running dependency-status auto-install when the dependency
status is unavailable without a project manifest.
- Using the `$` version reference syntax in `overrides` (e.g. `"react":
"$react"`) now prints a deprecation warning. The syntax still works, but
[catalogs](https://pnpm.io/catalogs) are the recommended way to keep an
overridden version in sync with the rest of the workspace. Reference a
catalog entry with the `catalog:` protocol instead.
- Fixed `pnpm config get globalconfig` to return the global
`config.yaml` path again
[pnpm/pnpm#11962](https://redirect.github.com/pnpm/pnpm/issues/11962).
- Fixed bare `--color` so it does not consume the following CLI flag,
allowing command shorthands like `--parallel` to expand correctly and
forms like `pnpm --color with current <command>` to dispatch the inner
command instead of failing with `MISSING_WITH_CURRENT_CMD`.
- Fix `pnpm install` ignoring `enableGlobalVirtualStore` toggle by
including it in the workspace state settings check
[#​12142](https://redirect.github.com/pnpm/pnpm/issues/12142).
- Security: pnpm now verifies the npm registry signature of a
package-manager binary before spawning it, so a cloned repository cannot
make pnpm download and execute an arbitrary native binary.
This covers two paths that select an executable from
repository-controlled input:
- **pacquet install engine** β declaring `pacquet` (or `@pnpm/pacquet`)
in `configDependencies` opts in to pnpm's Rust install engine. pnpm now
verifies that the installed `pacquet` shim and the host's
`@pacquet/<platform>-<arch>` binary carry a valid npm registry signature
for their exact `name@version`, and refuses to run pacquet (failing the
command) if the signature does not verify or cannot be checked. The only
graceful fallback to pnpm's own engine is when pacquet has no binary for
the current platform.
- **automatic version switch / `self-update`** β the `packageManager` /
`devEngines.packageManager` field makes pnpm download and run a specific
pnpm version. pnpm now verifies the registry signature of `pnpm`,
`@pnpm/exe`, and the host platform binary before installing/spawning
them, and refuses to run an engine whose signature does not match a
published, signed release. The check runs only on an actual download
(store cache miss), so it does not add a network round trip to every
command.
In both cases the signature is verified over the *installed* integrity,
against npm's public signing keys that ship embedded in the pnpm CLI
(like corepack), so bytes substituted via a tampered lockfile or a
repository-controlled registry fail verification β and a registry the
user did not vouch for cannot supply its own signing keys. The signed
packument is fetched from the configured registry, so an npm mirror
works transparently. Verification fails closed: if it cannot be
completed (for example, the registry is unreachable), the command fails
rather than running an unverified binary. The embedded keys are kept
current by a release-time check against npm's signing-keys endpoint.
- Made peer-dependent deduplication deterministic. When a peer-suffixed
package variant was a subset of two or more mutually incompatible larger
variants, the variant it collapsed into depended on the order importers
were resolved in, which varies between machines. This could resolve the
same workspace to different lockfiles on different platforms and make
`pnpm dedupe --check` alternate between passing and failing.
- Reject invalid package names and versions from staged tarball
manifests before deriving filenames for `pnpm stage download`.
- Clarified in CLI help that the pnpm store is trusted shared state and
store integrity checks are corruption detection, not a tamper boundary
for untrusted store writers.
- Reject reserved manifest `bin` names (`""`, `"."`, `".."`, and scoped
forms such as `@scope/..`) when resolving a package's bins. These names
previously passed the bin-name guard and, when joined to the global bin
directory during global remove/update/add operations, could resolve to
the global bin directory itself or its parent and have it recursively
deleted.
- Require trusted package identity before package-name `allowBuilds`
entries can approve lifecycle scripts for git, git-hosted tarball,
direct tarball, and local directory artifacts. To approve one of those
artifacts explicitly, use its peer-suffix-free lockfile depPath as the
`allowBuilds` key. Lockfile verification now rejects lockfiles where a
registry-style dependency path (`name@semver`) is backed by a git,
directory, or git-hosted tarball resolution
(`ERR_PNPM_RESOLUTION_SHAPE_MISMATCH`), so the dependency path is a
reliable artifact identity by the time scripts can run.
- Security: pnpm now verifies the OpenPGP signature of a downloaded
Node.js runtime's `SHASUMS256.txt` before trusting its integrity hashes.
When a repository requests a Node.js runtime (e.g. via
`devEngines.runtime` / `useNodeVersion`), the download mirror is
repository-configurable through `node-mirror:<channel>`. The integrity
of the downloaded binary was only checked against `SHASUMS256.txt`
fetched from that same mirror β a circular check that a malicious mirror
could satisfy by serving a tampered binary together with a matching
`SHASUMS256.txt`. pnpm then executes the binary (for example to run
lifecycle scripts).
pnpm now fetches `SHASUMS256.txt.sig` and verifies the detached OpenPGP
signature against the Node.js release team's public keys, which ship
embedded in the pnpm CLI. A mirror that serves a tampered binary cannot
also produce a valid signature, so the download fails to verify. The
embedded keys are kept current by a release-time check against the
canonical `nodejs/release-keys` list.
The musl variants from the hardcoded `unofficial-builds.nodejs.org`
mirror are not repository-configurable and are signed by a different
key, so they continue to be trusted over TLS.
###
[`v11.5.2`](https://redirect.github.com/pnpm/pnpm/blob/HEAD/pnpm/CHANGELOG.md#1152)
[Compare
Source](https://redirect.github.com/pnpm/pnpm/compare/v11.5.1...v11.5.2)
##### Patch Changes
- Peer dependency resolution now reuses the peer contexts already
recorded in the lockfile when those providers are still present in the
dependency graph and still satisfy the peer ranges. This avoids
unnecessary peer-context rewrites during lockfile regeneration. Current
manifest choices remain authoritative: a newly added, explicitly
updated, or aliased direct provider, a changed nested provider, or a
locked version that no longer satisfies the range still takes
precedence.
- The lockfile verifier now checks that a registry entry pinning an
explicit `tarball` URL points at the artifact the registry's own
metadata lists for that `name@version`. Previously a tampered lockfile
could pair a trusted `name@version` with an attacker-chosen tarball URL
(and a matching integrity for those bytes), so the install fetched the
attacker's bytes. A mismatch β or any entry that can't be confirmed
against the registry β is rejected with `ERR_PNPM_TARBALL_URL_MISMATCH`.
Non-registry resolutions (`file:`, git-hosted, etc.) and registry
entries without an explicit tarball URL (the URL is reconstructed from
name+version+registry, so it is inherently bound) are unaffected;
non-standard registry tarball URLs (npm Enterprise, GitHub Packages)
still pass because they match the metadata.
- Fix `pnpm update --recursive --lockfile-only <pkg>@​<version>`
crashing with `Invalid Version` when the catalog entry for `<pkg>` is a
version range (e.g. `^21.2.10`) and `catalogMode` is `strict` or
`prefer`. The catalogβversion comparison now skips the equality check
when either side is a range rather than passing a range to
`semver.eq()`, so range specifiers fall through to the existing mismatch
handling instead of throwing
[#​11570](https://redirect.github.com/pnpm/pnpm/issues/11570).
- Avoided a Node.js crash when pnpm exits after network requests on
Windows.
- Fixed packages being materialized into the virtual store without their
root-level files (`package.json`, `LICENSE`, README, root entrypoints)
when multiple `pnpm install` processes ran against the same
store/workspace concurrently. The fast import path used to destructively
empty the shared target directory, so a concurrent importer could wipe
files another importer had already written; if the surviving files
included the `package.json` completion marker, every later install
treated the broken directory as complete and never repaired it. The fast
path now imports directly only when it can create the target directory
exclusively, and otherwise builds the package in a private temp
directory and atomically renames it into place
[#​12197](https://redirect.github.com/pnpm/pnpm/issues/12197).
- Fix dependency build scripts not running under the global virtual
store (`enableGlobalVirtualStore`).
In a workspace install, dependency build scripts are deferred to a
single `rebuild` pass (`buildProjects`). That pass resolved each
package's location from the classic
`node_modules/.pnpm/<depPathToFilename>` layout, which does not exist
under the global virtual store β so native dependencies (e.g. packages
using `node-gyp` / `prebuild-install`) were never built and failed to
load at runtime (`Cannot find module .../build/Release/*.node`).
`buildProjects` now resolves the global-virtual-store projection
directory (`<storeDir>/links/<hash>`, computed with the same graph hash
the installer uses) when `enableGlobalVirtualStore` is set, and
serializes concurrent builds of the same shared projection so parallel
workspace projects don't race on the same directory.
- Don't promote a `runtime:` dependency (such as the Node.js version
from `devEngines.runtime` or `pnpm runtime set`) into a catalog when
`catalogMode` is `strict` or `prefer`. A `runtime:` dependency
round-trips to `devEngines.runtime`, which only recognizes the
`runtime:` protocol; cataloging it rewrote the manifest entry to
`catalog:`, which broke that round-trip, stranded it in
`devDependencies`, and left `devEngines.runtime` untouched.
- Skip lockfile `minimumReleaseAge`/`trustPolicy` verification for
non-registry tarball protocols (for example `file:`), so local tarball
dependencies are not incorrectly checked against npm registry metadata.
###
[`v11.5.1`](https://redirect.github.com/pnpm/pnpm/blob/HEAD/pnpm/CHANGELOG.md#1151)
[Compare
Source](https://redirect.github.com/pnpm/pnpm/compare/v11.5.0...v11.5.1)
##### Patch Changes
- Improve `pnpm audit` performance by pruning non-vulnerable lockfile
subtrees and stopping path enumeration once vulnerable findings reach
the path cap.
- Avoid crashing when the workspace state cache is partially written or
malformed.
- Set `npm_config_user_agent` for root lifecycle scripts during headless
installs.
- Preserve the `integrity` field of a remote (non-registry) tarball
dependency when its lockfile entry is rebuilt. Re-resolving such a
dependency without re-fetching it (for example via `pnpm update`, or
when another dependency changes) produced a resolution with no integrity
β URL/tarball resolvers only learn the integrity after the tarball is
downloaded β so the previously recorded integrity was dropped, making
later installs fail with `ERR_PNPM_MISSING_TARBALL_INTEGRITY`
[#​12067](https://redirect.github.com/pnpm/pnpm/issues/12067).
- Normalize a string `repository` field into the `{ type, url }` object
form when creating the publish manifest, matching npm's behavior. Some
registries (e.g. Gitea/Codeberg) reject a string `repository` with a 500
Internal Server Error during `pnpm publish`
[#​12099](https://redirect.github.com/pnpm/pnpm/issues/12099).
- Preserve compatible optional peer versions already present in the
lockfile when resolving dependencies.
- Fixed inconsistent resolution of a peer dependency that is shared
through a diamond. When a package peer-depends on both another package
and one of that package's own peer dependencies (for example
`@typescript-eslint/eslint-plugin` peer-depends on both
`@typescript-eslint/parser` and `typescript`, and
`@typescript-eslint/parser` peer-depends on `typescript`), pnpm no
longer reuses a hoisted instance of the shared peer that was resolved
against a different version
[#​12079](https://redirect.github.com/pnpm/pnpm/issues/12079).
###
[`v11.5.0`](https://redirect.github.com/pnpm/pnpm/blob/HEAD/pnpm/CHANGELOG.md#1150)
[Compare
Source](https://redirect.github.com/pnpm/pnpm/compare/v11.4.0...v11.5.0)
##### Minor Changes
- Added a new `hoistingLimits` setting for `nodeLinker: hoisted`
installs, mirroring yarn's `nmHoistingLimits`. It accepts `none` (the
default β hoist as far as possible), `workspaces` (hoist only as far as
each workspace package), or `dependencies` (hoist only up to each
workspace package's direct dependencies). Originally proposed in
[#​6468](https://redirect.github.com/pnpm/pnpm/pull/6468), closing
[#​6457](https://redirect.github.com/pnpm/pnpm/issues/6457).
- Replaced `enquirer` with `@inquirer/prompts` for all interactive
prompts. Fixes the `update -i` scrolling overflow bug where long choice
lists were clipped in the terminal
[#​6643](https://redirect.github.com/pnpm/pnpm/issues/6643).
**User-facing changes:**
- `pnpm update -i` / `pnpm update -i --latest`: Scrolling now works
correctly when many packages are available; the new library uses
visual-line-aware pagination via `usePagination`
- `pnpm audit --fix -i`: Same scrolling fix for vulnerability selection
- `pnpm approve-builds`: Interactive build approval prompts updated
- `pnpm patch`: Version selection and "apply to all" prompts updated
- `pnpm patch-remove`: Patch removal selection updated
- `pnpm publish`: Branch confirmation prompt updated
- `pnpm login`: Credential prompts updated
- `pnpm run` / `pnpm exec` (with `verifyDepsBeforeRun=prompt`):
Confirmation prompt updated
Vim-style `j`/`k` keys still work for up/down navigation in all
interactive prompts.
**Internal:** The `OtpEnquirer` and `LoginEnquirer` DI interfaces
changed from `{ prompt }` to `{ input }` / `{ input, password }`
respectively. Plugins or custom builds that inject their own enquirer
mock will need to update.
- Staged publishes are now recognized in the trust scale. When a package
version's registry metadata carries an `approver` field, it is treated
as the strongest trust evidence (ranked above trusted publishers and
provenance attestations), since staged publishes require 2FA publish
approvals. This prevents false-positive trust downgrade errors when
moving from a staged publish to a lower trust level
[#​11887](https://redirect.github.com/pnpm/pnpm/issues/11887).
##### Patch Changes
- Fix pnpm hanging during peer resolution when an aliased install pulls
in transitive packages with mutual peer cycles at different depths in
the dependency tree (for example, `pnpm i nuxt@npm:nuxt-nightly@5x`).
Cycles whose members hit the `findHit` cache instead of running their
own `calculateDepPath` are now short-circuited by sibling resolutions at
the level where the cycle is detected, so the cached path promises no
longer deadlock.
[#​11999](https://redirect.github.com/pnpm/pnpm/issues/11999).
- Fix `pnpm dist-tag add` and `pnpm dist-tag rm` against npmjs.org
failing without `--otp` with `[ERR_PNPM_UNAUTHORIZED] You must be logged
in to set dist-tag β¦ "You must provide a one-time pass. Upgrade your
client to npm@latest in order to use 2FA."`. pnpm now sends
`npm-auth-type: web` on dist-tag writes and surfaces the resulting OTP
challenge through the existing browser-based 2FA flow (the same
`withOtpHandling` helper used by `pnpm publish`), so the browser opens,
the user authenticates, and the dist-tag is set on retry. `--otp=<code>`
continues to work via the classic flow.
- Fix `minimumReleaseAgeExclude` handling in npm resolution fast paths
so excluded packages do not get pinned to stale versions. Excludes are
honored consistently during `publishedBy` metadata selection and
cache-mtime shortcuts.
- Fix the `integrity` field being dropped from the lockfile entry of a
remote (non-registry) https-tarball dependency when an unrelated package
is installed afterwards. URL/tarball resolvers do not return an
integrity (it is only known after the tarball is downloaded), so when
such a dependency was reused from the lockfile without being re-fetched,
its integrity was lost. It is now carried over from the existing
resolution. With pnpm's lockfile-integrity hardening, the missing
integrity made subsequent `--frozen-lockfile` installs fail with
`ERR_PNPM_MISSING_TARBALL_INTEGRITY`.
[#​12001](https://redirect.github.com/pnpm/pnpm/issues/12001).
- Skip dependency re-resolution when `pnpm-lock.yaml` is missing but
`node_modules/.pnpm/lock.yaml` exists and still satisfies the manifest.
`pnpm install` now reuses the materialized snapshot to regenerate
`pnpm-lock.yaml` instead of walking the registry to rebuild it from
scratch, turning the cache+node\_modules variation into a near-no-op for
users who deleted the lockfile but kept the install
[#​11993](https://redirect.github.com/pnpm/pnpm/issues/11993).
`--frozen-lockfile` still refuses to proceed when `pnpm-lock.yaml` is
absent β the regenerated lockfile must be committed, so failing loudly
is the correct behavior for CI.
</details>
---
### Configuration
π
**Schedule**: (in timezone Asia/Shanghai)
- Branch creation
- "before 10am on the first day of the month"
- Automerge
- At any time (no schedule defined)
π¦ **Automerge**: Enabled.
β» **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.
π **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/oxc-project/oxc-resolver).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDIuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI0Mi4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> perf: type-erase the filesystem into a non-generic ResolverImpl core (#1255)
## What
Refactor `ResolverGeneric<Fs>` into a thin generic shell over a new
non-generic `ResolverImpl` that type-erases the filesystem to `Arc<dyn
FileSystem>`. The heavy resolution algorithm now lives on `ResolverImpl`
and is reached through `ResolverGeneric<Fs>`'s `Deref`, so the public
API is unchanged.
## Why
`ResolverGeneric<Fs>` monomorphizes the entire resolution algorithm once
per `Fs` type **and** once per downstream crate β cross-crate generic
instantiations aren't shared in optimized builds, and fat-LTO doesn't
merge them. In a real consumer (rolldown) the heavy methods
(`load_node_modules`, `package_target_resolve`, `load_tsconfig`,
`load_as_file`, ...) were each emitted **8Γ**. Making the core
non-generic emits it **once**.
## Measured impact (rolldown, fat-LTO release)
- Every heavy resolver method: **8 copies β 1**
- `oxc_resolver` code in the binary: **728 KiB β 282 KiB (β61%)**
- Whole-binary `__text`: **β650 KiB** (downstream generic wrappers
de-duplicate too)
- All existing tests pass; no public API change.
## Perf
Resolution is I/O-bound and the metadata cache absorbs most FS calls, so
the `dyn` dispatch is perf-neutral. An A/B on `benches/resolver.rs`
(static `ResolverGeneric<FileSystemOs>` vs an `Arc<dyn
FileSystem>`-backed resolver) showed **β€0.6%** on the in-memory
benchmark β the worst case, where there are no syscalls to amortize the
vtable against β and **no measurable change** on real-disk resolution.
## Notes
- `FileSystem::new` is gated `where Self: Sized` so the trait is
object-safe; trait objects never call `new`.
- The generic constructors gain `Fs: 'static` (required to coerce into
`Arc<dyn FileSystem>`; satisfied by every real FS type).
- `ResolverImpl` is currently `pub` and reached through `Deref`. If
you'd rather keep it private, the shell can carry explicit forwarding
methods instead β easy to switch.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Latest Branches
+7%
renovate/pnpm-package-manager +1%
release-plz-2026-06-18T23-48-37Z -1%
perf/non-generic-resolver-core Β© 2026 CodSpeed Technology