fix(deps): update website non-major dependencies #123

Open
Renovate wants to merge 1 commit from renovate/website-non-major-dependencies into main
Collaborator

This PR contains the following updates:

Package Change Age Confidence
@astrojs/starlight (source) ^0.41.0^0.42.0 age confidence
pnpm (source) 11.9.011.26.0 age confidence

Release Notes

withastro/starlight (@​astrojs/starlight)

v0.42.0

Compare Source

Minor Changes
  • #​3572 292fb17 Thanks @​HiDeoo! - Distributes package as JavaScript files with dedicated type declaration files instead of TypeScript source files.

  • #​4121 2623ae6 Thanks @​delucis! - Simplifies markup for Starlight’s mobile menu toggle

    ⚠️ Potentially breaking change: If you use a theme plugin, custom styles, or component overrides targeting the MobileMenuToggle button or PageFrame components, you may need to adjust these for the new markup. The button is no longer wrapped in a <starlight-menu-button> custom element and no longer uses the aria-expanded attribute. Instead, you can use the .sl-menu-button class name to target the button and the :popover-open pseudo-class to style the menu open state specifically.

    In the following example, custom styles for the menu button are updated for the new approach:

    - starlight-menu-button button {
    + .sl-menu-button {
      color: var(--sl-color-text);
    }
    
    - starlight-menu-button[aria-expanded='true'] button {
    + .sl-menu-button:has(~ :popover-open) {
      color: var(--sl-color-text-accent-high);
    }
    

    See MobileMenuToggle.astro and PageFrame.astro on GitHub for the full source code of the updated components.

  • #​3572 292fb17 Thanks @​HiDeoo! - Removes the tagline configuration option, which was never used.

    If your configuration included a tagline option, you can safely remove it without any replacement.

  • #​4134 6135f01 Thanks @​HiDeoo! - Updates internal @astrojs/mdx, @astrojs/markdown-satteri, and satteri dependencies.

    ⚠️ BREAKING CHANGE: The following minimum versions are now required:

    • astro v7.2.10 or later
    • @astrojs/markdown-satteri 0.4.0 or later (if you use it)
    • @astrojs/markdown-remark 7.3.0 or later (if you use it)

    Please update Starlight and Astro together:

    npx @astrojs/upgrade
    
  • #​4121 2623ae6 Thanks @​delucis! - Refactors Starlight’s mobile menu toggle to work when JavaScript fails or is disabled

    ⚠️ BREAKING CHANGE: This release drops official support for Chromium-based browsers prior to version 116 (released August 2023), Safari-based browsers prior to version 17.0 (released September 2023), and Firefox prior to version 125 (released April 2024). You can find a list of currently supported browsers and their versions using this browserslist query.

    This change also removes the data-mobile-menu-expanded attribute, which was previously added to <body> while the mobile menu is open. If you have custom code that was depending on this attribute, you will need to update it to use a new selector to check if the mobile menu is open.

    In the following example, a custom background colour for the site header while the menu is open is updated for the new approach:

    - [data-mobile-menu-expanded] header {
    + body:has(sl-sidebar-pane:popover-open) header {
      background-color: var(--sl-color-bg);
    }
    

v0.41.11

Compare Source

Patch Changes
  • #​4167 32a5e29 Thanks @​HiDeoo! - Fixes a layout issue and anchor links appearing for hidden headings, e.g. footnote headings, when markdown.headingLinks is enabled or the <AnchorHeading> component is used.

  • #​4148 cdfafd8 Thanks @​ematipico! - Optimizes sidebar data generation logic to speed up sites with large sidebars

v0.41.10

Compare Source

Patch Changes

v0.41.9

Compare Source

Patch Changes

v0.41.8

Compare Source

Patch Changes

v0.41.7

Compare Source

Patch Changes
  • #​4114 3e486fb Thanks @​delucis! - Fixes processing of code examples in RTL languages when using Astro’s Sätteri Markdown processor

v0.41.6

Compare Source

Patch Changes

v0.41.5

Compare Source

Patch Changes

v0.41.4

Compare Source

Patch Changes

v0.41.3

Compare Source

Patch Changes
  • #​3911 1686ecc Thanks @​timothyjordan! - Keeps keyboard focus inside the mobile menu while it is open, preventing focus moving to hidden interactive elements in page content.

v0.41.2

Compare Source

Patch Changes
  • #​4008 58a3520 Thanks @​FrancoKaddour! - Fixes the table of contents overflowing the right edge of the viewport when a custom --sl-content-width value exceeds available space

  • #​4015 bdbfffc Thanks @​delucis! - Fixes an issue where aside icons were rendered incorrectly in projects where Astro’s MDX integration had optimization disabled

pnpm/pnpm (pnpm)

v11.26.0: pnpm 11.26

Compare Source

Minor Changes

  • Catalogs can now resolve workspace dependencies through the workspace: protocol.

  • pnpm remove and pnpm update now accept --trust-lockfile, --no-trust-lockfile, --trust-policy, --trust-policy-exclude, and --trust-policy-ignore-after. pnpm remove checks the whole lockfile against the active policies unless --trust-lockfile is set.

  • Added pnpm change check for CI validation of package versions against the versioning.epics bands and versioning.fixed groups in pnpm-workspace.yaml.

Patch Changes

  • Fetch and tarball errors and retry logs now hide URL credentials, query strings, and fragments that could expose secrets.

  • Fixed a race during config dependency updates that could redirect a lockfile write through a symlink #​14322.

  • pnpm add --allow-build=!<pkg> now correctly denies builds, including in global installs. pnpm approve-builds <pkg> and pnpm approve-builds !<pkg> now save decisions even when the package is not awaiting approval, with a warning #​14067.

  • Fixed pnpm audit --fix failing without a value or when followed by another flag. pnpm audit --fix=override now respects saveExact and savePrefix when writing overrides #​13261, #​11523.

  • pnpm audit now excludes ignored advisories from vulnerability totals and severity counts, and reports them separately #​14535.

  • pnpm deploy no longer requires injectWorkspacePackages. If a workspace dependency's peer has multiple possible versions, deployment reports ERR_PNPM_DEPLOY_AMBIGUOUS_PEER with the conflicting versions. Pin the peer with overrides to deploy without injection #​9386.

  • Fixed concurrent installs sharing a store occasionally failing with an ENOENT error while importing a package file #​14353.

  • Fixed installation failures when a linked local dependency provides a peer dependency also provided by an ancestor, including with pnpm deploy --legacy.

  • pnpm install --node-linker=hoisted no longer downloads skipped optional dependencies when node_modules already exists #​14139.

  • Fixed pnpm install rejecting a symlinked lockfile when config dependencies are unchanged. Updates to config dependencies also preserve lockfiles with a byte order mark. Writes through symlinked lockfiles remain blocked #​14372.

  • pnpm install now relinks workspace packages when publishConfig.linkDirectory changes. Frozen installs require the lockfile to be regenerated #​14488.

  • Auto-installed optional peers now satisfy their declared range even when the workspace root uses a version outside that range #​13867.

  • Fixed global virtual store paths for dependency cycles to consistently account for the runtime engine when dependencies have allowed builds #​14341.

  • Standalone installations now preserve the bundled node-gyp files needed to build native dependencies.

  • Downloaded runtimes are now available to dependency lifecycle scripts during installation.

  • Node.js downloads from nodeDownloadMirrors now use URL-scoped npm credentials, including bearer tokens, basic auth, and tokenHelper #​14334.

  • Fixed globalDir and globalBinDir handling in global configuration and environment variables, including ~/ expansion. This fixes pnpm add -g failing after pnpm config set -g global-bin-dir #​14336.

  • The JavaScript pnpm can again switch to the project's pinned pnpm version on hosts without a matching native binary. If the requested version requires an unavailable native binary, the error now identifies the unsupported host #​13622.

  • Global pnpm config commands now skip project package manager version switching, allowing authentication to be configured before downloading the pinned version #​14463.

  • pnpm self-update, pnpm with, and automatic version switching no longer wait through registry retries when a configured registry has no signatures and registry.npmjs.org is unavailable #​14483.

  • Fixed argument forwarding on Windows with shellEmulator enabled. Trailing backslashes, line breaks, and literal shell expressions are preserved #​14548.

  • Relative scriptShell paths now resolve from the workspace root. Bare command names such as bash still use PATH #​14422.

  • pnpm import now preserves the project-local lockfile when lockfileDir points elsewhere and restores the destination lockfile on failure. Branch lockfile imports leave the shared lockfile unchanged #​14563.

  • catalogMode and --save-catalog no longer move local paths, tarballs, or workspace:<path> specifiers into catalogs #​14437.

  • --side-effects-cache, --no-side-effects-cache, and PNPM_CONFIG_SIDE_EFFECTS_CACHE now toggle only the local cache, preserving any remote cache configured in sideEffectsCache.

  • pnpm unpublish now handles registry two-factor authentication challenges through web authentication or a one-time password prompt #​14464.

  • pnpm outdated and pnpm update now follow GitHub Actions references using self-repository syntax, such as uses: $/.github/actions/setup.

  • pnpm remove now accepts --unsafe-perm.

Platinum Sponsors

Bit OpenAI Notion
CodeRabbit

Gold Sponsors

Sanity Discord Vite
SerpApi Stackblitz Workleap
Nx Latitude

v11.25.0: pnpm 11.25

Compare Source

Minor Changes

  • Added an opt-in proof of concept that lets installs reuse a dependency's build output across machines, by publishing and restoring signed, organization-scoped artifacts through pnpr instead of running the lifecycle scripts locally.

    Configure it with the new remoteSideEffectsCache setting. A workspace names the eligible organization and packages; everything describing the act of signing — publish, keyId, builderId, trustedKeys, privateKey and the provenance fields — is refused in pnpm-workspace.yaml and read from the global config file or the environment instead.

  • Added macOS and Windows x64 and arm64 support to remote shared build artifacts pnpm/pnpm#13771.

  • Added the audit.ignorePrune setting. When set to true, pnpm audit --fix removes ignored GHSA entries that no longer appear in the audit report.

  • Generalized the experimental shared-artifact protocol so candidates and signed payloads identify a discriminated subject. Dependency side effects use package and source-integrity subjects, while workspace tasks use project and task subjects.

    This changes shared-artifact request bodies and signed payloads. A pnpr server and its clients have to be on matching versions.

  • pnpm init now pins the latest pnpm version, instead of the version of pnpm that ran the command. A project scaffolded by an outdated pnpm therefore no longer inherits that staleness through its own devEngines.packageManager / packageManager pin #​7490.

    The version is read from the latest tag on the package-manager registries. When that lookup cannot answer — no network, an unreachable or slow registry, offline, or a latest that the minimumReleaseAge / trustPolicy settings reject — pnpm init pins the running version as before, and never fails or hangs on the lookup. A latest that is older than the running pnpm is never pinned either.

  • A scope set in a project's pnpm-workspace.yaml is now ignored, with a warning naming where to set it instead. pnpm login records the scope as a @scope:registry route in the machine-global auth.ini, which outranks ~/.npmrc in every project — so a repository-committed file could redirect a scope such as @acme for all of a user's other projects after one routine login. Use --scope, the PNPM_CONFIG_SCOPE environment variable, or the global config file instead #​13557.

  • Verified remote build artifacts are persisted in the shared store with their signed origin metadata. Later installs reverify the artifact against current trust, policy, platform, and source before reuse, while invalid remote variants are quarantined per channel (pnpm/pnpm#13771).

  • Persist completed recursive tasks so --resume-from skips exactly the work that passed during a matching interrupted or failed pnpm -r run / pnpm -r exec invocation. When no compatible state exists, pnpm retains its graph-based resume behavior.

  • Allowed pnpm update --patches to refresh registry revisions through a configured pnpr server while retaining locked package versions.

  • Added explicit registry revision selection with <version>+rN and pnpm update --patches for refreshing revision artifacts without changing package versions. Registry-backed lockfile policy checks recognize historical revisions, and pnpr now preserves safe revision histories from upstream registries.

  • Workspace install, rebuild, pack, publish, stage, and lifecycle work now starts as soon as its dependencies finish instead of waiting for an unrelated topological group.

  • pnpm stage approve now approves several staged packages at once. Run it without a stage id to pick from the staged versions interactively, or pass a list of stage ids. The whole batch is approved with a single one-time password, and pnpm asks for a new one only once the registry stops accepting it. Inside a workspace, the selected packages are approved in dependency order, and a package whose workspace dependency could not be approved is skipped instead of being published against a dependency that never reached the registry.

  • Added per-task concurrency limits to workspace task orchestration. Set tasks.<name>.concurrency in pnpm-workspace.yaml to limit how many instances of that task may run across workspace projects at once:

    tasks:
      build:
        concurrency: 2
    
  • Added support for registry replacement tarballs using standard integrity values, explicit revision fields, registry routing from the registries setting, non-redirecting integrity-addressed URLs, canonical safe-integer revision numbers, and pnpr proxying for immutable upstream revision artifacts.

  • sideEffectsCache now declares the whole of how a package's build output is reused — whether one is restored, whether one is saved, and the remote tier that shares it between machines:

    sideEffectsCache:
      read: true
      write: true
      remote:
        org: acme
        packages: ['native-addon']
    

    sideEffectsCache: true, sideEffectsCacheReadonly, remoteSideEffectsCache, and its organization field all keep working. Where a field is set under both spellings the one above wins; where it is set under only one, it is kept.

    Two behaviors change, both bringing this CLI in line with what the Rust one already did: sideEffectsCacheReadonly: true now blocks writing to the cache, and setting it alongside sideEffectsCache: false gives a read-only view rather than switching the cache off entirely. A cache can also be declared write-only now, to populate one the run does not read.

  • Workspace task orchestration (pnpm/rfcs#23). pnpm -r run and pnpm -r exec now schedule per task instead of in topological chunks: a task starts as soon as the tasks it depends on have finished, so a project no longer waits for unrelated projects that happen to share its chunk.

    A new tasks section in pnpm-workspace.yaml declares what a task depends on, using the ^ convention:

    tasks:
      build:
        dependsOn: ['^build']
      test:
        dependsOn: ['build']
      lint: {}
    

    ^name means the named task in each of the project's workspace dependencies; a bare name means the task in the same project; an entry with no dependsOn declares an empty dependency list. A task with no entry behaves as dependsOn: ['^<its own name>'], which is exactly what the previous chunked ordering implied — an unconfigured workspace gets the scheduler improvement and nothing else changes meaning. A project without the script is reported skipped and passes its edges through to its own dependencies, so a scriptless package does not sever a chain.

    Also part of this change:

    • A dependency cycle among the tasks of a run is now an error naming the participating tasks (ERR_PNPM_TASK_CYCLE) instead of silently running in an arbitrary order. Setting ignoreWorkspaceCycles: true downgrades the error to a warning: the cycle's tasks run in an arbitrary order relative to each other.
    • --resume-from now skips exactly the transitive dependencies of the anchor package; work unrelated to the anchor still runs.
    • Under --no-bail, tasks whose dependencies failed are reported as skipped, not failed, and do not add to the exit code.
    • With --bail (the default), the first failure still ends the run at once and nothing new is dispatched — including scripts already queued behind the concurrency limit.
    • pnpm -r run --dry-run <script> prints the task graph that would execute without running anything (including skipping the verifyDepsBeforeRun check); --json emits the tasks and their resolved dependency edges.
    • Output is inherited rather than piped only when at most one script can ever be in flight (--workspace-concurrency=1, or the graph forces the scripts to run one after another).

Patch Changes

  • An _auth entry in the global config file no longer decides which registry packages come from when something else says. A registry or registries declared in pnpm-workspace.yaml or the global config now wins over the route inferred from a stored credential, which still applies where nothing else declares one. The pnpm_config__auth environment variable is unchanged: it stays the way to point a CI runner at a mandated proxy, and still overrides what a repository declares.

  • Prevent installs through a symlinked node_modules directory from rewriting the target checkout pnpm/pnpm#14286.

  • Treat empty scripts selected by a regular expression as missing before running dependent tasks.

  • The options type of the fetch command now declares allowBuilds, a setting its handler already forwarded to the installer. Type-level only — what pnpm fetch does is unchanged.

  • Filter hidden scripts matched by a regular expression during recursive runs when a visible script also matches.

  • Fixed automatically switched pnpm versions forcing all descendant pnpm processes to use the same version pnpm/pnpm#14309.

  • Fixed ERR_PNPM_UNUSED_PATCH validation during incremental installs pnpm/pnpm#13692.

  • Fixed pnpm deploy --prod failing when an excluded dev dependency was also declared as an optional peer dependency pnpm/pnpm#14302.

  • pnpm update -g no longer downgrades a global package. --latest resolves the latest dist-tag, which can point at an older release than the one installed — after pnpm add -g <pkg>@next, for instance #​14270.

    pnpm update -g also no longer changes the pnpm version. pnpm's own global install belongs to pnpm self-update #​14270.

  • Copying a built package to its other hoisted locations no longer replaces the destination directory. With nodeLinker: hoisted, that replacement deleted the dependencies nested inside the destination's node_modules, and made concurrent copies of the same build chunk fail with ERR_PNPM_ENOENT: no such file or directory, rename '.../node_modules/_tmp_...' #​12880.

  • pnpm update no longer replaces the specifier a project declares for a dependency that is also listed in overrides. A catalog: reference stays a catalog: reference, and a declared range stays as written, instead of being rewritten to the version the override resolved to #​12115.

  • pnpm update no longer moves the range a project declares for a dependency that overrides also lists, even when the override repeats that range verbatim. Previously the updated package.json disagreed with the lockfile, so the next pnpm install --frozen-lockfile failed with a specifier mismatch #​14224.

  • Make pnpm add --lockfile-only skip dependency linking pnpm/pnpm#14286.

  • --production is accepted again as an alias of --prod on install, fetch, prune, update, list, why, and sbom, and the install that verifyDepsBeforeRun reproduces is now spelled with --prod. pnpm run no longer aborts with "unexpected argument '--production' found" after a production-only install #​14147.

  • The progress output no longer overwrites the lines above it once it grows taller than the terminal window #​14270.

  • Restoring a dependency's build from the remote side-effects cache no longer downloads files the store already holds.

  • Forward patchedDependencies hashes and packageExtensions to pnpr so server-side resolution preserves patches and package extensions in the lockfile and installed packages.

  • Published the workspace task graph and scheduler as @pnpm/workspace.task-scheduler so other workspace commands can use the same dependency-aware scheduling as recursive run and exec.

  • The environment variables for the remote side-effects cache are named for the setting they configure: PNPM_SIDE_EFFECTS_CACHE_REMOTE_KEY_ID, ..._BUILDER_ID, ..._IMAGE_DIGEST, ..._ARCHITECTURE_BASELINE, ..._PRIVATE_KEY, ..._BUILD_ENV, ..._TRUSTED_KEYS and ..._PUBLISH. The PNPM_REMOTE_SIDE_EFFECTS_CACHE_* names keep working, and the new one wins when both are set.

  • A devEngines.packageManager range pin on pnpm is now recorded in pnpm-lock.yaml's packageManagerDependencies when the running pnpm already satisfies it, using the running version and keeping the range as the recorded specifier. Previously only an exact pin — or a range resolved on the way through a version switch — reached the lockfile, so a range pin written by hand (or by any tool other than pnpm add / pnpm self-update) left the project without the shared resolution the pin exists to provide.

  • Fixed recursive run cleanup on Windows when a lifecycle script fails while another script's process tree is still running.

  • The update notification now suggests pnpm self-update when PNPM_HOME manages the pnpm in use, and the standalone install script otherwise — under Corepack, or when another package manager installed pnpm. pnpm self-update under Corepack names the standalone install script too.

  • Enforce allowBuilds when a prepared git dependency is reused from the shared store, and use the lockfile's canonical git resolution ID in approval suggestions.

  • Topologically sorting workspace projects now runs in linear time, fixing installs and lockfile updates that stalled for seconds on workspaces with thousands of projects forming deep dependency chains #​14149, #​14151.

Platinum Sponsors

Bit OpenAI Notion

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx Latitude

v11.24.0: pnpm 11.24

Compare Source

Minor Changes
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.

  • 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. 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.

  • 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.

  • 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.

v11.23.0: pnpm 11.23

Compare Source

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.

  • 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. 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:

    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.

    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.
    • 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:

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:

    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.

  • 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.

    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.

    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.

  • 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.

  • 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.

  • 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.

  • 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.

  • 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.

  • 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. 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.

  • 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.

  • 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.

  • 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.

  • 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.

  • 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.

  • 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.

  • Fixed pnpm failing to read .modules.yaml files containing long dependency paths #​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.

  • Reduced registry metadata requests during dependency resolution by reusing cached metadata when lockfile preferences prove that no uncached version can win pnpm/pnpm#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.

  • 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.

  • 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.

  • 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.

  • 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.

  • 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.

  • pnpm update now preserves the existing range operator when updating a prerelease dependency. See #​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.

  • 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.

  • 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.

    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. 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.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.22.0: pnpm 11.22

Compare Source

Minor Changes

  • Added pnpm cache path, which prints the directory pnpm uses for its metadata cache. CI setups can use it to cache that directory — including the lockfile verification log, which lets a job skip re-checking an unchanged lockfile against the configured supply-chain policies.

  • --config.config-dir no longer reaches the config through a project's pnpm-workspace.yaml, and neither do the --config. spellings of the other settings a project manifest may no longer contribute (--config.pnpm-home-dir, --config.workspace-dir, --config.global-pkg-dir, --config.root-project-manifest-dir). None of them was ever a supported way to set those directories: pnpm resolves them from the environment, and these flags took effect only because the project-manifest merge re-applied the command line afterwards. The dedicated flags, such as --dir and --global-dir, are unaffected #​13629.

  • pnpm config set refuses to write a setting to a project's pnpm-workspace.yaml that pnpm does not read from there, rather than leaving a key in the file that does nothing. Those settings are configDir, pnpmHomeDir, stateDir and the others that name machine-level state. The command fails with ERR_PNPM_CONFIG_SET_NOT_A_PROJECT_SETTING, naming where the setting does belong when it belongs somewhere. pnpm config delete still clears one that a file already carries, in whichever spelling it uses #​13629.

  • Added a new setting minimumReleaseAgeExcludePrune. When enabled, pnpm add, pnpm update, and pnpm remove prune the entries of minimumReleaseAgeExclude in pnpm-workspace.yaml that the freshly written lockfile no longer resolves: versions that are gone are dropped (an entry is removed once none of its versions remain), and entries for packages that are no longer in the lockfile are removed too. Name patterns (@scope/*) are always kept. The cleanup is skipped when the install's lockfile does not cover the whole workspace (sharedWorkspaceLockfile: false), since entries another project still needs would look stale.

    Renamed cleanupUnusedCatalogs to catalogPrune, so that catalog pruning and release-age exclude pruning use one vocabulary. cleanupUnusedCatalogs continues to work; when both are set, catalogPrune wins.

  • A project's pnpm-workspace.yaml can no longer choose where pnpm keeps its credentials, its own installation, or the registry it downloads its next version from. One of those settings is configDir, which decided where pnpm login writes the granted token. bin, dir, globalBinDir, globalDir, npmrcAuthFile, pnpmHomeDir, stateDir, userconfig and workspaceDir are ignored there now too, and pnpm warns about the ones it finds. cacheDir and storeDir are unaffected #​13629.

  • Resolving a Node.js runtime version (devEngines.runtime / runtime: specifiers) is now much faster: the per-version release metadata is cached in the pnpm cache directory after its signature is verified, and an exact stable version such as runtime:22.23.2 no longer downloads the Node.js release index. A pinned runtime whose metadata was fetched once resolves without any network access, which removes the noticeable delay on the first node invocation in a project pinning an already-downloaded runtime #​13899.

Patch Changes

  • Fixed intermittent ERR_PNPM_ENOENT and ERR_PNPM_ENOTEMPTY errors while renaming _tmp_* directories during installation with nodeLinker: hoisted, in workspaces that also use patchedDependencies.

  • pnpm add no longer re-resolves the dependency graph when pnpm-lock.yaml already holds a version satisfying the request — promoting a transitive dependency to a direct one, or adding to a second workspace package what a first one already depends on, now only saves the dependency in package.json and records its importer entry. A satisfying locked version is necessary but not sufficient: the install still falls back to a full resolution for a dist tag, an alias, a workspace:/catalog:/git/tarball specifier, --save-peer, an overridden package, a catalogMode other than manual, and — under resolutionMode: time-based or lowest-direct, which resolve a direct dependency to the low end of its range — a range several locked versions satisfy.

  • Global installs now switch over atomically. The command shims in the global bin directory point at a stable per-package link rather than at the directory a particular install produced, so pnpm add -g and pnpm update -g activate a new version by moving that one link instead of rewriting every shim. A command can no longer be missing from PATH while an install is in progress, and a failed install leaves the previous version in place.

  • pnpm audit --fix and pnpm audit --fix update no longer add minimumReleaseAgeExclude entries for patched versions that were published before the minimumReleaseAge cutoff. The publish time of each minimum patched version is now checked against the registry metadata, and only versions young enough to be blocked by the age gate get an exclusion entry #​11563.

  • pnpm add <pkg>@<version> and pnpm update <pkg>@<version> under a non-manual catalogMode now move the catalog entry's resolution to the requested version. Previously, when the catalog entry was a range that covered the requested version but resolved to a different one, the request was dropped silently: nothing was installed, nothing was written, and no error was raised.

  • A project that wasn't part of an install that moved a catalog entry now follows the entry the next time it is installed. It used to keep the version the entry resolved to before — a version the entry no longer allowed — and no later install corrected it, so one catalog entry ended up resolved to two versions.

  • pnpm add <pkg>@<version> and pnpm update <pkg>@<version> under catalogMode: strict no longer fail with ERR_PNPM_CATALOG_VERSION_MISMATCH when the catalog entry is a range that the wanted version satisfies. The dependency keeps using the catalog; only a version that really falls outside the catalog's range is rejected #​13715.

  • A changed catalogs or pnpm.overrides block no longer has to be the only change for pnpm install to update the lockfile in place. Editing an override while also removing a dependency, or changing a catalog entry in the same commit as a range bump, is now absorbed in one pass instead of re-resolving the whole dependency graph #​13799.

    Fixed the lockfile an in-place override update wrote when the overridden package was also a catalog entry: the entry kept the version it had before the override moved the package. The same could happen in reverse, when a catalog entry moved a package an override pins. Both cases now re-resolve instead.

  • pnpm install now updates the lockfile in place even when several kinds of changes happened since the last install — for example a removed dependency together with a widened ignoredOptionalDependencies list, or a dependency edit alongside a patch or settings change. Previously any combination of changes forced a full re-resolution #​13763.

  • pnpm deploy injects workspace dependencies again, so the deploy directory is self-contained instead of symlinking back into the source workspace #​13754. Enabling injectWorkspacePackages with dedupeInjectedDeps disabled now also rewrites already-linked workspace dependencies to injected copies.

  • pnpm deploy --no-optional no longer writes a lockfile whose snapshots reference optional dependencies that the deploy excluded.

  • Removing the last dependency that references a catalog entry via the fast lockfile update no longer leaves the stale catalog entry in pnpm-lock.yaml.

  • A git dependency whose clone (or shallow fetch) fails now reports which package it belongs to, under the ERR_PNPM_GIT_FETCH_FAILED code, with credentials in the repository URL redacted. When the lockfile records an SSH remote, the error also explains that fetching it needs an SSH key for that host, and that a lockfile entry written before pnpm v11.21 can be re-recorded over HTTPS with pnpm update <package> #​13743.

  • An integrity recorded on a git dependency's resolution (resolution: {type: git, repo, commit, integrity: sha512-…}) is no longer treated as a checksum. pnpm never verifies a git checkout against such a hash — the commit pins the content — so it is now dropped when the lockfile is rewritten, and pnpm sbom no longer republishes it as a CycloneDX/SPDX checksum. Lockfiles carrying one also load again instead of failing with ERR_PNPM_BROKEN_LOCKFILE #​13042.

    pnpm sbom now also publishes the checksum of a type: binary runtime archive, which pnpm does verify.

  • A git dependency whose git ls-remote fails now reports the ERR_PNPM_GIT_RESOLVE_FAILED code, naming the dependency instead of printing a bare git invocation, with credentials in the repository URL redacted. A specifier that does not ask for SSH resolves over HTTPS, because the URL recorded in the lockfile has to work on every machine that installs it, so the error explains how to substitute the transport on a machine that can only reach the host over SSH (git config --global url."git@<host>:".insteadOf "https://<host>/") #​13743.

    A missing git executable is reported as one, instead of surfacing the raw failure to start the process.

    Credentials embedded in a git specifier are redacted from the "Could not resolve <ref> to a commit of <repo>" errors too.

    Resolving a public repository makes one git ls-remote round-trip instead of two.

  • pnpm install after moving a dependency between dependencies, devDependencies, and optionalDependencies now updates the lockfile in place instead of re-resolving the whole dependency graph #​13696.

  • syncInjectedDepsAfterScripts no longer fails with ERR_PNPM_UNSUPPORTED_INODE_TYPE when a workspace package contains an inode that is neither a file nor a directory, such as the FIFO 1Password's environments create for .env. Such an inode cannot be hardlinked into the injected copy, so it is skipped and the rest of the package still syncs #​13550.

    syncInjectedDepsAfterScripts also no longer fails with EEXIST when a workspace package replaced a file with a directory of the same name since the injected copy was last synced.

  • syncInjectedDepsAfterScripts no longer fails with ENOTDIR when a workspace package replaced a directory with a file of the same name and the injected copy still held that directory's contents.

  • syncInjectedDepsAfterScripts now removes the bin link of a bin the script dropped. Previously only new bins were linked, so a build step that stopped declaring one left its shim behind, pointing at a command that was no longer there.

  • syncInjectedDepsAfterScripts now identifies a file by its device as well as its inode number. An inode number is only unique within one filesystem, so on its own it could match an unrelated file on another device and leave that path stale in the injected copy.

  • pnpm store prune no longer deletes the lockfile verification log. The log records which lockfile passed which supply-chain policies, so it stays valid across a prune of the store; keeping it lets the next install skip re-verifying an unchanged lockfile.

  • Widening a dependency's range no longer leaves the project on an older version. The lockfile update now points the project at the highest version of that dependency already in the lockfile that satisfies the new range — matching what a full resolution records — instead of keeping the locked version whenever it happened to satisfy, which could leave a duplicate behind. A range change that only an already-locked version satisfies is now also handled without re-resolving #​13778.

  • resolutionMode is no longer ignored when minimumReleaseAge is in effect. lowest-direct and time-based pick the lowest satisfying version of a direct dependency again; previously any active release-age cutoff — including the built-in default — silently forced the highest, so resolutionMode only worked when minimumReleaseAge: 0 was set explicitly #​13752.

  • Adding a package to a workspace no longer forces a full re-resolution when every dependency it declares is already locked for a sibling. The lockfile update writes the new project's importer entry from the versions the lockfile already holds; a dependency no locked version satisfies still reaches the resolver #​13696.

  • pnpm config delete <key> no longer fails with ENOENT when the config file it would edit does not exist. Clearing a setting that was never set is a no-op #​13651.

  • Changing a pnpm.overrides entry to a version range now updates the lockfile in place when a version the lockfile already holds satisfies the range, instead of re-resolving the whole dependency graph. Only exact versions were handled before #​13696.

  • Changing a parent-scoped pnpm.overrides entry ("parent>child": "2.0.0") now updates the lockfile in place instead of re-resolving the whole dependency graph. Only the named parent's dependency moves; every other package keeps the version it had #​13795.

  • Removing a dependency, or moving one to another already-locked version, no longer re-resolves the whole dependency graph just because some package resolves a peer with the same name. The lockfile update now compares the peer suffixes against the exact name@version the removal severed, so a suffix that names a different — still present — version of that dependency is left alone #​13781.

  • Projects with a pnpmfile now use the fast lockfile update paths: an unchanged pnpmfile (proven by the recorded pnpmfileChecksum) no longer forces a full re-resolution for removals, dependency group moves, compatible range changes, and the other in-place lockfile rewrites #​13696.

  • A lockfile entry whose resolution is unchanged no longer loses its recorded deprecated marker when a registry serves the package's metadata inconsistently — re-resolving to the same version keeps the deprecation instead of silently dropping the line #​13846.

  • pnpm prune is now recursive by default inside a workspace, just like pnpm install. This fixes pnpm prune --prod in a workspace root emptying the node_modules directories of the other workspace projects, dropping the links to the workspace packages they depend on in production #​13718.

  • A setting written in kebab-case in the global config.yaml is now reported instead of being silently ignored #​13650.

  • pnpm remove no longer re-resolves the dependency graph. The removed dependency's entries are dropped from pnpm-lock.yaml and anything they made unreachable is pruned, without registry access. The install still falls back to a full resolution when a surviving package resolves a peer dependency through the removed one.

  • Removing a package from a workspace no longer forces a full re-resolution. The lockfile update drops the departed project's importer entry and prunes whatever only it depended on. A project that is still linked from a surviving project continues to be reported as an error #​13696.

  • An install sharing a global virtual store no longer removes an incomplete package directory that another importer is still writing, which could fail with failed to remove existing directory ... prior to swap: Directory not empty. Such a directory is now repaired in place, and a package file left damaged by an interrupted install is restored instead of being kept.

  • pnpm sbom no longer emits components for optional platform-specific dependencies that cannot be installed on the current platform (for example, the native @rolldown/binding-* variants for other operating systems). Such packages are present in the lockfile but are never downloaded, so their license (and other metadata) could not be resolved and they appeared in the SBOM without one. pnpm sbom --lockfile-only still describes the whole lockfile graph, which is platform-independent by design.

  • An ssh:// git dependency pointing at a bracketed IPv6 host, such as ssh://[::1]/repo.git, is resolved now. Its colons were read as an SCP-style path separator, which turned the address into [:/1] and left the specifier unresolvable. Applies to both the TypeScript CLI and pacquet.

    In the TypeScript CLI, an ssh:// git dependency written without user info — ssh://git.example.com/team/repo.git, git+ssh://git.example.com:2222/team/repo.git — no longer fails with TypeError: Cannot read properties of undefined (reading 'includes'). Only the user@host form worked before.

  • packageExtensions is now validated when the configuration is read, so a malformed entry (for instance a dependency range set to null) fails with an actionable error instead of crashing later during peer dependency resolution #​13756.

  • Projects using resolutionMode: time-based now benefit from the fast lockfile update paths. A removal, a dependency group move, or a compatible range change no longer forces a full re-resolution just because the lockfile carries a time field #​13696.

  • An install that drops the last dependent of a patched package no longer updates the lockfile in place and succeeds silently. Removing a dependency, widening ignoredOptionalDependencies, or adding a removal override could each prune the package while the patch stayed configured; such an install now falls back to a full resolution, which reports the unused patch with ERR_PNPM_UNUSED_PATCH. Under allowUnusedPatches, where the lockfile update is kept, the same install now warns that the patch went unused instead of saying nothing #​13827.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.21.0: pnpm 11.21

Compare Source

Minor Changes

  • Added interactive group selection to pnpm update --global --interactive.

  • Running pnpm setup, pnpm self-update, or a command that modifies the global installation (such as pnpm add --global) through sudo now prints a warning. pnpm keeps global packages and configuration in the invoking user's home directory, so running these commands as root silently operates on the root user's home directory instead of yours. They will fail with ERR_PNPM_SUDO_NOT_SUPPORTED in pnpm v12. Read-only global commands (such as pnpm bin --global) are unaffected.

Patch Changes

  • Fixed pnpm failing to start under asynchronous Node.js module loaders when no .pnpmfile.mjs exists pnpm/pnpm#11701.

  • Fixed minimumReleaseAge fallback for custom dist-tags so the selected version does not exceed the registry’s original tag target.

  • Removing a dependency from package.json and reinstalling no longer re-resolves the dependency graph. The importer's entry is dropped from pnpm-lock.yaml, anything it made unreachable is pruned, and a catalog entry that loses its last referent is removed — all without registry access. Installs still fall back to a full resolution when a package that stays resolves a peer dependency through the removed one, since that would change the surviving package's entry rather than only prune.

  • Changing a catalog entry to a different exact version no longer re-resolves the dependency graph. The package is replaced in pnpm-lock.yaml directly, reusing the same check the pnpm.overrides fast path applies: every locked dependency of the package must still satisfy the new version's manifest. Installs fall back to a full resolution when anything other than the catalog reaches the package — an importer that depends on it directly, or another package that depends on it — since the graph would then need both versions.

  • Fixed a CI regression where github:owner/repo dependencies (and other shorthand Git specifiers) would fail to install with Permission denied (publickey) on CI runners that lack SSH keys. The Git resolver no longer records an SSH URL unless the user explicitly wrote one (e.g. git+ssh:// or git@host:...):

    • The repository visibility probe (an HTTP HEAD request) now retries transient failures such as 429 Too Many Requests, so host throttling of CI runners is no longer mistaken for a private repository.
    • For non-SSH specifiers, anonymous HTTPS git ls-remote access is now tried before SSH, so a public repository whose visibility probe fails still resolves to a portable HTTPS URL instead of an SSH URL that only works where SSH keys are configured.
    • When every probe fails, the resolver falls back to HTTPS for shorthand and HTTPS-style specifiers, and only guesses SSH when the user explicitly provided an SSH URL.
    • A repository that could not be confirmed public is no longer resolved to the host's anonymous archive URL (e.g. codeload.github.com, which would fail to download for a private repository); it stays a regular git resolution so installs can use ambient Git credentials such as credential helpers and tokens.

    Note that a private repository that is reachable both over authenticated HTTPS and over SSH now resolves to its HTTPS URL, where previous versions recorded the SSH URL.

    Fixes pnpm/pnpm#13276.

  • ng build and nuxt build now work under the global virtual store: pnpm's built-in compatibility extensions add the tslib dependency that @angular/build uses without declaring and the unplugin dependency that @nuxt/vite-builder v4 uses without declaring.

  • Fixed link: dependencies under enableGlobalVirtualStore so linked children are materialized and slots remain isolated by their resolved link targets.

  • An install that skips resolution because pnpm-lock.yaml is already up to date now reacts fully to packages the lockfile removed — for example after pulling a lockfile in which a dependency was deleted. The hoist layer is recomputed, so a package that became hoistable when a direct dependency was removed is hoisted, and pendingBuilds entries for removed packages are dropped instead of staying pending forever.

  • The held-back-update warning printed by pnpm update no longer fires when minimumReleaseAge is the actual reason a newer version was not picked. The warning's baseline now applies the same maturity cutoff as the pick itself, so it no longer wrongly attributes the hold-back to "your manifests and already installed dependencies" or recommends an override that would defeat the age gate. See #​13071.

  • Checking whether ignoredOptionalDependencies is up to date no longer reorders the configured patterns. The check sorted them in place, which could move an ! exclusion ahead of the pattern it excludes from and flip which optional dependencies were ignored.

  • Changing autoInstallPeers, dedupePeers, peersSuffixMaxLength, excludeLinksFromLockfile, or injectWorkspacePackages no longer re-resolves the dependency graph when the lockfile proves the setting cannot affect it: no package or project declares a peer dependency for the peer settings, and no project depends on a directory or on another workspace project for the link and injection settings. The new setting is recorded in pnpm-lock.yaml and the install proceeds from the existing resolution. Every other case still falls back to a full resolution.

  • Adding, editing, or removing an entry in patchedDependencies no longer re-resolves the dependency graph. Resolution never reads a patch — it only records the patch file's hash against the package it matches — so the install now rewrites the affected entries in pnpm-lock.yaml and materializes the patched package from the store instead. Installs still fall back to a full resolution when the patched package is reachable as a peer dependency, and when the new configuration would leave a patch unused while allowUnusedPatches is off, so ERR_PNPM_UNUSED_PATCH is still reported.

  • Resolving a private git repository no longer blocks on an interactive credential prompt: git ls-remote now fails fast with an authentication error when git has no credentials for the repository #​13522.

  • Lockfile verification now honors offline mode by using cached registry metadata instead of reaching the registry. When the required metadata is not available locally, verification reports the same ERR_PNPM_NO_OFFLINE_META condition used by offline resolution.

  • POSIX shell shims now follow symbolic links before computing basedir, preventing execution failures when a shim is invoked via an external symlink on PATH #​13405.

  • The automatic packageManager version switch works again on registries whose tarball URLs point at a different host than the registry itself (load-balanced feed proxies, Artifactory-style mirrors). Package-manager entries are now always recorded with integrity-only resolutions — the download URL is derived from the trusted bootstrap registry instead — and entries persisted in an invalid shape by an earlier pnpm are discarded and re-resolved instead of failing every command #​13619.

  • Registries that serve no npm signature metadata (private mirrors and feed proxies commonly strip dist.signatures) no longer break the automatic packageManager version switch and pnpm self-update #​13147. When the configured registry cannot provide a verifiable signature, pnpm now fetches the signature from registry.npmjs.org and verifies it against the same embedded npm keys over the installed integrity — which proves exactly the same thing. If no signature can be obtained from either source (for example, both are unreachable, or the registry publishes only a shasum), pnpm proceeds with a warning instead of failing, but only when the packages resolve through a registry configured in the user's own (non-project) configuration; the download stays pinned by the lockfile integrity, and a signature that exists but does not validate still fails the switch.

  • pnpm fetch, and any install run with virtualStoreOnly, no longer writes a .pnp.cjs loader under nodeLinker: pnp. These installs populate the virtual store without linking the project, so the loader would have claimed the project resolves out of a store it was never linked into. The importer links and node_modules/.package-map.json were already skipped; the PnP loader now follows the same rule.

  • Prevent pnpm from removing project files when modulesDir resolves to the project root.

  • Speed up installs after adding ignoredOptionalDependencies patterns by removing newly ignored optional dependencies and pruning packages that are no longer reachable without resolving the dependency graph again.

  • When a failed install re-copies a bin script from the store, rerunning pnpm install now reapplies the executable bit to the bin instead of leaving it non-executable #​12742.

  • pnpm root -g and pnpm bin -g now print warnings to stderr instead of stdout, so their stdout stays a clean, machine-readable path. Previously, running either command with --global in a project that pins a package manager (e.g. via the packageManager field) printed a warning like [WARN] Using --global skips the package manager check for this project ahead of the path, breaking programs that capture the output as a path #​13672.

    In pnpm 12, pnpm root -g and pnpm prefix -g are now supported (they previously failed with ERR_PNPM_CLI_ROOT_GLOBAL_UNSUPPORTED / ERR_PNPM_CLI_PREFIX_GLOBAL_UNSUPPORTED), and the reporter output of dlx, create, config, sbom, with, store, prefix, root, and bin goes to stderr, matching pnpm 11.

  • pnpm setup no longer makes Node.js print a MODULE_TYPELESS_PACKAGE_JSON warning about dist/worker.js on every command. The package.json it writes next to a standalone executable now declares "type": "module".

  • pnpm update without saving no longer records a version that the manifest's range excludes. The kept range stays authoritative: a requested version outside it is skipped with a warning, and a requested range, a dist tag, or --latest resolves within it instead of past it. Previously each of these could write a lockfile entry that contradicted its own specifier, which the next pnpm install --frozen-lockfile rejected with ERR_PNPM_OUTDATED_LOCKFILE #​12764.

  • pnpm version -r --json now outputs [] instead of human-readable text when no pending changes exist pnpm/pnpm#13217.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.20.0: pnpm 11.20

Compare Source

Minor Changes

  • Security fix. Affects projects using namedRegistries on pnpm 11.1.0–11.19.x. It is semi-breaking for those projects — see "If you use named registries" below.

    The lockfile recorded no marker for which registry a package came from. Packages were keyed by name@version alone, and entry lookup went through refToRelative(ref, name), so a dependency you declared against one registry could be satisfied by an entry that was actually resolved from another. When two registries served the same name and version, both collapsed onto a single packages: entry and whichever resolved first decided the tarball every consumer got.

    That is a package-substitution risk: a package you expect from your private registry could be installed from a different registry that publishes the same name and version, and the lockfile recorded nothing that would let you tell.

    Packages resolved from a named registry are now recorded under registry-qualified keys (<name>@<registryName>:<version>, e.g. foo@work:1.0.0), so each registry gets its own entry and the lockfile pins which one a dependency came from.

    The lockfile format version is unchanged. Registry-qualified keys appear only for packages resolved from a named registry, so a project that does not use namedRegistries sees no difference, and older pnpm versions keep reading the file.

If you use named registries

Your next non-frozen install re-keys those entries, which shows up as a lockfile diff. Commit it — that diff is the fix being applied. Review it: an entry that moves to a registry you did not expect is worth investigating.

Everyone working on the project should be on this version or newer before you do. An older pnpm reads the re-keyed lockfile fine — frozen installs are unaffected — but it does not produce registry-qualified keys itself, so any install that updates the lockfile writes those entries back to the old shape, and the next install on a current pnpm re-qualifies them. The result is a lockfile that flips back and forth, and while it is in the old shape the project is exposed again. Because the lockfile format version is deliberately unchanged, pnpm cannot detect this and warn you about it.

There is no setting to keep the old behavior: the old shape is the vulnerability.

Tarball URLs that follow the standard registry layout are no longer written to the lockfile for named-registry packages; they are recomputed from the namedRegistries setting on demand.

To use named registries, map your aliases in pnpm-workspace.yaml:

namedRegistries:
  work: https://npm.enterprise.example.com/
New built-in npmjs: alias

npmjs: now resolves to https://registry.npmjs.org/ with no configuration, alongside the existing gh: alias for GitHub Packages. It pins a dependency to the public registry even when registry points elsewhere, such as an internal proxy:

{ "dependencies": { "left-pad": "npmjs:^1.3.0" } }

npm: cannot do this — it is the alias protocol (npm:<name>@<range>) and resolves through whatever registry points at.

If you mirror or proxy npmjs, point the alias at your mirror:

namedRegistries:
  npmjs: https://npm.internal.example.com/

Built-in registry URLs are also the prefixes a lockfile's recorded tarball URL is matched against when pnpm verifies a package. Without the override, an entry whose tarball URL is on registry.npmjs.org is verified against the public registry rather than your mirror. This only affects lockfiles that record such URLs — a canonical URL for your configured registry is omitted from the lockfile and unaffected — and only when a tarball-URL, minimumReleaseAge, or trustPolicy check runs. Overriding the alias is the same escape hatch GHES users already have for gh.

Every alias the lockfile references must stay in namedRegistries: reading an entry whose alias is gone fails with ERR_PNPM_MISSING_NAMED_REGISTRY rather than silently falling back to the default registry, since that would fetch a different package. Renaming an alias re-resolves the packages that used it.

Named registry aliases that shadow a reserved dependency specifier prefix (file, link, workspace, runtime, npm, jsr, ...) are now rejected with ERR_PNPM_RESERVED_NAMED_REGISTRY_NAME instead of being silently shadowed by the corresponding resolver.

pnpm licenses and pnpm sbom now keep the two artifacts apart as well: license records carry the registry alias, and SBOM components carry the purl repository_url qualifier.

Patch Changes

  • An empty http-proxy, https-proxy, proxy, or no-proxy value — from the .npmrc, pnpm-workspace.yaml, the CLI, or the HTTP_PROXY / HTTPS_PROXY / PROXY / NO_PROXY environment variables — no longer fails the install with ERR_PNPM_INVALID_PROXY. Empty settings read as unset, so a shell exporting HTTP_PROXY= disables the proxy, and an empty proxy= in the .npmrc no longer suppresses HTTPS_PROXY #​13533.

    proxy=false in the .npmrc or proxy: false in pnpm-workspace.yaml now turns proxying off instead of being read as a proxy host named false. false and null on https-proxy / http-proxy / no-proxy read as unset, and on the command line they are ordinary host names, since a flag carries its value verbatim.

  • The env lockfile no longer pins @pnpm/exe alongside pnpm when the wanted pnpm version is 12 or newer. From v12 the unscoped pnpm package is itself the native executable, so @pnpm/exe is not published for it and resolving it would fail. The engine identity check now verifies the native binary through whichever package ships it.

  • lexCompare and nerfDart are now published as @pnpm/text.ordinal-comparator and @pnpm/config.registry-auth-key. Use these instead of @pnpm/util.lex-comparator and @pnpm/config.nerf-dart.

  • Fixed the order in which pnpm matches a lockfile's recorded tarball URL against known registry URLs. Two registry URLs of equal length were previously ordered arbitrarily, so which one a tarball URL matched could differ between runs.

  • Dependency resolution is faster: package metadata is now filtered once per packument instead of once per dependency edge when minimumReleaseAge is active, and parsed semver versions and ranges are reused instead of re-parsed on every comparison.

  • Security: pnpm rebuild now refuses a lockfile whose packages key carries a path traversal in the package name (e.g. ../../../escaped@1.0.0), instead of running that package's lifecycle scripts and linking its bins in a directory outside the virtual store. Such a name is rejected with ERR_PNPM_INVALID_DEPENDENCY_NAME.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.19.0: pnpm 11.19

Compare Source

Minor Changes

  • pnpm login no longer requires an interactive terminal when the registry supports web-based login: without a TTY it prints the authentication URL (skipping the QR code and the "Press ENTER to open the URL in your browser" prompt) and polls the registry until the browser approval completes. Only the classic username/password login still fails with ERR_PNPM_LOGIN_NON_INTERACTIVE in a non-interactive terminal.

  • The save-prefix setting now accepts =: newly added dependencies are saved with an explicit = operator (=1.2.3) instead of the setting being silently treated as the default ^.

Patch Changes

  • allowBuilds entries can now approve git-hosted packages that pnpm downloads as a tarball, such as github: dependencies (which are fetched from codeload.github.com rather than cloned), by their repository URL without the resolved commit hash. This matches the hashless git+ matching already supported for cloned git dependencies. For example:

    allowBuilds:
      "foo@git+https://github.com/org/foo.git": true
    

    This approves the package whether pnpm clones it or downloads a tarball, so the entry no longer has to be updated every time the pinned commit changes. GitLab and Bitbucket tarball downloads are matched the same way. Approving or denying a specific resolved commit by its full tarball dep path continues to work.

  • pnpm outdated --include-github-actions no longer blocks on an interactive git credential prompt when a workflow uses a private action repo.

  • Prevented minimumReleaseAge from replacing latest with a SemVer-greater version than the registry tag target #​13034.

  • Fixed empty bundledDependencies and bundleDependencies arrays causing nondeterministic lockfile changes. See #​13123.

  • The install summary no longer prints (X is available) when the registry's dist-tags.latest is still held back by the active minimumReleaseAge policy. The hint only ever names the actual latest tag, so an immature latest suppresses the hint instead of advertising the version pnpm just refused to install #​11698.

  • pnpm update keeps the explicit = operator of an exact version pin: a dependency saved as =3.5.1 now updates to =3.5.2 instead of the bare 3.5.2. See #​13168.

  • Preserve a workspace dependency's link: entry when a run does not target it — e.g. pnpm update <other-pkg> (with or without --recursive), or a plain install after a root/catalog dependency change — with injectWorkspacePackages, instead of spuriously rewriting it to a peer-suffixed file: protocol. See #​10433.

  • Workspace dependencies declared with a relative path (e.g. "foo": "workspace:../foo") are no longer silently dropped from the workspace projects graph, so --filter selection and the topological order of recursive commands take them into account.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.18.0: pnpm 11.18

Compare Source

Minor Changes

  • Fixed an installed optional dependency being left without one of its own required dependencies. When a package reached through optionalDependencies is installable on the current system but one of its regular dependencies is not, a lockfile-based install skipped that dependency and installed the parent anyway, so importing the parent failed with MODULE_NOT_FOUND. The dependency is now installed, and an install-check warning reports the incompatibility. A dependency is still only skipped when every path to it is optional, or when the package that pulls it in was itself skipped #​13286.

  • pnpm setup now appends PNPM_HOME and the global bin directory to the GitHub Actions environment files (GITHUB_ENV and GITHUB_PATH), so later steps in the same job can run pnpm add --global and other global commands #​9191.

  • Added support for publishConfig.name, which publishes a package under a different name than the one its manifest carries in the workspace. It is for a project whose published name is already taken by a sibling project, which otherwise has to be renamed by a build step just before publishing. Only the published artifact is renamed — dependents, pnpm-lock.yaml, and release tooling keep addressing the project by its manifest name — and the new name reaches the packed manifest, the tarball filename, and everything that addresses the package at the registry: the already-published check of pnpm publish -r, its registry selection, and the release-planning probes of pnpm change status and pnpm version -r #​13345.

  • pnpm self-update no longer takes any instruction from the project it is run in:

    • pnpm is fetched through the same trusted registry and auth configuration used when switching pnpm versions, so a project .npmrc or pnpm-workspace.yaml can no longer redirect the download or attach credentials to it, and the project's default .pnpmfile.(c|m)js is no longer loaded. Pnpmfiles from trusted sources (the pnpmfile setting, the global pnpmfile, config dependencies) still apply.
    • The minimumReleaseAge settings in pnpm-workspace.yaml no longer affect self-update. They still govern the project's own dependencies; for self-update the cooldown now comes from the built-in default, your global config, a PNPM_CONFIG_* environment variable, or a command-line flag. This fixes self-update failing inside a workspace that raises the cutoff while succeeding everywhere else, and stops a repository from either waiving the cooldown or keeping you on an outdated pnpm by raising it.
    • The same applies to the trustPolicy settings and to ci: a project can no longer weaken the trust check that guards the pnpm download, nor re-enable the confirmation prompt that a CI run suppresses.

    When self-update refuses a version that is younger than the cutoff, an interactive run now offers to update anyway; non-interactive runs still fail. CI never prompts, even on a runner that attaches a TTY.

Patch Changes

  • Fixed pnpm licenses list to report every version when the same package is installed under multiple aliases pnpm/pnpm#13438.

  • Sort pnpm dedupe --check snapshot changes for stable output across pnpm implementations.

  • Strip Unicode formatting characters from registry- and manifest-derived terminal output.

  • Speed up installs after compatible catalog or direct dependency range changes by retaining the locked version without resolving the dependency graph again.

  • Speed up installs after safe override changes by reusing unambiguous compatible dependency resolutions, pruning obsolete dependencies, applying independent replacements and removals together, and handling parent-scoped "-" overrides without full lockfile resolution.

  • Installing a local file: directory dependency with the global virtual store enabled no longer fails with TypeError: Cannot read properties of undefined (reading 'split') #​13335.

    Local directory dependencies — file: directories and injected workspace packages — now get a global-virtual-store slot of their own per project. They used to share one slot across every project that depended on a directory of the same name, so a project could end up linked to another project's copy of the dependency.

  • The Workspace column of pnpm update --interactive now falls back to the project's path when its name is only whitespace, as it already did for a missing or empty one — all three render an equally blank label otherwise.

  • Checking GitHub Actions dependencies for updates is now opt-in for every command. Neither pnpm outdated nor pnpm update reads the workflow files unless --include-github-actions is passed or update.githubActions is set to true in pnpm-workspace.yaml. Reading them runs git ls-remote against every referenced repository, which fails in environments where GitHub is not reachable the way pnpm assumes (a GitHub Enterprise Server, a custom certificate authority, or an offline network) #​13254.

    pnpm outdated accepts the --include-github-actions option too.

  • pnpm update --interactive now measures its table in terminal columns rather than in characters. A package name, workspace name, or version containing wide characters (CJK, most emoji) no longer knocks its row's columns out of line with the rest of the group, and a wide character in a version no longer aborts the command with Subject parameter value width cannot be greater than the container width #​13357.

  • The Workspace column of pnpm update --interactive is more informative in two cases. A dependency outdated at the same version in several workspace projects is offered as one choice, since selecting it updates every project — that choice now names all of them instead of only the first. And a workspace project without a name is now labelled with its path rather than left blank, so several unnamed projects can be told apart.

  • An auto-installed optional peer is no longer hoisted at a version the workspace root's own dependency on that package excludes. resolvePeersFromWorkspaceRoot already made the workspace root's specifier decide which version a missing required peer is installed at; the optional-peer picker ignored it and always took the highest version present anywhere in the graph. In a workspace whose root pins postcss: 8.5.10, an importer that depends on webpack and declares no postcss of its own got postcss@8.5.22 hoisted for terser-webpack-plugin's optional postcss peer, leaving two postcss@8.5.x instances in the graph #​13320.

  • overrides now also govern peers that pnpm auto-installs. Previously an override only rewrote dependencies declared in a manifest, so a peer nobody declares — installed because autoInstallPeers is on — resolved against its declared peer range and could bring in a second copy of the very package the override pinned. For example, with overrides: { react: npm:react@19.2.0 } and a lone lucide-react dependency, pnpm installed react@18.3.1; it now installs the pinned react@19.2.0 #​13320.

  • Under resolvePeersFromWorkspaceRoot, a workspace root dependency declared with link: or file: (or the path form of workspace:, such as workspace:../pkg) now satisfies another project's missing peer dependency at the linked package's own version, instead of being hoisted as a path. Those specifiers are relative to the project that declares them, so the same specifier reached a different directory — or none — from the project the peer was hoisted into, leaving a broken link. The root now has the same authority over the peer as it has when it declares the package with a version range #​13373.

  • Installs through a pnpr server now apply the project's whole verification policy. minimumReleaseAgeExclude, minimumReleaseAgeIgnoreMissingTime, trustPolicy, trustPolicyExclude, trustPolicyIgnoreAfter, and trustLockfile were ignored, so excluded packages were still held back and a lockfile containing them could be rejected.

    trustPolicy: no-downgrade no longer fails with TRUST_POLICY_INCOMPATIBLE_WITH_PNPR when a pnpr server is configured.

    --frozen-lockfile and --no-prefer-frozen-lockfile are now honored on the pnpr path, instead of resolving and rewriting the lockfile anyway. Since frozenLockfile defaults to true on CI, a CI install through a pnpr server now fails on an out-of-date lockfile rather than updating it.

  • Workspace installs through a pnpr server no longer crash with Cannot read properties of undefined (reading 'filter') after linking, when minimumReleaseAge is active #​13275.

  • Fixed pnpm dedupe updating valid catalog resolutions when another matching version exists in the lockfile.

  • pnpm -r run "/pattern/" --no-bail no longer exits zero when one of a project's matched scripts fails and a later one passes. The run summary carries a single status per project, and the passing script overwrote the recorded failure.

  • Restored the store block a first install prints, naming how packages were materialized and where the stores live #​13315:

    Packages are hard linked from the content-addressable store to the virtual store.
      Content-addressable store is at: ~/.local/share/pnpm/store/v11
      Virtual store is at:             node_modules/.pnpm
    
  • The root project's pnpm:devPreinstall script now runs before resolution and linking, as it does in pnpm 11. It is skipped under --ignore-scripts, --lockfile-only and --dry-run, by pnpm fetch and pnpm rebuild, and by a repeat install that is already up to date. Workspaces that use the hook to prepare state the install depends on — such as next.js, which generates a placeholder next bin with it — were left with dependents linked against files that were never created #​13313.

  • Prevented pnpm dedupe --check from removing an incompatible node_modules directory.

  • pnpm update --workspace no longer links dependencies the user never named:

    • Running it with updateConfig.ignoreDependencies configured no longer fails with ERR_PNPM_WORKSPACE_PACKAGE_NOT_FOUND for a dependency that is only published to the registry. Such dependencies keep their specifiers, as they already did when no dependencies were ignored.
    • Passing package selectors that match no direct dependency no longer falls back to linking every workspace dependency.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.17.0: pnpm 11.17

Compare Source

Minor Changes

  • Added a new setting, update.githubActionsServer, for specifying the base URL of the GitHub server that hosts the repositories of the GitHub Actions referenced by the workflow files (for example, a GitHub Enterprise Server). When the setting is not defined, the URL is read from the GITHUB_SERVER_URL environment variable, falling back to https://github.com. The URL must use the https:// or http:// protocol #​13220.

    pnpm outdated and pnpm update no longer fail when the refs of a GitHub Action's repository cannot be read (for example, when the action's repository is private or hosted on a different GitHub server). Such actions are now skipped with a warning.

    Setting update.githubActions to false now makes pnpm outdated and the interactive pnpm update skip GitHub Actions dependencies.

Patch Changes

  • The token poll for web-based authentication no longer reads the body of non-OK or still-pending (HTTP 202) responses, and caps the token response body it does read at 64 KiB, so a malicious or compromised registry cannot exhaust memory through the poll pnpm/pnpm#12721.

  • Fixed catalog: references in dependencies and overrides failing to resolve when installing through a pnpr server, which errored with "No catalog entry '' was found for catalog 'default'." even though the catalog entry existed. Also fixed a crash on Windows when installing a nested workspace member (e.g. packages/foo) through a pnpr server #​13232.

  • Republished every package: the tarballs published by the v11.13.1 through v11.16.0 releases were missing most of their compiled files due to a packing bug #​13164.

  • Revert script ordering change for pnpm run --sequential /regex/

  • Support the from-git argument in the pnpm version command.

  • When the authentication URL cannot be rendered as a QR code (for example when it exceeds the maximum QR data capacity), web-based login now displays the URL alone with a warning instead of aborting authentication pnpm/pnpm#12721.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.16.0: pnpm 11.16

Compare Source

Minor Changes

  • The first release of a package now publishes the version written in its manifest verbatim, instead of bumping off it. pnpm version -r and pnpm change status check the registry for each release's current version; when that version is not yet published, the package debuts at it and its pending changesets apply only from the next release. A newly added package seeded at 1100.0.0 with a minor changeset is therefore published as 1100.0.0 rather than skipping straight to 1100.1.0.

  • Added a --changeset flag to pnpm update. Set update.changeset to true in pnpm-workspace.yaml to enable this behavior by default, and use --no-changeset to override the setting for one update. After the update completes, pnpm writes a .changeset/pnpm-update-<suffix>.md file declaring a patch bump for every workspace package whose dependencies or optionalDependencies were changed by the update and a major bump when peerDependencies changed, including packages that consume an updated catalog entry via the catalog: protocol. Private packages, packages without a name, and packages listed in the ignore array of .changeset/config.json are skipped. If .changeset/config.json does not exist, a warning is printed and no changeset is generated.

  • Added GitHub Actions dependencies to pnpm outdated and interactive pnpm update. Non-interactive updates can include them with --include-github-actions or by setting update.githubActions to true in pnpm-workspace.yaml. Updated actions are pinned to exact commit hashes with their release tags preserved in comments.

  • Added update and audit settings sections to pnpm-workspace.yaml, superseding the awkwardly named updateConfig, auditConfig, and top-level auditLevel settings:

    update:
      ignoreDeps: # was updateConfig.ignoreDependencies
        - webpack
        - "@babel/*"
    
    audit:
      level: high # was auditLevel
      ignore: # was auditConfig.ignoreGhsas
        - GHSA-xxxx-yyyy-zzzz
    

    update.ignoreDeps lists dependency name patterns that pnpm update and pnpm outdated should skip. audit.level and audit.ignore tune pnpm audit.

    The deprecated updateConfig, auditConfig, and auditLevel settings keep working until the next major version. When both a new section value and its deprecated counterpart are set, the new section takes precedence and a warning is printed. Both the TypeScript CLI and the Rust config surface (pacquet) recognize the new sections.

Patch Changes

  • Fixed pnpm add --save-exact/--save-prefix and pnpm update writing a package's version with the peerDependencies range's prefix (e.g. ^19.2.7 instead of the requested 19.2.7) whenever the same package also appeared in peerDependencies. A real dependencies/devDependencies/optionalDependencies entry now takes precedence over a same-named peerDependencies entry when computing the current specifiers #​13108.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.15.1: pnpm 11.15.1

Compare Source

Patch Changes

  • pnpm install now detects a supportedArchitectures change and re-evaluates previously skipped platform-specific optional dependencies, instead of reporting the project as up to date and leaving the packages for the old architecture set in place.

  • pnpm setup now removes leftover v10-layout shims at the top of PNPM_HOME, so pnpm self-update no longer warns about a v10 installation layout after PATH has been migrated to the v11 PNPM_HOME/bin layout. Applies to both the TypeScript CLI and pacquet.

    In the TypeScript CLI, self-update also no longer treats a dangling legacy shim (one whose install target was garbage-collected) as a real v10 layout, so the warning can no longer fire on dead shim files.

    Closes #​12496.

  • Completed pnpm runtime installation parity for Node.js, Deno, and Bun, including runtime failure policy, target architecture selection, and dependency runtime engines. Runtime failure overrides now preserve explicit runtime dependencies without matching engine entries.

  • Fixed pnpm install running out of memory while resolving large dependency graphs #​8441. The resolver kept full registry documents — per-version readmes, scripts, descriptions, and other install-irrelevant bulk — in memory for every package fetched with full metadata (optional dependencies, and packages re-fetched for minimumReleaseAge's publish timestamps). Every retained document is now condensed down to the field set installation actually reads, which reduces peak resolution memory by several times on workspaces with more than a thousand packages.

  • When a dependency's build script fails under enableGlobalVirtualStore, the global virtual store directory it was being built in is now removed for scoped packages too. Previously the cleanup resolved one directory level short of the hash directory for a scoped name, leaving a half-built directory behind that later installs would reuse.

  • Fixed pnpm login, pnpm adduser, and pnpm logout against a registry hosted under a URL subpath (e.g. https://example.com/npm/registry) when the configured URL has no trailing slash. Such URLs were left unnormalized, so the last path segment was dropped when building the login and token endpoints and the auth token was stored under a truncated key. Registry URLs with a path now always get a trailing slash appended during normalization, matching how root-level registry URLs are handled.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.15.0: pnpm 11.15

Compare Source

Minor Changes
  • Optional peer dependencies declared only via peerDependenciesMeta (for example debug's supports-color peer) are now resolved from a satisfying version already present in the dependency graph, the same way explicitly declared optional peer dependencies are. Previously such peers were only resolved this way when the package's metadata was read back from the lockfile, so an unrelated dependency change could rewrite peer resolutions across the whole lockfile.
Patch Changes
  • Updated adm-zip to prevent crafted ZIP archives from causing excessive memory allocation.

  • pnpm version -r no longer writes a versioning-ledger entry with no consumed intents as a bare intents: key, which the next run failed to read with ERR_PNPM_INVALID_VERSIONING_LEDGER. Empty intent lists are now written as intents: [], and the ledger reader accepts the bare form left by earlier releases.

  • Fixed pnpr workspace resolution to preserve project names and versions for workspace: dependencies.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.14.0: pnpm 11.14

Compare Source

Minor Changes

  • peerDependencies now accept dependency specifiers that carry a scheme — a named-registry spec (<registry>:<version>), an npm: alias, or a file:/git/URL spec — instead of rejecting them with ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION #​13095. Such a peer is matched against the semver range carried by the specifier (work:5.x.x is checked as 5.x.x, npm:bar@^5 as ^5), or against * when it carries no version, while the original specifier still selects the package to auto-install. Bare name@version values, which are almost always a mistake, are still rejected.

  • Added pnpm doctor, which diagnoses the pnpm installation and the environment it runs in: the versions and install method, whether the global bin directory is on PATH, whether the store and cache are writable, which link strategies (reflink, hardlink, symlink) the store's filesystem supports, registry connectivity, and an offline file: install that exercises the resolve/store/link path end to end. Each check reports how to fix what it finds, and the command exits non-zero when any check fails.

    Use --offline to skip the checks that need network access, --json for machine-readable output, and --benchmark to time the filesystem and install checks.

  • Added support for executing multiple scripts matching a RegExp passed to pnpm run (e.g., pnpm run "/^build:.*/"), running matched scripts in deterministic lexicographical order. Restored the --sequential (-s) CLI option for pnpm run, which forces workspaceConcurrency to 1 so that matched scripts run sequentially one by one across and within packages.

Patch Changes

  • Fixed pnpm install failing with ERR_PNPM_LOCKFILE_IS_SYMLINK when pnpm-lock.yaml is a symlink, as build sandboxes such as Bazel and Nix stage it #​13073. Reading a lockfile through a symlink is allowed again, and an install that leaves the lockfile unchanged no longer rewrites it, so --frozen-lockfile no longer needs to write at all. Writing a changed lockfile through a symlink is still refused, as that would redirect the write onto the symlink's target.

  • Fixed frozen installs incorrectly treating equivalent Git dependency specifiers as a stale lockfile. See #​13039.

  • pnpm owner ls now reports authentication and authorization failures (401/403) as dedicated errors that include the registry's response body, matching pnpm owner add/rm, instead of a generic Failed to fetch owners message.

  • Recover from a metadata cache entry that disappears (concurrent cache cleanup, antivirus) after the registry has already answered the conditional request with 304 Not Modified. The metadata is re-requested once without cache validators instead of failing the install with ERR_PNPM_CACHE_MISSING_AFTER_304.

  • A project pinned to a broken pnpm release via packageManager or devEngines.packageManager now reports which release is broken and what to do about it, instead of failing inside the installer. pnpm self-update already refused these releases; the version switch does too.

  • Prevent broken-lockfile errors from including snippets of the lockfile's contents.

  • pnpm self-update now checks that the version it installed can run before making it the active pnpm. A release that installs but cannot execute is discarded with an error instead of replacing a working installation.

  • Fixed an out-of-memory regression when workspace projects concurrently resolve a package with large registry metadata pnpm/pnpm#13077.

  • Fixed pnpm update rewriting exact version pins that use the = operator (for example =3.5.1) to a caret range (^3.5.1). Exact pins are now preserved and written back as the bare version. See #​12745.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.13.1: pnpm 11.13.1

Compare Source

Patch Changes

  • Fixed pnpm pack applying workspace-root ignore rules when a workspace package has its own .npmignore file.
  • Keep the interactive minimumReleaseAge approval prompt visible during pnpm install. The progress reporter now pauses its redraws while a prompt is waiting for input instead of overwriting it, so the install no longer hangs on a question the user cannot see #​13019.
  • Fixed pnpm self-update failing to link native platform binaries stored in sibling global virtual store slots.

v11.13.0: pnpm 11.13

Compare Source

Minor Changes

  • Added versioning.epics to pnpm-workspace.yaml. An epic ties a group of member packages to a lead package, constraining every member's major version to a band derived from the lead's major: while the lead is on major M, members live in M*100 … M*100+99. Members move independently inside the band (patch, minor, and a major intent that stays in-band); a bump that would carry a member past the band ceiling is rejected until the lead advances its own major. When a release plan takes the lead to a new stable major, every member re-bases to the band floor in the same plan. Membership is matched with pnpm's package selectors — name globs, ./-prefixed directory globs, and !-prefixed negations.

  • Added the team command for managing organization teams and team memberships on the registry, with create, destroy, add, rm, and ls subcommands and support for --otp, --parseable, and --json flags.

  • Added native workspace release management #​12952: the new pnpm change command records change intents as changesets-compatible .changeset/*.md files (pnpm change status shows the pending release plan), and the bare pnpm version -r consumes them — bumping versions across the workspace with dependent propagation through workspace: ranges, fixed groups, a maxBump cap, --filter narrowing, and --dry-run — writing changelogs, and recording consumed intents in a committed ledger that keeps cherry-picks and merge-backs between release branches safe. Packages can be moved onto per-package release lanes with the new pnpm lane <name> --filter <pkg> command and back with pnpm lane main --filter <pkg> (pnpm lane shows the membership), releasing X.Y.Z-lane.N prereleases from the same runs that release stable versions of the packages on the main lane. Configuration lives under the new versioning key of pnpm-workspace.yaml (fixed, ignore, maxBump, lanes, changelog). When two workspace projects publish the same name, intent files, versioning.lanes, and versioning.fixed/ignore may reference a project by its workspace-relative directory path (e.g. "./pnpm/npm/pnpm") — the one additive extension to the changesets format, applied automatically by pnpm change.

    Release changelogs default to registry storage (versioning.changelog.storage): no CHANGELOG.md is committed. Each release's section is composed at publish time and packed into the published tarball on top of the previously published version's changelog, and the consumed change intents are garbage-collected by a later pnpm version -r only once the registry confirms the version is published with its section. Set versioning.changelog.storage: repository to keep committed CHANGELOG.md files instead.

  • Added a new override selector form with an empty range — "pkg@": "<version>" — called a convergence override. It rewrites a dependency edge only when its exact version satisfies the edge's declared range, so compatible consumers converge on one version while incompatible consumers keep their own resolution — now and for any dependent added in the future #​12794.

    overrides:
      "form-data@": 4.0.6
    

    The value must be an exact version. When a full resolution detects that every declared range also admits a newer version, pnpm warns that the override is stale and names the version to converge on. Previously an empty range in an override selector was undocumented and behaved like a bare (unscoped) override.

Patch Changes

  • A tokenHelper set in the global pnpm auth.ini is no longer rejected as project-level configuration. The guard that blocks tokenHelper from a project .npmrc only treated ~/.npmrc as a trusted source, so a helper written to auth.ini (for example by pnpm config set) failed on every command and could not even be removed with pnpm config delete. A tokenHelper in a workspace or project .npmrc is still rejected.

  • pnpm cache delete now removes a package's metadata from every metadata cache directory (metadata, metadata-full, and metadata-full-filtered), instead of only the one the current resolution mode reads. Previously a package cached under a different mode (e.g. metadata-full-filtered) was left behind. Closes #​12753.

  • Fixed an injected workspace dependency (injectWorkspacePackages: true) incorrectly staying as file: instead of deduping back to link: when an unrelated, ordinary shared dependency resolved to a peer-suffixed variant for the target project's own copy but not for the injected occurrence. See #​10433.

  • pnpm deploy now supports workspaces that use catalogs.

  • Fixed pnpm deploy with a shared lockfile so local file: tarball dependencies keep their package name in the generated deploy lockfile. This prevents warm-store deploys from failing with ERR_PNPM_UNEXPECTED_PKG_CONTENT_IN_STORE when the tarball filename includes the version.

  • Options that follow create, exec, or test appearing as a subcommand of another command are now parsed instead of being silently treated as positional parameters. For example, pnpm team create @org:team --registry <url> previously ignored the --registry option and sent the request to the default registry.

  • pnpm add -g, pnpm update -g, pnpm setup, and the self-updater no longer fail with ERR_PNPM_MISSING_TIME when trustPolicy: no-downgrade or resolutionMode: time-based is set in the global config #​12883. The decision to fetch full registry metadata now lives in one place, and the no-downgrade trust policy always requests full metadata (matching the self-updater), since the trust evidence it checks is missing from abbreviated metadata even on registries that include the time field.

  • pnpm list and pnpm why no longer crash with EMFILE: too many open files when a project has a large number of unsaved dependencies (packages present in node_modules but not in the lockfile). The reads of those packages are now concurrency-limited.

  • The published pnpm package no longer declares dependencies or devDependencies. Because the CLI bundles its runtime dependencies into dist/node_modules, those fields are dropped when packing, so npm install of the tarball no longer tries to resolve internal-only packages such as @pnpm/test-ipc-server. Closes #​12955.

  • Fixed pnpm publish --otp and pnpm publish --batch --otp to send the configured OTP to the registry.

  • pnpm publish again sends the package's README to the registry as metadata, so registries can render it on the package page. The readme is always included in the published metadata (matching the npm CLI), while the embed-readme setting continues to control only whether the readme is written into the package.json inside the tarball. This restores the behavior that was lost when publishing became fully native. Closes #​12966.

  • Fixed the dependency status check wrongly reporting "up to date" when a package.json, .pnpmfile.cjs, or patch file was edited in the same second as the previous install, on filesystems that record mtimes at whole-second resolution (for example ext4 with 128-byte inodes). The optimistic repeat-install fast path and verify-deps-before-run compared mtimes strictly, so a same-second edit whose mtime rounded down looked unchanged and re-resolution was skipped. Such a file's whole second is now treated as possibly-modified, falling through to the content check; behavior on sub-second filesystems is unchanged.

  • Retry package metadata requests when a registry or proxy returns 304 Not Modified to an unconditional request, preventing false ERR_PNPM_CACHE_MISSING_AFTER_304 failures pnpm/pnpm#12882.

    If the retry also returns 304, report ERR_PNPM_META_NOT_MODIFIED_WITHOUT_CACHE instead.

  • Fixed pnpm update removing transitive lockfile entries when dedupePeerDependents is disabled and the selected package is absent pnpm/pnpm#12456.

  • Limit modern deploy lockfiles and localized virtual stores to dependencies reachable from the selected dependency groups.

  • A tokenHelper command is now given a 60-second time limit. A helper that hangs (deadlock, stuck I/O) is killed and reported as an error instead of leaving the command waiting forever.

  • Fixed orphaned child processes on Windows when pnpm exits on an error while commands spawned by pnpm exec or pnpm dlx are still running (for example, when one project's command fails during pnpm --recursive exec). The PIDs of these commands are now recorded when they are spawned and their whole process trees are terminated with taskkill on an error exit. Previously the cleanup relied on enumerating the system process list, which is so slow on Windows that the enumeration hit its timeout and the cleanup was silently skipped #​12406.

  • pnpm pack now respects workspace-root .npmignore and .gitignore files when packing workspace packages.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.12.0: pnpm 11.12

Compare Source

Minor Changes

  • a897ef7: Custom fetchers exported from a pnpmfile can now delegate by returning a { delegate: <resolution> } envelope: pnpm rewrites the package's resolution to the delegated shape and runs the built-in fetcher on it. This is the portable delegation form that also works in pacquet, where cafs and fetchers cannot be passed to the hook. Related to pnpm/pnpm#11685.

Patch Changes

  • 2b02764: The changed-packages filter (--filter "...[<since>]") no longer allows an option-like <since> value (such as --output=<path>) to be interpreted as a git option — git now rejects it as a bad revision. The repository root is also resolved to the nearest .git entry, so the filter works in a git worktree checked out inside another repository's tree.

  • 43711ce: pnpm outdated no longer checks the registry for dependencies that are resolved from local link:, file:, or workspace: references in the lockfile #​12827.

  • 3c6718b: Fixed a deadlock in peer dependency resolution: pnpm install hung forever when a peer dependency cycle spanned a project's own dependencies and auto-installed peer providers, for example when installing electron-builder@26.15.3 #​12921.

  • 252f15e: Fixed peer dependency auto-install picking a version the peer range rejects. In a workspace with several projects, a package declaring a peer dependency with a semver range (for example ^1.0.0) could get the highest version found anywhere in the workspace (for example a 2.0.0 resolved for another project) instead of a version that satisfies the range. Peers are now deduplicated onto the highest preferred version that satisfies the declared range, and when none does, the range is resolved from the registry.

    Also fixed re-resolving with an existing lockfile hoisting a different peer version than a fresh install of the same manifest: root dependencies reused from the lockfile were invisible to peer hoisting, so a peer that a root dependency provides could be bound to another version.

  • a38adda: pnpm self-update <version> now installs the requested pnpm version when it matches the currently running version but is missing from the global self-update directory.

  • 6a85968: pnpm stage list now stops paginating after a fail-safe cap of 1000 pages, so a misbehaving registry cannot keep the command looping forever.

  • eee7c9a: verify-deps-before-run no longer spawns a pnpm install when pnpm is executed in a directory that has no package.json. A mistyped command run outside a project (for example pnpm witch 10 login) used to crash with a confusing error from the spawned install; now it fails with the regular "no package.json found" error.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.11.0: pnpm 11.11

Compare Source

Minor Changes

  • 508b8c2: Added the pnpm access command for managing package access and visibility on the registry, supporting listing packages and collaborators, getting and setting package status and MFA requirements, and granting or revoking team access.

Patch Changes

  • c70e33e: Allow allowBuilds entries for git-hosted packages to match by repository URL without pinning the resolved commit hash. This lets trusted git repositories keep running their build scripts after branch updates without approving each new commit, while package-name-only rules still do not approve git-hosted artifacts.
  • 3067e4f: Reduced peak memory usage during cold-cache dependency resolution. The metadata fetch is memoized for the whole resolution phase, and it was retaining each package's raw registry response body (used only to mirror the response to disk) for that entire time. The memoized cache now holds a body-less copy, so the raw body only lives as long as the call that writes the disk mirror. On large graphs that fetch full metadata (e.g. with minimumReleaseAge or trustPolicy enabled) this cuts peak RSS by roughly 30%, back in line with pnpm 10. The resolved lockfile is unchanged.
  • 51300fd: Prevent a crafted pnpm-lock.yaml from writing package content outside the virtual store. A dependency path key whose name reconstructs to a path-traversal sequence (e.g. ../../../tmp/x@1.0.0) is now rejected by the isolated (virtual-store) linker and the Plug'n'Play resolver map, matching the containment already applied to the hoisted linker. Under the global virtual store, a traversal in the version-derived path segment (e.g. a snapshot version: "../../x") is now rejected at formatGlobalVirtualStorePath, the single point every global-virtual-store slot path funnels through — closing the same escape in the isolated linker, the resolver's dependency-graph builder, and the config-dependency installer.
  • f8058eb: Reject symlinked pnpm-lock.yaml files when reading or writing the env lockfile document.
  • 9318a11: Allow registries and namedRegistries to be configured in the global config.yaml file.
  • 51300fd: Fixed a path traversal vulnerability where a dependency whose manifest name was a scoped path traversal (e.g. @x/../../../<path>) could be written outside node_modules to an attacker-controlled location during pnpm install, even with --ignore-scripts. The isolated linker now validates the package name before using it as a directory name, matching the existing protection in the hoisted linker.
  • 14332f0: Fail instead of silently removing an optional dependency's locked entries from pnpm-lock.yaml when the registry cannot resolve it. Previously, when registry metadata lacked a version that the lockfile already pinned (for example, a mirror that had not synced a recent release yet), pnpm install and pnpm dedupe silently dropped the optional dependency's entries — emptying maps such as the platform binaries of @napi-rs/canvas — so the lockfile differed between machines and frozen installs on other hosts had nothing to link #​12853.
  • fecfe83: Fixed peer dependency resolution with autoInstallPeers when a workspace package depends on a version of a package that a transitive dependency's self-contained closure also provides for itself. The peer providers that are attached to the root project for reuse are no longer peer-resolved a second time in the root context, so packages inside such a closure no longer get their peers bound to the root project's incompatible version #​4993.
  • 5a4daec: ${...} environment-variable placeholders in the httpProxy, httpsProxy, noProxy, proxy, and noproxy settings are no longer expanded when these settings come from a project's pnpm-workspace.yaml. They now receive the same protection already applied to registry, namedRegistries, and pnprServer.
  • d1da02e: pnpm publish no longer prints credentials when the target registry is configured with inline user:pass@ credentials (e.g. registry=https://user:pass@example.com/). They are now redacted both from the "publishing to registry" line and from the OIDC (trusted publishing) failure messages.
  • dcfc611: pnpm self-update now honors trustPolicy=no-downgrade. It resolves the target pnpm version against full registry metadata, so it refuses to switch to a version whose supply-chain trust evidence is weaker than an earlier-published one, the same way a regular install does.
  • a8ad82d: Register the pn alias in generated shell completion scripts.
  • 25bd5c3: Fixed standalone installer downgrades from pnpm v12 to v11.
  • 23996e9: pnpm runtime set <name> <version> now validates its arguments: the name must be node, deno, or bun, and the version must not contain a comma. Previously these were interpolated straight into a pnpm add selector, where an unsupported name or a comma (e.g. node 22,is-positive) could be misread as a list of packages or a local directory and install unintended packages or bins.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.10.0: pnpm 11.10

Compare Source

Minor Changes

  • e2e3c81: Added the issues command as an alias of bugs, so pnpm issues opens the package's bug tracker URL in the browser.

  • 8491f8e: Added the prefix command which prints the current package prefix directory (or global prefix directory if -g / --global is used).

  • 3425e80: Added an _auth setting for configuring registry authentication as a single structured (URL-keyed) value. It can be set in the global pnpm config (config.yaml) or, for CI, via the pnpm_config__auth environment variable. The env form sidesteps the GitHub Actions / bash / zsh limitation that broke the existing pnpm_config_//host/:_authToken=… form (env var names containing /, :, or . are silently dropped). Closes #​12314.

    The value is keyed by registry URL so each secret is explicitly bound to the host that may receive it. Registry URL keys must use http or https and must not include credentials, query strings, or fragments:

    export pnpm_config__auth='{"https://registry.npmjs.org":{"@":{"authToken":"npm-token"},"@org":{"authToken":"org-token"}}}'
    

    The equivalent in the global config.yaml:

    _auth:
      https://registry.npmjs.org:
        "@":
          authToken: npm-token
        "@org":
          authToken: org-token
    

    Within each registry URL, @ means registry-wide/default credentials and package scopes like @org bind credentials to that scope on the same host. The only supported credential field is authToken (maps to _authToken / bearer auth); the deprecated basicAuth / username + password forms are intentionally not accepted here.

    Each entry also infers a trusted registry route: @ routes the default registry (and pnpm add <pkg> resolves there), and @org routes that scope. Because the credential and destination host arrive in one trusted value, repo-controlled pnpm-workspace.yaml or project .npmrc cannot redirect the token to a different host. _auth is honored only from the env var and the global config — it is ignored in a project pnpm-workspace.yaml / .npmrc, so repo-controlled config can never supply registry auth. Precedence: CLI flags (--registry, --@scope:registry) > pnpm_config__auth > global config.yaml _auth > pnpm-workspace.yaml.

    Both pnpm_config__auth (lowercase, documented form) and PNPM_CONFIG__AUTH (all-caps, the shell convention some CI runners apply) are honored. If both are set, lowercase wins unless it is empty, in which case uppercase is used. The env var wins over the global config.yaml _auth on a conflicting key. tokenHelper is not supported in _auth. Parsing is strict: a malformed value (bad JSON, wrong shape, invalid registry URL or scope, an unsupported credential field) fails fast with an error rather than being silently dropped.

    Pacquet parity note: the pacquet (Rust) port supports the same single credential field as the TS CLI: authToken.

  • a33eeec: pnpm self-update and packageManager version-switching can now install and link pnpm v12 (the Rust port), published with equal content under both the pnpm and @pnpm/exe names on the next-12 dist-tag. Its native binaries ship as @pnpm/exe.<platform>-<arch> packages, which pnpm's built-in installer links directly — no Node.js launcher, so the command pays no Node startup cost. v12 is initialized exactly like @pnpm/exe, including per-platform global-virtual-store hashing. From v12 onward the install converges on the unscoped pnpm package (the Rust exe) — even when updating from the SEA @pnpm/exe build.

  • 1dd12bd: When resolving through a pnpr install-accelerator server, pnpm no longer forwards its own upstream registry credentials in the resolve request. Only the Authorization header identifying the caller to pnpr is sent. The pnpr server now selects upstream credentials from its own route policy (operator-configured upstream credential aliases), so private dependencies resolve through a pnpr-managed alias the caller is authorized to use, rather than by sending the client's registry tokens to the server.

  • 1e81761: Expose web authentication authUrl and doneUrl in JSON error output when OTP is required in a non-interactive terminal #​12724.

Patch Changes

  • 2f389d6: Added the Node.js release team's new signing key (Stewart X Addison, 655F3B5C1FB3FA8D1A0CA6BDE4A7D232B936D2FD) to the embedded Node.js release keys, so runtimes whose SHASUMS256.txt is signed by the new releaser verify successfully.

  • acbdb94: Fixed shell tab completion not suggesting workspaces after the -F alias for --filter option.

  • dcabb78: Fixed pnpm up -r <pkg> bumping unrelated packages that have open semver ranges. Previously, any update mutation nullified the lockfile-derived preferredVersions globally, so packages with ^x.y.z ranges could re-resolve to newer compatible versions even though the user only asked to update a specific package. The install layer now always seeds preferredVersions from the lockfile, and caller-supplied preferred versions (such as the vulnerability penalties of pnpm audit --fix) layer on top of the seed instead of replacing it. The targeted package still bumps: the per-resolve updateRequested flag makes the resolver ignore the target's own lockfile pins.

    Closes #​10662.

  • d539172: Fixed pnpm pack and pnpm publish failing when prepack generates files that are included in the package and postpack cleans them up.

  • be6505a: Hardened global package management:

    • On Windows, removing or updating a global package now also cleans up the node.exe flavor of a bin, so a stale node.exe no longer survives on PATH after uninstall, and a new global install no longer silently overwrites an existing node.exe.
    • pnpm add -g pnpm@<version> (and @pnpm/exe@<version>) is now rejected like the bare pnpm form, pointing to pnpm self-update.
    • Dependency aliases read from a global package's manifest are validated before being joined onto node_modules paths, preventing a tampered manifest from escaping the install directory.
    • Each global install group is created in its own freshly-made directory (no longer reusing a colliding or pre-existing path).
    • Removing or updating a global package no longer unlinks a bin that belongs to a different globally installed package.
  • 25c7388: pnpm now rejects jsr: specifiers whose package name is not a valid npm package name — an empty scope or name (e.g. jsr:@scope/), path separators inside the name, or any other shape validate-npm-package-name rejects — with ERR_PNPM_INVALID_JSR_PACKAGE_NAME instead of silently converting them into a malformed @jsr/... npm package name.

  • 25c7388: pnpm now rejects named-registry specifiers (e.g. gh:) whose package name is not a valid npm package name — an empty scope (e.g. gh:@/bar), path separators inside the name (e.g. gh:@scope/../name), or any other shape validate-npm-package-name rejects — with ERR_PNPM_INVALID_NAMED_REGISTRY_PACKAGE_NAME instead of passing the name through to registry URLs and metadata cache file paths.

  • 96da7c5: node-gyp's gyp_main.py and gyp entrypoints are now packed with the executable bit in the pnpm and @pnpm/exe tarballs. Without it, building native addons from source could fail with a permission error.

  • 99982b9: Sped up resolution and reduced memory use against registries that ignore npm's abbreviated metadata format and always return the full package document (for example, Azure DevOps Artifacts). pnpm now strips such documents down to the abbreviated field set before caching them. Resolution output is unchanged, and registries that honor the abbreviated format (such as the npm registry) pay no extra cost.

  • 11a7fdd: Sped up offline and --prefer-offline resolution on large workspaces (e.g. pnpm dedupe --offline, pnpm install --offline). Package metadata loaded from the local cache is now kept in memory, so each package's metadata is parsed once per command instead of once per dependent that references it.

  • 2c7369d: pnpm pack-app now rejects --entry / pnpm.app.entry and --output-dir / pnpm.app.outputDir values that are absolute paths or escape the project directory via .. (or a symlink that resolves outside it), and refuses to write the produced executable when its target path already exists as a symlink (or other non-regular file). This prevents a repository-controlled package.json from embedding host files (such as an SSH key) into the produced executable, writing build artifacts outside the project, or overwriting an arbitrary file through a committed symlink. The new error codes are ERR_PNPM_PACK_APP_ENTRY_OUTSIDE_PROJECT, ERR_PNPM_PACK_APP_OUTPUT_DIR_OUTSIDE_PROJECT, and ERR_PNPM_PACK_APP_OUTPUT_FILE_NOT_REGULAR.

    When ad-hoc signing macOS targets, pnpm pack-app now runs the system codesign by absolute path and resolves ldid to a location outside the project, so a repository-controlled node_modules/.bin on PATH cannot hijack the signer.

  • ce5d5a5: Relative paths in patchedDependencies are now resolved against the lockfile directory when computing patch file hashes, so running pnpm install from a subdirectory no longer fails with ENOENT looking for the patch file in the wrong location #​12762.

  • ebb4096: pnpm peers no longer reports a conflict for a missing peer dependency that is ignored via pnpm.peerDependencyRules.ignoreMissing.

  • dcabb78: Fixed a prototype-pollution hazard when seeding preferred versions: a dependency named __proto__ in a manifest or in pnpm-lock.yaml could write through Object.prototype (or crash the install) while the preferred-versions map was being built. The maps are now null-prototype objects, so crafted package names land as plain keys.

  • f38e696: Hardened pnpm deploy --force so it refuses unsafe deploy targets such as workspace roots, parent directories, out-of-workspace paths, and symlinked target parents.

  • 806c3ec: pnpm no longer warns about ignored project-level auth settings when PNPM_CONFIG_NPMRC_AUTH_FILE points at the project .npmrc — setting it to that file is an explicit opt-in to trusting it, so auth env variables in it are expanded pnpm/pnpm#12480.

  • 991405e: Restore differential rendering (ansi-diff) to fix duplicated output lines introduced by #​12351.

  • c121235: Fixed the topological order of --filtered commands (pnpm run, pnpm exec, pnpm publish, pnpm pack, pnpm rebuild) when the selected projects depend on each other only transitively through projects that were not selected. Previously such selected projects could run concurrently or in the wrong order; now a project always runs after the selected projects it transitively depends on, while projects without a real dependency relationship still run concurrently. This now also holds for prod-only filters (--filter-prod), which resolve order through the production dependency graph so transitive production dependencies are respected without pulling back the dev dependencies the filter drops, and for selections that mix --filter with --filter-prod #​8335.

  • d539172: pnpm pack and pnpm publish no longer follow a symlinked workspace LICENSE file when injecting it into a package that has no license of its own. Following the symlink could pack bytes from outside the workspace into the published tarball.

  • dcabb78: Fixed pnpm up <pkg> producing a different result than a fresh install of the same manifests would. The resolver now distinguishes updateRequested (true only for packages that match the user's update target) from the broader update flag, and for the targeted package ignores only its own lockfile-derived preferred-version pins — so the target re-resolves exactly as if its lockfile entries were deleted and pnpm install ran. Preferred versions a fresh install applies (manifest pins, versions propagated down the dependency chain, and the vulnerability-avoidance penalties of pnpm audit --fix) stay in effect, so an update never installs duplicate versions that a reinstall from scratch would not reproduce. When a preferred version holds the update target below the newest version its range admits, pnpm now prints a warning explaining that reaching the newer version everywhere requires an override.

  • dcabb78: pnpm update <dep>@<version> now prints a warning when <dep> is only present as a transitive dependency: the requested version cannot be applied there (updates resolve the target the way a fresh install would), and the warning recommends adding the version to pnpm.overrides instead, which is the mechanism that does pin transitive dependencies. Closes #​12744.

  • a6c4d5f: When a dependency cannot be found in the registry (404) or the registry has no matching version, and a workspace project with the same name exists only at non-matching versions, the error now reports the available workspace versions (ERR_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE) instead of the raw registry failure pnpm/pnpm#1379. Other registry failures (authorization, network, server errors) still propagate unchanged. The pacquet (Rust) resolver applies the same behavior.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

Configuration

📅 Schedule: (in timezone Europe/Berlin)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Mend Renovate CLI.

This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@astrojs/starlight](https://starlight.astro.build) ([source](https://github.com/withastro/starlight/tree/HEAD/packages/starlight)) | [`^0.41.0` → `^0.42.0`](https://renovatebot.com/diffs/npm/@astrojs%2fstarlight/0.41.1/0.42.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@astrojs%2fstarlight/0.42.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@astrojs%2fstarlight/0.41.1/0.42.0?slim=true) | | [pnpm](https://github.com/pnpm/pnpm/tree/main/pnpm) ([source](https://github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm)) | [`11.9.0` → `11.26.0`](https://renovatebot.com/diffs/npm/pnpm/11.9.0/11.26.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/pnpm/11.26.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/pnpm/11.9.0/11.26.0?slim=true) | --- ### Release Notes <details> <summary>withastro/starlight (@&#8203;astrojs/starlight)</summary> ### [`v0.42.0`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0420) [Compare Source](https://github.com/withastro/starlight/compare/@astrojs/starlight@0.41.11...@astrojs/starlight@0.42.0) ##### Minor Changes - [#&#8203;3572](https://github.com/withastro/starlight/pull/3572) [`292fb17`](https://github.com/withastro/starlight/commit/292fb179cbf6e2cd970ced46359c520aa14a80b4) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Distributes package as JavaScript files with dedicated type declaration files instead of TypeScript source files. - [#&#8203;4121](https://github.com/withastro/starlight/pull/4121) [`2623ae6`](https://github.com/withastro/starlight/commit/2623ae60af0fdafc71938f96153b0427fcc2b5aa) Thanks [@&#8203;delucis](https://github.com/delucis)! - Simplifies markup for Starlight’s mobile menu toggle **⚠️ Potentially breaking change:** If you use a theme plugin, custom styles, or component overrides targeting the `MobileMenuToggle` button or `PageFrame` components, you may need to adjust these for the new markup. The button is no longer wrapped in a `<starlight-menu-button>` custom element and no longer uses the `aria-expanded` attribute. Instead, you can use the `.sl-menu-button` class name to target the button and the `:popover-open` pseudo-class to style the menu open state specifically. In the following example, custom styles for the menu button are updated for the new approach: ```diff - starlight-menu-button button { + .sl-menu-button { color: var(--sl-color-text); } - starlight-menu-button[aria-expanded='true'] button { + .sl-menu-button:has(~ :popover-open) { color: var(--sl-color-text-accent-high); } ``` See [`MobileMenuToggle.astro`](https://github.com/withastro/starlight/blob/main/packages/starlight/components/MobileMenuToggle.astro) and [`PageFrame.astro`](https://github.com/withastro/starlight/blob/main/packages/starlight/components/PageFrame.astro) on GitHub for the full source code of the updated components. - [#&#8203;3572](https://github.com/withastro/starlight/pull/3572) [`292fb17`](https://github.com/withastro/starlight/commit/292fb179cbf6e2cd970ced46359c520aa14a80b4) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Removes the `tagline` configuration option, which was never used. If your configuration included a `tagline` option, you can safely remove it without any replacement. - [#&#8203;4134](https://github.com/withastro/starlight/pull/4134) [`6135f01`](https://github.com/withastro/starlight/commit/6135f015b527453d7083ad01d2dc6c61d72fbefc) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Updates internal `@astrojs/mdx`, `@astrojs/markdown-satteri`, and `satteri` dependencies. ⚠️ **BREAKING CHANGE:** The following minimum versions are now required: - `astro` v7.2.10 or later - `@astrojs/markdown-satteri` 0.4.0 or later (if you use it) - `@astrojs/markdown-remark` 7.3.0 or later (if you use it) Please update Starlight and Astro together: ```sh npx @astrojs/upgrade ``` - [#&#8203;4121](https://github.com/withastro/starlight/pull/4121) [`2623ae6`](https://github.com/withastro/starlight/commit/2623ae60af0fdafc71938f96153b0427fcc2b5aa) Thanks [@&#8203;delucis](https://github.com/delucis)! - Refactors Starlight’s mobile menu toggle to work when JavaScript fails or is disabled ⚠️ **BREAKING CHANGE:** This release drops official support for Chromium-based browsers prior to version 116 (released August 2023), Safari-based browsers prior to version 17.0 (released September 2023), and Firefox prior to version 125 (released April 2024). You can find a list of currently supported browsers and their versions using this [browserslist query](https://browsersl.ist/#q=%3E+0.5%25%2C+not+dead%2C+Chrome+%3E%3D+116%2C+Edge+%3E%3D+116%2C+Firefox+%3E%3D+125%2C+Safari+%3E%3D+17.0%2C+iOS+%3E%3D+17.0%2C+not+op_mini+all). This change also removes the `data-mobile-menu-expanded` attribute, which was previously added to `<body>` while the mobile menu is open. If you have custom code that was depending on this attribute, you will need to update it to use a new selector to check if the mobile menu is open. In the following example, a custom background colour for the site header while the menu is open is updated for the new approach: ```diff - [data-mobile-menu-expanded] header { + body:has(sl-sidebar-pane:popover-open) header { background-color: var(--sl-color-bg); } ``` ### [`v0.41.11`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#04111) [Compare Source](https://github.com/withastro/starlight/compare/@astrojs/starlight@0.41.10...@astrojs/starlight@0.41.11) ##### Patch Changes - [#&#8203;4167](https://github.com/withastro/starlight/pull/4167) [`32a5e29`](https://github.com/withastro/starlight/commit/32a5e29127c13946ce746c36d7cb194f35d11952) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Fixes a layout issue and anchor links appearing for hidden headings, e.g. footnote headings, when `markdown.headingLinks` is enabled or the `<AnchorHeading>` component is used. - [#&#8203;4148](https://github.com/withastro/starlight/pull/4148) [`cdfafd8`](https://github.com/withastro/starlight/commit/cdfafd8eedbbd6653cd6a1d3b1542b44ed1ba19e) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Optimizes sidebar data generation logic to speed up sites with large sidebars ### [`v0.41.10`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#04110) [Compare Source](https://github.com/withastro/starlight/compare/@astrojs/starlight@0.41.9...@astrojs/starlight@0.41.10) ##### Patch Changes - [#&#8203;4160](https://github.com/withastro/starlight/pull/4160) [`88442c3`](https://github.com/withastro/starlight/commit/88442c333612058cb1b2c56ef689053a5994e31e) Thanks [@&#8203;ArmandPhilippot](https://github.com/ArmandPhilippot)! - Adds the missing translations for Expressive Code in the following locales: `ar`, `hi`, `it`, `ja`, `ko`, `pt`, `zh-CN`, and `zh-TW`. - [#&#8203;4156](https://github.com/withastro/starlight/pull/4156) [`57af92d`](https://github.com/withastro/starlight/commit/57af92d91f9a9d0936a06efb4387898aded62aa9) Thanks [@&#8203;pyxelr](https://github.com/pyxelr)! - Adds 1 new icon: `hypothesis` - [#&#8203;4159](https://github.com/withastro/starlight/pull/4159) [`a0e0ba2`](https://github.com/withastro/starlight/commit/a0e0ba263c93bab214222b798b52a759fb799ba7) Thanks [@&#8203;trueberryless](https://github.com/trueberryless)! - Adds 1 new icon: `npmx` ### [`v0.41.9`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0419) [Compare Source](https://github.com/withastro/starlight/compare/@astrojs/starlight@0.41.8...@astrojs/starlight@0.41.9) ##### Patch Changes - [#&#8203;4145](https://github.com/withastro/starlight/pull/4145) [`1c90d69`](https://github.com/withastro/starlight/commit/1c90d69b424aedef096e12577e9e7ec96bed4efc) Thanks [@&#8203;pyxelr](https://github.com/pyxelr)! - Adds 1 new icon: `goodreads` ### [`v0.41.8`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0418) [Compare Source](https://github.com/withastro/starlight/compare/@astrojs/starlight@0.41.7...@astrojs/starlight@0.41.8) ##### Patch Changes - [#&#8203;4142](https://github.com/withastro/starlight/pull/4142) [`cacbc9f`](https://github.com/withastro/starlight/commit/cacbc9ff45c551c2f20c6b307800f7ff7ee07db2) Thanks [@&#8203;alebelcor](https://github.com/alebelcor)! - Adds 1 new icon: `whatsApp` - [#&#8203;4133](https://github.com/withastro/starlight/pull/4133) [`3944311`](https://github.com/withastro/starlight/commit/3944311bed46f956e12e79a8eb0c32084eeeaea6) Thanks [@&#8203;delucis](https://github.com/delucis)! - Internal refactor: numbered `id` attributes in the `<Tabs>` component are now page-specific instead of using a global counter - [#&#8203;4138](https://github.com/withastro/starlight/pull/4138) [`cd4b665`](https://github.com/withastro/starlight/commit/cd4b6655f3235220cded12f0567e0dad1bf5adac) Thanks [@&#8203;delucis](https://github.com/delucis)! - Fixes localisation of code block UI elements when using the Sätteri Markdown processor ### [`v0.41.7`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0417) [Compare Source](https://github.com/withastro/starlight/compare/@astrojs/starlight@0.41.6...@astrojs/starlight@0.41.7) ##### Patch Changes - [#&#8203;4114](https://github.com/withastro/starlight/pull/4114) [`3e486fb`](https://github.com/withastro/starlight/commit/3e486fba2015589c55aa452746eb17b407a4716e) Thanks [@&#8203;delucis](https://github.com/delucis)! - Fixes processing of code examples in RTL languages when using Astro’s Sätteri Markdown processor ### [`v0.41.6`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0416) [Compare Source](https://github.com/withastro/starlight/compare/@astrojs/starlight@0.41.5...@astrojs/starlight@0.41.6) ##### Patch Changes - [#&#8203;4102](https://github.com/withastro/starlight/pull/4102) [`f1dddc0`](https://github.com/withastro/starlight/commit/f1dddc057dc2b52092058f39679a5790b9cf0a38) Thanks [@&#8203;delucis](https://github.com/delucis)! - Adds 1 new icon: `forgejo` ### [`v0.41.5`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0415) [Compare Source](https://github.com/withastro/starlight/compare/@astrojs/starlight@0.41.4...@astrojs/starlight@0.41.5) ##### Patch Changes - [#&#8203;4095](https://github.com/withastro/starlight/pull/4095) [`bb06434`](https://github.com/withastro/starlight/commit/bb06434a1bc199b59f9d7953d0e41c6c287b1aa2) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Fixes a regression when using a union to [extend](https://starlight.astro.build/reference/frontmatter/#extend) Starlight’s `docsSchema()`. ### [`v0.41.4`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0414) [Compare Source](https://github.com/withastro/starlight/compare/@astrojs/starlight@0.41.3...@astrojs/starlight@0.41.4) ##### Patch Changes - [#&#8203;3936](https://github.com/withastro/starlight/pull/3936) [`712eedd`](https://github.com/withastro/starlight/commit/712eedd8e0d28329feb361edc392438f37ba2095) Thanks [@&#8203;miichom](https://github.com/miichom)! - Fixes support for modifying Zod enums when passing an [`extend` option](https://starlight.astro.build/reference/frontmatter/#extend) to Starlight’s `docsSchema()` - [#&#8203;4092](https://github.com/withastro/starlight/pull/4092) [`0896b91`](https://github.com/withastro/starlight/commit/0896b91607325b9d8494eb665cd1716a40025a5a) Thanks [@&#8203;delucis](https://github.com/delucis)! - Fixes support for links containing a protocol like `mailto:` in the sidebar - [#&#8203;4088](https://github.com/withastro/starlight/pull/4088) [`4486ba4`](https://github.com/withastro/starlight/commit/4486ba432afe9e206f0b05651de24a9c25bdf6dd) Thanks [@&#8203;delucis](https://github.com/delucis)! - Simplifies Starlight’s client-side sidebar state persistence script slightly ### [`v0.41.3`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0413) [Compare Source](https://github.com/withastro/starlight/compare/@astrojs/starlight@0.41.2...@astrojs/starlight@0.41.3) ##### Patch Changes - [#&#8203;3911](https://github.com/withastro/starlight/pull/3911) [`1686ecc`](https://github.com/withastro/starlight/commit/1686ecce3fd2da2aa9973faba2dc585abebb93a3) Thanks [@&#8203;timothyjordan](https://github.com/timothyjordan)! - Keeps keyboard focus inside the mobile menu while it is open, preventing focus moving to hidden interactive elements in page content. ### [`v0.41.2`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0412) [Compare Source](https://github.com/withastro/starlight/compare/@astrojs/starlight@0.41.1...@astrojs/starlight@0.41.2) ##### Patch Changes - [#&#8203;4008](https://github.com/withastro/starlight/pull/4008) [`58a3520`](https://github.com/withastro/starlight/commit/58a352097016ffbd98716688e4f2dfb97e5a6f44) Thanks [@&#8203;FrancoKaddour](https://github.com/FrancoKaddour)! - Fixes the table of contents overflowing the right edge of the viewport when a custom `--sl-content-width` value exceeds available space - [#&#8203;4015](https://github.com/withastro/starlight/pull/4015) [`bdbfffc`](https://github.com/withastro/starlight/commit/bdbfffc044dbf119e5085c3e67722494ae3c85c6) Thanks [@&#8203;delucis](https://github.com/delucis)! - Fixes an issue where aside icons were rendered incorrectly in projects where Astro’s MDX integration had optimization disabled </details> <details> <summary>pnpm/pnpm (pnpm)</summary> ### [`v11.26.0`](https://github.com/pnpm/pnpm/releases/tag/v11.26.0): pnpm 11.26 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.25.0...v11.26.0) #### Minor Changes - Catalogs can now resolve workspace dependencies through the `workspace:` protocol. - `pnpm remove` and `pnpm update` now accept `--trust-lockfile`, `--no-trust-lockfile`, `--trust-policy`, `--trust-policy-exclude`, and `--trust-policy-ignore-after`. `pnpm remove` checks the whole lockfile against the active policies unless `--trust-lockfile` is set. - Added `pnpm change check` for CI validation of package versions against the `versioning.epics` bands and `versioning.fixed` groups in `pnpm-workspace.yaml`. #### Patch Changes - Fetch and tarball errors and retry logs now hide URL credentials, query strings, and fragments that could expose secrets. - Fixed a race during config dependency updates that could redirect a lockfile write through a symlink [#&#8203;14322](https://github.com/pnpm/pnpm/issues/14322). - `pnpm add --allow-build=!<pkg>` now correctly denies builds, including in global installs. `pnpm approve-builds <pkg>` and `pnpm approve-builds !<pkg>` now save decisions even when the package is not awaiting approval, with a warning [#&#8203;14067](https://github.com/pnpm/pnpm/issues/14067). - Fixed `pnpm audit --fix` failing without a value or when followed by another flag. `pnpm audit --fix=override` now respects `saveExact` and `savePrefix` when writing overrides [#&#8203;13261](https://github.com/pnpm/pnpm/issues/13261), [#&#8203;11523](https://github.com/pnpm/pnpm/issues/11523). - `pnpm audit` now excludes ignored advisories from vulnerability totals and severity counts, and reports them separately [#&#8203;14535](https://github.com/pnpm/pnpm/issues/14535). - `pnpm deploy` no longer requires `injectWorkspacePackages`. If a workspace dependency's peer has multiple possible versions, deployment reports `ERR_PNPM_DEPLOY_AMBIGUOUS_PEER` with the conflicting versions. Pin the peer with `overrides` to deploy without injection [#&#8203;9386](https://github.com/pnpm/pnpm/issues/9386). - Fixed concurrent installs sharing a store occasionally failing with an `ENOENT` error while importing a package file [#&#8203;14353](https://github.com/pnpm/pnpm/issues/14353). - Fixed installation failures when a linked local dependency provides a peer dependency also provided by an ancestor, including with `pnpm deploy --legacy`. - `pnpm install --node-linker=hoisted` no longer downloads skipped optional dependencies when `node_modules` already exists [#&#8203;14139](https://github.com/pnpm/pnpm/issues/14139). - Fixed `pnpm install` rejecting a symlinked lockfile when config dependencies are unchanged. Updates to config dependencies also preserve lockfiles with a byte order mark. Writes through symlinked lockfiles remain blocked [#&#8203;14372](https://github.com/pnpm/pnpm/issues/14372). - `pnpm install` now relinks workspace packages when `publishConfig.linkDirectory` changes. Frozen installs require the lockfile to be regenerated [#&#8203;14488](https://github.com/pnpm/pnpm/issues/14488). - Auto-installed optional peers now satisfy their declared range even when the workspace root uses a version outside that range [#&#8203;13867](https://github.com/pnpm/pnpm/issues/13867). - Fixed global virtual store paths for dependency cycles to consistently account for the runtime engine when dependencies have allowed builds [#&#8203;14341](https://github.com/pnpm/pnpm/issues/14341). - Standalone installations now preserve the bundled `node-gyp` files needed to build native dependencies. - Downloaded runtimes are now available to dependency lifecycle scripts during installation. - Node.js downloads from `nodeDownloadMirrors` now use URL-scoped npm credentials, including bearer tokens, basic auth, and `tokenHelper` [#&#8203;14334](https://github.com/pnpm/pnpm/issues/14334). - Fixed `globalDir` and `globalBinDir` handling in global configuration and environment variables, including `~/` expansion. This fixes `pnpm add -g` failing after `pnpm config set -g global-bin-dir` [#&#8203;14336](https://github.com/pnpm/pnpm/issues/14336). - The JavaScript pnpm can again switch to the project's pinned pnpm version on hosts without a matching native binary. If the requested version requires an unavailable native binary, the error now identifies the unsupported host [#&#8203;13622](https://github.com/pnpm/pnpm/issues/13622). - Global `pnpm config` commands now skip project package manager version switching, allowing authentication to be configured before downloading the pinned version [#&#8203;14463](https://github.com/pnpm/pnpm/issues/14463). - `pnpm self-update`, `pnpm with`, and automatic version switching no longer wait through registry retries when a configured registry has no signatures and `registry.npmjs.org` is unavailable [#&#8203;14483](https://github.com/pnpm/pnpm/issues/14483). - Fixed argument forwarding on Windows with `shellEmulator` enabled. Trailing backslashes, line breaks, and literal shell expressions are preserved [#&#8203;14548](https://github.com/pnpm/pnpm/issues/14548). - Relative `scriptShell` paths now resolve from the workspace root. Bare command names such as `bash` still use `PATH` [#&#8203;14422](https://github.com/pnpm/pnpm/issues/14422). - `pnpm import` now preserves the project-local lockfile when `lockfileDir` points elsewhere and restores the destination lockfile on failure. Branch lockfile imports leave the shared lockfile unchanged [#&#8203;14563](https://github.com/pnpm/pnpm/issues/14563). - `catalogMode` and `--save-catalog` no longer move local paths, tarballs, or `workspace:<path>` specifiers into catalogs [#&#8203;14437](https://github.com/pnpm/pnpm/issues/14437). - `--side-effects-cache`, `--no-side-effects-cache`, and `PNPM_CONFIG_SIDE_EFFECTS_CACHE` now toggle only the local cache, preserving any remote cache configured in `sideEffectsCache`. - `pnpm unpublish` now handles registry two-factor authentication challenges through web authentication or a one-time password prompt [#&#8203;14464](https://github.com/pnpm/pnpm/issues/14464). - `pnpm outdated` and `pnpm update` now follow GitHub Actions references using self-repository syntax, such as `uses: $/.github/actions/setup`. - `pnpm remove` now accepts `--unsafe-perm`. <!-- 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> <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> <td align="center" valign="middle"> <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a> </td> </tr> <tr> <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> </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://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> <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> </tr> <tr> <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> <td align="center" valign="middle"> <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a> </td> </tr> </tbody> </table> <!-- sponsors end --> ### [`v11.25.0`](https://github.com/pnpm/pnpm/releases/tag/v11.25.0): pnpm 11.25 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.24.0...v11.25.0) #### Minor Changes - Added an opt-in proof of concept that lets installs reuse a dependency's build output across machines, by publishing and restoring signed, organization-scoped artifacts through pnpr instead of running the lifecycle scripts locally. Configure it with the new `remoteSideEffectsCache` setting. A workspace names the eligible `organization` and `packages`; everything describing the act of signing — `publish`, `keyId`, `builderId`, `trustedKeys`, `privateKey` and the provenance fields — is refused in `pnpm-workspace.yaml` and read from the global config file or the environment instead. - Added macOS and Windows x64 and arm64 support to remote shared build artifacts [pnpm/pnpm#13771](https://github.com/pnpm/pnpm/issues/13771). - Added the `audit.ignorePrune` setting. When set to `true`, `pnpm audit --fix` removes ignored GHSA entries that no longer appear in the audit report. - Generalized the experimental shared-artifact protocol so candidates and signed payloads identify a discriminated subject. Dependency side effects use package and source-integrity subjects, while workspace tasks use project and task subjects. This changes shared-artifact request bodies and signed payloads. A pnpr server and its clients have to be on matching versions. - `pnpm init` now pins the latest pnpm version, instead of the version of pnpm that ran the command. A project scaffolded by an outdated pnpm therefore no longer inherits that staleness through its own `devEngines.packageManager` / `packageManager` pin [#&#8203;7490](https://github.com/pnpm/pnpm/issues/7490). The version is read from the `latest` tag on the package-manager registries. When that lookup cannot answer — no network, an unreachable or slow registry, `offline`, or a `latest` that the `minimumReleaseAge` / `trustPolicy` settings reject — `pnpm init` pins the running version as before, and never fails or hangs on the lookup. A `latest` that is older than the running pnpm is never pinned either. - A `scope` set in a project's `pnpm-workspace.yaml` is now ignored, with a warning naming where to set it instead. `pnpm login` records the scope as a `@scope:registry` route in the machine-global `auth.ini`, which outranks `~/.npmrc` in every project — so a repository-committed file could redirect a scope such as `@acme` for all of a user's other projects after one routine login. Use `--scope`, the `PNPM_CONFIG_SCOPE` environment variable, or the global config file instead [#&#8203;13557](https://github.com/pnpm/pnpm/issues/13557). - Verified remote build artifacts are persisted in the shared store with their signed origin metadata. Later installs reverify the artifact against current trust, policy, platform, and source before reuse, while invalid remote variants are quarantined per channel ([pnpm/pnpm#13771](https://github.com/pnpm/pnpm/issues/13771)). - Persist completed recursive tasks so `--resume-from` skips exactly the work that passed during a matching interrupted or failed `pnpm -r run` / `pnpm -r exec` invocation. When no compatible state exists, pnpm retains its graph-based resume behavior. - Allowed `pnpm update --patches` to refresh registry revisions through a configured pnpr server while retaining locked package versions. - Added explicit registry revision selection with `<version>+rN` and `pnpm update --patches` for refreshing revision artifacts without changing package versions. Registry-backed lockfile policy checks recognize historical revisions, and pnpr now preserves safe revision histories from upstream registries. - Workspace install, rebuild, pack, publish, stage, and lifecycle work now starts as soon as its dependencies finish instead of waiting for an unrelated topological group. - `pnpm stage approve` now approves several staged packages at once. Run it without a stage id to pick from the staged versions interactively, or pass a list of stage ids. The whole batch is approved with a single one-time password, and pnpm asks for a new one only once the registry stops accepting it. Inside a workspace, the selected packages are approved in dependency order, and a package whose workspace dependency could not be approved is skipped instead of being published against a dependency that never reached the registry. - Added per-task concurrency limits to workspace task orchestration. Set `tasks.<name>.concurrency` in `pnpm-workspace.yaml` to limit how many instances of that task may run across workspace projects at once: ```yaml tasks: build: concurrency: 2 ``` - Added support for registry replacement tarballs using standard integrity values, explicit revision fields, registry routing from the `registries` setting, non-redirecting integrity-addressed URLs, canonical safe-integer revision numbers, and pnpr proxying for immutable upstream revision artifacts. - `sideEffectsCache` now declares the whole of how a package's build output is reused — whether one is restored, whether one is saved, and the remote tier that shares it between machines: ```yaml sideEffectsCache: read: true write: true remote: org: acme packages: ['native-addon'] ``` `sideEffectsCache: true`, `sideEffectsCacheReadonly`, `remoteSideEffectsCache`, and its `organization` field all keep working. Where a field is set under both spellings the one above wins; where it is set under only one, it is kept. Two behaviors change, both bringing this CLI in line with what the Rust one already did: `sideEffectsCacheReadonly: true` now blocks writing to the cache, and setting it alongside `sideEffectsCache: false` gives a read-only view rather than switching the cache off entirely. A cache can also be declared write-only now, to populate one the run does not read. - Workspace task orchestration ([pnpm/rfcs#23](https://github.com/pnpm/rfcs/pull/23)). `pnpm -r run` and `pnpm -r exec` now schedule per task instead of in topological chunks: a task starts as soon as the tasks it depends on have finished, so a project no longer waits for unrelated projects that happen to share its chunk. A new `tasks` section in `pnpm-workspace.yaml` declares what a task depends on, using the `^` convention: ```yaml tasks: build: dependsOn: ['^build'] test: dependsOn: ['build'] lint: {} ``` `^name` means the named task in each of the project's workspace dependencies; a bare `name` means the task in the same project; an entry with no `dependsOn` declares an empty dependency list. A task with no entry behaves as `dependsOn: ['^<its own name>']`, which is exactly what the previous chunked ordering implied — an unconfigured workspace gets the scheduler improvement and nothing else changes meaning. A project without the script is reported skipped and passes its edges through to its own dependencies, so a scriptless package does not sever a chain. Also part of this change: - A dependency cycle among the tasks of a run is now an error naming the participating tasks (`ERR_PNPM_TASK_CYCLE`) instead of silently running in an arbitrary order. Setting `ignoreWorkspaceCycles: true` downgrades the error to a warning: the cycle's tasks run in an arbitrary order relative to each other. - `--resume-from` now skips exactly the transitive dependencies of the anchor package; work unrelated to the anchor still runs. - Under `--no-bail`, tasks whose dependencies failed are reported as skipped, not failed, and do not add to the exit code. - With `--bail` (the default), the first failure still ends the run at once and nothing new is dispatched — including scripts already queued behind the concurrency limit. - `pnpm -r run --dry-run <script>` prints the task graph that would execute without running anything (including skipping the `verifyDepsBeforeRun` check); `--json` emits the tasks and their resolved dependency edges. - Output is inherited rather than piped only when at most one script can ever be in flight (`--workspace-concurrency=1`, or the graph forces the scripts to run one after another). #### Patch Changes - An `_auth` entry in the global config file no longer decides which registry packages come from when something else says. A `registry` or `registries` declared in `pnpm-workspace.yaml` or the global config now wins over the route inferred from a stored credential, which still applies where nothing else declares one. The `pnpm_config__auth` environment variable is unchanged: it stays the way to point a CI runner at a mandated proxy, and still overrides what a repository declares. - Prevent installs through a symlinked `node_modules` directory from rewriting the target checkout [pnpm/pnpm#14286](https://github.com/pnpm/pnpm/issues/14286). - Treat empty scripts selected by a regular expression as missing before running dependent tasks. - The options type of the `fetch` command now declares `allowBuilds`, a setting its handler already forwarded to the installer. Type-level only — what `pnpm fetch` does is unchanged. - Filter hidden scripts matched by a regular expression during recursive runs when a visible script also matches. - Fixed automatically switched pnpm versions forcing all descendant pnpm processes to use the same version [pnpm/pnpm#14309](https://github.com/pnpm/pnpm/issues/14309). - Fixed `ERR_PNPM_UNUSED_PATCH` validation during incremental installs [pnpm/pnpm#13692](https://github.com/pnpm/pnpm/issues/13692). - Fixed `pnpm deploy --prod` failing when an excluded dev dependency was also declared as an optional peer dependency [pnpm/pnpm#14302](https://github.com/pnpm/pnpm/issues/14302). - `pnpm update -g` no longer downgrades a global package. `--latest` resolves the `latest` dist-tag, which can point at an older release than the one installed — after `pnpm add -g <pkg>@next`, for instance [#&#8203;14270](https://github.com/pnpm/pnpm/issues/14270). `pnpm update -g` also no longer changes the pnpm version. pnpm's own global install belongs to `pnpm self-update` [#&#8203;14270](https://github.com/pnpm/pnpm/issues/14270). - Copying a built package to its other hoisted locations no longer replaces the destination directory. With `nodeLinker: hoisted`, that replacement deleted the dependencies nested inside the destination's `node_modules`, and made concurrent copies of the same build chunk fail with `ERR_PNPM_ENOENT: no such file or directory, rename '.../node_modules/_tmp_...'` [#&#8203;12880](https://github.com/pnpm/pnpm/issues/12880). - `pnpm update` no longer replaces the specifier a project declares for a dependency that is also listed in `overrides`. A `catalog:` reference stays a `catalog:` reference, and a declared range stays as written, instead of being rewritten to the version the override resolved to [#&#8203;12115](https://github.com/pnpm/pnpm/issues/12115). - `pnpm update` no longer moves the range a project declares for a dependency that `overrides` also lists, even when the override repeats that range verbatim. Previously the updated `package.json` disagreed with the lockfile, so the next `pnpm install --frozen-lockfile` failed with a specifier mismatch [#&#8203;14224](https://github.com/pnpm/pnpm/issues/14224). - Make `pnpm add --lockfile-only` skip dependency linking [pnpm/pnpm#14286](https://github.com/pnpm/pnpm/issues/14286). - `--production` is accepted again as an alias of `--prod` on `install`, `fetch`, `prune`, `update`, `list`, `why`, and `sbom`, and the install that `verifyDepsBeforeRun` reproduces is now spelled with `--prod`. `pnpm run` no longer aborts with "unexpected argument '--production' found" after a production-only install [#&#8203;14147](https://github.com/pnpm/pnpm/issues/14147). - The progress output no longer overwrites the lines above it once it grows taller than the terminal window [#&#8203;14270](https://github.com/pnpm/pnpm/issues/14270). - Restoring a dependency's build from the remote side-effects cache no longer downloads files the store already holds. - Forward `patchedDependencies` hashes and `packageExtensions` to pnpr so server-side resolution preserves patches and package extensions in the lockfile and installed packages. - Published the workspace task graph and scheduler as `@pnpm/workspace.task-scheduler` so other workspace commands can use the same dependency-aware scheduling as recursive run and exec. - The environment variables for the remote side-effects cache are named for the setting they configure: `PNPM_SIDE_EFFECTS_CACHE_REMOTE_KEY_ID`, `..._BUILDER_ID`, `..._IMAGE_DIGEST`, `..._ARCHITECTURE_BASELINE`, `..._PRIVATE_KEY`, `..._BUILD_ENV`, `..._TRUSTED_KEYS` and `..._PUBLISH`. The `PNPM_REMOTE_SIDE_EFFECTS_CACHE_*` names keep working, and the new one wins when both are set. - A `devEngines.packageManager` range pin on pnpm is now recorded in `pnpm-lock.yaml`'s `packageManagerDependencies` when the running pnpm already satisfies it, using the running version and keeping the range as the recorded specifier. Previously only an exact pin — or a range resolved on the way through a version switch — reached the lockfile, so a range pin written by hand (or by any tool other than `pnpm add` / `pnpm self-update`) left the project without the shared resolution the pin exists to provide. - Fixed recursive `run` cleanup on Windows when a lifecycle script fails while another script's process tree is still running. - The update notification now suggests `pnpm self-update` when `PNPM_HOME` manages the pnpm in use, and the [standalone install script](https://pnpm.io/installation) otherwise — under Corepack, or when another package manager installed pnpm. `pnpm self-update` under Corepack names the standalone install script too. - Enforce `allowBuilds` when a prepared git dependency is reused from the shared store, and use the lockfile's canonical git resolution ID in approval suggestions. - Topologically sorting workspace projects now runs in linear time, fixing installs and lockfile updates that stalled for seconds on workspaces with thousands of projects forming deep dependency chains [#&#8203;14149](https://github.com/pnpm/pnpm/issues/14149), [#&#8203;14151](https://github.com/pnpm/pnpm/issues/14151). <!-- 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> <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> <td align="center" valign="middle"> <a href="https://notion.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/notion.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/notion_light.svg" /> <img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion" /> </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> <td align="center" valign="middle"> <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a> </td> </tr> </tbody> </table> <!-- sponsors end --> ### [`v11.24.0`](https://github.com/pnpm/pnpm/releases/tag/v11.24.0): pnpm 11.24 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.23.0...v11.24.0) ##### Minor Changes - Added global build approvals [pnpm/pnpm#14101](https://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 [#&#8203;14124](https://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 [#&#8203;13966](https://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://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 [#&#8203;14101](https://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 [#&#8203;14124](https://github.com/pnpm/pnpm/issues/14124). ### [`v11.23.0`](https://github.com/pnpm/pnpm/releases/tag/v11.23.0): pnpm 11.23 [Compare Source](https://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 [#&#8203;14101](https://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://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://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 [#&#8203;13872](https://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 [#&#8203;13824](https://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 [#&#8203;11563](https://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://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 [#&#8203;13814](https://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://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 [#&#8203;13765](https://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 [#&#8203;13623](https://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 [#&#8203;13534](https://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://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://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 [#&#8203;13919](https://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://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 [#&#8203;14009](https://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 [#&#8203;13999](https://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 [#&#8203;12854](https://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` [#&#8203;12297](https://github.com/pnpm/pnpm/issues/12297). - Fixed pnpm failing to read `.modules.yaml` files containing long dependency paths [#&#8203;13875](https://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 [#&#8203;13912](https://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://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://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://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 [#&#8203;14047](https://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 [#&#8203;13898](https://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 [#&#8203;13935](https://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://github.com/pnpm/pnpm/issues/13956). - `pnpm update` now preserves the existing range operator when updating a prerelease dependency. See [#&#8203;7002](https://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://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 [#&#8203;10167](https://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 [#&#8203;12446](https://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 [#&#8203;13816](https://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://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 --> ### [`v11.22.0`](https://github.com/pnpm/pnpm/releases/tag/v11.22.0): pnpm 11.22 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.21.0...v11.22.0) #### Minor Changes - Added `pnpm cache path`, which prints the directory pnpm uses for its metadata cache. CI setups can use it to cache that directory — including the lockfile verification log, which lets a job skip re-checking an unchanged lockfile against the configured supply-chain policies. - `--config.config-dir` no longer reaches the config through a project's `pnpm-workspace.yaml`, and neither do the `--config.` spellings of the other settings a project manifest may no longer contribute (`--config.pnpm-home-dir`, `--config.workspace-dir`, `--config.global-pkg-dir`, `--config.root-project-manifest-dir`). None of them was ever a supported way to set those directories: pnpm resolves them from the environment, and these flags took effect only because the project-manifest merge re-applied the command line afterwards. The dedicated flags, such as `--dir` and `--global-dir`, are unaffected [#&#8203;13629](https://github.com/pnpm/pnpm/issues/13629). - `pnpm config set` refuses to write a setting to a project's `pnpm-workspace.yaml` that pnpm does not read from there, rather than leaving a key in the file that does nothing. Those settings are `configDir`, `pnpmHomeDir`, `stateDir` and the others that name machine-level state. The command fails with `ERR_PNPM_CONFIG_SET_NOT_A_PROJECT_SETTING`, naming where the setting does belong when it belongs somewhere. `pnpm config delete` still clears one that a file already carries, in whichever spelling it uses [#&#8203;13629](https://github.com/pnpm/pnpm/issues/13629). - Added a new setting `minimumReleaseAgeExcludePrune`. When enabled, `pnpm add`, `pnpm update`, and `pnpm remove` prune the entries of `minimumReleaseAgeExclude` in `pnpm-workspace.yaml` that the freshly written lockfile no longer resolves: versions that are gone are dropped (an entry is removed once none of its versions remain), and entries for packages that are no longer in the lockfile are removed too. Name patterns (`@scope/*`) are always kept. The cleanup is skipped when the install's lockfile does not cover the whole workspace (`sharedWorkspaceLockfile: false`), since entries another project still needs would look stale. Renamed `cleanupUnusedCatalogs` to `catalogPrune`, so that catalog pruning and release-age exclude pruning use one vocabulary. `cleanupUnusedCatalogs` continues to work; when both are set, `catalogPrune` wins. - A project's `pnpm-workspace.yaml` can no longer choose where pnpm keeps its credentials, its own installation, or the registry it downloads its next version from. One of those settings is `configDir`, which decided where `pnpm login` writes the granted token. `bin`, `dir`, `globalBinDir`, `globalDir`, `npmrcAuthFile`, `pnpmHomeDir`, `stateDir`, `userconfig` and `workspaceDir` are ignored there now too, and pnpm warns about the ones it finds. `cacheDir` and `storeDir` are unaffected [#&#8203;13629](https://github.com/pnpm/pnpm/issues/13629). - Resolving a Node.js runtime version (`devEngines.runtime` / `runtime:` specifiers) is now much faster: the per-version release metadata is cached in the pnpm cache directory after its signature is verified, and an exact stable version such as `runtime:22.23.2` no longer downloads the Node.js release index. A pinned runtime whose metadata was fetched once resolves without any network access, which removes the noticeable delay on the first `node` invocation in a project pinning an already-downloaded runtime [#&#8203;13899](https://github.com/pnpm/pnpm/issues/13899). #### Patch Changes - Fixed intermittent `ERR_PNPM_ENOENT` and `ERR_PNPM_ENOTEMPTY` errors while renaming `_tmp_*` directories during installation with `nodeLinker: hoisted`, in workspaces that also use `patchedDependencies`. - `pnpm add` no longer re-resolves the dependency graph when `pnpm-lock.yaml` already holds a version satisfying the request — promoting a transitive dependency to a direct one, or adding to a second workspace package what a first one already depends on, now only saves the dependency in `package.json` and records its importer entry. A satisfying locked version is necessary but not sufficient: the install still falls back to a full resolution for a dist tag, an alias, a `workspace:`/`catalog:`/git/tarball specifier, `--save-peer`, an overridden package, a `catalogMode` other than `manual`, and — under `resolutionMode: time-based` or `lowest-direct`, which resolve a direct dependency to the low end of its range — a range several locked versions satisfy. - Global installs now switch over atomically. The command shims in the global bin directory point at a stable per-package link rather than at the directory a particular install produced, so `pnpm add -g` and `pnpm update -g` activate a new version by moving that one link instead of rewriting every shim. A command can no longer be missing from `PATH` while an install is in progress, and a failed install leaves the previous version in place. - `pnpm audit --fix` and `pnpm audit --fix update` no longer add `minimumReleaseAgeExclude` entries for patched versions that were published before the `minimumReleaseAge` cutoff. The publish time of each minimum patched version is now checked against the registry metadata, and only versions young enough to be blocked by the age gate get an exclusion entry [#&#8203;11563](https://github.com/pnpm/pnpm/issues/11563). - `pnpm add <pkg>@<version>` and `pnpm update <pkg>@<version>` under a non-manual `catalogMode` now move the catalog entry's resolution to the requested version. Previously, when the catalog entry was a range that covered the requested version but resolved to a different one, the request was dropped silently: nothing was installed, nothing was written, and no error was raised. - A project that wasn't part of an install that moved a catalog entry now follows the entry the next time it is installed. It used to keep the version the entry resolved to before — a version the entry no longer allowed — and no later install corrected it, so one catalog entry ended up resolved to two versions. - `pnpm add <pkg>@<version>` and `pnpm update <pkg>@<version>` under `catalogMode: strict` no longer fail with `ERR_PNPM_CATALOG_VERSION_MISMATCH` when the catalog entry is a range that the wanted version satisfies. The dependency keeps using the catalog; only a version that really falls outside the catalog's range is rejected [#&#8203;13715](https://github.com/pnpm/pnpm/issues/13715). - A changed `catalogs` or `pnpm.overrides` block no longer has to be the only change for `pnpm install` to update the lockfile in place. Editing an override while also removing a dependency, or changing a catalog entry in the same commit as a range bump, is now absorbed in one pass instead of re-resolving the whole dependency graph [#&#8203;13799](https://github.com/pnpm/pnpm/issues/13799). Fixed the lockfile an in-place override update wrote when the overridden package was also a catalog entry: the entry kept the version it had before the override moved the package. The same could happen in reverse, when a catalog entry moved a package an override pins. Both cases now re-resolve instead. - `pnpm install` now updates the lockfile in place even when several kinds of changes happened since the last install — for example a removed dependency together with a widened `ignoredOptionalDependencies` list, or a dependency edit alongside a patch or settings change. Previously any combination of changes forced a full re-resolution [#&#8203;13763](https://github.com/pnpm/pnpm/issues/13763). - `pnpm deploy` injects workspace dependencies again, so the deploy directory is self-contained instead of symlinking back into the source workspace [#&#8203;13754](https://github.com/pnpm/pnpm/issues/13754). Enabling `injectWorkspacePackages` with `dedupeInjectedDeps` disabled now also rewrites already-linked workspace dependencies to injected copies. - `pnpm deploy --no-optional` no longer writes a lockfile whose snapshots reference optional dependencies that the deploy excluded. - Removing the last dependency that references a catalog entry via the fast lockfile update no longer leaves the stale catalog entry in `pnpm-lock.yaml`. - A git dependency whose clone (or shallow fetch) fails now reports which package it belongs to, under the `ERR_PNPM_GIT_FETCH_FAILED` code, with credentials in the repository URL redacted. When the lockfile records an SSH remote, the error also explains that fetching it needs an SSH key for that host, and that a lockfile entry written before pnpm v11.21 can be re-recorded over HTTPS with `pnpm update <package>` [#&#8203;13743](https://github.com/pnpm/pnpm/issues/13743). - An `integrity` recorded on a git dependency's resolution (`resolution: {type: git, repo, commit, integrity: sha512-…}`) is no longer treated as a checksum. pnpm never verifies a git checkout against such a hash — the commit pins the content — so it is now dropped when the lockfile is rewritten, and `pnpm sbom` no longer republishes it as a CycloneDX/SPDX checksum. Lockfiles carrying one also load again instead of failing with `ERR_PNPM_BROKEN_LOCKFILE` [#&#8203;13042](https://github.com/pnpm/pnpm/issues/13042). `pnpm sbom` now also publishes the checksum of a `type: binary` runtime archive, which pnpm does verify. - A git dependency whose `git ls-remote` fails now reports the `ERR_PNPM_GIT_RESOLVE_FAILED` code, naming the dependency instead of printing a bare `git` invocation, with credentials in the repository URL redacted. A specifier that does not ask for SSH resolves over HTTPS, because the URL recorded in the lockfile has to work on every machine that installs it, so the error explains how to substitute the transport on a machine that can only reach the host over SSH (`git config --global url."git@<host>:".insteadOf "https://<host>/"`) [#&#8203;13743](https://github.com/pnpm/pnpm/issues/13743). A missing `git` executable is reported as one, instead of surfacing the raw failure to start the process. Credentials embedded in a git specifier are redacted from the "Could not resolve \<ref> to a commit of \<repo>" errors too. Resolving a public repository makes one `git ls-remote` round-trip instead of two. - `pnpm install` after moving a dependency between `dependencies`, `devDependencies`, and `optionalDependencies` now updates the lockfile in place instead of re-resolving the whole dependency graph [#&#8203;13696](https://github.com/pnpm/pnpm/issues/13696). - `syncInjectedDepsAfterScripts` no longer fails with `ERR_PNPM_UNSUPPORTED_INODE_TYPE` when a workspace package contains an inode that is neither a file nor a directory, such as the FIFO 1Password's environments create for `.env`. Such an inode cannot be hardlinked into the injected copy, so it is skipped and the rest of the package still syncs [#&#8203;13550](https://github.com/pnpm/pnpm/issues/13550). `syncInjectedDepsAfterScripts` also no longer fails with `EEXIST` when a workspace package replaced a file with a directory of the same name since the injected copy was last synced. - `syncInjectedDepsAfterScripts` no longer fails with `ENOTDIR` when a workspace package replaced a directory with a file of the same name and the injected copy still held that directory's contents. - `syncInjectedDepsAfterScripts` now removes the bin link of a bin the script dropped. Previously only new bins were linked, so a build step that stopped declaring one left its shim behind, pointing at a command that was no longer there. - `syncInjectedDepsAfterScripts` now identifies a file by its device as well as its inode number. An inode number is only unique within one filesystem, so on its own it could match an unrelated file on another device and leave that path stale in the injected copy. - `pnpm store prune` no longer deletes the lockfile verification log. The log records which lockfile passed which supply-chain policies, so it stays valid across a prune of the store; keeping it lets the next install skip re-verifying an unchanged lockfile. - Widening a dependency's range no longer leaves the project on an older version. The lockfile update now points the project at the highest version of that dependency already in the lockfile that satisfies the new range — matching what a full resolution records — instead of keeping the locked version whenever it happened to satisfy, which could leave a duplicate behind. A range change that only an already-locked version satisfies is now also handled without re-resolving [#&#8203;13778](https://github.com/pnpm/pnpm/issues/13778). - `resolutionMode` is no longer ignored when `minimumReleaseAge` is in effect. `lowest-direct` and `time-based` pick the lowest satisfying version of a direct dependency again; previously any active release-age cutoff — including the built-in default — silently forced the highest, so `resolutionMode` only worked when `minimumReleaseAge: 0` was set explicitly [#&#8203;13752](https://github.com/pnpm/pnpm/issues/13752). - Adding a package to a workspace no longer forces a full re-resolution when every dependency it declares is already locked for a sibling. The lockfile update writes the new project's importer entry from the versions the lockfile already holds; a dependency no locked version satisfies still reaches the resolver [#&#8203;13696](https://github.com/pnpm/pnpm/issues/13696). - `pnpm config delete <key>` no longer fails with `ENOENT` when the config file it would edit does not exist. Clearing a setting that was never set is a no-op [#&#8203;13651](https://github.com/pnpm/pnpm/issues/13651). - Changing a `pnpm.overrides` entry to a version range now updates the lockfile in place when a version the lockfile already holds satisfies the range, instead of re-resolving the whole dependency graph. Only exact versions were handled before [#&#8203;13696](https://github.com/pnpm/pnpm/issues/13696). - Changing a parent-scoped `pnpm.overrides` entry (`"parent>child": "2.0.0"`) now updates the lockfile in place instead of re-resolving the whole dependency graph. Only the named parent's dependency moves; every other package keeps the version it had [#&#8203;13795](https://github.com/pnpm/pnpm/issues/13795). - Removing a dependency, or moving one to another already-locked version, no longer re-resolves the whole dependency graph just because some package resolves a peer with the same name. The lockfile update now compares the peer suffixes against the exact `name@version` the removal severed, so a suffix that names a different — still present — version of that dependency is left alone [#&#8203;13781](https://github.com/pnpm/pnpm/issues/13781). - Projects with a pnpmfile now use the fast lockfile update paths: an unchanged pnpmfile (proven by the recorded `pnpmfileChecksum`) no longer forces a full re-resolution for removals, dependency group moves, compatible range changes, and the other in-place lockfile rewrites [#&#8203;13696](https://github.com/pnpm/pnpm/issues/13696). - A lockfile entry whose resolution is unchanged no longer loses its recorded `deprecated` marker when a registry serves the package's metadata inconsistently — re-resolving to the same version keeps the deprecation instead of silently dropping the line [#&#8203;13846](https://github.com/pnpm/pnpm/issues/13846). - `pnpm prune` is now recursive by default inside a workspace, just like `pnpm install`. This fixes `pnpm prune --prod` in a workspace root emptying the `node_modules` directories of the other workspace projects, dropping the links to the workspace packages they depend on in production [#&#8203;13718](https://github.com/pnpm/pnpm/issues/13718). - A setting written in kebab-case in the global `config.yaml` is now reported instead of being silently ignored [#&#8203;13650](https://github.com/pnpm/pnpm/issues/13650). - `pnpm remove` no longer re-resolves the dependency graph. The removed dependency's entries are dropped from `pnpm-lock.yaml` and anything they made unreachable is pruned, without registry access. The install still falls back to a full resolution when a surviving package resolves a peer dependency through the removed one. - Removing a package from a workspace no longer forces a full re-resolution. The lockfile update drops the departed project's importer entry and prunes whatever only it depended on. A project that is still linked from a surviving project continues to be reported as an error [#&#8203;13696](https://github.com/pnpm/pnpm/issues/13696). - An install sharing a global virtual store no longer removes an incomplete package directory that another importer is still writing, which could fail with `failed to remove existing directory ... prior to swap: Directory not empty`. Such a directory is now repaired in place, and a package file left damaged by an interrupted install is restored instead of being kept. - `pnpm sbom` no longer emits components for optional platform-specific dependencies that cannot be installed on the current platform (for example, the native `@rolldown/binding-*` variants for other operating systems). Such packages are present in the lockfile but are never downloaded, so their license (and other metadata) could not be resolved and they appeared in the SBOM without one. `pnpm sbom --lockfile-only` still describes the whole lockfile graph, which is platform-independent by design. - An `ssh://` git dependency pointing at a bracketed IPv6 host, such as `ssh://[::1]/repo.git`, is resolved now. Its colons were read as an SCP-style path separator, which turned the address into `[:/1]` and left the specifier unresolvable. Applies to both the TypeScript CLI and pacquet. In the TypeScript CLI, an `ssh://` git dependency written without user info — `ssh://git.example.com/team/repo.git`, `git+ssh://git.example.com:2222/team/repo.git` — no longer fails with `TypeError: Cannot read properties of undefined (reading 'includes')`. Only the `user@host` form worked before. - `packageExtensions` is now validated when the configuration is read, so a malformed entry (for instance a dependency range set to `null`) fails with an actionable error instead of crashing later during peer dependency resolution [#&#8203;13756](https://github.com/pnpm/pnpm/issues/13756). - Projects using `resolutionMode: time-based` now benefit from the fast lockfile update paths. A removal, a dependency group move, or a compatible range change no longer forces a full re-resolution just because the lockfile carries a `time` field [#&#8203;13696](https://github.com/pnpm/pnpm/issues/13696). - An install that drops the last dependent of a patched package no longer updates the lockfile in place and succeeds silently. Removing a dependency, widening `ignoredOptionalDependencies`, or adding a removal override could each prune the package while the patch stayed configured; such an install now falls back to a full resolution, which reports the unused patch with `ERR_PNPM_UNUSED_PATCH`. Under `allowUnusedPatches`, where the lockfile update is kept, the same install now warns that the patch went unused instead of saying nothing [#&#8203;13827](https://github.com/pnpm/pnpm/issues/13827). <!-- 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 --> ### [`v11.21.0`](https://github.com/pnpm/pnpm/releases/tag/v11.21.0): pnpm 11.21 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.20.0...v11.21.0) #### Minor Changes - Added interactive group selection to `pnpm update --global --interactive`. - Running `pnpm setup`, `pnpm self-update`, or a command that modifies the global installation (such as `pnpm add --global`) through `sudo` now prints a warning. pnpm keeps global packages and configuration in the invoking user's home directory, so running these commands as root silently operates on the root user's home directory instead of yours. They will fail with `ERR_PNPM_SUDO_NOT_SUPPORTED` in pnpm v12. Read-only global commands (such as `pnpm bin --global`) are unaffected. #### Patch Changes - Fixed pnpm failing to start under asynchronous Node.js module loaders when no `.pnpmfile.mjs` exists [pnpm/pnpm#11701](https://github.com/pnpm/pnpm/issues/11701). - Fixed `minimumReleaseAge` fallback for custom dist-tags so the selected version does not exceed the registry’s original tag target. - Removing a dependency from `package.json` and reinstalling no longer re-resolves the dependency graph. The importer's entry is dropped from `pnpm-lock.yaml`, anything it made unreachable is pruned, and a catalog entry that loses its last referent is removed — all without registry access. Installs still fall back to a full resolution when a package that stays resolves a peer dependency through the removed one, since that would change the surviving package's entry rather than only prune. - Changing a catalog entry to a different exact version no longer re-resolves the dependency graph. The package is replaced in `pnpm-lock.yaml` directly, reusing the same check the `pnpm.overrides` fast path applies: every locked dependency of the package must still satisfy the new version's manifest. Installs fall back to a full resolution when anything other than the catalog reaches the package — an importer that depends on it directly, or another package that depends on it — since the graph would then need both versions. - Fixed a CI regression where `github:owner/repo` dependencies (and other shorthand Git specifiers) would fail to install with `Permission denied (publickey)` on CI runners that lack SSH keys. The Git resolver no longer records an SSH URL unless the user explicitly wrote one (e.g. `git+ssh://` or `git@host:...`): - The repository visibility probe (an HTTP HEAD request) now retries transient failures such as `429 Too Many Requests`, so host throttling of CI runners is no longer mistaken for a private repository. - For non-SSH specifiers, anonymous HTTPS `git ls-remote` access is now tried before SSH, so a public repository whose visibility probe fails still resolves to a portable HTTPS URL instead of an SSH URL that only works where SSH keys are configured. - When every probe fails, the resolver falls back to HTTPS for shorthand and HTTPS-style specifiers, and only guesses SSH when the user explicitly provided an SSH URL. - A repository that could not be confirmed public is no longer resolved to the host's anonymous archive URL (e.g. `codeload.github.com`, which would fail to download for a private repository); it stays a regular `git` resolution so installs can use ambient Git credentials such as credential helpers and tokens. Note that a private repository that is reachable both over authenticated HTTPS and over SSH now resolves to its HTTPS URL, where previous versions recorded the SSH URL. Fixes [pnpm/pnpm#13276](https://github.com/pnpm/pnpm/issues/13276). <!-- cspell:ignore publickey --> - `ng build` and `nuxt build` now work under the global virtual store: pnpm's built-in compatibility extensions add the `tslib` dependency that `@angular/build` uses without declaring and the `unplugin` dependency that `@nuxt/vite-builder` v4 uses without declaring. - Fixed `link:` dependencies under `enableGlobalVirtualStore` so linked children are materialized and slots remain isolated by their resolved link targets. - An install that skips resolution because `pnpm-lock.yaml` is already up to date now reacts fully to packages the lockfile removed — for example after pulling a lockfile in which a dependency was deleted. The hoist layer is recomputed, so a package that became hoistable when a direct dependency was removed is hoisted, and `pendingBuilds` entries for removed packages are dropped instead of staying pending forever. - The held-back-update warning printed by `pnpm update` no longer fires when `minimumReleaseAge` is the actual reason a newer version was not picked. The warning's baseline now applies the same maturity cutoff as the pick itself, so it no longer wrongly attributes the hold-back to "your manifests and already installed dependencies" or recommends an override that would defeat the age gate. See [#&#8203;13071](https://github.com/pnpm/pnpm/issues/13071). - Checking whether `ignoredOptionalDependencies` is up to date no longer reorders the configured patterns. The check sorted them in place, which could move an `!` exclusion ahead of the pattern it excludes from and flip which optional dependencies were ignored. - Changing `autoInstallPeers`, `dedupePeers`, `peersSuffixMaxLength`, `excludeLinksFromLockfile`, or `injectWorkspacePackages` no longer re-resolves the dependency graph when the lockfile proves the setting cannot affect it: no package or project declares a peer dependency for the peer settings, and no project depends on a directory or on another workspace project for the link and injection settings. The new setting is recorded in `pnpm-lock.yaml` and the install proceeds from the existing resolution. Every other case still falls back to a full resolution. - Adding, editing, or removing an entry in `patchedDependencies` no longer re-resolves the dependency graph. Resolution never reads a patch — it only records the patch file's hash against the package it matches — so the install now rewrites the affected entries in `pnpm-lock.yaml` and materializes the patched package from the store instead. Installs still fall back to a full resolution when the patched package is reachable as a peer dependency, and when the new configuration would leave a patch unused while `allowUnusedPatches` is off, so `ERR_PNPM_UNUSED_PATCH` is still reported. - Resolving a private git repository no longer blocks on an interactive credential prompt: `git ls-remote` now fails fast with an authentication error when git has no credentials for the repository [#&#8203;13522](https://github.com/pnpm/pnpm/issues/13522). - Lockfile verification now honors offline mode by using cached registry metadata instead of reaching the registry. When the required metadata is not available locally, verification reports the same `ERR_PNPM_NO_OFFLINE_META` condition used by offline resolution. - POSIX shell shims now follow symbolic links before computing `basedir`, preventing execution failures when a shim is invoked via an external symlink on `PATH` [#&#8203;13405](https://github.com/pnpm/pnpm/issues/13405). - The automatic `packageManager` version switch works again on registries whose tarball URLs point at a different host than the registry itself (load-balanced feed proxies, Artifactory-style mirrors). Package-manager entries are now always recorded with integrity-only resolutions — the download URL is derived from the trusted bootstrap registry instead — and entries persisted in an invalid shape by an earlier pnpm are discarded and re-resolved instead of failing every command [#&#8203;13619](https://github.com/pnpm/pnpm/issues/13619). - Registries that serve no npm signature metadata (private mirrors and feed proxies commonly strip `dist.signatures`) no longer break the automatic `packageManager` version switch and `pnpm self-update` [#&#8203;13147](https://github.com/pnpm/pnpm/issues/13147). When the configured registry cannot provide a verifiable signature, pnpm now fetches the signature from `registry.npmjs.org` and verifies it against the same embedded npm keys over the installed integrity — which proves exactly the same thing. If no signature can be obtained from either source (for example, both are unreachable, or the registry publishes only a `shasum`), pnpm proceeds with a warning instead of failing, but only when the packages resolve through a registry configured in the user's own (non-project) configuration; the download stays pinned by the lockfile integrity, and a signature that exists but does not validate still fails the switch. - `pnpm fetch`, and any install run with `virtualStoreOnly`, no longer writes a `.pnp.cjs` loader under `nodeLinker: pnp`. These installs populate the virtual store without linking the project, so the loader would have claimed the project resolves out of a store it was never linked into. The importer links and `node_modules/.package-map.json` were already skipped; the PnP loader now follows the same rule. - Prevent pnpm from removing project files when `modulesDir` resolves to the project root. - Speed up installs after adding `ignoredOptionalDependencies` patterns by removing newly ignored optional dependencies and pruning packages that are no longer reachable without resolving the dependency graph again. - When a failed install re-copies a bin script from the store, rerunning `pnpm install` now reapplies the executable bit to the bin instead of leaving it non-executable [#&#8203;12742](https://github.com/pnpm/pnpm/issues/12742). - `pnpm root -g` and `pnpm bin -g` now print warnings to stderr instead of stdout, so their stdout stays a clean, machine-readable path. Previously, running either command with `--global` in a project that pins a package manager (e.g. via the `packageManager` field) printed a warning like `[WARN] Using --global skips the package manager check for this project` ahead of the path, breaking programs that capture the output as a path [#&#8203;13672](https://github.com/pnpm/pnpm/issues/13672). In pnpm 12, `pnpm root -g` and `pnpm prefix -g` are now supported (they previously failed with `ERR_PNPM_CLI_ROOT_GLOBAL_UNSUPPORTED` / `ERR_PNPM_CLI_PREFIX_GLOBAL_UNSUPPORTED`), and the reporter output of `dlx`, `create`, `config`, `sbom`, `with`, `store`, `prefix`, `root`, and `bin` goes to stderr, matching pnpm 11. - `pnpm setup` no longer makes Node.js print a `MODULE_TYPELESS_PACKAGE_JSON` warning about `dist/worker.js` on every command. The `package.json` it writes next to a standalone executable now declares `"type": "module"`. - `pnpm update` without saving no longer records a version that the manifest's range excludes. The kept range stays authoritative: a requested version outside it is skipped with a warning, and a requested range, a dist tag, or `--latest` resolves within it instead of past it. Previously each of these could write a lockfile entry that contradicted its own specifier, which the next `pnpm install --frozen-lockfile` rejected with `ERR_PNPM_OUTDATED_LOCKFILE` [#&#8203;12764](https://github.com/pnpm/pnpm/issues/12764). - `pnpm version -r --json` now outputs `[]` instead of human-readable text when no pending changes exist [`pnpm/pnpm#13217`](https://github.com/pnpm/pnpm/issues/13217). <!-- 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 --> ### [`v11.20.0`](https://github.com/pnpm/pnpm/releases/tag/v11.20.0): pnpm 11.20 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.19.0...v11.20.0) #### Minor Changes - **Security fix.** Affects projects using `namedRegistries` on pnpm 11.1.0–11.19.x. It is **semi-breaking** for those projects — see "If you use named registries" below. The lockfile recorded no marker for which registry a package came from. Packages were keyed by `name@version` alone, and entry lookup went through `refToRelative(ref, name)`, so a dependency you declared against one registry could be satisfied by an entry that was actually resolved from another. When two registries served the same name and version, both collapsed onto a single `packages:` entry and whichever resolved first decided the tarball every consumer got. That is a package-substitution risk: a package you expect from your private registry could be installed from a different registry that publishes the same name and version, and the lockfile recorded nothing that would let you tell. Packages resolved from a named registry are now recorded under registry-qualified keys (`<name>@<registryName>:<version>`, e.g. `foo@work:1.0.0`), so each registry gets its own entry and the lockfile pins which one a dependency came from. The lockfile format version is unchanged. Registry-qualified keys appear only for packages resolved from a named registry, so a project that does not use `namedRegistries` sees no difference, and older pnpm versions keep reading the file. ##### If you use named registries Your next non-frozen install re-keys those entries, which shows up as a lockfile diff. Commit it — that diff is the fix being applied. Review it: an entry that moves to a registry you did not expect is worth investigating. Everyone working on the project should be on this version or newer before you do. An older pnpm reads the re-keyed lockfile fine — frozen installs are unaffected — but it does not produce registry-qualified keys itself, so any install that updates the lockfile writes those entries back to the old shape, and the next install on a current pnpm re-qualifies them. The result is a lockfile that flips back and forth, and while it is in the old shape the project is exposed again. Because the lockfile format version is deliberately unchanged, pnpm cannot detect this and warn you about it. There is no setting to keep the old behavior: the old shape is the vulnerability. Tarball URLs that follow the standard registry layout are no longer written to the lockfile for named-registry packages; they are recomputed from the `namedRegistries` setting on demand. To use named registries, map your aliases in `pnpm-workspace.yaml`: ```yaml namedRegistries: work: https://npm.enterprise.example.com/ ``` ##### New built-in `npmjs:` alias `npmjs:` now resolves to `https://registry.npmjs.org/` with no configuration, alongside the existing `gh:` alias for GitHub Packages. It pins a dependency to the public registry even when `registry` points elsewhere, such as an internal proxy: ```json { "dependencies": { "left-pad": "npmjs:^1.3.0" } } ``` `npm:` cannot do this — it is the alias protocol (`npm:<name>@<range>`) and resolves through whatever `registry` points at. **If you mirror or proxy npmjs, point the alias at your mirror:** ```yaml namedRegistries: npmjs: https://npm.internal.example.com/ ``` Built-in registry URLs are also the prefixes a lockfile's recorded tarball URL is matched against when pnpm verifies a package. Without the override, an entry whose tarball URL is on `registry.npmjs.org` is verified against the public registry rather than your mirror. This only affects lockfiles that record such URLs — a canonical URL for your configured registry is omitted from the lockfile and unaffected — and only when a tarball-URL, `minimumReleaseAge`, or `trustPolicy` check runs. Overriding the alias is the same escape hatch GHES users already have for `gh`. Every alias the lockfile references must stay in `namedRegistries`: reading an entry whose alias is gone fails with `ERR_PNPM_MISSING_NAMED_REGISTRY` rather than silently falling back to the default registry, since that would fetch a different package. Renaming an alias re-resolves the packages that used it. Named registry aliases that shadow a reserved dependency specifier prefix (`file`, `link`, `workspace`, `runtime`, `npm`, `jsr`, ...) are now rejected with `ERR_PNPM_RESERVED_NAMED_REGISTRY_NAME` instead of being silently shadowed by the corresponding resolver. `pnpm licenses` and `pnpm sbom` now keep the two artifacts apart as well: license records carry the registry alias, and SBOM components carry the purl `repository_url` qualifier. #### Patch Changes - An empty `http-proxy`, `https-proxy`, `proxy`, or `no-proxy` value — from the `.npmrc`, `pnpm-workspace.yaml`, the CLI, or the `HTTP_PROXY` / `HTTPS_PROXY` / `PROXY` / `NO_PROXY` environment variables — no longer fails the install with `ERR_PNPM_INVALID_PROXY`. Empty settings read as unset, so a shell exporting `HTTP_PROXY=` disables the proxy, and an empty `proxy=` in the `.npmrc` no longer suppresses `HTTPS_PROXY` [#&#8203;13533](https://github.com/pnpm/pnpm/issues/13533). `proxy=false` in the `.npmrc` or `proxy: false` in `pnpm-workspace.yaml` now turns proxying off instead of being read as a proxy host named `false`. `false` and `null` on `https-proxy` / `http-proxy` / `no-proxy` read as unset, and on the command line they are ordinary host names, since a flag carries its value verbatim. - The env lockfile no longer pins `@pnpm/exe` alongside `pnpm` when the wanted pnpm version is 12 or newer. From v12 the unscoped `pnpm` package is itself the native executable, so `@pnpm/exe` is not published for it and resolving it would fail. The engine identity check now verifies the native binary through whichever package ships it. - `lexCompare` and `nerfDart` are now published as `@pnpm/text.ordinal-comparator` and `@pnpm/config.registry-auth-key`. Use these instead of `@pnpm/util.lex-comparator` and `@pnpm/config.nerf-dart`. - Fixed the order in which pnpm matches a lockfile's recorded tarball URL against known registry URLs. Two registry URLs of equal length were previously ordered arbitrarily, so which one a tarball URL matched could differ between runs. - Dependency resolution is faster: package metadata is now filtered once per packument instead of once per dependency edge when `minimumReleaseAge` is active, and parsed semver versions and ranges are reused instead of re-parsed on every comparison. - Security: `pnpm rebuild` now refuses a lockfile whose `packages` key carries a path traversal in the package name (e.g. `../../../escaped@1.0.0`), instead of running that package's lifecycle scripts and linking its bins in a directory outside the virtual store. Such a name is rejected with `ERR_PNPM_INVALID_DEPENDENCY_NAME`. <!-- 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 --> ### [`v11.19.0`](https://github.com/pnpm/pnpm/releases/tag/v11.19.0): pnpm 11.19 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.18.0...v11.19.0) #### Minor Changes - `pnpm login` no longer requires an interactive terminal when the registry supports web-based login: without a TTY it prints the authentication URL (skipping the QR code and the "Press ENTER to open the URL in your browser" prompt) and polls the registry until the browser approval completes. Only the classic username/password login still fails with `ERR_PNPM_LOGIN_NON_INTERACTIVE` in a non-interactive terminal. - The `save-prefix` setting now accepts `=`: newly added dependencies are saved with an explicit `=` operator (`=1.2.3`) instead of the setting being silently treated as the default `^`. #### Patch Changes - `allowBuilds` entries can now approve git-hosted packages that pnpm downloads as a tarball, such as `github:` dependencies (which are fetched from `codeload.github.com` rather than cloned), by their repository URL without the resolved commit hash. This matches the hashless `git+` matching already supported for cloned git dependencies. For example: ```yaml allowBuilds: "foo@git+https://github.com/org/foo.git": true ``` This approves the package whether pnpm clones it or downloads a tarball, so the entry no longer has to be updated every time the pinned commit changes. GitLab and Bitbucket tarball downloads are matched the same way. Approving or denying a specific resolved commit by its full tarball dep path continues to work. - `pnpm outdated --include-github-actions` no longer blocks on an interactive git credential prompt when a workflow uses a private action repo. - Prevented `minimumReleaseAge` from replacing `latest` with a SemVer-greater version than the registry tag target [#&#8203;13034](https://github.com/pnpm/pnpm/issues/13034). - Fixed empty `bundledDependencies` and `bundleDependencies` arrays causing nondeterministic lockfile changes. See [#&#8203;13123](https://github.com/pnpm/pnpm/issues/13123). - The install summary no longer prints `(X is available)` when the registry's `dist-tags.latest` is still held back by the active `minimumReleaseAge` policy. The hint only ever names the actual latest tag, so an immature latest suppresses the hint instead of advertising the version pnpm just refused to install [#&#8203;11698](https://github.com/pnpm/pnpm/issues/11698). - `pnpm update` keeps the explicit `=` operator of an exact version pin: a dependency saved as `=3.5.1` now updates to `=3.5.2` instead of the bare `3.5.2`. See [#&#8203;13168](https://github.com/pnpm/pnpm/issues/13168). - Preserve a workspace dependency's `link:` entry when a run does not target it — e.g. `pnpm update <other-pkg>` (with or without `--recursive`), or a plain install after a root/catalog dependency change — with `injectWorkspacePackages`, instead of spuriously rewriting it to a peer-suffixed `file:` protocol. See [#&#8203;10433](https://github.com/pnpm/pnpm/issues/10433). - Workspace dependencies declared with a relative path (e.g. `"foo": "workspace:../foo"`) are no longer silently dropped from the workspace projects graph, so `--filter` selection and the topological order of recursive commands take them into account. <!-- 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 --> ### [`v11.18.0`](https://github.com/pnpm/pnpm/releases/tag/v11.18.0): pnpm 11.18 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.17.0...v11.18.0) #### Minor Changes - Fixed an installed optional dependency being left without one of its own required dependencies. When a package reached through `optionalDependencies` is installable on the current system but one of its regular `dependencies` is not, a lockfile-based install skipped that dependency and installed the parent anyway, so importing the parent failed with `MODULE_NOT_FOUND`. The dependency is now installed, and an install-check warning reports the incompatibility. A dependency is still only skipped when every path to it is optional, or when the package that pulls it in was itself skipped [#&#8203;13286](https://github.com/pnpm/pnpm/issues/13286). - `pnpm setup` now appends `PNPM_HOME` and the global bin directory to the GitHub Actions environment files (`GITHUB_ENV` and `GITHUB_PATH`), so later steps in the same job can run `pnpm add --global` and other global commands [#&#8203;9191](https://github.com/pnpm/pnpm/issues/9191). - Added support for `publishConfig.name`, which publishes a package under a different name than the one its manifest carries in the workspace. It is for a project whose published name is already taken by a sibling project, which otherwise has to be renamed by a build step just before publishing. Only the published artifact is renamed — dependents, `pnpm-lock.yaml`, and release tooling keep addressing the project by its manifest name — and the new name reaches the packed manifest, the tarball filename, and everything that addresses the package at the registry: the already-published check of `pnpm publish -r`, its registry selection, and the release-planning probes of `pnpm change status` and `pnpm version -r` [#&#8203;13345](https://github.com/pnpm/pnpm/issues/13345). - `pnpm self-update` no longer takes any instruction from the project it is run in: - pnpm is fetched through the same trusted registry and auth configuration used when switching pnpm versions, so a project `.npmrc` or `pnpm-workspace.yaml` can no longer redirect the download or attach credentials to it, and the project's default `.pnpmfile.(c|m)js` is no longer loaded. Pnpmfiles from trusted sources (the `pnpmfile` setting, the global pnpmfile, config dependencies) still apply. - The `minimumReleaseAge` settings in `pnpm-workspace.yaml` no longer affect `self-update`. They still govern the project's own dependencies; for `self-update` the cooldown now comes from the built-in default, your global config, a `PNPM_CONFIG_*` environment variable, or a command-line flag. This fixes `self-update` failing inside a workspace that raises the cutoff while succeeding everywhere else, and stops a repository from either waiving the cooldown or keeping you on an outdated pnpm by raising it. - The same applies to the `trustPolicy` settings and to `ci`: a project can no longer weaken the trust check that guards the pnpm download, nor re-enable the confirmation prompt that a CI run suppresses. When `self-update` refuses a version that is younger than the cutoff, an interactive run now offers to update anyway; non-interactive runs still fail. CI never prompts, even on a runner that attaches a TTY. #### Patch Changes - Fixed `pnpm licenses list` to report every version when the same package is installed under multiple aliases [pnpm/pnpm#13438](https://github.com/pnpm/pnpm/issues/13438). - Sort `pnpm dedupe --check` snapshot changes for stable output across pnpm implementations. - Strip Unicode formatting characters from registry- and manifest-derived terminal output. - Speed up installs after compatible catalog or direct dependency range changes by retaining the locked version without resolving the dependency graph again. - Speed up installs after safe override changes by reusing unambiguous compatible dependency resolutions, pruning obsolete dependencies, applying independent replacements and removals together, and handling parent-scoped `"-"` overrides without full lockfile resolution. - Installing a local `file:` directory dependency with the global virtual store enabled no longer fails with `TypeError: Cannot read properties of undefined (reading 'split')` [#&#8203;13335](https://github.com/pnpm/pnpm/issues/13335). Local directory dependencies — `file:` directories and injected workspace packages — now get a global-virtual-store slot of their own per project. They used to share one slot across every project that depended on a directory of the same name, so a project could end up linked to another project's copy of the dependency. - The `Workspace` column of `pnpm update --interactive` now falls back to the project's path when its `name` is only whitespace, as it already did for a missing or empty one — all three render an equally blank label otherwise. - Checking GitHub Actions dependencies for updates is now opt-in for every command. Neither `pnpm outdated` nor `pnpm update` reads the workflow files unless `--include-github-actions` is passed or `update.githubActions` is set to `true` in `pnpm-workspace.yaml`. Reading them runs `git ls-remote` against every referenced repository, which fails in environments where GitHub is not reachable the way pnpm assumes (a GitHub Enterprise Server, a custom certificate authority, or an offline network) [#&#8203;13254](https://github.com/pnpm/pnpm/issues/13254). `pnpm outdated` accepts the `--include-github-actions` option too. - `pnpm update --interactive` now measures its table in terminal columns rather than in characters. A package name, workspace name, or version containing wide characters (CJK, most emoji) no longer knocks its row's columns out of line with the rest of the group, and a wide character in a version no longer aborts the command with `Subject parameter value width cannot be greater than the container width` [#&#8203;13357](https://github.com/pnpm/pnpm/issues/13357). - The `Workspace` column of `pnpm update --interactive` is more informative in two cases. A dependency outdated at the same version in several workspace projects is offered as one choice, since selecting it updates every project — that choice now names all of them instead of only the first. And a workspace project without a `name` is now labelled with its path rather than left blank, so several unnamed projects can be told apart. - An auto-installed *optional* peer is no longer hoisted at a version the workspace root's own dependency on that package excludes. `resolvePeersFromWorkspaceRoot` already made the workspace root's specifier decide which version a missing *required* peer is installed at; the optional-peer picker ignored it and always took the highest version present anywhere in the graph. In a workspace whose root pins `postcss: 8.5.10`, an importer that depends on `webpack` and declares no `postcss` of its own got `postcss@8.5.22` hoisted for `terser-webpack-plugin`'s optional `postcss` peer, leaving two `postcss@8.5.x` instances in the graph [#&#8203;13320](https://github.com/pnpm/pnpm/issues/13320). - `overrides` now also govern peers that pnpm auto-installs. Previously an override only rewrote dependencies declared in a manifest, so a peer nobody declares — installed because `autoInstallPeers` is on — resolved against its declared peer range and could bring in a second copy of the very package the override pinned. For example, with `overrides: { react: npm:react@19.2.0 }` and a lone `lucide-react` dependency, pnpm installed `react@18.3.1`; it now installs the pinned `react@19.2.0` [#&#8203;13320](https://github.com/pnpm/pnpm/issues/13320). - Under `resolvePeersFromWorkspaceRoot`, a workspace root dependency declared with `link:` or `file:` (or the path form of `workspace:`, such as `workspace:../pkg`) now satisfies another project's missing peer dependency at the linked package's own version, instead of being hoisted as a path. Those specifiers are relative to the project that declares them, so the same specifier reached a different directory — or none — from the project the peer was hoisted into, leaving a broken link. The root now has the same authority over the peer as it has when it declares the package with a version range [#&#8203;13373](https://github.com/pnpm/pnpm/issues/13373). - Installs through a pnpr server now apply the project's whole verification policy. `minimumReleaseAgeExclude`, `minimumReleaseAgeIgnoreMissingTime`, `trustPolicy`, `trustPolicyExclude`, `trustPolicyIgnoreAfter`, and `trustLockfile` were ignored, so excluded packages were still held back and a lockfile containing them could be rejected. `trustPolicy: no-downgrade` no longer fails with `TRUST_POLICY_INCOMPATIBLE_WITH_PNPR` when a pnpr server is configured. `--frozen-lockfile` and `--no-prefer-frozen-lockfile` are now honored on the pnpr path, instead of resolving and rewriting the lockfile anyway. Since `frozenLockfile` defaults to `true` on CI, a CI install through a pnpr server now fails on an out-of-date lockfile rather than updating it. - Workspace installs through a pnpr server no longer crash with `Cannot read properties of undefined (reading 'filter')` after linking, when `minimumReleaseAge` is active [#&#8203;13275](https://github.com/pnpm/pnpm/issues/13275). - Fixed `pnpm dedupe` updating valid catalog resolutions when another matching version exists in the lockfile. - `pnpm -r run "/pattern/" --no-bail` no longer exits zero when one of a project's matched scripts fails and a later one passes. The run summary carries a single status per project, and the passing script overwrote the recorded failure. - Restored the store block a first install prints, naming how packages were materialized and where the stores live [#&#8203;13315](https://github.com/pnpm/pnpm/issues/13315): ```text Packages are hard linked from the content-addressable store to the virtual store. Content-addressable store is at: ~/.local/share/pnpm/store/v11 Virtual store is at: node_modules/.pnpm ``` - The root project's `pnpm:devPreinstall` script now runs before resolution and linking, as it does in pnpm 11. It is skipped under `--ignore-scripts`, `--lockfile-only` and `--dry-run`, by `pnpm fetch` and `pnpm rebuild`, and by a repeat install that is already up to date. Workspaces that use the hook to prepare state the install depends on — such as [next.js](https://github.com/vercel/next.js), which generates a placeholder `next` bin with it — were left with dependents linked against files that were never created [#&#8203;13313](https://github.com/pnpm/pnpm/issues/13313). - Prevented `pnpm dedupe --check` from removing an incompatible `node_modules` directory. - `pnpm update --workspace` no longer links dependencies the user never named: - Running it with `updateConfig.ignoreDependencies` configured no longer fails with `ERR_PNPM_WORKSPACE_PACKAGE_NOT_FOUND` for a dependency that is only published to the registry. Such dependencies keep their specifiers, as they already did when no dependencies were ignored. - Passing package selectors that match no direct dependency no longer falls back to linking every workspace dependency. <!-- 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 --> ### [`v11.17.0`](https://github.com/pnpm/pnpm/releases/tag/v11.17.0): pnpm 11.17 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.16.0...v11.17.0) #### Minor Changes - Added a new setting, `update.githubActionsServer`, for specifying the base URL of the GitHub server that hosts the repositories of the GitHub Actions referenced by the workflow files (for example, a GitHub Enterprise Server). When the setting is not defined, the URL is read from the `GITHUB_SERVER_URL` environment variable, falling back to `https://github.com`. The URL must use the `https://` or `http://` protocol [#&#8203;13220](https://github.com/pnpm/pnpm/issues/13220). `pnpm outdated` and `pnpm update` no longer fail when the refs of a GitHub Action's repository cannot be read (for example, when the action's repository is private or hosted on a different GitHub server). Such actions are now skipped with a warning. Setting `update.githubActions` to `false` now makes `pnpm outdated` and the interactive `pnpm update` skip GitHub Actions dependencies. #### Patch Changes - The token poll for web-based authentication no longer reads the body of non-OK or still-pending (HTTP 202) responses, and caps the token response body it does read at 64 KiB, so a malicious or compromised registry cannot exhaust memory through the poll [pnpm/pnpm#12721](https://github.com/pnpm/pnpm/issues/12721). - Fixed `catalog:` references in dependencies and overrides failing to resolve when installing through a pnpr server, which errored with "No catalog entry '<name>' was found for catalog 'default'." even though the catalog entry existed. Also fixed a crash on Windows when installing a nested workspace member (e.g. `packages/foo`) through a pnpr server [#&#8203;13232](https://github.com/pnpm/pnpm/issues/13232). - Republished every package: the tarballs published by the v11.13.1 through v11.16.0 releases were missing most of their compiled files due to a packing bug [#&#8203;13164](https://github.com/pnpm/pnpm/issues/13164). - Revert script ordering change for `pnpm run --sequential /regex/` - Support the `from-git` argument in the `pnpm version` command. - When the authentication URL cannot be rendered as a QR code (for example when it exceeds the maximum QR data capacity), web-based login now displays the URL alone with a warning instead of aborting authentication [pnpm/pnpm#12721](https://github.com/pnpm/pnpm/issues/12721). <!-- 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 --> ### [`v11.16.0`](https://github.com/pnpm/pnpm/releases/tag/v11.16.0): pnpm 11.16 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.15.1...v11.16.0) #### Minor Changes - The first release of a package now publishes the version written in its manifest verbatim, instead of bumping off it. `pnpm version -r` and `pnpm change status` check the registry for each release's current version; when that version is not yet published, the package debuts at it and its pending changesets apply only from the next release. A newly added package seeded at `1100.0.0` with a `minor` changeset is therefore published as `1100.0.0` rather than skipping straight to `1100.1.0`. - Added a `--changeset` flag to `pnpm update`. Set `update.changeset` to `true` in `pnpm-workspace.yaml` to enable this behavior by default, and use `--no-changeset` to override the setting for one update. After the update completes, pnpm writes a `.changeset/pnpm-update-<suffix>.md` file declaring a patch bump for every workspace package whose `dependencies` or `optionalDependencies` were changed by the update and a major bump when `peerDependencies` changed, including packages that consume an updated catalog entry via the `catalog:` protocol. Private packages, packages without a name, and packages listed in the `ignore` array of `.changeset/config.json` are skipped. If `.changeset/config.json` does not exist, a warning is printed and no changeset is generated. - Added GitHub Actions dependencies to `pnpm outdated` and interactive `pnpm update`. Non-interactive updates can include them with `--include-github-actions` or by setting `update.githubActions` to `true` in `pnpm-workspace.yaml`. Updated actions are pinned to exact commit hashes with their release tags preserved in comments. - Added `update` and `audit` settings sections to `pnpm-workspace.yaml`, superseding the awkwardly named `updateConfig`, `auditConfig`, and top-level `auditLevel` settings: ```yaml update: ignoreDeps: # was updateConfig.ignoreDependencies - webpack - "@babel/*" audit: level: high # was auditLevel ignore: # was auditConfig.ignoreGhsas - GHSA-xxxx-yyyy-zzzz ``` `update.ignoreDeps` lists dependency name patterns that `pnpm update` and `pnpm outdated` should skip. `audit.level` and `audit.ignore` tune `pnpm audit`. The deprecated `updateConfig`, `auditConfig`, and `auditLevel` settings keep working until the next major version. When both a new section value and its deprecated counterpart are set, the new section takes precedence and a warning is printed. Both the TypeScript CLI and the Rust config surface (pacquet) recognize the new sections. #### Patch Changes - Fixed `pnpm add --save-exact`/`--save-prefix` and `pnpm update` writing a package's version with the `peerDependencies` range's prefix (e.g. `^19.2.7` instead of the requested `19.2.7`) whenever the same package also appeared in `peerDependencies`. A real `dependencies`/`devDependencies`/`optionalDependencies` entry now takes precedence over a same-named `peerDependencies` entry when computing the current specifiers [#&#8203;13108](https://github.com/pnpm/pnpm/issues/13108). <!-- 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 --> ### [`v11.15.1`](https://github.com/pnpm/pnpm/releases/tag/v11.15.1): pnpm 11.15.1 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.15.0...v11.15.1) #### Patch Changes - `pnpm install` now detects a `supportedArchitectures` change and re-evaluates previously skipped platform-specific optional dependencies, instead of reporting the project as up to date and leaving the packages for the old architecture set in place. - `pnpm setup` now removes leftover v10-layout shims at the top of `PNPM_HOME`, so `pnpm self-update` no longer warns about a v10 installation layout after PATH has been migrated to the v11 `PNPM_HOME/bin` layout. Applies to both the TypeScript CLI and pacquet. In the TypeScript CLI, `self-update` also no longer treats a dangling legacy shim (one whose install target was garbage-collected) as a real v10 layout, so the warning can no longer fire on dead shim files. Closes [#&#8203;12496](https://github.com/pnpm/pnpm/issues/12496). - Completed pnpm runtime installation parity for Node.js, Deno, and Bun, including runtime failure policy, target architecture selection, and dependency runtime engines. Runtime failure overrides now preserve explicit runtime dependencies without matching engine entries. - Fixed `pnpm install` running out of memory while resolving large dependency graphs [#&#8203;8441](https://github.com/pnpm/pnpm/issues/8441). The resolver kept full registry documents — per-version readmes, scripts, descriptions, and other install-irrelevant bulk — in memory for every package fetched with full metadata (optional dependencies, and packages re-fetched for `minimumReleaseAge`'s publish timestamps). Every retained document is now condensed down to the field set installation actually reads, which reduces peak resolution memory by several times on workspaces with more than a thousand packages. - When a dependency's build script fails under `enableGlobalVirtualStore`, the global virtual store directory it was being built in is now removed for scoped packages too. Previously the cleanup resolved one directory level short of the hash directory for a scoped name, leaving a half-built directory behind that later installs would reuse. - Fixed `pnpm login`, `pnpm adduser`, and `pnpm logout` against a registry hosted under a URL subpath (e.g. `https://example.com/npm/registry`) when the configured URL has no trailing slash. Such URLs were left unnormalized, so the last path segment was dropped when building the login and token endpoints and the auth token was stored under a truncated key. Registry URLs with a path now always get a trailing slash appended during normalization, matching how root-level registry URLs are handled. <!-- 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 --> ### [`v11.15.0`](https://github.com/pnpm/pnpm/releases/tag/v11.15.0): pnpm 11.15 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.14.0...v11.15.0) ##### Minor Changes - Optional peer dependencies declared only via `peerDependenciesMeta` (for example `debug`'s `supports-color` peer) are now resolved from a satisfying version already present in the dependency graph, the same way explicitly declared optional peer dependencies are. Previously such peers were only resolved this way when the package's metadata was read back from the lockfile, so an unrelated dependency change could rewrite peer resolutions across the whole lockfile. ##### Patch Changes - Updated `adm-zip` to prevent crafted ZIP archives from causing excessive memory allocation. - `pnpm version -r` no longer writes a versioning-ledger entry with no consumed intents as a bare `intents:` key, which the next run failed to read with `ERR_PNPM_INVALID_VERSIONING_LEDGER`. Empty intent lists are now written as `intents: []`, and the ledger reader accepts the bare form left by earlier releases. - Fixed pnpr workspace resolution to preserve project names and versions for `workspace:` dependencies. <!-- 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 --> ### [`v11.14.0`](https://github.com/pnpm/pnpm/releases/tag/v11.14.0): pnpm 11.14 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.13.1...v11.14.0) #### Minor Changes - `peerDependencies` now accept dependency specifiers that carry a scheme — a named-registry spec (`<registry>:<version>`), an `npm:` alias, or a `file:`/git/URL spec — instead of rejecting them with `ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION` [#&#8203;13095](https://github.com/pnpm/pnpm/issues/13095). Such a peer is matched against the semver range carried by the specifier (`work:5.x.x` is checked as `5.x.x`, `npm:bar@^5` as `^5`), or against `*` when it carries no version, while the original specifier still selects the package to auto-install. Bare `name@version` values, which are almost always a mistake, are still rejected. - Added `pnpm doctor`, which diagnoses the pnpm installation and the environment it runs in: the versions and install method, whether the global bin directory is on `PATH`, whether the store and cache are writable, which link strategies (reflink, hardlink, symlink) the store's filesystem supports, registry connectivity, and an offline `file:` install that exercises the resolve/store/link path end to end. Each check reports how to fix what it finds, and the command exits non-zero when any check fails. Use `--offline` to skip the checks that need network access, `--json` for machine-readable output, and `--benchmark` to time the filesystem and install checks. - Added support for executing multiple scripts matching a RegExp passed to `pnpm run` (e.g., `pnpm run "/^build:.*/"`), running matched scripts in deterministic lexicographical order. Restored the `--sequential` (`-s`) CLI option for `pnpm run`, which forces `workspaceConcurrency` to 1 so that matched scripts run sequentially one by one across and within packages. #### Patch Changes - Fixed `pnpm install` failing with `ERR_PNPM_LOCKFILE_IS_SYMLINK` when `pnpm-lock.yaml` is a symlink, as build sandboxes such as Bazel and Nix stage it [#&#8203;13073](https://github.com/pnpm/pnpm/issues/13073). Reading a lockfile through a symlink is allowed again, and an install that leaves the lockfile unchanged no longer rewrites it, so `--frozen-lockfile` no longer needs to write at all. Writing a *changed* lockfile through a symlink is still refused, as that would redirect the write onto the symlink's target. - Fixed frozen installs incorrectly treating equivalent Git dependency specifiers as a stale lockfile. See [#&#8203;13039](https://github.com/pnpm/pnpm/issues/13039). - `pnpm owner ls` now reports authentication and authorization failures (401/403) as dedicated errors that include the registry's response body, matching `pnpm owner add`/`rm`, instead of a generic `Failed to fetch owners` message. - Recover from a metadata cache entry that disappears (concurrent cache cleanup, antivirus) after the registry has already answered the conditional request with `304 Not Modified`. The metadata is re-requested once without cache validators instead of failing the install with `ERR_PNPM_CACHE_MISSING_AFTER_304`. - A project pinned to a broken pnpm release via `packageManager` or `devEngines.packageManager` now reports which release is broken and what to do about it, instead of failing inside the installer. `pnpm self-update` already refused these releases; the version switch does too. - Prevent broken-lockfile errors from including snippets of the lockfile's contents. - `pnpm self-update` now checks that the version it installed can run before making it the active pnpm. A release that installs but cannot execute is discarded with an error instead of replacing a working installation. - Fixed an out-of-memory regression when workspace projects concurrently resolve a package with large registry metadata [pnpm/pnpm#13077](https://github.com/pnpm/pnpm/issues/13077). - Fixed `pnpm update` rewriting exact version pins that use the `=` operator (for example `=3.5.1`) to a caret range (`^3.5.1`). Exact pins are now preserved and written back as the bare version. See [#&#8203;12745](https://github.com/pnpm/pnpm/issues/12745). <!-- 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 --> ### [`v11.13.1`](https://github.com/pnpm/pnpm/releases/tag/v11.13.1): pnpm 11.13.1 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.13.0...v11.13.1) #### Patch Changes - Fixed `pnpm pack` applying workspace-root ignore rules when a workspace package has its own `.npmignore` file. - Keep the interactive `minimumReleaseAge` approval prompt visible during `pnpm install`. The progress reporter now pauses its redraws while a prompt is waiting for input instead of overwriting it, so the install no longer hangs on a question the user cannot see [#&#8203;13019](https://github.com/pnpm/pnpm/issues/13019). - Fixed `pnpm self-update` failing to link native platform binaries stored in sibling global virtual store slots. ### [`v11.13.0`](https://github.com/pnpm/pnpm/releases/tag/v11.13.0): pnpm 11.13 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.12.0...v11.13.0) #### Minor Changes - Added `versioning.epics` to `pnpm-workspace.yaml`. An epic ties a group of member packages to a lead package, constraining every member's major version to a band derived from the lead's major: while the lead is on major `M`, members live in `M*100 … M*100+99`. Members move independently inside the band (patch, minor, and a `major` intent that stays in-band); a bump that would carry a member past the band ceiling is rejected until the lead advances its own major. When a release plan takes the lead to a new stable major, every member re-bases to the band floor in the same plan. Membership is matched with pnpm's package selectors — name globs, `./`-prefixed directory globs, and `!`-prefixed negations. - Added the `team` command for managing organization teams and team memberships on the registry, with create, destroy, add, rm, and ls subcommands and support for --otp, --parseable, and --json flags. - Added native workspace release management [#&#8203;12952](https://github.com/pnpm/pnpm/issues/12952): the new `pnpm change` command records change intents as changesets-compatible `.changeset/*.md` files (`pnpm change status` shows the pending release plan), and the bare `pnpm version -r` consumes them — bumping versions across the workspace with dependent propagation through `workspace:` ranges, fixed groups, a `maxBump` cap, `--filter` narrowing, and `--dry-run` — writing changelogs, and recording consumed intents in a committed ledger that keeps cherry-picks and merge-backs between release branches safe. Packages can be moved onto per-package release lanes with the new `pnpm lane <name> --filter <pkg>` command and back with `pnpm lane main --filter <pkg>` (`pnpm lane` shows the membership), releasing `X.Y.Z-lane.N` prereleases from the same runs that release stable versions of the packages on the main lane. Configuration lives under the new `versioning` key of `pnpm-workspace.yaml` (`fixed`, `ignore`, `maxBump`, `lanes`, `changelog`). When two workspace projects publish the same name, intent files, `versioning.lanes`, and `versioning.fixed`/`ignore` may reference a project by its workspace-relative directory path (e.g. `"./pnpm/npm/pnpm"`) — the one additive extension to the changesets format, applied automatically by `pnpm change`. Release changelogs default to `registry` storage (`versioning.changelog.storage`): no `CHANGELOG.md` is committed. Each release's section is composed at publish time and packed into the published tarball on top of the previously published version's changelog, and the consumed change intents are garbage-collected by a later `pnpm version -r` only once the registry confirms the version is published with its section. Set `versioning.changelog.storage: repository` to keep committed `CHANGELOG.md` files instead. - Added a new override selector form with an empty range — `"pkg@": "<version>"` — called a convergence override. It rewrites a dependency edge only when its exact version satisfies the edge's declared range, so compatible consumers converge on one version while incompatible consumers keep their own resolution — now and for any dependent added in the future [#&#8203;12794](https://github.com/pnpm/pnpm/issues/12794). ```yaml overrides: "form-data@": 4.0.6 ``` The value must be an exact version. When a full resolution detects that every declared range also admits a newer version, pnpm warns that the override is stale and names the version to converge on. Previously an empty range in an override selector was undocumented and behaved like a bare (unscoped) override. #### Patch Changes - A `tokenHelper` set in the global pnpm `auth.ini` is no longer rejected as project-level configuration. The guard that blocks `tokenHelper` from a project `.npmrc` only treated `~/.npmrc` as a trusted source, so a helper written to `auth.ini` (for example by `pnpm config set`) failed on every command and could not even be removed with `pnpm config delete`. A `tokenHelper` in a workspace or project `.npmrc` is still rejected. - `pnpm cache delete` now removes a package's metadata from every metadata cache directory (`metadata`, `metadata-full`, and `metadata-full-filtered`), instead of only the one the current resolution mode reads. Previously a package cached under a different mode (e.g. `metadata-full-filtered`) was left behind. Closes [#&#8203;12753](https://github.com/pnpm/pnpm/issues/12753). - Fixed an injected workspace dependency (`injectWorkspacePackages: true`) incorrectly staying as `file:` instead of deduping back to `link:` when an unrelated, ordinary shared dependency resolved to a peer-suffixed variant for the target project's own copy but not for the injected occurrence. See [#&#8203;10433](https://github.com/pnpm/pnpm/issues/10433). - `pnpm deploy` now supports workspaces that use catalogs. - Fixed `pnpm deploy` with a shared lockfile so local `file:` tarball dependencies keep their package name in the generated deploy lockfile. This prevents warm-store deploys from failing with `ERR_PNPM_UNEXPECTED_PKG_CONTENT_IN_STORE` when the tarball filename includes the version. - Options that follow `create`, `exec`, or `test` appearing as a subcommand of another command are now parsed instead of being silently treated as positional parameters. For example, `pnpm team create @org:team --registry <url>` previously ignored the `--registry` option and sent the request to the default registry. - `pnpm add -g`, `pnpm update -g`, `pnpm setup`, and the self-updater no longer fail with `ERR_PNPM_MISSING_TIME` when `trustPolicy: no-downgrade` or `resolutionMode: time-based` is set in the global config [#&#8203;12883](https://github.com/pnpm/pnpm/issues/12883). The decision to fetch full registry metadata now lives in one place, and the `no-downgrade` trust policy always requests full metadata (matching the self-updater), since the trust evidence it checks is missing from abbreviated metadata even on registries that include the `time` field. - `pnpm list` and `pnpm why` no longer crash with `EMFILE: too many open files` when a project has a large number of unsaved dependencies (packages present in `node_modules` but not in the lockfile). The reads of those packages are now concurrency-limited. - The published `pnpm` package no longer declares `dependencies` or `devDependencies`. Because the CLI bundles its runtime dependencies into `dist/node_modules`, those fields are dropped when packing, so `npm install` of the tarball no longer tries to resolve internal-only packages such as `@pnpm/test-ipc-server`. Closes [#&#8203;12955](https://github.com/pnpm/pnpm/issues/12955). - Fixed `pnpm publish --otp` and `pnpm publish --batch --otp` to send the configured OTP to the registry. - `pnpm publish` again sends the package's README to the registry as metadata, so registries can render it on the package page. The readme is always included in the published metadata (matching the npm CLI), while the `embed-readme` setting continues to control only whether the readme is written into the `package.json` inside the tarball. This restores the behavior that was lost when publishing became fully native. Closes [#&#8203;12966](https://github.com/pnpm/pnpm/issues/12966). - Fixed the dependency status check wrongly reporting "up to date" when a `package.json`, `.pnpmfile.cjs`, or patch file was edited in the same second as the previous install, on filesystems that record mtimes at whole-second resolution (for example ext4 with 128-byte inodes). The optimistic repeat-install fast path and `verify-deps-before-run` compared mtimes strictly, so a same-second edit whose mtime rounded down looked unchanged and re-resolution was skipped. Such a file's whole second is now treated as possibly-modified, falling through to the content check; behavior on sub-second filesystems is unchanged. - Retry package metadata requests when a registry or proxy returns `304 Not Modified` to an unconditional request, preventing false `ERR_PNPM_CACHE_MISSING_AFTER_304` failures [pnpm/pnpm#12882](https://github.com/pnpm/pnpm/issues/12882). If the retry also returns `304`, report `ERR_PNPM_META_NOT_MODIFIED_WITHOUT_CACHE` instead. - Fixed `pnpm update` removing transitive lockfile entries when `dedupePeerDependents` is disabled and the selected package is absent [pnpm/pnpm#12456](https://github.com/pnpm/pnpm/issues/12456). - Limit modern deploy lockfiles and localized virtual stores to dependencies reachable from the selected dependency groups. - A `tokenHelper` command is now given a 60-second time limit. A helper that hangs (deadlock, stuck I/O) is killed and reported as an error instead of leaving the command waiting forever. - Fixed orphaned child processes on Windows when pnpm exits on an error while commands spawned by `pnpm exec` or `pnpm dlx` are still running (for example, when one project's command fails during `pnpm --recursive exec`). The PIDs of these commands are now recorded when they are spawned and their whole process trees are terminated with `taskkill` on an error exit. Previously the cleanup relied on enumerating the system process list, which is so slow on Windows that the enumeration hit its timeout and the cleanup was silently skipped [#&#8203;12406](https://github.com/pnpm/pnpm/issues/12406). - `pnpm pack` now respects workspace-root `.npmignore` and `.gitignore` files when packing workspace packages. <!-- 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 --> ### [`v11.12.0`](https://github.com/pnpm/pnpm/releases/tag/v11.12.0): pnpm 11.12 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.11.0...v11.12.0) #### Minor Changes - [`a897ef7`](https://github.com/pnpm/pnpm/commit/a897ef7): Custom fetchers exported from a pnpmfile can now delegate by returning a `{ delegate: <resolution> }` envelope: pnpm rewrites the package's resolution to the delegated shape and runs the built-in fetcher on it. This is the portable delegation form that also works in pacquet, where `cafs` and `fetchers` cannot be passed to the hook. Related to [pnpm/pnpm#11685](https://github.com/pnpm/pnpm/issues/11685). #### Patch Changes - [`2b02764`](https://github.com/pnpm/pnpm/commit/2b02764): The changed-packages filter (`--filter "...[<since>]"`) no longer allows an option-like `<since>` value (such as `--output=<path>`) to be interpreted as a git option — git now rejects it as a bad revision. The repository root is also resolved to the nearest `.git` entry, so the filter works in a git worktree checked out inside another repository's tree. - [`43711ce`](https://github.com/pnpm/pnpm/commit/43711ce): `pnpm outdated` no longer checks the registry for dependencies that are resolved from local `link:`, `file:`, or `workspace:` references in the lockfile [#&#8203;12827](https://github.com/pnpm/pnpm/issues/12827). - [`3c6718b`](https://github.com/pnpm/pnpm/commit/3c6718b): Fixed a deadlock in peer dependency resolution: `pnpm install` hung forever when a peer dependency cycle spanned a project's own dependencies and auto-installed peer providers, for example when installing `electron-builder@26.15.3` [#&#8203;12921](https://github.com/pnpm/pnpm/issues/12921). - [`252f15e`](https://github.com/pnpm/pnpm/commit/252f15e): Fixed peer dependency auto-install picking a version the peer range rejects. In a workspace with several projects, a package declaring a peer dependency with a semver range (for example `^1.0.0`) could get the highest version found anywhere in the workspace (for example a `2.0.0` resolved for another project) instead of a version that satisfies the range. Peers are now deduplicated onto the highest preferred version that satisfies the declared range, and when none does, the range is resolved from the registry. Also fixed re-resolving with an existing lockfile hoisting a different peer version than a fresh install of the same manifest: root dependencies reused from the lockfile were invisible to peer hoisting, so a peer that a root dependency provides could be bound to another version. - [`a38adda`](https://github.com/pnpm/pnpm/commit/a38adda): `pnpm self-update <version>` now installs the requested pnpm version when it matches the currently running version but is missing from the global self-update directory. - [`6a85968`](https://github.com/pnpm/pnpm/commit/6a85968): `pnpm stage list` now stops paginating after a fail-safe cap of 1000 pages, so a misbehaving registry cannot keep the command looping forever. - [`eee7c9a`](https://github.com/pnpm/pnpm/commit/eee7c9a): `verify-deps-before-run` no longer spawns a `pnpm install` when pnpm is executed in a directory that has no `package.json`. A mistyped command run outside a project (for example `pnpm witch 10 login`) used to crash with a confusing error from the spawned install; now it fails with the regular "no package.json found" error. <!-- 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 --> ### [`v11.11.0`](https://github.com/pnpm/pnpm/releases/tag/v11.11.0): pnpm 11.11 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.10.0...v11.11.0) #### Minor Changes - [`508b8c2`](https://github.com/pnpm/pnpm/commit/508b8c2): Added the `pnpm access` command for managing package access and visibility on the registry, supporting listing packages and collaborators, getting and setting package status and MFA requirements, and granting or revoking team access. #### Patch Changes - [`c70e33e`](https://github.com/pnpm/pnpm/commit/c70e33e): Allow `allowBuilds` entries for git-hosted packages to match by repository URL without pinning the resolved commit hash. This lets trusted git repositories keep running their build scripts after branch updates without approving each new commit, while package-name-only rules still do not approve git-hosted artifacts. - [`3067e4f`](https://github.com/pnpm/pnpm/commit/3067e4f): Reduced peak memory usage during cold-cache dependency resolution. The metadata fetch is memoized for the whole resolution phase, and it was retaining each package's raw registry response body (used only to mirror the response to disk) for that entire time. The memoized cache now holds a body-less copy, so the raw body only lives as long as the call that writes the disk mirror. On large graphs that fetch full metadata (e.g. with `minimumReleaseAge` or `trustPolicy` enabled) this cuts peak RSS by roughly 30%, back in line with pnpm 10. The resolved lockfile is unchanged. - [`51300fd`](https://github.com/pnpm/pnpm/commit/51300fd): Prevent a crafted `pnpm-lock.yaml` from writing package content outside the virtual store. A dependency path key whose name reconstructs to a path-traversal sequence (e.g. `../../../tmp/x@1.0.0`) is now rejected by the isolated (virtual-store) linker and the Plug'n'Play resolver map, matching the containment already applied to the hoisted linker. Under the global virtual store, a traversal in the version-derived path segment (e.g. a snapshot `version: "../../x"`) is now rejected at `formatGlobalVirtualStorePath`, the single point every global-virtual-store slot path funnels through — closing the same escape in the isolated linker, the resolver's dependency-graph builder, and the config-dependency installer. - [`f8058eb`](https://github.com/pnpm/pnpm/commit/f8058eb): Reject symlinked `pnpm-lock.yaml` files when reading or writing the env lockfile document. - [`9318a11`](https://github.com/pnpm/pnpm/commit/9318a11): Allow `registries` and `namedRegistries` to be configured in the global `config.yaml` file. - [`51300fd`](https://github.com/pnpm/pnpm/commit/51300fd): Fixed a path traversal vulnerability where a dependency whose manifest `name` was a scoped path traversal (e.g. `@x/../../../<path>`) could be written outside `node_modules` to an attacker-controlled location during `pnpm install`, even with `--ignore-scripts`. The isolated linker now validates the package name before using it as a directory name, matching the existing protection in the hoisted linker. - [`14332f0`](https://github.com/pnpm/pnpm/commit/14332f0): Fail instead of silently removing an optional dependency's locked entries from `pnpm-lock.yaml` when the registry cannot resolve it. Previously, when registry metadata lacked a version that the lockfile already pinned (for example, a mirror that had not synced a recent release yet), `pnpm install` and `pnpm dedupe` silently dropped the optional dependency's entries — emptying maps such as the platform binaries of `@napi-rs/canvas` — so the lockfile differed between machines and frozen installs on other hosts had nothing to link [#&#8203;12853](https://github.com/pnpm/pnpm/issues/12853). - [`fecfe83`](https://github.com/pnpm/pnpm/commit/fecfe83): Fixed peer dependency resolution with `autoInstallPeers` when a workspace package depends on a version of a package that a transitive dependency's self-contained closure also provides for itself. The peer providers that are attached to the root project for reuse are no longer peer-resolved a second time in the root context, so packages inside such a closure no longer get their peers bound to the root project's incompatible version [#&#8203;4993](https://github.com/pnpm/pnpm/issues/4993). - [`5a4daec`](https://github.com/pnpm/pnpm/commit/5a4daec): `${...}` environment-variable placeholders in the `httpProxy`, `httpsProxy`, `noProxy`, `proxy`, and `noproxy` settings are no longer expanded when these settings come from a project's `pnpm-workspace.yaml`. They now receive the same protection already applied to `registry`, `namedRegistries`, and `pnprServer`. - [`d1da02e`](https://github.com/pnpm/pnpm/commit/d1da02e): `pnpm publish` no longer prints credentials when the target registry is configured with inline `user:pass@` credentials (e.g. `registry=https://user:pass@example.com/`). They are now redacted both from the "publishing to registry" line and from the OIDC (trusted publishing) failure messages. - [`dcfc611`](https://github.com/pnpm/pnpm/commit/dcfc611): `pnpm self-update` now honors `trustPolicy=no-downgrade`. It resolves the target pnpm version against full registry metadata, so it refuses to switch to a version whose supply-chain trust evidence is weaker than an earlier-published one, the same way a regular install does. - [`a8ad82d`](https://github.com/pnpm/pnpm/commit/a8ad82d): Register the `pn` alias in generated shell completion scripts. - [`25bd5c3`](https://github.com/pnpm/pnpm/commit/25bd5c3): Fixed standalone installer downgrades from pnpm v12 to v11. - [`23996e9`](https://github.com/pnpm/pnpm/commit/23996e9): `pnpm runtime set <name> <version>` now validates its arguments: the name must be `node`, `deno`, or `bun`, and the version must not contain a comma. Previously these were interpolated straight into a `pnpm add` selector, where an unsupported name or a comma (e.g. `node 22,is-positive`) could be misread as a list of packages or a local directory and install unintended packages or bins. <!-- 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 --> ### [`v11.10.0`](https://github.com/pnpm/pnpm/releases/tag/v11.10.0): pnpm 11.10 [Compare Source](https://github.com/pnpm/pnpm/compare/v11.9.0...v11.10.0) #### Minor Changes - [`e2e3c81`](https://github.com/pnpm/pnpm/commit/e2e3c81): Added the `issues` command as an alias of `bugs`, so `pnpm issues` opens the package's bug tracker URL in the browser. - [`8491f8e`](https://github.com/pnpm/pnpm/commit/8491f8e): Added the `prefix` command which prints the current package prefix directory (or global prefix directory if `-g` / `--global` is used). - [`3425e80`](https://github.com/pnpm/pnpm/commit/3425e80): Added an `_auth` setting for configuring registry authentication as a single structured (URL-keyed) value. It can be set in the **global** pnpm config (`config.yaml`) or, for CI, via the `pnpm_config__auth` environment variable. The env form sidesteps the GitHub Actions / bash / zsh limitation that broke the existing `pnpm_config_//host/:_authToken=…` form (env var names containing `/`, `:`, or `.` are silently dropped). Closes [#&#8203;12314](https://github.com/pnpm/pnpm/issues/12314). The value is keyed by registry URL so each secret is explicitly bound to the host that may receive it. Registry URL keys must use `http` or `https` and must not include credentials, query strings, or fragments: ```sh export pnpm_config__auth='{"https://registry.npmjs.org":{"@":{"authToken":"npm-token"},"@org":{"authToken":"org-token"}}}' ``` The equivalent in the global `config.yaml`: ```yaml _auth: https://registry.npmjs.org: "@": authToken: npm-token "@org": authToken: org-token ``` Within each registry URL, `@` means registry-wide/default credentials and package scopes like `@org` bind credentials to that scope on the same host. The only supported credential field is `authToken` (maps to `_authToken` / bearer auth); the deprecated `basicAuth` / `username` + `password` forms are intentionally not accepted here. Each entry also infers a trusted registry route: `@` routes the default registry (and `pnpm add <pkg>` resolves there), and `@org` routes that scope. Because the credential and destination host arrive in one trusted value, repo-controlled `pnpm-workspace.yaml` or project `.npmrc` cannot redirect the token to a different host. `_auth` is honored **only** from the env var and the global config — it is ignored in a project `pnpm-workspace.yaml` / `.npmrc`, so repo-controlled config can never supply registry auth. Precedence: CLI flags (`--registry`, `--@scope:registry`) > `pnpm_config__auth` > global `config.yaml` `_auth` > `pnpm-workspace.yaml`. Both `pnpm_config__auth` (lowercase, documented form) and `PNPM_CONFIG__AUTH` (all-caps, the shell convention some CI runners apply) are honored. If both are set, lowercase wins unless it is empty, in which case uppercase is used. The env var wins over the global `config.yaml` `_auth` on a conflicting key. `tokenHelper` is not supported in `_auth`. Parsing is strict: a malformed value (bad JSON, wrong shape, invalid registry URL or scope, an unsupported credential field) fails fast with an error rather than being silently dropped. **Pacquet parity note:** the pacquet (Rust) port supports the same single credential field as the TS CLI: `authToken`. - [`a33eeec`](https://github.com/pnpm/pnpm/commit/a33eeec): `pnpm self-update` and `packageManager` version-switching can now install and link pnpm v12 (the Rust port), published with equal content under both the `pnpm` and `@pnpm/exe` names on the `next-12` dist-tag. Its native binaries ship as `@pnpm/exe.<platform>-<arch>` packages, which pnpm's built-in installer links directly — no Node.js launcher, so the command pays no Node startup cost. v12 is initialized exactly like `@pnpm/exe`, including per-platform global-virtual-store hashing. From v12 onward the install converges on the unscoped `pnpm` package (the Rust exe) — even when updating from the SEA `@pnpm/exe` build. - [`1dd12bd`](https://github.com/pnpm/pnpm/commit/1dd12bd): When resolving through a pnpr install-accelerator server, pnpm no longer forwards its own upstream registry credentials in the resolve request. Only the `Authorization` header identifying the caller to pnpr is sent. The pnpr server now selects upstream credentials from its own route policy (operator-configured upstream credential aliases), so private dependencies resolve through a pnpr-managed alias the caller is authorized to use, rather than by sending the client's registry tokens to the server. - [`1e81761`](https://github.com/pnpm/pnpm/commit/1e81761): Expose web authentication `authUrl` and `doneUrl` in JSON error output when OTP is required in a non-interactive terminal [#&#8203;12724](https://github.com/pnpm/pnpm/issues/12724). #### Patch Changes - [`2f389d6`](https://github.com/pnpm/pnpm/commit/2f389d6): Added the Node.js release team's new signing key (Stewart X Addison, `655F3B5C1FB3FA8D1A0CA6BDE4A7D232B936D2FD`) to the embedded Node.js release keys, so runtimes whose `SHASUMS256.txt` is signed by the new releaser verify successfully. - [`acbdb94`](https://github.com/pnpm/pnpm/commit/acbdb94): Fixed shell tab completion not suggesting workspaces after the `-F` alias for `--filter` option. - [`dcabb78`](https://github.com/pnpm/pnpm/commit/dcabb78): Fixed `pnpm up -r <pkg>` bumping unrelated packages that have open semver ranges. Previously, any update mutation nullified the lockfile-derived `preferredVersions` globally, so packages with `^x.y.z` ranges could re-resolve to newer compatible versions even though the user only asked to update a specific package. The install layer now always seeds `preferredVersions` from the lockfile, and caller-supplied preferred versions (such as the vulnerability penalties of `pnpm audit --fix`) layer on top of the seed instead of replacing it. The targeted package still bumps: the per-resolve `updateRequested` flag makes the resolver ignore the target's own lockfile pins. Closes [#&#8203;10662](https://github.com/pnpm/pnpm/issues/10662). - [`d539172`](https://github.com/pnpm/pnpm/commit/d539172): Fixed pnpm pack and pnpm publish failing when prepack generates files that are included in the package and postpack cleans them up. - [`be6505a`](https://github.com/pnpm/pnpm/commit/be6505a): Hardened global package management: - On Windows, removing or updating a global package now also cleans up the `node.exe` flavor of a bin, so a stale `node.exe` no longer survives on `PATH` after uninstall, and a new global install no longer silently overwrites an existing `node.exe`. - `pnpm add -g pnpm@<version>` (and `@pnpm/exe@<version>`) is now rejected like the bare `pnpm` form, pointing to `pnpm self-update`. - Dependency aliases read from a global package's manifest are validated before being joined onto `node_modules` paths, preventing a tampered manifest from escaping the install directory. - Each global install group is created in its own freshly-made directory (no longer reusing a colliding or pre-existing path). - Removing or updating a global package no longer unlinks a bin that belongs to a different globally installed package. - [`25c7388`](https://github.com/pnpm/pnpm/commit/25c7388): pnpm now rejects `jsr:` specifiers whose package name is not a valid npm package name — an empty scope or name (e.g. `jsr:@scope/`), path separators inside the name, or any other shape `validate-npm-package-name` rejects — with `ERR_PNPM_INVALID_JSR_PACKAGE_NAME` instead of silently converting them into a malformed `@jsr/...` npm package name. - [`25c7388`](https://github.com/pnpm/pnpm/commit/25c7388): pnpm now rejects named-registry specifiers (e.g. `gh:`) whose package name is not a valid npm package name — an empty scope (e.g. `gh:@/bar`), path separators inside the name (e.g. `gh:@scope/../name`), or any other shape `validate-npm-package-name` rejects — with `ERR_PNPM_INVALID_NAMED_REGISTRY_PACKAGE_NAME` instead of passing the name through to registry URLs and metadata cache file paths. - [`96da7c5`](https://github.com/pnpm/pnpm/commit/96da7c5): node-gyp's `gyp_main.py` and `gyp` entrypoints are now packed with the executable bit in the `pnpm` and `@pnpm/exe` tarballs. Without it, building native addons from source could fail with a permission error. - [`99982b9`](https://github.com/pnpm/pnpm/commit/99982b9): Sped up resolution and reduced memory use against registries that ignore npm's abbreviated metadata format and always return the full package document (for example, Azure DevOps Artifacts). pnpm now strips such documents down to the abbreviated field set before caching them. Resolution output is unchanged, and registries that honor the abbreviated format (such as the npm registry) pay no extra cost. - [`11a7fdd`](https://github.com/pnpm/pnpm/commit/11a7fdd): Sped up offline and `--prefer-offline` resolution on large workspaces (e.g. `pnpm dedupe --offline`, `pnpm install --offline`). Package metadata loaded from the local cache is now kept in memory, so each package's metadata is parsed once per command instead of once per dependent that references it. - [`2c7369d`](https://github.com/pnpm/pnpm/commit/2c7369d): `pnpm pack-app` now rejects `--entry` / `pnpm.app.entry` and `--output-dir` / `pnpm.app.outputDir` values that are absolute paths or escape the project directory via `..` (or a symlink that resolves outside it), and refuses to write the produced executable when its target path already exists as a symlink (or other non-regular file). This prevents a repository-controlled `package.json` from embedding host files (such as an SSH key) into the produced executable, writing build artifacts outside the project, or overwriting an arbitrary file through a committed symlink. The new error codes are `ERR_PNPM_PACK_APP_ENTRY_OUTSIDE_PROJECT`, `ERR_PNPM_PACK_APP_OUTPUT_DIR_OUTSIDE_PROJECT`, and `ERR_PNPM_PACK_APP_OUTPUT_FILE_NOT_REGULAR`. When ad-hoc signing macOS targets, `pnpm pack-app` now runs the system `codesign` by absolute path and resolves `ldid` to a location outside the project, so a repository-controlled `node_modules/.bin` on `PATH` cannot hijack the signer. - [`ce5d5a5`](https://github.com/pnpm/pnpm/commit/ce5d5a5): Relative paths in `patchedDependencies` are now resolved against the lockfile directory when computing patch file hashes, so running `pnpm install` from a subdirectory no longer fails with `ENOENT` looking for the patch file in the wrong location [#&#8203;12762](https://github.com/pnpm/pnpm/pull/12762). - [`ebb4096`](https://github.com/pnpm/pnpm/commit/ebb4096): `pnpm peers` no longer reports a conflict for a missing peer dependency that is ignored via `pnpm.peerDependencyRules.ignoreMissing`. - [`dcabb78`](https://github.com/pnpm/pnpm/commit/dcabb78): Fixed a prototype-pollution hazard when seeding preferred versions: a dependency named `__proto__` in a manifest or in `pnpm-lock.yaml` could write through `Object.prototype` (or crash the install) while the preferred-versions map was being built. The maps are now null-prototype objects, so crafted package names land as plain keys. - [`f38e696`](https://github.com/pnpm/pnpm/commit/f38e696): Hardened `pnpm deploy --force` so it refuses unsafe deploy targets such as workspace roots, parent directories, out-of-workspace paths, and symlinked target parents. - [`806c3ec`](https://github.com/pnpm/pnpm/commit/806c3ec): pnpm no longer warns about ignored project-level auth settings when `PNPM_CONFIG_NPMRC_AUTH_FILE` points at the project `.npmrc` — setting it to that file is an explicit opt-in to trusting it, so auth env variables in it are expanded [pnpm/pnpm#12480](https://github.com/pnpm/pnpm/issues/12480). - [`991405e`](https://github.com/pnpm/pnpm/commit/991405e): Restore differential rendering (`ansi-diff`) to fix duplicated output lines introduced by [#&#8203;12351](https://github.com/pnpm/pnpm/issues/12351). - [`c121235`](https://github.com/pnpm/pnpm/commit/c121235): Fixed the topological order of `--filter`ed commands (`pnpm run`, `pnpm exec`, `pnpm publish`, `pnpm pack`, `pnpm rebuild`) when the selected projects depend on each other only transitively through projects that were not selected. Previously such selected projects could run concurrently or in the wrong order; now a project always runs after the selected projects it transitively depends on, while projects without a real dependency relationship still run concurrently. This now also holds for prod-only filters (`--filter-prod`), which resolve order through the production dependency graph so transitive production dependencies are respected without pulling back the dev dependencies the filter drops, and for selections that mix `--filter` with `--filter-prod` [#&#8203;8335](https://github.com/pnpm/pnpm/issues/8335). - [`d539172`](https://github.com/pnpm/pnpm/commit/d539172): `pnpm pack` and `pnpm publish` no longer follow a symlinked workspace `LICENSE` file when injecting it into a package that has no license of its own. Following the symlink could pack bytes from outside the workspace into the published tarball. - [`dcabb78`](https://github.com/pnpm/pnpm/commit/dcabb78): Fixed `pnpm up <pkg>` producing a different result than a fresh install of the same manifests would. The resolver now distinguishes `updateRequested` (true only for packages that match the user's update target) from the broader `update` flag, and for the targeted package ignores only its own lockfile-derived preferred-version pins — so the target re-resolves exactly as if its lockfile entries were deleted and `pnpm install` ran. Preferred versions a fresh install applies (manifest pins, versions propagated down the dependency chain, and the vulnerability-avoidance penalties of `pnpm audit --fix`) stay in effect, so an update never installs duplicate versions that a reinstall from scratch would not reproduce. When a preferred version holds the update target below the newest version its range admits, pnpm now prints a warning explaining that reaching the newer version everywhere requires an override. - [`dcabb78`](https://github.com/pnpm/pnpm/commit/dcabb78): `pnpm update <dep>@<version>` now prints a warning when `<dep>` is only present as a transitive dependency: the requested version cannot be applied there (updates resolve the target the way a fresh install would), and the warning recommends adding the version to `pnpm.overrides` instead, which is the mechanism that does pin transitive dependencies. Closes [#&#8203;12744](https://github.com/pnpm/pnpm/issues/12744). - [`a6c4d5f`](https://github.com/pnpm/pnpm/commit/a6c4d5f): When a dependency cannot be found in the registry (404) or the registry has no matching version, and a workspace project with the same name exists only at non-matching versions, the error now reports the available workspace versions (`ERR_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE`) instead of the raw registry failure [pnpm/pnpm#1379](https://github.com/pnpm/pnpm/issues/1379). Other registry failures (authorization, network, server errors) still propagate unchanged. The pacquet (Rust) resolver applies the same behavior. <!-- 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 Europe/Berlin) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNTEuMyIsInVwZGF0ZWRJblZlciI6IjQ0LjY5LjE0IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
chore(deps): update pnpm to v11.10.0
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 15s
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 17s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 35s
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
229232c9d3
Renovate force-pushed renovate/website-non-major-dependencies from 229232c9d3
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 15s
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 17s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 35s
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
to 72c519b99c
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 14s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 22s
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 1m0s
2026-07-10 02:01:44 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.10.0 to chore(deps): update pnpm to v11.11.0 2026-07-10 02:01:46 +02:00
Renovate changed title from chore(deps): update pnpm to v11.11.0 to chore(deps): update pnpm to v11.12.0 2026-07-12 02:16:33 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from 72c519b99c
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 14s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 22s
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 1m0s
to 832ddbf552
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 14s
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 33s
2026-07-12 02:16:33 +02:00
Compare
Renovate force-pushed renovate/website-non-major-dependencies from 832ddbf552
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 14s
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 33s
to e01d380c85
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 16s
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 39s
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
2026-07-14 02:01:37 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.12.0 to chore(deps): update pnpm to v11.13.0 2026-07-14 02:01:38 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from e01d380c85
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 16s
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 39s
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
to 902ee9f8b6
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 13s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 25s
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 1m0s
2026-07-16 06:02:49 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.13.0 to chore(deps): update pnpm to v11.13.1 2026-07-16 06:02:52 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from 902ee9f8b6
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 13s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 25s
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 1m0s
to f3f2d03425
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 23s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 49s
2026-07-18 02:02:33 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.13.1 to chore(deps): update pnpm to v11.14.0 2026-07-18 02:02:36 +02:00
Renovate changed title from chore(deps): update pnpm to v11.14.0 to chore(deps): update pnpm to v11.15.0 2026-07-18 22:01:33 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from f3f2d03425
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 23s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 49s
to d9acce58d1
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 13s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 23s
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 49s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
2026-07-18 22:01:34 +02:00
Compare
Renovate force-pushed renovate/website-non-major-dependencies from d9acce58d1
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 13s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 23s
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 49s
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
to 2e9f9d7a8f
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 12s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 25s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 54s
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
2026-07-20 02:01:27 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.15.0 to chore(deps): update pnpm to v11.15.1 2026-07-20 02:01:29 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from 2e9f9d7a8f
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 12s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 25s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 54s
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
to b7b7beb127
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 22s
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 46s
2026-07-23 02:02:41 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.15.1 to chore(deps): update pnpm to v11.16.0 2026-07-23 02:02:43 +02:00
Renovate changed title from chore(deps): update pnpm to v11.16.0 to chore(deps): update pnpm to v11.17.0 2026-07-23 22:03:04 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from b7b7beb127
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 22s
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 46s
to ae01511033
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 27s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 51s
2026-07-23 22:03:10 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.17.0 to chore(deps): update pnpm to v11.18.0 2026-07-29 18:02:34 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from ae01511033
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 27s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 51s
to 446314025a
Some checks failed
CI/CD Workflow / Detect changed paths (pull_request) Successful in 14s
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 25s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 34s
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Failing after 11m11s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been cancelled
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been cancelled
2026-07-29 18:02:37 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.18.0 to chore(deps): update pnpm to v11.20.0 2026-08-03 18:02:27 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from 446314025a
Some checks failed
CI/CD Workflow / Detect changed paths (pull_request) Successful in 14s
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 25s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 34s
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Failing after 11m11s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been cancelled
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been cancelled
to 99c224db71
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 21s
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 42s
2026-08-03 18:02:35 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.20.0 to chore(deps): update pnpm to v11.21.0 2026-08-09 18:01:46 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from 99c224db71
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 11s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 21s
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 42s
to 30c39e9f4e
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 16s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 29s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 1m4s
2026-08-09 18:01:54 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.21.0 to chore(deps): update pnpm to v11.22.0 2026-08-15 22:02:12 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from 30c39e9f4e
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 16s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 29s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 1m4s
to 39dc65039b
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 13s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 41s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 57s
2026-08-15 22:02:26 +02:00
Compare
Renovate force-pushed renovate/website-non-major-dependencies from 39dc65039b
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 13s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 41s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 57s
to 097c61ffc3
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 15s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 39s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 46s
2026-08-23 18:03:24 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.22.0 to chore(deps): update pnpm to v11.23.0 2026-08-23 18:03:25 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from 097c61ffc3
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 15s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 39s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 46s
to 09549ceb7f
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 15s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 37s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 46s
2026-08-25 18:06:52 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.23.0 to chore(deps): update pnpm to v11.24.0 2026-08-25 18:06:54 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from 09549ceb7f
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 15s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 37s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 46s
to 29b99c126f
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 20s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 28s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 56s
2026-09-01 06:01:05 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.24.0 to chore(deps): update pnpm to v11.25.0 2026-09-01 06:01:06 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from 29b99c126f
All checks were successful
CI/CD Workflow / Detect changed paths (pull_request) Successful in 20s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 28s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Successful in 56s
to dd8e5a8f28
Some checks failed
CI/CD Workflow / Detect changed paths (pull_request) Successful in 16s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 19s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Failing after 29s
2026-09-02 14:03:09 +02:00
Compare
Renovate changed title from chore(deps): update pnpm to v11.25.0 to fix(deps): update website non-major dependencies 2026-09-02 14:03:16 +02:00
Renovate force-pushed renovate/website-non-major-dependencies from dd8e5a8f28
Some checks failed
CI/CD Workflow / Detect changed paths (pull_request) Successful in 16s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 19s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Failing after 29s
to 094eb16189
Some checks failed
CI/CD Workflow / Detect changed paths (pull_request) Successful in 21s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 24s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Failing after 57s
2026-09-07 06:02:31 +02:00
Compare
Some checks failed
CI/CD Workflow / Detect changed paths (pull_request) Successful in 21s
CI/CD Workflow / Test Backend (Go) (pull_request) Has been skipped
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Has been skipped
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Has been skipped
CI/CD Workflow / Test Postgres Store (pull_request) Has been skipped
CI/CD Workflow / Security (Go: govulncheck + gosec) (pull_request) Has been skipped
CI/CD Workflow / Security (Secrets: gitleaks) (pull_request) Successful in 24s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
Website CI / Build website (pull_request) Failing after 57s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin renovate/website-non-major-dependencies:renovate/website-non-major-dependencies
git switch renovate/website-non-major-dependencies

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff renovate/website-non-major-dependencies
git switch renovate/website-non-major-dependencies
git rebase main
git switch main
git merge --ff-only renovate/website-non-major-dependencies
git switch renovate/website-non-major-dependencies
git rebase main
git switch main
git merge --no-ff renovate/website-non-major-dependencies
git switch main
git merge --squash renovate/website-non-major-dependencies
git switch main
git merge --ff-only renovate/website-non-major-dependencies
git switch main
git merge renovate/website-non-major-dependencies
git push origin main
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kreativmonkey/goloom!123
No description provided.