* chore(release): 11.28.7, pacquet 12.15.0, pnpr 0.1.0-alpha.16
* docs(release): curate 11.28.7, pacquet 12.15.0, and pnpr 0.1.0-alpha.16 release notes
Add lead paragraphs and group the patch entries by what the reader was
doing, with security fixes first on each page.
- v11: lead with the registry metadata name check, pinned pnpm
trustPolicy check, maxTarballUnpackedSize, and the removal of registry
revisions; merge overlapping entries for minimumReleaseAge metadata
revalidation, frozen-lockfile directory dependency checks,
syncInjectedDepsAfterScripts, verifyDepsBeforeRun, pnpm sbom, and
unreadable config settings.
- v12: order minor highlights from config includes and
lockfile.integrityTarget down to interactive update styling; drop the
pnpr-only distribution note from the CLI changelog; merge overlapping
entries for workspace and pnpr install speedups, minimumReleaseAge and
If-Modified-Since revalidation, frozen-lockfile and injected package
checks, verifyDepsBeforeRun, pnpm prune, pnpm pack, pnpm licenses
list, pnpm self-update, pinned pnpm flag forwarding, and WebContainer
fixes.
- pnpr: lead the minor section with the breaking ecosystem URL prefix,
hosted storage layout, artifacts.orgs, and strict config schema
changes; fold dist.pnpr tarball metadata into one entry and drop the
redundant /metrics note; lead the patch section with reserved-username
and auth security fixes.
- Use one issue-link label style across the release pages.
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* chore(release): 11.28.6, pacquet 12.14.0
* docs(release): curate 11.28.6 and pacquet 12.14.0 release notes
Add lead paragraphs and group the patch entries by what the reader was
doing, with security first on the v11 page.
- v11: lead with the NPM_ID_TOKEN audience check and the invalid file
name warning as security fixes; say the `pnpm setup` message was
printed, since v11 reports it as a warning.
- v12: merge the stdout and stderr broken-pipe entries and the two
optional-peer entries; say the Windows Rust toolchain change undoes
the MinGW fallback a MinGW-built pnpm used since 12.13.0; correct the
`browser` packing claim; drop the reference to another release line.
- Use one issue-link label style on both pages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(release): smooth two sentences in the 11.28.6 and 12.14.0 notes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(release): say sideEffectsCache.exclude covers the local and remote cache
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: give sideEffectsCache.exclude an example a cache key cannot cover
An environment variable such as JAVA_HOME could be added to the cache
key, so it does not justify excluding a package. Name the system
libraries a native addon links against instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore(release): 11.28.5, pacquet 12.10.0
* docs(release): curate the 11.28.5 and 12.10.0 release notes
Add a lead paragraph to each page and group the patch entries under
subject headings, with the security fixes first.
- Merge the unpublish path-boundary fix into the unpublish entry, the
legacy metadata cache prune into the metadata cache entry, the two
binary size entries, and the two 64 MiB archive limits on the v11 page.
- Drop the manifest-utils saveType entry from the CLI page: the CLI
only passes saveType values derived from its own flags, so no CLI user
can reach the fixed path.
- Rewrite mechanism-only entries (PAX header validation, bundled
dependency symlinks, custom resolutions under name@version keys, proxy
setting validation) in terms of what the user sees.
- Use one link style for pnpm/pnpm issues.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore(release): 11.28.4, pacquet 12.9.1
* docs(release): curate 11.28.4 and pacquet 12.9.1 release notes
Add a lead paragraph to both pages. Move the two credential-leak fixes
in 11.28.4 to the top and group the remaining 27 entries by what the
reader was doing. Merge entries that described one change: the two
INVALID_SETTING validations, the two metadata revalidation fixes, the
two hoisted filtered-install fixes, and, in pacquet 12.9.1, the two
[<since>] selector fixes. Order pacquet 12.9.1 so the `@pnpm/wasm` move,
which WebContainer users must act on, comes first.
Normalize issue links to the [#N] form and link the ETag fix to
pnpm/pnpm#16506.
Every fix was checked against the code released in 11.28.3 and
pacquet 12.9.0; none fixes a defect that never shipped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(release): describe the frozen-lockfile fixes accurately in the 11.28.4 lead
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A filtered install leaves the workspace dependencies of the projects it
selected without a modules directory, but the install the
verifyDepsBeforeRun gate spawns for a filtered run or exec selects them
(each selector gets "..."). The status check only looked at the projects
the command selected, so it reported the state up to date and the
dependency stayed uninstalled.
Both stacks now verify the selected projects plus their transitive
workspace dependencies, computed at the gate from the graphs the
selection was drawn from. A --filter-prod-only project walks the
prod-pruned graph. When a negated selector is present, the set is
intersected with the projects the spawned install selects, because
negated selectors reach that install unchanged; otherwise the gate would
require an excluded project and install on every run. The v12 helper
returns early when verification is disabled.
In v11 the computation lives in exec.commands (selectProjectsToVerify)
and is passed to checkDepsStatus as selectedProjectsGraph, leaving
deps.status unchanged. In v12 the graph walks the recursive commands
share move to cli_args/recursive/dependencies.rs.
Refs pnpm/tasks#45.
---------
Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore(release): 11.28.3
* docs(changelog): curate the 11.28.3 release notes
Merge the eleven entries about names such as `constructor` into one,
group the patch changes by what the reader was doing, and add a lead
paragraph.
Drop the zlib-rs entry: that change only touched Cargo.toml, and the
TypeScript CLI does not use the Rust engine. It belongs to the next
pacquet release.
Rewrite the store index entry for CLI users. `ReadOnlyStoreIndex` and
`ImmutableStoreIndex` are library API documented in the
`@pnpm/store.index` changelog.
Say what the store prune mark-phase fix changes for the user.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore(release): 11.28.2, pacquet 12.8.1
* docs(release): curate the 11.28.2 and 12.8.1 release notes
Add the lead paragraph the previous release pages carry, order the
pacquet entries by impact (failing installs and damaged files first,
then wrong lockfiles, speed, and command behavior), lead the exec-bit
entry with what the reader sees, and use the same issue-link style in
every entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The verify-deps-before-run gate spawned an install whenever no workspace
state file existed, even for projects that give an install nothing to
do. That install only wrote node_modules and pnpm-lock.yaml, which
became visible in directories the workspace packages patterns leave
out after pnpm/pnpm#15125.
The gate now returns up to date when no state file exists and no
project the install would cover declares a dependency, a required peer
that autoInstallPeers would fetch, or an install lifecycle script
(preinstall through postprepare, or an implicit binding.gyp build) that
ignoreScripts does not disable. pnpm:devPreinstall counts only in the
workspace root, the only place an install runs it. Under a shared
lockfile every workspace project is checked, and the projects are walked
only when the root manifest itself has nothing to install. With separate
lockfiles a non-recursive run checks only its own project. A
non-recursive pnpm 11 command gets no allProjects, so the gate reads the
workspace itself, as checkDepsStatus does.
A readPackage hook can add dependencies, so any pnpmfile the install
would load (looked up from the lockfile directory) keeps the install.
A workspace that cannot be read, or a lockfileDir pinned away from the
project, also keeps it. Projects with lifecycle scripts still
install on the first run, so those scripts keep running on first use.
Closespnpm/pnpm#16313
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore(release): 11.28.1, pacquet 12.8.0, pnpr 0.1.0-alpha.14
* docs(release): park Rust changelogs and curate release notes
The release bump ran with pnpm 12.7.0, which since pnpm/pnpm#14934
writes the changelog of every private project to a committed
CHANGELOG.md. pacquet, `@pnpm/napi` and `@pnpm/pnpr` are private
in-repo but published, and release.yml and the publish-time changelog
composer both read their parked section. Move the three sections back
to .changeset/changelogs so the GitHub release bodies and the npm
tarball changelogs are not empty. The tooling follow-up is recorded
on pnpm/tasks#38.
Curate the pnpm 11.28.1, pnpm 12.8.0 and pnpr 0.1.0-alpha.14 pages:
add lead paragraphs, group patch entries by subject, merge entries
describing one change, drop internals no CLI user sees, and fix
"instead of" tails, implementation names and link placement. This
addresses both Qodo findings on the catalog entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The remaining packages of the pnpm/components Bit workspace now live
under pnpm11/ and are published from here, keeping their npm names:
@pnpm/config.env-replace -> pnpm11/config/env-replace
@pnpm/log.group -> pnpm11/log/group
@pnpm/network.agent -> pnpm11/network/agent
@pnpm/network.ca-file -> pnpm11/network/ca-file
@pnpm/network.config -> pnpm11/network/config
@pnpm/network.proxy-agent -> pnpm11/network/proxy-agent
@pnpm/os.env.path-extender -> pnpm11/os/env/path-extender
@pnpm/os.env.path-extender-posix -> pnpm11/os/env/path-extender-posix
@pnpm/os.env.path-extender-windows -> pnpm11/os/env/path-extender-windows
lex-comparator and nerf-dart were already moved under new names, and the
Bit env component is build tooling with no counterpart here.
The sources are carried over from pnpm/components at 42ec0f7, which
includes the Windows fix that rewrites a PNPM_HOME value stored with the
wrong registry type. pnpm v12 got the same fix in pnpm/pnpm#15746, so
its changeset now also covers pnpm v11, which bundles the workspace copy.
Porting to the monorepo's ES module setup:
- relative imports carry .js and type re-exports are marked `type`;
- tests use jest.unstable_mockModule and import through the package;
- string.prototype.matchall and graceful-fs give way to built-ins;
- safe-execa and lru-cache move to the catalog versions (0.3, 11);
- network.config depends on @pnpm/config.registry-auth-key;
- the strict-ssl proxy test uses node:https and proxy v4 instead of
node-fetch and proxy v1.
The packages start at 1100.0.0 to sit in the pnpm11 version band.
Concurrent installs that resolve to the same global virtual store slot
ran its lifecycle scripts in the one shared directory at once. A script
that rewrites its own files, such as esbuild's install.js, then failed in
one install, and that install removed the slot the other installs had
already linked.
Builds into a global virtual store slot now hold a directory lock at
`<slot>/.pnpm-build.lock`, in both pnpm v11 and v12. A build that finds
the slot's `.pnpm-needs-build` marker gone once it gets the lock skips
the build, since another install finished it. A build marks the slot
"started" before it writes into it, and a waiter that finds that mark
skips the build too, since another install's build of it failed or died. An install also takes
the lock before it re-imports a slot that still carries the marker,
which would otherwise overwrite a build in progress, and so does a
side-effects cache hit that re-imports its overlay into the slot. A
patched package whose scripts --ignore-scripts withheld keeps its marker
until the scripts have run.
A failed build no longer removes the slot. It leaves the "started"
`.pnpm-needs-build` marker instead, which the next install already treats
as an interrupted build: it re-imports the pristine files and builds
again.
The TypeScript directory lock moves from `@pnpm/exec.commands` into a new
`@pnpm/fs.dir-lock` package so the build step can use it.
Closespnpm/pnpm#15568
---------
Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore(release): 11.28.0, pacquet 12.7.0, pnpr 0.1.0-alpha.13
* docs(release): curate the 11.28.0 and 12.7.0 release pages
Group both pages under subject headings with a lead paragraph that
names the headline changes and the security fixes, and move the
security entries to the top of the patch section.
Merge entries that describe one change: the node shim version files,
ranged selectors without a package.json, SemVer build metadata,
completion injection, custom modulesDir command lookup,
list --only-projects, store status, npm_execpath, catalog and npm-alias
workspace edges, the macOS CA fallback, and the prepare/devPreinstall
skips.
Drop entries that describe no change a published version could show:
the test-only --no-optional entry on both pages, the exported
CreateNewStoreControllerOptions type, the publish availability-probe
fix for a flag first shipping in 12.7.0, and the executable-bit check
the Rust CLI never had wrong.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(release): describe the bin shim part of the modulesDir entry accurately
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The dlx cache key covered the packages, the registries, the allowed
builds and the architectures, but not the Node.js runtime. A command run
under a different Node.js major reused the cache entry installed under
the first one, including packages built by lifecycle scripts, so a
native addon failed to load with ERR_DLOPEN_FAILED.
Both stacks now add the engine name (`<platform>;<arch>;node<major>`)
of the Node.js that runs the builds to the key: `getSystemNodeVersion()`
in v11 and the `node` on PATH in v12. The `nodeVersion` setting is not
used, since it only sets the target of the engines check.
Closespnpm/pnpm#8611
pnpm exec and pnpm dlx spawned the command through execa and handled
no signal themselves. A Ctrl+C ended pnpm at once, and execa's exit
cleanup then sent the command a SIGTERM while it was still handling
the terminal's SIGINT, so it never finished shutting down. A signal
sent to pnpm alone never reached the command at all.
The signal relay of the lifecycle runner is now a shared module of
`@pnpm/exec.npm-lifecycle`, and the tracked execa wrapper uses it: the
command gets a process group of its own when pnpm has no terminal,
pnpm relays SIGTERM and a SIGINT it received alone, escalates a second
interrupt to SIGTERM, and waits for the command and its group before
going on. The runner's own relay is unchanged in behavior. Terminating a
still-running child when pnpm exits is an option of the relay: the runner
always takes it, exec and dlx only for a detached command, since execa's
own cleanup terminates an attached one.
A recursive run that a signal reaches stops dispatching: queued
commands never start, a command the signal cut short is not journaled
as passed, and once every running command has settled pnpm re-raises
the signal on itself, so the shell sees an interrupted run. A single
command's exit status stays pnpm's.
Related to pnpm/pnpm#7374
Persist passed recursive tasks incrementally in a versioned journal under the workspace's node_modules directory. Hash the invocation's task graph, script commands, arguments, environment, and execution settings so only compatible state is reused.
Use unique per-run journals, cross-process serialization around the atomically published latest-run pointer, open file handles, and newline-committed records to reject stale, superseded, malformed, torn, or unsafe state. Remove each journal after its invocation succeeds and preserve the existing dependency-pruning fallback when no compatible state is available.
Implement the same state format and resume semantics in the TypeScript CLI and Rust pnpm port, with shared identity fixtures guarding cross-implementation compatibility.
Related to pnpm/pnpm#14211.
Move workspace task graph construction and scheduling out of the recursive command implementations into dedicated TypeScript and Rust packages.
The Rust crate depends on the lower-level dependency graph sequencer so package-manager code can consume it without creating a dependency cycle. Existing recursive run and exec consumers now import the shared implementations, preserving their current behavior while enabling the follow-up scheduler migrations tracked by pnpm/pnpm#14211.
Related to pnpm/pnpm#14211.
Replace the chunked topological scheduler of recursive run/exec with
per-task scheduling in both stacks: a task — a (project, script) pair —
becomes runnable when every task it depends on has completed
successfully, and runnable tasks are dispatched under the
workspace-concurrency limit with no barrier between
dependency-independent tasks.
A new "tasks" section in pnpm-workspace.yaml declares task dependencies
with the caret convention ("^build" = the task in each workspace
dependency, "build" = the task in the same project). A task with no
entry behaves as depending on its own name in the workspace
dependencies, which is exactly what chunking implied. A project without
the script becomes a pass-through node that is reported skipped and
keeps the chain intact.
Also per the RFC: task-graph cycles are ERR_PNPM_TASK_CYCLE naming the
participating tasks, scoped to the invocation's selected graph, with
ignoreWorkspaceCycles: true downgrading the error to a warning;
--resume-from excludes exactly the anchor's transitive dependencies;
--reverse runs the reverse graph; under --no-bail dependents of a
failed task are reported skipped and do not add to the exit code; with
--bail, the first failure ends the run at once and nothing new is
dispatched; output is inherited only when at most one script can ever
be in flight; and pnpm -r run --dry-run [--json] prints the resolved
task graph without running anything (the verify-deps check included).
Implementation: the projects sorter exposes the tunneled dependency-edge
map (filteredProjectsDependencies / filtered_projects_dependencies)
instead of only its flattened chunks; the recursive summary is
task-keyed (dependsOn-pulled tasks get "<dir>#<task>" keys); pacquet's
inert --sequential now means concurrency 1 and its --no-sort
resume/reverse handling is aligned with the TypeScript CLI; the dead
chunk helpers are removed.
Related to https://github.com/pnpm/rfcs/pull/23
With a global virtual store, package directories live outside the
project, so Node's upward node_modules walk from their real paths never
reaches the project's hoisted node_modules — undeclared (phantom)
dependencies stop resolving, and Node ignores NODE_PATH entirely for
ESM imports (Related to pnpm/pnpm#9618).
When enableGlobalVirtualStore is on (and extendNodePath is not
disabled), every child process pnpm spawns — run, exec, lifecycle
scripts, dlx — now receives NODE_PATH pointing at the private hoist dir
and the root node_modules, plus a NODE_OPTIONS --import flag that
registers a resolve hook restoring NODE_PATH lookups for ESM, replacing
the plugin-esm-node-path config dependency.
The flag is a self-contained constant: the registration module and the
hook are nested data: URLs, so no loader file has to exist on disk and
the flag stays valid regardless of which project or pnpm version
spawned the child. It prefers module.registerHooks() (in-thread, no
DEP0205 deprecation warning) and falls back to module.register() on
Node >=18.19 <22.15. Everything outside the RFC 3986 unreserved set is
percent-encoded: encodeURIComponent alone leaves single quotes bare,
and the NODE_OPTIONS tokenizer treats those as quote delimiters.
Both stacks embed identical hook sources and each asserts its derived
flag against the shared golden file
pnpm11/exec/esm-node-path-loader/test/import-flag.txt, so they cannot
drift apart silently.
Where the nodeOptions setting overwrites NODE_OPTIONS (run, exec,
recursive run, install lifecycle), the flag is re-applied. pacquet's
dlx and global installs force GVS off for their self-contained trees
and now strip the injected env from the caller's cloned config, so a
tool never resolves phantoms from the invoking project's tree; the
TypeScript dlx (GVS on by default) adds the flag and lets the bin shim
supply NODE_PATH.
This deliberately does not address the TypeScript-compiler side of
pnpm/pnpm#13210: tsc, tsserver, and bundlers implement their own
resolution and honor neither NODE_PATH nor Node loader hooks. That
class remains packageExtensions / compat-DB territory.
Both utilities lived in the pnpm/components Bit workspace, where their
component names collide with same-named components elsewhere in the Bit
registry. Move the code here and publish it under new names:
`@pnpm/util.lex-comparator` -> `@pnpm/text.ordinal-comparator`
`@pnpm/config.nerf-dart` -> `@pnpm/config.registry-auth-key`
The new names describe what the utilities do: the comparator is ordinal
rather than locale-aware, and the mapped URL is the key that registry
settings are stored under in `.npmrc`. The exported functions keep their
names, so consumers only change their import specifiers.
Implementations and tests are carried over unchanged. The rationale from
the components' docs pages moves into doc comments: why `localeCompare`
cannot be used for values compared across machines, and where `nerfDart`
originates.
No pacquet counterpart is needed. The Rust stack has its own
implementations of both and no user-visible behavior changes.
The TypeScript pnpm CLI freezes at v11; pnpm 12 will be the Rust pacquet
port. To make that split legible, all TypeScript source, test, and build
directories move under a new top-level pnpm11/ directory. The name states
the version boundary rather than implying a behavioral fork, since the two
stacks are meant to behave identically.
Scope is source-only: the shared workspace root stays at the repo root.
pnpm-workspace.yaml, package.json, pnpm-lock.yaml, .pnpmfile.cjs,
.meta-updater, __patches__, .changeset, .husky, and the lint/spell configs
remain in place, so one pnpm workspace and one Cargo workspace still span
all three products. pnpr/client and pacquet/tasks/registry-mock stay as
cross-product workspace members.
Rewiring the move required:
- pnpm-workspace.yaml globs prefixed with pnpm11/
- root package.json script paths, eslint.config.mjs, tsconfig.lint.json,
.gitignore, and CODEOWNERS updated
- .meta-updater/src/index.ts literals repointed (pnpm11/pnpm/package.json,
pnpm11/__utils__, pnpm11/__typings__, and the main package directory)
- regenerated every moved package's repository/homepage URL via meta-updater
- pnpm11/pnpm/bundle-deps.ts and __utils__/scripts/src/typecheck-only.ts
climb one more level to reach the repo root
.meta-updater stays at the repo root because @pnpm/meta-updater resolves
its config at <cwd>/.meta-updater/main.mjs.
TS CI (.github/workflows/ci.yml) now only runs when pnpm11/-relevant paths
change, via a dorny/paths-filter changes job plus a TS CI / Success
aggregate gate; branch protection should require only that gate.