chore(deps): update pnpm to v11.24.0
Metadata
Current evaluation
Merged automated dependency update upgrading pnpm from v11.22.0 to v11.24.0. Approved by one reviewer, passed all CI checks, and merged with a single line change to the lockfile.
Suggested action: —
No scores available.
Issue body
This PR contains the following updates:
| Package | Change | [Age](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/pnpm11/pnpm)) | [`11.22.0` → `11.24.0`](https://renovatebot.com/diffs/npm/pnpm/11.22.0/11.24.0) |  |  |
---
### Release Notes
<details>
<summary>pnpm/pnpm (pnpm)</summary>
### [`v11.24.0`](https://redirect.github.com/pnpm/pnpm/releases/tag/v11.24.0): pnpm 11.24
[Compare Source](https://redirect.github.com/pnpm/pnpm/compare/v11.23.0...v11.24.0)
##### Minor Changes
- Added global build approvals [pnpm/pnpm#14101](https://redirect.github.com/pnpm/pnpm/issues/14101).
##### Patch Changes
- Fixed pnpm v11 incorrectly reporting `confirmModulesPurge` as unrecognized when set in `pnpm-workspace.yaml`. The Rust CLI now identifies the unsupported option as a pnpm v11 setting instead of suggesting an unrelated setting.
- `pnpm install --frozen-lockfile` no longer fails with `ERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE` when the pinned pnpm version recorded in `pnpm-lock.yaml` has to be re-resolved before it can be installed. It runs the pnpm version the lockfile pins and leaves the lockfile unchanged [#​14124](https://redirect.github.com/pnpm/pnpm/issues/14124).
- Under `nodeLinker: hoisted`, peer-resolution variants of an injected directory dependency (a `file:` snapshot) are materialized as separate copies again instead of collapsing onto the first-seen variant. Each copy keeps its own peer-resolved dependency set, so a project pinning one peer version no longer resolves another project's variant — Bit root components with conflicting peers across injected copies rely on this.
- Fixed `pnpm install --merge-git-branch-lockfiles --frozen-lockfile` failing with `ERR_PNPM_OUTDATED_LOCKFILE` when a branch lockfile predates the removal of a dependency, or its move to another dependency group [#​13966](https://redirect.github.com/pnpm/pnpm/issues/13966). A dependency that no project declares anymore is no longer reinstated by the merge, and the packages it was the only path to are dropped with it.
- Batch workspace publishing accepts a shared scope-specific credential, rejects mismatched credentials for a registry before publishing, and runs the `publish` and `postpublish` scripts after each completed registry group [pnpm/pnpm#14101](https://redirect.github.com/pnpm/pnpm/issues/14101).
- The Rust CLI now honors five settings it recognized but ignored: `updateNotifier`, `legacyDirFiltering`, `initAuthorName` / `initAuthorEmail` / `initAuthorUrl`, `initLicense`, and `initVersion`. `pnpm install` and `pnpm add` check once a day for a newer pnpm and print how to get it (turn it off with `updateNotifier: false`); a `{<dir>}` filter selector can go back to matching the subtree below the directory with `legacyDirFiltering: true`; and `pnpm init` writes the configured author, license, and version into the `package.json` it scaffolds. `PNPM_CONFIG_INIT_VERSION` is now read as well.
`maxsockets`, npm's spelling of `maxSockets`, is no longer ignored: both spellings are read from `pnpm-workspace.yaml`, the global config file, the environment, and the command line, in that increasing order of precedence — a value passed on the command line now wins even when the two sides spelled the setting differently.
A `lastUpdateCheck` timestamp dated in the future — after a clock change, a restored snapshot, or a hand-edited state file — no longer silences the update check until that time comes around.
`legacyDirFiltering` no longer reaches the workspace-root selectors pnpm generates for itself: the `!{<workspace-root>}` exclusion a recursive `run` / `exec` / `add` / `test` appends, and the `{<workspace-root>}` inclusion `--workspace-root` appends. Read as subtree matches they named every project below the root, so a recursive command under the setting selected nothing at all, and `--workspace-root` pulled in every project below the root instead of the root alone [#​14101](https://redirect.github.com/pnpm/pnpm/issues/14101).
- `pnpm install --frozen-lockfile` no longer fails when `pnpm-lock.yaml` records the pinned pnpm version alongside an engine package the running pnpm does not install it from. An entry pinning another version is still refused, and a plain install rewrites the block [#​14124](https://redirect.github.com/pnpm/pnpm/issues/14124).
### [`v11.23.0`](https://redirect.github.com/pnpm/pnpm/releases/tag/v11.23.0): pnpm 11.23
[Compare Source](https://redirect.github.com/pnpm/pnpm/compare/v11.22.0...v11.23.0)
#### Minor Changes
- `pnpm config get` and `pnpm config list` now show the settings pnpm acts on under their documented names:
- `registries` shows the registries pnpm resolves from, merged across every source (`.npmrc`, `pnpm-workspace.yaml`, the global config, CLI flags), in the shape the setting is written in: keyed by registry URL, with the default registry declared as the bare `@` scope. Built-in routes are included — the `@jsr` scope and the `npmjs` and `gh` prefixes — unless pointed elsewhere. Previously `pnpm config get registries` printed `undefined`.
- `update` and `audit` show the effective sections, whichever spelling set them. The deprecated internal spellings (`updateConfig`, `auditConfig`, `auditLevel`) are no longer listed.
- `catalogs` shows the complete resolved catalog set — the singular `catalog` block is its `default` entry — whichever spelling declared it.
- The `registry` and `@scope:registry` entries show the merged routes rather than raw `.npmrc` values, so they always agree with the `registries` view.
- Settings that no supported pnpm version recognizes get their own warning. A key in the global config file that this version of pnpm does not read is no longer reported with advice to move it to a project-level `pnpm-workspace.yaml` (where it would be ignored too); the warning now says the setting is not recognized by this version of pnpm, names the pnpm version that does read it when there is one (for example, `globalShims` is a pnpm v12 setting), and suggests the closest real setting name when the key looks like a typo. Unrecognized and non-camelCase keys in a project's `pnpm-workspace.yaml`, previously ignored silently, are now reported the same way. `pnpm config get <key>` and `pnpm get <key>` no longer print config-load warnings, so a script capturing the value gets the value alone.
- The `importPackage` pnpmfile hook is deprecated. pnpm now prints a warning when a pnpmfile defines it, and the hook will be removed in the next major version. It also opts the installation out of the parallel package importer, making installation slower. If you rely on this hook, comment on [#​14101](https://redirect.github.com/pnpm/pnpm/issues/14101).
- `node_modules/.modules.yaml` no longer records the registries an install resolved from, and the recorded copy is dropped from the file on the first install that rewrites it.
It dated from the lockfile format that spelled a dependency's path relative to its registry, where reading an installed tree meant knowing the registries it was installed with. Dependency paths have not carried a registry for several major versions, and the recorded copy outlived its use: `pnpm list`, `pnpm why`, and single-project installs preferred it over the project's own configuration, so a project whose registry had changed since its last install was still read through the old one.
They now use the configured registries, like every other command already did.
- When `enableGlobalVirtualStore` is on, every process pnpm spawns for the project (`pnpm run`, `pnpm exec`, lifecycle scripts) now receives a `NODE_PATH` pointing at the project's hoisted `node_modules`, plus a `NODE_OPTIONS` `--import` flag that registers a resolve hook restoring `NODE_PATH` lookups for ESM imports. Dependencies that import undeclared ("phantom") packages keep resolving under the global virtual store — for both CommonJS and ESM — without installing the `@pnpm/plugin-esm-node-path` config dependency [pnpm/pnpm#9618](https://redirect.github.com/pnpm/pnpm/issues/9618). Tools run by `pnpm dlx` resolve such dependencies too: the JS CLI passes them the same environment, while the Rust CLI's dlx cache is self-contained, so its layout already exposes them.
- A registry can now declare that its abbreviated metadata carries the `time` field, so `resolutionMode: time-based` reads the full metadata document only from the registries that need it:
```yaml
resolutionMode: time-based
registries:
https://npm.internal.example/:
supportsTimeField: true
```
`registry.npmjs.org` omits `time` from abbreviated metadata, so a time-based resolution has to fall back to the much larger full document. That fallback used to be all-or-nothing: `registrySupportsTimeField` answered for every registry at once, so a project resolving from both the public registry and a Verdaccio instance either paid for full metadata everywhere or claimed a `time` field npmjs does not serve. The answer is now per registry, and `registrySupportsTimeField` remains the answer for every registry that does not declare one.
The declaration is also sent to a pnpr server, which applies it to the resolution it runs on the client's behalf.
- A pnpr resolve request now carries the client's registries the way the `registries` setting declares them — keyed by URL, with the scopes routed to each, the bare-specifier prefix each answers to, and each one's `serverType` — in place of the prefix map it used to send.
The server routes them through the same inversion the config reader runs, so a pnpr-served install resolves a scoped dependency from the registry that scope is routed to, which it previously could not: only the default registry and the prefix-addressed ones reached the server. A declared `serverType` reaches it too, so the tarball URLs pnpr omits from the lockfile match the ones the client reconstructs.
Built-in scope routes the project has not pointed elsewhere are not declared, so a pnpr server's allowlist is not asked about `npm.jsr.io` on requests that resolve no JSR package.
A registry a request only declares is no longer refused up front for being off the server's allowlist — a client describes its whole configuration, including scopes a given resolve never reaches, so a stray `@scope:registry` in a developer's `~/.npmrc` no longer fails every install against a pnpr server that does not serve it. The boundary moves to the fetch itself: an origin the resolve does reach is refused before the request leaves the server, with the same message.
This changes the resolve and verify-lockfile request bodies. A pnpr server and its clients have to be on matching versions; the protocol is still experimental and unversioned.
- The `registries` setting now declares a registry once, keyed by its URL, with everything about that registry in the entry: how it lays out tarball URLs, the scopes routed to it, and the bare-specifier prefix it answers to.
```yaml
registries:
https://artifactory.example.com/artifactory/api/npm/npm-virtual/:
serverType: artifactory
scopes: ['@acme', '@acme-internal']
prefix: work
```
- **`serverType`** tells pnpm how the registry lays out its tarball URLs, which decides whether a URL can be omitted from `pnpm-lock.yaml`:
- **undeclared** (the default) — strict. Only the exact canonical URL is treated as reconstructible.
- **`npm`** — the registry behaves like `registry.npmjs.org`, which also serves a scoped package from its percent-encoded path. Declare this for a faithful mirror or caching proxy of the public registry so its tarball URLs can be omitted too.
- **`artifactory`** — JFrog Artifactory repeats the scope in a scoped package's tarball filename (`@acme/widget/-/@acme/widget-1.0.0.tgz`) where the npm registry strips it (`@acme/widget/-/widget-1.0.0.tgz`). Declaring it lets pnpm rebuild that URL, so it is omitted from `pnpm-lock.yaml` instead of being written out for every scoped package [pnpm/get-npm-tarball-url#16](https://redirect.github.com/pnpm/get-npm-tarball-url/issues/16).
- **`scopes`** lists the `@`-prefixed scopes that resolve from this registry. A bare `'@'` is the scope-less default registry, the one the `registry` setting names.
- **`prefix`** is the alias a dependency addresses this registry by, as in `"foo": "work:^1.0.0"`.
The layout is never inferred from the registry URL, so nothing changes unless you declare it; `registry.npmjs.org` continues to behave as `npm` without being declared. Because the lockfile depends on `serverType`, it is read from `pnpm-workspace.yaml` only — a `serverType` in the global `config.yaml` is ignored, so one developer's machine cannot shape a lockfile their collaborators read back with a different layout. Credentials are rejected in this setting, in a key as well as in a field, and still belong in `.npmrc`. An entry that routes nothing to itself and matches no configured registry is reported as a warning rather than silently ignored.
##### Migrating
The older `registries` shape, a map of `<scope>: <url>` strings, still works and needs no change:
```yaml
registries:
'@acme': https://npm.acme.example/
```
`namedRegistries` is deprecated in favor of the `prefix` field, and is still read for prefixes `registries` does not declare.
`toLockfileResolution` and `isCanonicalRegistryTarballUrl` now take their registry and layout as an options object rather than positional arguments, so `@pnpm/lockfile.utils` and `@pnpm/resolving.tarball-url` get a major bump.
- An install that had to re-hash store files to verify them now reports it. If that cost more than a second, it says how long — `The integrity of N files was checked in 2.5s.` — and if it was quick but covered more than a thousand files, it names the cause instead: their timestamps changed since the store recorded them, which a backup tool, an antivirus scan or a copied store can do.
- Added `virtualStoreType`, which names where the virtual store lives — one store per machine, or one per project:
```yaml
virtualStoreType: global # or: project
```
It is the canonical spelling of `enableGlobalVirtualStore`, which keeps working. When a project sets both, `virtualStoreType` wins. It can also be set through `PNPM_CONFIG_VIRTUAL_STORE_TYPE` and read back with `pnpm config get virtualStoreType`. The default is unchanged — `project`, so the shared store stays opt-in.
The setting is independent of `nodeLinker`. `isolated` and `pnp` both work with either store type, and `hoisted` writes no virtual store at all, so it is unaffected.
#### Patch Changes
- `pnpm add --allow-build` now adds to the `allowBuilds` entries already in `pnpm-workspace.yaml` instead of replacing them [#​13872](https://redirect.github.com/pnpm/pnpm/issues/13872).
- Kept pending build approvals available after removing an unrelated dependency.
- `pnpm approve-builds` now removes `onlyBuiltDependencies`, `onlyBuiltDependenciesFile`, `neverBuiltDependencies`, and `ignoredBuiltDependencies` from `pnpm-workspace.yaml` when it writes `allowBuilds`. Those settings were replaced by `allowBuilds` in pnpm 11 and silently ignored since, so a workspace migrated from pnpm 10 kept them around looking active.
- `pnpm audit` no longer reports a patched version that was never published or is deprecated. The inferred patched range (e.g. `>=4.17.24` from `<=4.17.23`) is now checked against the registry packument, and the report is corrected to the lowest non-deprecated published version that satisfies it (e.g. `>=4.18.1` when `4.17.24` does not exist and `4.18.0` is deprecated). When no published version satisfies the range, the report shows `Patched versions: None`. This also prevents `pnpm audit --fix` from adding overrides or `minimumReleaseAgeExclude` entries for patches that do not exist [#​13824](https://redirect.github.com/pnpm/pnpm/issues/13824).
`pnpm audit --fix` and `pnpm audit --fix update` no longer add a `minimumReleaseAgeExclude` entry when the registry packument shows that the minimum patched version was never published. Previously such entries were written for versions that do not exist, which would have let a later publish of that version bypass the `minimumReleaseAge` gate [#​11563](https://redirect.github.com/pnpm/pnpm/issues/11563).
The `--json` output of `pnpm audit` now returns `patched_versions: null` for advisories whose inferred patch is not available (never published, skipped, yanked, or deprecated), making it easier for tooling to distinguish "no fix available" from "fix available at version X".
- Fixed `pnpm patch-commit` in project and edit paths containing non-ASCII characters.
- The package and bump pickers of `pnpm change` now size their page from the terminal height instead of always showing 7 rows. They fall back to 7 rows when the terminal height is unknown [`pnpm/pnpm#13815`](https://redirect.github.com/pnpm/pnpm/issues/13815).
- Canceling a `pnpm change` prompt with Ctrl-c no longer prints a stack trace. It reports `Change canceled` and exits with a success status, like the other interactive commands [#​13814](https://redirect.github.com/pnpm/pnpm/issues/13814).
- Re-fetch full registry metadata when `minimumReleaseAge` is enabled and an abbreviated packument's `time` map omits timestamps for some versions. This prevents mature versions from being filtered out and resolution from falling back to the lowest matching version [pnpm/pnpm#13741](https://redirect.github.com/pnpm/pnpm/issues/13741).
- A config dependency carrying an inline integrity (the `<version>+<integrity>` form, or the object form without a `tarball`) now takes its tarball URL from the registry's packument instead of deriving it from the registry URL, so migrating one costs an extra metadata request. On a registry that serves tarballs from a path pnpm cannot derive, GitLab's group endpoint for one, installing such a config dependency failed with a 404 while the same package installed fine as a regular dependency [#​13765](https://redirect.github.com/pnpm/pnpm/issues/13765).
- Fixed `PNPM_CONFIG_NODE_VERSION` being ignored when setting the Node.js version used for compatibility checks.
- A custom fetcher can no longer replace the archive integrity that `pnpm-lock.yaml` pins: the locked value is restored after a `canFetch` or `fetch` hook rewrites the resolution, and delegating a locked archive to a directory or git source now fails instead of installing unverified content.
The Rust CLI now also loads the pnpmfiles named by the `pnpmfile` setting (a single path or an ordered list), and hands custom fetchers native `localTarball` and `remoteTarball` callbacks — including on a fresh install that has to compute a missing tarball integrity, which is then reused by later offline installs. File maps a fetcher returns are accepted only when they match what those native callbacks extracted.
- Fixed an issue where running `pnpm dedupe --check` in projects with `nodeLinker: hoisted` would cause dependencies to be moved out of `node_modules` into `node_modules/.ignored`.
- `pnpm deploy --prod` and `pnpm deploy --no-optional` no longer list the excluded dependency groups in the deployed `package.json` and `pnpm-lock.yaml`. The deployed lockfile referenced packages that the deploy left out of its graph, so installing in the deploy directory afterwards created dangling symlinks [#​13623](https://redirect.github.com/pnpm/pnpm/issues/13623).
- Don't treat files like `license16.json` as a package license when deciding if the workspace LICENSE file should be included in the packed package.
- `pnpm exec --recursive --no-reporter-hide-prefix` no longer prints a blank prefixed line after each chunk of a command's output, and no longer splits a line in two when it straddles a chunk boundary.
- Fixed `404` errors when installing from a registry that serves scoped packages only from a percent-encoded path, such as GitHub Enterprise Server. Outside `registry.npmjs.org`, a tarball URL that encodes the scope separator as `%2f` or `%2F` is no longer mistaken for one that pnpm can rebuild from the package name, version, and registry, so it is kept in `pnpm-lock.yaml` and requested verbatim on the next install [#​13534](https://redirect.github.com/pnpm/pnpm/issues/13534).
- Fixed `trustPolicyExclude` and `minimumReleaseAgeExclude` being ignored when set to a single string instead of a list. The value was read one character at a time, so the exclusion never matched the package it named — and a `*` anywhere in it matched every package, silently switching the policy off.
- `pnpm init` now pins the exact pnpm version instead of a `^` range, and records it in the `packageManager` field alongside `devEngines.packageManager`. Corepack reads only `packageManager` and accepts nothing but an exact version, so it rejected the generated `package.json` with "expected a semver version" [pnpm/pnpm#13969](https://redirect.github.com/pnpm/pnpm/issues/13969). A package created inside an existing workspace is still left unpinned — it follows the pin at the workspace root — and `--no-init-package-manager` still scaffolds a manifest without any pin. In pnpm 12, `pnpm init` also honors `initType` and its `--init-type` flag, so the manifest it writes is the same one pnpm 11 writes.
- Fixed an issue where package overrides were written into the metadata cache, causing removed overrides to keep applying on subsequent installs [pnpm/pnpm#13918](https://redirect.github.com/pnpm/pnpm/issues/13918).
- On Windows, upgrading pnpm no longer leaves a stale `pnpm.ps1` behind. PowerShell resolves `pnpm.ps1` ahead of `pnpm.cmd`, so a shim written by an older installation kept running the previous version. Linking the pnpm CLI's bins now deletes it [#​13919](https://redirect.github.com/pnpm/pnpm/issues/13919).
- Fixed an inconsistency where `minimumReleaseAgeExclude` (and `trustPolicyExclude`) wildcard/bare-name rules behaved differently in the evaluator and normalizer. A bare rule now consistently evaluates as matching every version, preventing unexpected behavior and silent widening of version policy exemptions when pnpm rewrites the workspace manifest [pnpm/pnpm#13725](https://redirect.github.com/pnpm/pnpm/issues/13725).
- A frozen install no longer rewrites the `packageManagerDependencies` block of `pnpm-lock.yaml`. When the pnpm version pinned by `devEngines.packageManager` (or by `packageManager`) is missing from the lockfile or no longer matches it, `--frozen-lockfile` now fails with `ERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE` instead of resolving the version and saving it, so a manifest whose pin was bumped without regenerating the lockfile can no longer pass CI [#​14009](https://redirect.github.com/pnpm/pnpm/issues/14009).
- A git dependency installed over HTTPS from a hosted repository now keeps its branch, tag, or version range in the specifier recorded in `package.json`. It was written back without one, so the next `pnpm update` moved the dependency to the repository's default branch [#​13999](https://redirect.github.com/pnpm/pnpm/issues/13999).
- Fixed `pnpm update --global --latest` failing with a 404 error when a globally installed package was not added from the registry by name. Packages installed from a local path (`link:`/`file:`), a git repository, a tarball URL, an `npm:` alias, or a named registry now keep their spec during a global update instead of being looked up by name in the default registry. See [#​12854](https://redirect.github.com/pnpm/pnpm/issues/12854).
- Fix recursive `pnpm update <name>@<version>` so an exact pinned update stays scoped to the requested version line: copies of the same package on another major line — or, for a `0.x` request, another minor line — keep their locked resolution instead of being re-resolved along with the target.
- Under `nodeLinker: hoisted`, a dependency declared against a peer-resolution variant of a package version is no longer dropped from the installed layout. All variants of a version share one hoisted copy, and edges pointing at any of them now resolve to it, so the depending project keeps the package in its `.package-map.json` and the depending package keeps it in its `node_modules/.bin`.
- Fixed `pnpm install --merge-git-branch-lockfiles` deleting the per-branch lockfiles when the `lockfile` setting is `false`. Such an install never reads them, so it has nothing to merge them into and now leaves them alone.
- Fixed `pnpm install` sometimes not exiting after printing `Done in Xs` [#​12297](https://redirect.github.com/pnpm/pnpm/issues/12297).
- Fixed pnpm failing to read `.modules.yaml` files containing long dependency paths [#​13875](https://redirect.github.com/pnpm/pnpm/issues/13875). The manifest is now parsed as JSON (the format pnpm writes it in), falling back to the YAML parser only for manifests written by old pnpm versions.
- With `preferSymlinkedExecutables`, `NODE_PATH` again points at the virtual store of the workspace root when pnpm is run from inside a workspace package, so scripts can resolve dependencies that live only in the hoisted store [#​13912](https://redirect.github.com/pnpm/pnpm/issues/13912).
- Reduced registry metadata requests during dependency resolution by reusing cached metadata when lockfile preferences prove that no uncached version can win [pnpm/pnpm#13976](https://redirect.github.com/pnpm/pnpm/issues/13976).
- `pnpm pkg get` and `pnpm pkg set` now accept hyphens inside a dot-notation property path, so `pnpm pkg get dependencies.some-package-name` reads the key instead of failing with `ERR_PNPM_UNEXPECTED_TOKEN_IN_PROPERTY_PATH`. The bracketed and quoted forms already worked and are unchanged.
- A resolve request now carries the client's `resolutionMode`, so an install delegated to a pnpr server picks versions the way the client would. `time-based` and `lowest-direct` reached the server as nothing at all, leaving it on its `highest` default: the returned lockfile pinned the highest satisfying version of every dependency, and the setting appeared to be ignored.
This adds a field to the resolve request body. A server older than its client ignores it and keeps resolving `highest`; the protocol is still experimental and unversioned.
- Fixed `pnpm` installs using pnpr to honor the client's `autoInstallPeers`, `dedupePeers`, and `excludeLinksFromLockfile` settings [pnpm/pnpm#13389](https://redirect.github.com/pnpm/pnpm/issues/13389).
- `pnpm remove` now prunes undecided entries (`"set this to true or false"`) from `allowBuilds` in `pnpm-workspace.yaml` when `sharedWorkspaceLockfile: true` and the corresponding packages are removed [pnpm/pnpm#13892](https://redirect.github.com/pnpm/pnpm/issues/13892).
- Fixed workspace discovery for `pnpm-workspace.yaml` files without a `packages` field so commands only consider the workspace root instead of recursively scanning nested projects [#​14047](https://redirect.github.com/pnpm/pnpm/issues/14047).
- A runtime installed through `devEngines.runtime` now matches the host when `supportedArchitectures` lists several platforms. Listing `os: [darwin, linux]` and `cpu: [x64, arm64]` used to install the runtime built for the first entry of each list, so a machine running Linux on arm64 got a macOS x64 Node.js that could not execute [#​13898](https://redirect.github.com/pnpm/pnpm/issues/13898).
- `pnpm sbom` now fails with `ERR_PNPM_SBOM_MISSING_IMPORTERS` when `pnpm-lock.yaml` has no entry for a selected project, instead of writing an SBOM that under-reports that project's dependencies. Previously this crashed with `Cannot read properties of undefined (reading 'devDependencies')`.
- `pnpm self-update` now rewrites a simple `devEngines.packageManager.version` range (`^`/`~`) to the newly installed version, keeping the operator — matching how `pnpm update` and `pnpm runtime set` rewrite ranges. Complex ranges such as `>=8.0.0` that the new version satisfies are still left unchanged [#​13935](https://redirect.github.com/pnpm/pnpm/issues/13935).
- `pnpm self-update <tag>` no longer downgrades when the dist-tag points at the pnpm version already running and that version is younger than `minimumReleaseAge`. The maturity cutoff moved the tag back to the previous mature release, so `pnpm self-update next-12` on v12.0.0-rc.4 switched to v12.0.0-rc.3.
- `pnpm set-script` now updates `package.json` instead of failing with `ERR_PNPM_NOT_IMPLEMENTED` [`pnpm/pnpm#13956`](https://redirect.github.com/pnpm/pnpm/issues/13956).
- `pnpm update` now preserves the existing range operator when updating a prerelease dependency. See [#​7002](https://redirect.github.com/pnpm/pnpm/issues/7002).
- Installs are faster in workspaces that declare inter-workspace dependencies with plain ranges (`"*"`, `"^1.2.3"`) rather than the `workspace:` protocol. With `preferWorkspacePackages` enabled, linking such a dependency no longer makes a registry request that cannot change the outcome — and workspace packages that were never published no longer cost a 404 on every install.
- Added `fetchWarnTimeoutMs` and `fetchMinSpeedKiBps` to the Rust pnpm CLI and its N-API bindings. Slow registry metadata requests and tarball downloads now emit pnpm-compatible warnings without exposing URL credentials, query parameters, fragments, or control characters [pnpm/pnpm#12042](https://redirect.github.com/pnpm/pnpm/issues/12042).
- An override change is now absorbed by the fast lockfile update even when another, unchanged override uses the `catalog:` protocol. Previously any `catalog:`-valued override forced a full re-resolution whenever the override list changed, which could move unrelated packages in the lockfile (for example after `pnpm audit --fix` added an override).
- Packed workspace package manifests now preserve dependency order, making repeated `pnpm pack` output deterministic [#​10167](https://redirect.github.com/pnpm/pnpm/issues/10167).
- `pnpm update <name>@<version>` now fails with `ERR_PNPM_UPDATE_VERSION_ON_INDIRECT_DEP` when the package is not a direct dependency of any selected project, instead of quietly updating it to whatever a fresh install would resolve. There is nowhere to record the version in that case, so the request cannot be honored, and the error points at the `overrides` entry that does pin a transitive dependency. Ranges and tags are unaffected, and a package that any selected project declares directly still takes its version as before.
- `trustPolicy: no-downgrade` no longer aborts the install with `ERR_PNPM_MISSING_TIME` on registries that serve no per-version `time` field when `minimumReleaseAgeIgnoreMissingTime` is set. The trust check reads the same publish dates the `minimumReleaseAge` check does, so it now honors the same opt-in and skips the affected package with a warning [#​12446](https://redirect.github.com/pnpm/pnpm/issues/12446).
`minimumReleaseAgeIgnoreMissingTime` no longer lets a lockfile entry the registry does not list pass the `minimumReleaseAge` check during lockfile verification. The opt-in covers a registry that cannot date its releases; a packument that does date every version it lists is saying it never published this one, which stays a hard failure.
The missing-`time` warning now names the check it is reporting on, so a package whose `minimumReleaseAge` and `trustPolicy` checks are both skipped warns about both instead of only the first.
- `pnpm update <pkg>@<version>` now updates only the selected packages and leaves unrelated dependencies unchanged. A selector that renames the package it installs — `pnpm update <alias>@npm:<pkg>@<version>` or the `jsr:` equivalent — now targets the package the alias installs rather than the alias.
- Fixed `verifyDepsBeforeRun` being ignored when set to `install`, `warn`, `error`, or `prompt` through the `PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN` environment variable or the `--config.verify-deps-before-run` flag [#​13816](https://redirect.github.com/pnpm/pnpm/issues/13816). Only the boolean values were accepted before, so a string value was silently dropped.
- `pnpm version <bump>` with `--dry-run` no longer edits `package.json` files. It now only reports the bumps it would make, and skips the working tree check, the version lifecycle scripts, the commit, and the tag [`pnpm/pnpm#13953`](https://redirect.github.com/pnpm/pnpm/issues/13953).
<!-- sponsors -->
#### Platinum Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
<img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
</picture>
</a>
</td>
</tr>
</tbody>
</table>
#### Gold Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
<img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
<img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
<img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
<img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
<img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
</picture>
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
<img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
<img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
</picture>
</a>
</td>
</tr>
</tbody>
</table>
<!-- sponsors end -->
</details>
---
### Configuration
📅 **Schedule**: (in timezone Etc/UTC)
- Branch creation
- "every weekend"
- Automerge
- "after 1 am and before 7 am"
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, 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/canonical/craft-actions).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC40OS4wIiwidXBkYXRlZEluVmVyIjoiNDQuNDkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19-->
Evaluation history
| Date | Model | Scores | Action | Summary |
|---|---|---|---|---|
| qwen3.6-35b-a3b-mtp-q6 | — | — | Merged automated dependency update upgrading pnpm from v11.22.0 to v11.24.0. Approved by one reviewer, passed all CI checks, and merged with a single line change to the lockfile. |
Update history
| Date | Change |
|---|---|
| updated | |
| created |
Related issues
| Issue | Project | State | Summary | Similarity |
|---|---|---|---|---|
| #90 chore(deps): update pnpm to v11.22.0 | craft-actions | merged | Merged automated dependency update upgrading pnpm from v11.20.0 to v11.22.0. All CI checks passed, and the change was applied to a single file with minimal diff. | |
| #109 chore(deps): update pnpm to v11.25.0 | craft-actions | merged | Merged automated dependency update upgrading pnpm from v11.24.0 to v11.25.0. The change passed all CI checks, received approval, and was successfully integrated into the main branch by Renovate bot. | |
| #84 chore(deps): update pnpm to v11.18.0 | craft-actions | merged | Merged an automated dependency update upgrading pnpm from v11.11.0 to v11.18.0. The PR passed all CI checks, received reviewer approval, and was successfully merged into the main branch. | |
| #75 chore(deps): update pnpm to v11.11.0 | craft-actions | merged | Merged automated dependency update upgrading pnpm from v11.9.0 to v11.11.0. The change passed all CI checks and received approval, successfully integrating the new package manager version. | |
| #88 chore(deps): update pnpm to v11.20.0 | craft-actions | merged | Merged an automated Renovate update upgrading pnpm from v11.18.0 to v11.20.0. All CI checks passed, and the dependency change was successfully applied to a single file. | |
| #112 chore(deps): update pnpm to v12 | craft-actions | merged | Merged automated dependency update upgrading pnpm from v11.25.0 to v12.3.4. The Renovate-generated change passed all CI checks and received approval before merging. |