mirror of
https://github.com/pnpm/pnpm.git
synced 2026-10-30 03:28:35 -04:00
docs-migration-runtime-setting
14777
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
439b0f6f48 |
docs: name runtime: false in the v11 migration engine check
The setting skips runtimes like --no-runtime since pnpm/pnpm#17261, so it changes the engines.node check the same way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6ab3c27774 |
fix(config): honor the runtime setting (#17261)
pnpm v12 had Config::skip_runtimes but no source set it, so `runtime: false` was dropped and only `--no-runtime` skipped the runtimes from devEngines.runtime and engines.runtime. pnpm v11 passes `skipRuntimes: opts.runtime === false` (installSingleProject.ts, recursive/context.ts) and reads the setting from pnpm-workspace.yaml, `pnpm_config_runtime`, and `--config.runtime`, not from the global config.yaml, where `runtime` is an excluded key. WorkspaceSettings now has a `runtime` field, read from pnpm-workspace.yaml and PNPM_CONFIG_RUNTIME (and through it from `--config.runtime`), cleared from the global config.yaml, and applied as `skip_runtimes = !runtime`. `pnpm dedupe` and `pnpm prune` no longer force runtimes on, as in v11, where both go through installDeps. `pnpm dedupe` accepts `--no-runtime`, since v11 dedupe takes install's options. v11 prune does not, so v12 prune stays without it. updateConfig hooks see `runtime` as v11 shows it: absent until a source sets it, and `false` under `--no-runtime`. The install pipelines record `--no-runtime` as a CLI-set `runtime: false` before the hooks run, so a hook cannot turn runtimes back on (v11 restoreCliSettings). Intended difference from v11: with engineStrict, a skipped runtime makes the root engine check use the active Node.js, which is v12's existing `--no-runtime` behavior (pnpm/pnpm#15135). v11 checks against the manifest runtime version even when it does not install it. dedupe and prune now pass the configured runtime policy, like add, update, and remove, and RuntimePolicy::Always is removed. Tests: config-crate tests for the yaml, env override, and global config.yaml exclusion; e2e tests in install_runtimes::no_runtime for the yaml and `--config.runtime` skip, the CLI override, dedupe and prune under the setting and `dedupe --no-runtime`, the hook view, a hook unable to undo `--no-runtime`, and a hook setting `runtime: false`; install_root_engine covers the engine check of dedupe and prune with a skipped runtime. Each fix fails its test when reverted. The install docs now spell the setting `runtime: false`, and the dedupe docs list `--no-runtime`. |
||
|
|
da187027e0 |
fix(add): accept the lowercase save aliases (#17270)
`pnpm add` rejected the lowercase short options `-p`, `-d`, `-o`, and `-e` with "unexpected argument". pnpm v11 declares them as `add` shorthands for `--save-prod`, `--save-dev`, `--save-optional`, and `--save-exact` (pnpm11/installing/commands/src/add.ts), and the v12 docs already list them. Only `add` has them in v11; `install` and `update` map `-P`/`-D` to `--production`/`--dev` and have no lowercase forms, so they are unchanged. `pnpm install <pkg>` is rewritten to `add` in both versions, so it accepts them too. The aliases are visible clap short aliases on the existing options, so `--help` lists them the way v11's help does. No `add` option already used these letters. Adding them makes `-o` and `-d` ambiguous in the pre-subcommand arity union (`pack-app -o <dir>` and `patch -d <dir>` take a value). The union reads an ambiguous short as a flag, so `pnpm -o dist pack-app` would have found `dist` as the command. When the union's reading names no subcommand and an option only subcommands declare precedes it, or names a command that does not declare that option, flag_relocation now retries the scan with each subcommand's own arities and relocates for the one that matches. A moved option's value now stops before the subcommand token, so `pnpm -o pack-app target` becomes `pnpm pack-app -o target` and no longer drops the subcommand from argv. install_as_add::rewrite relocates once more after renaming `install` to `add`, so an `add`-only option written before the command, as in `pnpm -D install foo`, moves after `add` instead of being rejected at the top level. Only command lines that take the rewrite pay for the pass. The flag_relocation tests that used `-o` as their mixed-cluster example now use pack-app's unambiguous `-t`. The new e2e test `add::add_accepts_lowercase_save_aliases` runs `add -d -e`, `add -o`, and `add -p` and checks the manifest groups and the exact spec, then runs `install -d <pkg>` and checks devDependencies. It fails with "unexpected argument '-d'" without the aliases. flag_relocation unit tests pin that `pnpm -o dist pack-app`, `pnpm -d dir patch foo`, and `pnpm -d edit patch foo` relocate and parse, that `pnpm -d add foo` keeps the boolean reading, and that `pnpm -o dist build` stays untouched. Part of pnpm/tasks#191. |
||
|
|
901f2438e0 |
chore: update dependencies, Node.js, pnpm, and GitHub Actions (#17217)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> |
||
|
|
6d736a02a8 |
fix(add): resolve a version-less dependency to the tag setting (#17260)
pnpm v12 resolved `pnpm add <pkg>` without a version, and a version-less `npm:` or `jsr:` dependency, to the `latest` dist-tag even when the `tag` setting named another one. pnpm v11 reads `tag` from pnpm-workspace.yaml, the global config, `PNPM_CONFIG_TAG`, and `--config.tag`, and threads it as `defaultTag` into `parseWantedDependencies` (installSome.ts) and the resolution context (createResolutionContext.ts). `Config` now carries `tag`, read from the same layers, with `Config::default_tag` falling back to `latest`. A brand-new `add` with no version resolves a non-default tag the way an explicit `add <pkg>@<tag>` does, so the picked version is also locked. A `jsr:` add and the install resolver take the same default. `dlx`, `add --config`, `update --latest`, and `outdated` keep `latest`, as they do in v11. Tested with e2e tests for the yaml setting, `--config.tag`, and a version-less `npm:` alias, each seen to fail with `default_tag` forced to `latest`, plus config unit tests for apply, override, reset, and the resolved projection. Part of pnpm/tasks#191 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
be2f02ffc8 |
fix(pnpr): emit the access record outside the request span (#17267)
With the default pretty format, every pnpr::access line printed each field twice and user three times. emit_access_log entered the per-request span before emitting, the span records the same fields, and the compact formatter appends the current span's fields after the event's. The JSON format hid the same duplication under a span object. The access event now fires outside the request span. It carries every documented field itself, so nothing is lost in either format. The span keeps request_id, method, uri, client_ip, user_agent, and user for the events emitted inside a request: the audit records inherit them, and the authentication layer records user before the handler runs. The span's status, latency_ms, and bytes slots were only filled after the handler returned, when no event inside the request could see them, so they are removed along with record_span_and_metrics. The subscriber construction moves from main.rs into pnpr::logging::log_subscriber so a test can run a request through the real pretty and JSON configurations and assert on the formatted line. The audit-log test captures values recorded on the span after it opens and checks the span's user against the audit actor. Closes pnpm/tasks#195 |
||
|
|
3bca7ffd2b |
fix(pnpr): record a token's last use off the request path, at most once a minute (#17266)
Every token-authenticated request awaited an UPDATE of last_used_at before the handler ran. With the libsql backend that is a round trip to the primary even when an embedded replica answers the lookup locally, so a replica removed almost none of the latency for authenticated traffic. LastUsedWriter in pnpr-auth now owns the write. It skips the write while the stored time, which the authenticating lookup already read, is under a minute old; claims the interval per token in memory so a burst of concurrent requests from one token writes once even before the first write lands or the replica syncs it back; and runs the write in a spawned task the request never waits for. Both middleware sites, bearer tokens and OCI tokens' parents, go through it. GET /-/npm/v1/tokens therefore reports a last-used time accurate to within a minute. Related to https://github.com/pnpm/tasks/issues/194 |
||
|
|
c9ba76ff98 |
docs: add a guide for migrating from pnpm 11 (#17268)
The pnpm 12 docs covered only the move from pnpm 10. Add a "Migrating from pnpm 11" page with the upgrade steps and the behavior changes that can need action, each checked against both CLIs: - unrecognized pnpm-workspace.yaml settings: the pnpm 11 warning becomes an error under a satisfied pin; confirmModulesPurge is rejected - a boolean flag followed by a separate value token is rejected (--frozen-lockfile=false and --no-frozen-lockfile work) - hosted Git dependencies always resolve over HTTPS, no SSH probing; existing SSH lockfile entries change only on re-resolution - the importPackage hook is not run, filterLog is ignored with a warning - engineStrict fails through optional subtrees, and checks the active Node.js under --no-runtime - package managers and runtimes named as packages are provisioned (links the existing table in package-managers.md) - a globally installed Node.js follows the project's runtime pin (globalShims) - global commands under sudo fail - registry TLS trusts the OS store, then bundled Mozilla roots - maxsockets has no default of 50 for direct connections - shellEmulator uses the built-in shell, with its parameter-expansion limits - packageImportMethod auto hardlinks before cloning on Linux - canonical cycle breaking rewrites cyclic peer variants once - dlx/pnx do not use the global virtual store - pack-app defaults to Node.js 25.5.0 - --frozen-store has no Node.js version requirement Add the page to the sidebar and link it from the v10 guide. Part of pnpm/tasks#191. |
||
|
|
0e8584301b |
fix(store): remove the metadata cache on store prune (#17253)
`pnpm store prune` pruned the dlx cache, the global virtual store, and the
content-addressable store, but left the registry metadata cache in place.
The `ERR_PNPM_TARBALL_INTEGRITY` advice in the errors docs ("run pnpm store
prune") relies on prune clearing that cache, so a stale integrity
checksum stayed after following it.
pnpm v11 removes the cacheDir entries named `metadata*` or `vN` and
`<storeDir>/tmp`, and logs "Removed all cached metadata files"
(pnpm11/store/controller/src/storeController/prune.ts). The store's tmp
dir was already cleared by the v12 CAS prune.
`StoreDir::prune` now takes the cache directory and a list of protected
directories. Between the global virtual store and CAS passes, in v11's
order, it removes `<cacheDir>/metadata*` and `<cacheDir>/vN/metadata*`
and prints v11's message. That covers every mirror root v12 writes
(`v12/metadata`, `metadata-full`, `metadata-full-filtered`,
`metadata-private`), the `v11/` legacy roots, and older top-level ones.
Unlike v11 it keeps the rest of each `vN` directory (cargo index,
tarball resolutions, runtime shasums), and it keeps any matching
directory that is or holds the store, globalDir, the global packages and
bin dirs, the pnpm home, or the state dir, since a project's
pnpm-workspace.yaml can point cacheDir at a broad directory. Symbolic
links are kept, as v11's directory filter keeps them.
It runs under the exclusive store prune lock, which every install holds
shared for its whole run, so no concurrent install reads the cache
while it goes.
Tested by unit tests for the name filter, symlinks, an enclosing store,
cacheDir == globalDir, and a home-like cacheDir, and by CLI e2e tests
that install a package and prune, and that keep a pnpm home placed
inside cacheDir.
Part of pnpm/tasks#191.
|
||
|
|
7fbde8b1b4 |
fix(hooks): warn when a pnpmfile defines importPackage (#17263)
pnpm v12 does not run the `importPackage` pnpmfile hook. That is
deliberate: commit
|
||
|
|
c0539430d7 |
fix(help): accept --all and -a (#17262)
`pnpm help --all` and `pnpm help -a` failed with "unrecognized subcommand", because `help` is clap's built-in subcommand, which takes no options. pnpm v11 accepts the flag (pnpm11/pnpm/src/cmd/help.ts) and uses it to list the commands its default help hides. Clap's help already lists every command, so the flag is dropped from the arguments of `help` before clap parses argv. `pnpm help -a <command>` prints that command's help, as in v11. Plain `pnpm help` and `pnpm help <command>` are unchanged. An e2e test compares the output of `help --all` and `help -a` with `help`, with and without a command name. Both tests fail without the argv rewrite. `--all=<value>` and `--no-all`, which v11's nopt Boolean accepts, are dropped too. A unit test pins that only the arguments after `help` are touched: `pnpm run help -a` keeps `-a` for the script. `pnpm help help` does not list `--all`, since clap's built-in help subcommand declares no options and the flag has no effect in v12. Part of pnpm/tasks#191. |
||
|
|
359ff10948 |
fix(run): honor requiredScripts in recursive runs (#17257)
pnpm v12 read no `requiredScripts` setting, so `pnpm -r run <script>` ran normally when a selected project lacked a script the workspace declares required. pnpm v11 fails such a run before dispatching anything (`throwIfRequiredScriptIsMissing` in pnpm11/exec/commands/src/runRecursive.ts), with ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT and the message `Missing script "<script>" in packages: <names>`. The config crate now reads `requiredScripts` from pnpm-workspace.yaml into `Config::required_scripts`. The global config.yaml drops it, as v11 does, since it is not a config-file key there; v11 reads no environment variable for it either. The recursive run checks it where v11 does, after `--resume-from` and the dry-run exit, before the hidden-script filter and the no-script check, and after the `pnpm <script>` shorthand decides whether to fall back to `exec`. Projects are named by their manifest name, falling back to their directory. Tested by a run_recursive e2e module that fails without the check, including a nameless project listed by its directory and a --filter run that checks only the selected projects, and a config test for the workspace and global sources. The requiredScripts docs of both versions now say the check covers the projects selected for the run. Part of pnpm/tasks#191. |
||
|
|
4e6351952d |
docs: correct the guides from agent skills to lockfile (#17248)
Correct the v12 guides from agent-skills.md through lockfile.md against pnpm/crates, and fix the same wrong text on the v11 twins where pnpm11 confirms it. Part of pnpm/tasks#191. Wrong claims (both versions): - configuring: the global auth file is auth.ini, not rc; list the npm_config_* exceptions (workspace dir, userconfig, URL-scoped auth). - config-dependencies: the hook import used an invalid ESM specifier. - docker: Example 3 ran fetch --prod and built without an offline install or pnpm-workspace.yaml. - errors: drop MISMATCHED_RELEASE_CHANNEL, describe INVALID_NODE_VERSION through nodeVersion, PEER_DEP_ISSUES needs strictPeerDependencies. - faq: no lockfile deletion after a readPackage hook, default store order, junctions depend on symlink privilege. - filtering: `!./lib` matches one project; use `!./lib/**`. - git branch lockfiles: every non [A-Za-z0-9_.-] character becomes `!`, the name is lowercased, merging deletes branch lockfiles, "or" patterns. - git-worktrees: remove the nonexistent worktree:new and shell/wt.sh. - global-packages: orphaned dirs are cleaned by add -g (and update -g in v12), not store prune; document that add -g replaces every group that contains the package. - how-peers-are-resolved: current peer directory names. - completion: built-in completion already covers --filter and scripts. - feature-comparison: broken JSR anchor. Wrong claims (v12 only): - cargo: cargo.indexUrl fails the parse, overrides apply to adding only, merge the duplicated Cargo index section into Registries. - global-virtual-store: pnx keeps its own virtual store (dlx part only; the global-install claims are left for a separate decision). - installation: the npm package installs on Node.js 18, only get-pnpm needs 22.13; trim unverified compatibility rows. Missing facts (v12): agent-skills group filtering and naming, catalogs (catalogs.default, pnx, --save-catalog, error codes), cargo recursive add, build-std, resolution keys, completion zsh/pwsh setup, config dependency location and error codes, include error codes, CI opt-outs and pnpm ci, finders on list, filtering settings and --no-fail-if-no-match, global install refusals, trust for npm-registry package managers, Rust shims, overrides link from aliases. Stale history (v12): drop v11/v10 version markers and "since v11" narration in continuous-integration, filtering, finders, git-worktrees, global-packages, global-virtual-store, how-peers-are-resolved, lockfile, completion, configuring. Review follow-up: the Node.js compatibility table keeps the pnpm 10 column; Docker Example 3 builds with all dependencies in a build stage and ships only production dependencies, with the pnpm-workspace.yaml caveat (both versions); catalogs keep the bare-path form; v11 global packages name `pnpm update -g` as cleaning orphans too; the cargo build-std note says when rust-src must be listed. Second review: build-std needs a non-empty list; the Docker fetch layer also depends on the project settings; branch lockfile names keep only ASCII characters; the conflict merge needs both sides to parse; Rust shims and package managers are default globalShims participants; pnpm self-update also cleans orphaned global groups; v12 pnpm login writes tokens to config.yaml; legacyDirFiltering keeps the subtree wording (the v12 mismatch is pnpm/tasks#196); the v12 Docker example copies the files that `include` names before pnpm fetch. |
||
|
|
52dce0f4dc |
docs(settings): correct defaults and document missing settings (#17246)
Wrong claims (v12 and the v11 twin where the v11 source agrees): - proxy precedence: a configured httpsProxy/httpProxy/noProxy wins and the environment is only a fallback; HTTPS never falls back to HTTP_PROXY - proxy opt-out: false or null in httpsProxy, httpProxy, or noProxy reads as unset; only proxy: false turns proxying off - networkConcurrency auto value is clamped to 64-96, not 16-64 - maxsockets default: unset in v12, 50 in v11 - stateDir on macOS is ~/.local/state/pnpm - unsafePerm only moves TMPDIR to node_modules/.tmp - ignoreWorkspaceRootCheck affects pnpm add only; the duplicate CLI section is removed in favor of the workspaces page - useRunningStoreServer has no effect and is removed - .npmrc also supplies TLS, proxy, and local-address settings - the list of keys a project pnpm-workspace.yaml cannot set now names scope (both) and macosBackup, ci, tools, globalShims (v12) - v12: the GVS CI auto-disable sentence, the frozenStore Node.js requirement, the JavaScript shell emulator, and the dlx global virtual store claim are corrected Missing settings (v12): saveExact, embedReadme, scriptsPrependNodePath, userAgent, lockfileDir, excludeLinksFromLockfile, globalVirtualStoreDir, externalDependencies, autoInstallPeersFromHighestMatch; loglevel silent; PNPM_HOME defaults for globalDir/globalBinDir; childConcurrency negative values; what ci changes and where updateNotifier is skipped. Index (v12): lists every section on the group pages, including progress, saveTypes, autoDedupe, maxTarballUnpackedSize, and macosBackup. Stale history (v12): removed pre-v12 "Added in" markers and rewrote "Since v10/v11" and "Before v11" sentences as current behavior. Part of pnpm/tasks#191. |
||
|
|
b3eee14a96 |
docs(cli): correct the command reference from access to init (#17247)
Part of pnpm/tasks#191. Wrong claims, fixed on the v12 page and on the v11 twin where it had the same text: - audit: `--interactive` implies `--fix=override`; `info` is a valid audit level. - change check validates the pending intents. - deploy copies the publish packlist unless `deployAllFiles` is set. - deprecate: an empty message fails, use `pnpm undeprecate`. - deprecate, dist-tag: only `--otp` supplies the one-time password. - dist-tag add defaults to `latest`, not the `tag` setting. - docs takes exactly one package. - env: there is no `add` subcommand, and `list` queries the Node.js mirror only. - v12 only: clean no longer says it uses Node.js, add no longer presents `-w` as an alias, the deploy package-manager-pin history is no longer contradictory, doctor sample shows store v11. Missing facts: - add: `--save-prefix`, `--save-build`, `--no-save-peer`, `--no-save-types`, `--lockfile-only`, `--force`; where Yarn vs npm and Bun pins go; catalog and configDependencies links. - access status shorthands; approve-builds error codes, the placeholder value and the immediate rebuild; audit exit code, `--optional`, `--production`, and how to make `--ignore` write `audit.ignore`; `--ignore` exits with code 0 and `--ignore-unfixable` fails on an advisory without a GHSA ID. - bugs and docs `--registry`; ci accepts install options; clean script override and modulesDir; config `--location` type and `get` output; create name mapping, `--shell-mode`, `--cpu`, `--os`, `--libc`; dedupe flags; deploy error codes and Dockerfile WORKDIR; dist-tag aliases and web OTP; env `--global` requirement; exec recursive options and PNPM_PACKAGE_NAME; fetch `--ignore-pnpmfile`, lockfile requirement, optional pnpmfile in the Dockerfiles. - usage, examples, and output for bin, cache, cache list, cache delete (with the no-argument warning), cache view, cat-file, cat-index, find-hash, ignored-builds, import, and init (`initPackageManager`, `initType`, ERR_PNPM_PACKAGE_JSON_EXISTS). Stale history on v12 pages: removed "Added in" markers for v10 and v11 and rewrote "Since v11..." passages as current behavior in access, add, approve-builds, audit, bugs, cache-list-registries, cache-path, cache-prune, change, ci, config, create, deploy, deprecate, dist-tag, docs, doctor, help, ignored-builds, and init. Review follow-up: cat-file now says it rejects the hex digest that cat-index prints; the settings index links initPackageManager and initType; `change check` says it plans the release without reporting it (both versions); `pnpm bin` follows a custom modulesDir. |
||
|
|
30f6d58a56 |
fix(pnpr): bound libsql auth reads, and a write's wait for a connection, by the auth deadline (#17252)
Only reads ran under the auth timeout, so a write that never got an answer held its pool slot indefinitely, and once every slot was held that way each lookup waited for one until it timed out. Putting writes under the deadline instead would report a write the database committed late as a timeout. The pool now has a read and a write entry point. A read runs under the deadline end to end. A write is bounded only while it waits for a connection, before anything is sent, and then runs to completion, the half of the sqlx model libsql can follow without a server-side statement timeout. A database that stops answering writes can still hold every connection, but the pool recovers once those writes return. Connections open on the blocking thread pool, with at most as many in flight as the pool has connections, so a replica's stalled background sync can neither hold a slot past the deadline nor pile up blocked threads. Also drops the claim that a pooled connection avoids new TCP or TLS handshakes. Measured against a local libsql server, one pooled connection served 50 token lookups but libsql opened 45 TCP connections for them; the old shared connection behaved the same. |
||
|
|
cb3f29bc0a |
docs(cli): correct the command reference from install to with (#17251)
Part of pnpm/tasks#191. Wrong claims, fixed in the v12 docs and, where the same text is wrong for v11, in the v11 docs: - logout: drop the nonexistent --scope option. - patch: the edit dir is node_modules/.pnpm_patches/<name>@<version>; drop the "*" special case. - patch-commit: fix the ---patches-dir heading. - prefix: describe the real marker walk instead of "closest package.json". - setup: setup installs pnpm as a global package and writes pn/pnpx/pnx. - start, test: "run start" and "run test" are not aliases. - unlink: drop the system-wide and link --global wording. - unpublish: a package name is required; --otp has no env var; --registry wording. - update: --depth is dependency depth, not directory depth. - version: --tag-version-prefix defaults to the tagVersionPrefix setting. - view: the first argument is always the package. - why: drop --global and --only-projects; --exclude-peers has no effect; --prod includes optionalDependencies. - install: recursiveInstall spelling; replace the stale ci-info snippet. - v12 only: pack-app runtime default and builder download, pnx provisioning list and cmd.exe typo, tasks sample priority. Missing flags, settings, and error codes on the v12 pages: install (about twenty flags), lane, licenses, link, list, outdated, owner, pack-app path rules, pack, patch, patch-remove, peers, permissions, ping, pipeline, pm, pnx, prune, publish, rebuild, recursive, remove, repo, root, run, runtime, sbom, search, self-update, set-script, setup, shim, stage, star, start, store, tasks, team, test, token, unlink, unpublish, update, version, view, why, with. Stale history on the v12 pages: removed "Added in" markers older than v12 and rewrote "Since v11.x" and "In v11" sentences as current behavior (login, logout, licenses, link, outdated, patch, pnx, publish, runtime, sbom, self-update, setup, update, and others). Review follow-up: patch's default edit directory is named after the argument as typed (both versions); self-update installs the unscoped package only for pnpm 12 and newer; only --no-bail is restricted; npm_package_* covers name, version, config, engines and bin; prefix -g prints the parent of root -g; --optional no longer claims to override --no-optional. |
||
|
|
7fb2bfab4a |
docs(pnpr): correct configuration keys, defaults, and commands (#17245)
Part of https://github.com/pnpm/tasks/issues/191. Wrong claims: - cli: drop the nonexistent `add-user --password` flag and document the terminal/stdin password read; `oci-gc --min-age` and `cache-gc --max-age` are the flags (the `-secs` forms are aliases); `--from` defaults to the `storage` key, not an internal field; `info` and `http` log the same access records; only global and server options have PNPR_* variables. - configuration: `packumentTtl` defaults to 5m; the minimal config now sets `auth.htpasswd.file`, and the auth section says accounts live in memory without it or a backend; grouped hosted registries default their org to `<ecosystem>~<name>`; the handshake is served for any surface; the pipeline surface also satisfies "something must be served"; add `package.update_maintainers` to the audit list. - tls-and-reverse-proxy: remove the invalid `publicUrl:` YAML key and the nonexistent `rate_limiting` setting. - backup-and-restore: `pnpr verify` hashes by default (`--check-hashes` does not exist, `--skip-hashes` skips only the hash comparison); `migrate-storage --verify` compares bytes. - webhooks: audit events do not cover publishes or token creation; the example uses a real action and the `actor` field. - cli: the `RUST_LOG` example keeps `pnpr::audit=info`. - install-acceleration: the `--public-url` host is exempt from the private-address refusal. - shared-side-effects-cache: `pnpm pipeline` publishes workspace-task artifacts. Missing facts: `routes.allowedPrivateNetworks`, the upstream `search` key and canonical `maxAge`, built-in `npm.jsr.io` route, relative path resolution, `log` defaults and `type`, `${VAR:-default}` substitution and the full secret-field list, `oci` defaults, `artifacts.quota` bounds, `migrate-storage` destination rules, `--osv false`, `--packument-ttl` default, S3 `forcePathStyle`/`allowHttp` defaults, grouped org layout, OIDC key table, container image section, `backend:` ignoring the htpasswd and token files. Duplicates and links: health probe bodies, reload procedure, and the resolver allowlist now link to their one home; broken `#secret` anchor fixed; links into the pnpm docs use absolute pnpm.io URLs. Stale history: drop "pnpr v1" and the per-paragraph "Since alpha.N" / "Since pnpm 12.x" prefixes, and the contributor-only extensibility text. Also corrects the `as_filter_directive` doc comment, which claimed `http` quiets the rest of the crate. |
||
|
|
71d90a06a3 |
chore(release): 11.28.8 (#17256)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>v11.28.8 |
||
|
|
ae47bbf062 |
fix(pnpr): sync the embedded replica on a connection of its own (#17232)
libsql's read-your-writes pulls the primary's frames on the connection that issued the write and resets that connection's pager when the pull ends, so a read in flight on the same connection then fails with "file is not a database"; the schema setup at startup hit it deterministically, since it holds a count query open across the counter transaction. Turn read-your-writes off and pull through `Database::sync` after each write instead, which uses a fresh connection. A login that only checks a password does not pull. Schema setup takes the sync as a hook and runs it before each read that depends on a preceding write, and the login that follows a lost insert race syncs before reading the winner's row. TokenBackend gains revoke_all_for_user, so revoking every token of an account is one DELETE and one pull instead of one pull per token; the pull is skipped when nothing was deleted and the replica lists no token for the account. The test's mock primary now serves the frames of its own WAL, modelling a WAL restart as the next generation, so the test covers a write reaching the replica through background sync, a replica seeing its own write and bulk revocation, and schema setup against an empty primary. Related to pnpm/tasks#183. |
||
|
|
2f4535cf69 |
fix(add): lock the version an explicit tag or range picked (#17240)
`add` resolves an explicit registry selector (a tag, a range, a version) to a concrete version and saves it as a range. The install that follows re-resolved the saved range without knowing which version the add had picked, so it locked the newest match. A tag below the newest version of its major, or a range with an upper bound, locked a version the user did not ask for, and a re-add kept the importer's existing pin. `resolve_explicit_registry_spec` now returns the picked version with the range. `prepare_manifest` collects one `PickedVersion` per selector whose saved specifier is not that exact version, and the add seeds each one into `preferred_versions_override` with the requested-version weight, the way `pnpm update` and the catalog path do: - A pick is settled, and seeds nothing, when the fast lockfile update cannot move it: the importer keeps both its pin at the picked version and its specifier, or the lockfile holds no other version of the package. The fast path then still applies. When the specifier changes, the fast update retargets the edge to the highest locked version the new range admits, so a matching pin is not enough on its own. - If the importer locks another version, its pin is withheld (`ImporterUpdateSeedPolicy::DropOnly`) so the resolver can move it. - Otherwise only the preference is seeded. A dropped pin marks the edge as an update target, which revalidates its metadata. `prefer_frozen` is turned off whenever a preference is seeded, so the fast path cannot absorb a lower pick into a version the lockfile already holds. - A filtered add (`--filter`) seeds each selected project the same way. The seeded preference also lets the resolver take the metadata mirror the add's own pick just wrote, so an `add --save-types` companion package is fetched once. `bulk_add_fetches_only_required_metadata_formats` now expects that. The workspace specifier helpers move into `add/workspace_specifier.rs`, and two functions are split to stay within the size lints. `config_includes::a_config_dependency_packed_with_pnpm_pack_keeps_its_config_entry` now sets its own `storeDir`. Without one it fell back to the device store, which kept the byte-identical packed tarball from an earlier run, so every local run after the first failed. `jsr:` selectors are not seeded. They resolve through the npm alias of the package, and this change does not cover them. Closes pnpm/pnpm#17234 |
||
|
|
26047ac598 |
fix(pnpr): run libsql auth operations on a pool of connections (#17237)
The libsql backend ran every operation on one shared connection. On a remote database that connection is a Hrana stream, and each response carries the baton for the stream's next request. An operation dropped while its request was in flight (a client that disconnected, the auth timeout) never received the new baton, so the connection kept the spent one and the server refused every later request with "Received a baton that has already been used". From then on every login and every authenticated request failed with a 500 until pnpr restarted. One shared stream also put concurrent operations into each other's transactions. Operations now check a connection out of a pool and run alone on it. A connection returns to the pool only after its operation succeeded outside a transaction; any other is closed and replaced on a later checkout. This is what sqlx does for the PostgreSQL and MySQL backends. Pooled connections keep their HTTP client, so reuse costs no new TCP or TLS handshake, which a connection per operation would have paid on every token lookup. backend.libsql.maxConnections sizes the pool, defaulting to 10 like sqlx. |
||
|
|
be4d6f5924 |
fix(git-fetcher): build a git dependency in every slot it is linked into (#17231)
A git-hosted dependency with install scripts but no `prepare` (for example a native addon with `install: node-gyp rebuild`) was built only by the install that fetched it. A later install that linked the same commit from the store into a new location, such as a new global virtual store slot or a fresh `node_modules`, skipped the build and exited 0. The git fetchers wrote the store-index row with `requiresBuild` set to whether the package needed `prepare`. The warm prefetch trusts a stored `false`, so the build gate never marked or built the new slot. The cold path leaves the value unset and derives it from the fetched files, which is why the first install built it. - The git fetchers now store `requiresBuild` derived from the stored files (install scripts, `binding.gyp`, `.hooks/`), as for any other package. `requiresPrepare` still records whether `prepare` is needed. - The prefetch re-derives `requiresBuild` from the files for a row that records `requiresPrepare` and bundles no manifest. Only the v12 git fetcher writes that shape, so stores that already hold such a row are repaired without clearing them, and registry, zip and pnpm v11 rows keep their stored value. `type_git_dependency_reuses_side_effects_on_warm_install` passed only because of this bug: its postinstall wrote nothing inside the package, and since pnpm/pnpm#14717 a build with nothing to restore runs on every install. The postinstall now also writes a file inside the package, so the test checks that the warm install restores it from the side-effects cache without rerunning the script, which is what pnpm/pnpm#13426 meant to cover. pnpm v11 computes `requiresBuild` from the bundled manifest and files, so it is not affected. Closes pnpm/pnpm#17224 --------- Co-authored-by: Zoltan Kochan <z@kochan.io> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
40fab6f038 |
fix(fs): pass a replaced signal handler the details it expects (#17220)
`sigaction` keeps the one-argument and three-argument handler forms in a union, and nothing but `SA_SIGINFO` in the flags says which one it holds. The install read only the handler address out of the disposition it replaced and dropped the flags, so the chained call went through `extern "C" fn(c_int)` whatever the handler had been installed as. A handler installed with `SA_SIGINFO` was then called with its `siginfo_t` and context arguments left to whatever the registers happened to hold, which is undefined rather than merely incomplete. Nothing in pnpm's own process installs such a handler, which is what the old `// SAFETY:` comment rested on. pnpm also ships as a Node addon, where the process is the host's and the disposition being replaced is whatever the host installed, so the premise is not pnpm's to make. The flag is now read from `sa_flags` alongside the address and kept with it, and the chained call goes through the matching signature. pnpm's own handler takes `SA_SIGINFO` so that it has the delivery's `siginfo_t` and context to pass on. |
||
|
|
6f95957063 |
docs: lint prose with Vale (#17238)
* docs: lint prose with Vale Add Vale with an in-repo style, .vale/styles/pnpm, that turns the writing rules in AGENTS.md and the Google developer documentation style guide into lint rules. The stock Vale packages (Google, Microsoft, RedHat, write-good) were too noisy to block on, for example 1,300 passive-voice hits in the v12 docs alone, so the rules are written for this repo, following the GitLab model. Each one links the guideline it enforces. Levels: - error, fails CI: dashes, plain-word substitutions, condescending words, "please", "note that", hedges, puffery, optional plurals, exclamation marks, ellipses, curly quotes, generic link text, product spellings, sentence-case headings, repeated words, double spaces, implementation history in comments; in changesets, bold or italic emphasis, internal names and release-line names. - warning: future tense, sentences over 30 words, "instead of" and "so that" tails in changesets. - suggestion: passive voice. .vale.ini scopes the rules. Docs get every rule. Contributor Markdown and code comments keep dashes and headings as warnings until their roughly 8,100 em dashes are rewritten. Released changesets stay in .changeset/ and are not rewritten, so lint-prose.mjs lints only the changesets added since PROSE_BASE (the PR base in CI). The prose workflow installs a pinned, checksum-verified Vale release and runs `pnpm lint:prose`. The rest of this change rewrites the existing docs, READMEs, contributor Markdown, and code comments to pass the error-level rules. Only prose and comment text changed. Renamed headings keep their anchors. Spelling stays with cspell, which checks identifiers that Vale does not see. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: keep prose lint from flattening meaning --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
cb496f5427 |
docs(pnpr): get zstd through Cloudflare's edge (#17230)
Cloudflare removes zstd from Accept-Encoding before a Worker runs, and from cf.clientAcceptEncoding too, so pnpr behind the recipe's Worker never sent zstd. The Worker now asks pnpr for "zstd, gzip", the value pnpm sends on archive requests, and returns the coded body with encodeBody "manual". Cloudflare passes a zstd body to a client that accepts it and decodes it for one that does not. Verified on a trial deployment: a tar for pnpm arrives zstd-coded, metadata for a gzip client is still gzip-coded, and a gzip-only client gets the tar uncompressed. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
79b4dd2d78 |
fix(pnpr): follow a Turso Cloud primary from the embedded replica (#17225)
Enable libsql's `sync` feature so the embedded replica (`backend.libsql.replicaPath`) can follow a Turso Cloud primary. The replica was built with libsql's `replication` feature only, whose protocol Turso Cloud stopped serving: pnpr failed to start with "Write delegation: Unimplemented ... deprecated version of sync". With the `sync` feature, `Builder::new_remote_replica` defaults to `SyncProtocol::Auto`: it probes the primary's `/info` route and uses the newer sync protocol (bootstrap from `/export`, frames from `/sync`, writes over Hrana) when the primary answers, and keeps the replication protocol otherwise. A self-hosted `sqld` answers 404 on `/info`, so it is unaffected. The new test runs a mock primary that serves those routes in front of a local libsql database and checks that the replica bootstraps from the export, reads locally, and writes to the primary. Without the feature it fails the way the issue describes. `connect` also creates the replica's parent directory, as the local stores do for their files, and with `syncInterval: 0` it runs the initial sync under the startup deadline, since libsql would otherwise bootstrap inside `Database::connect` with no bound. Closes pnpm/tasks#183. |
||
|
|
aeeba836c4 |
fix(cli): accept a registries entry routed through the default scope (#17226)
The "registries entries do not match any configured registry" warning built its configured set from the scoped and prefixed routes and the built-in prefixes. An entry whose only route is the bare `@` scope is written to `config.registry` and stored under no scope, so the warning named it as ignored while the install resolved through it and kept its `serverType`. The warning now builds its configured set from Config::resolved_registries, the view that carries the default registry beside the scoped routes. The built-in `@jsr` route comes with it, so an entry for npm.jsr.io is a legitimate target too, as the built-in named registries already were. Closes https://github.com/pnpm/tasks/issues/184 |
||
|
|
7b5aab6aca |
test(cli): share and widen the recursive-run marker deadline (#17221)
`task_starts_as_soon_as_its_dependencies_finish` failed on macOS after 30.448s, which is its build script's own 30000ms poll deadline rather than any assertion in the test. The same test passed on ubuntu and windows in that attempt, and the rerun passed on macOS with the identical commit, so the threshold was marginal. The deadline is there so a fixture that never overlaps fails instead of hanging, but 30 seconds also asserts a schedule: the waiting script needs the package manager to finish one project and then install and spawn another, which on a loaded runner costs tens of seconds. `.config/nextest.toml` already says as much, giving a stalled test `period = 60s` and `terminate-after = 5`. The scripts were holding themselves to a tenth of that. `wait_for_marker_script` now builds the poll, so the deadline has one home and reads as the deadlock detector it is. It is separate from `CHILD_DEADLINE`, which is also 30 seconds but waits on a child the test spawned itself rather than on a whole run of the package manager. The deadline itself moves to 2 minutes. Three fixtures shared the inlined poll, two in `task_graph` and one in `environment`, so all three had the same latent flake and now share the helper. This widens the window rather than closing it. The fixture proves interleaving with a barrier, and from inside the waiting script a project that has not started yet is indistinguishable from one that never will without a bound, so a bound remains and a slow enough runner can still cross it. Asserting on dispatch order instead of blocking on a barrier is the design that would not need one, and is not attempted here. |
||
|
|
7ed672f914 |
fix(deploy): verify the source lockfile on legacy deploy (#17211)
The legacy deploy resolves the deployed project from scratch and seeds the resolver with the source lockfile's pins as preferred versions. The lockfile-verification gate only runs on the install's wanted lockfile, which for a legacy deploy is the deploy directory's own (absent) one, so the source lockfile was never verified. A pin that minimumReleaseAge rejects was then dropped by the resolver's release-age filter and replaced by a mature version outside the lockfile, with no diagnostic. pnpm 11 installs the legacy deploy against the shared lockfile and runs verifyLockfileResolutions on it, so it fails with ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION. Restore that behaviour by verifying the source lockfile with the same resolution verifiers before the deploy install. The check covers the whole source lockfile, as the v11 gate and a workspace install do. trustLockfile and --trust-lockfile skip it. LegacyDeploySource now holds the loaded source lockfile and its path, so the lockfile is read once for both the preferred versions and the verification, and the verification cache is keyed by the same path a workspace install records. Fixes https://github.com/pnpm/pnpm/issues/17209 |
||
|
|
81e6e9ec1a |
docs: say where serverType pnpr applies and what a tar main base costs (#17227)
The registries page presented the pnpr layout as the layout of every pnpr registry, but a pnpr .tgz base advertises the npm layout, which the undeclared default already reconstructs; serverType: pnpr belongs on a tar base. The pnpr configuration page now says tarballs: tar may go on the main router and what that costs other clients. The Cloudflare recipe's Worker waits through a cold start instead of failing it, and the page notes that the edge rewrites Accept-Encoding. |
||
|
|
5154bf82ae |
fix(exe): embed Node.js 26.12.1 in the standalone builds (#17228)
The standalone builds embedded Node.js 26.0.0, which predates the October 2026 CA distrust shipped in Node.js 26.12.1, so a standalone pnpm still accepted certificates issued under the distrusted intermediates. Node.js 26.12.1 is already the repo's devEngines runtime and is in the lockfile. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
f880d91124 |
fix(pnpr): recognise a unique violation reported by Turso (#17223)
The libsql auth backend classified a duplicate row only through SQLite result codes. Turso's remote protocol reports it as a Hrana stream error whose message carries the code name, so the counter seed on a second start against the same database failed instead of being tolerated, and pnpr exited at startup. A taken username reported the same way answered 500 instead of the login or name-taken path. Parse the Hrana message the way the lock-conflict check already does. Related to https://github.com/pnpm/tasks/issues/183. |
||
|
|
9c11ea9948 |
fix(fs/linux): read the umask without writing it (#17219)
`current_umask` set the mask to `0` to learn what it was and put it back, as POSIX leaves no other way: `umask(2)` returns the value it replaced. That window is process-wide, so a file another thread created inside it was created unmasked, with wider permissions than the user asked for. An install under `umask 077` could leave a world-readable file behind. The window was known and argued harmless, and the argument holds for what it reasoned about: an unmasked file carries no executable bit, matches no desired mode, and so is copied by the import tiers rather than linked. It says nothing about the file's permissions, which is the defect. Linux has published the mask in the `Umask:` line of `/proc/self/status` since 4.7, so it is read from there first and the window never opens. A kernel without the line, a host without `procfs`, and every other Unix fall back to the syscall pair, which now has nothing at all between its two calls. One read per process either way: the `LazyLock` that kept the import hot path from paying per file is unchanged. |
||
|
|
7e8e991e7e |
feat(pacquet): load dotenv files named by --env-file (#16570)
Related to pnpm/pnpm#7111. pnpm has no .env support, so users wrap every invocation with dotenv-cli or node --env-file. This adds the loading to the Rust CLI itself: a repeatable global --env-file <path> (clap Vec<PathBuf>) whose files are parsed with dotenvy and layered into the process environment in order so later files override earlier files while never overriding variables already in the initial process environment (matching node --env-file). Relative paths resolve against the invocation working directory before --dir or --workspace-root is applied. The load runs on the main thread before the startup thread exists (set_var must not run once another thread is alive), ahead of config resolution, so PNPM_CONFIG_* overrides and user-level .npmrc ${VAR} placeholders (the registry-token case from pnpm/pnpm#7111) resolve from the files, and ahead of any pinned-pnpm dispatch so the child inherits the variables. Shim invocations skip the load (their arguments belong to the target). The flag is stripped from the child argv (an older pnpm would reject it). dotenvy $VAR expansion is accepted as standard dotenv behavior (single-quote to keep a literal $); project-level .npmrc expansion stays refused as before. Parse errors name the file without echoing its contents, NUL keys or values fail instead of panicking, invalid UTF-8 fails as a parse error, and a UTF-8 BOM is tolerated. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
f0982d4f11 |
fix(resolver): refresh metadata during dedupe (#17215)
pnpm dedupe resolves a fresh lockfile without an existing wanted_lockfile and with prefer_highest_version: true, so locked versions sit in preferred_versions with VersionPreference::Highest while update remains UpdateBehavior::Off. Under that combination, MirrorShortcuts::decide allowed dominant_lockfile_version and DiskFirst::decide allowed reading the on-disk metadata mirror without a conditional registry request, leaving stale packument metadata (such as deprecation messages) in place even when pnpm dedupe --check on a cold cache reported a diff. Thread refresh_metadata through ResolutionRefreshOptions into MetadataPickRequest for dedupe runs, and disable unverified on-disk mirror shortcuts when refresh_metadata is set and the network policy is not cache-first. Conditional ETag / Last-Modified headers from the mirror are still sent so unchanged packuments get a 304 response. Closes pnpm/pnpm#17208. --------- Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
7ef0e9d540 |
fix(exportable-manifest): keep pnpm.config in packed and published manifests (#17213)
A config dependency declares its settings YAML file in `pnpm.config` of its `package.json`, which consumers read from the installed manifest under `node_modules/.pnpm-config`. Preserve `pnpm.config` when exporting a manifest for `pnpm pack` and `pnpm publish` while continuing to strip the rest of the `pnpm` field, and always include the file referenced by `pnpm.config` in the packlist. Closes pnpm/pnpm#17210 |
||
|
|
e2cc15488e |
fix(exec): do not dump core for pnpm when re-raising a child signal (#17214)
When a command started by `pnpm run`, `pnpm exec`, or `pnpm dlx` is killed by a signal on Unix, `pnpm_executor::exit_like` calls `pnpm_fs::die_from_signal` to reset that signal to `SIG_DFL` and re-raise it on pnpm so the parent shell observes the same wait status. When the child died from a core-dumping signal such as `SIGABRT` or `SIGSEGV` (for example when `tauri build` aborts after a panic in `@tauri-apps/cli`), re-raising that signal with `SIG_DFL` caused the kernel to generate a second core dump for `pnpm` itself, recording a `SIGABRT (SI_TKILL)` crash of `pacquet-main` in `systemd-coredump` or overwriting the child's `core` file. Suppress core dumps in `die_from_signal` before re-raising by setting `RLIMIT_CORE` to 0 and, on Linux and Android where `do_coredump` ignores `RLIMIT_CORE` when `kernel.core_pattern` pipes to a helper such as `systemd-coredump`, clearing `PR_SET_DUMPABLE`. Closes pnpm/pnpm#17204 |
||
|
|
3ce74f1e71 |
fix(tarball): do not retry rejected archive entry paths (#17212)
Attach a typed RejectedEntryPathError to TarballError::ReadTarballEntries when clean_archive_entry_path rejects a tar entry path, preserve the extraction error when a streaming tar hash is skipped after an extraction failure, and classify both RejectedEntryPathError and TarballError::PathTraversal as permanent in is_transient_error so rejected archives fail on the first attempt instead of sleeping through the retry budget. Closes pnpm/pnpm#17205 |
||
|
|
cbe5accc7a |
fix(dedupe): ignore deprecation notices in dedupe --check (#17199)
`dedupe --check` gates on the lockfile text, so that changes a run makes outside the resolution diff (config dependencies, settings) also fail the check. A `deprecated` notice the registry added after the lockfile was written changed that text too, and the check failed with "no dependency resolution would change". The result depended on how fresh the metadata cache was, so the same commit could pass locally and fail in CI. When the text differs, the check now passes if the env document is unchanged, the old main document is in canonical form, and serializing it with the new `deprecated` notices reproduces the rewritten text exactly. Any other rewrite, formatting included, still fails. The guard still restores the original lockfile. pnpm v11 gates on the resolution diff only and is not affected. Closes pnpm/pnpm#17198 |
||
|
|
7e81d63bdb |
refactor: replace bare bools with named enums and structs (#16501)
Several facts travelled through the resolver, the lockfile's YAML emitter,
the default reporter and pnpr as bare `bool`s that could not be read at
the use site: a `true`/`false` literal selecting which of two behaviors a
function performs, a `bool` travelling in a tuple with no name, two flags
where one implies the other, and a `bool` return meaning "which of two
things happened". Each of those now has a name.
In the resolver:
- `PeerWalkMode { Discovery, Final }` replaces `Walker::new`'s
`discovery: bool` and the traversal field it was stored in, and
`ChildOutputs::push` takes that mode instead of its own
`collect_dep_paths: bool`.
- `PeerIdRecording { Record, Skip }` replaces the `record_peer_ids: bool`
of the final-path pass, parameter and field.
- `ChildrenRecording` travels back to the callers of the children
realisation next to the children, instead of the `bool` that
`into_children` flattened it to; its callers ask it
`stales_other_occurrences`.
- `PackageRegistration { Created(Arc<str>), Existing(Arc<str>) }`
replaces the `(Arc<str>, bool)` the two package registrations
returned.
- `DependencySpec { alias, bare_specifier, optional, injected }` replaces
the `(String, String, bool, bool)` tuple that lived under two aliases,
`WantedSpec` and `ChildSpec`, and was read positionally in six files. Its
fields are named after `WantedDependency`'s, the resolved form they feed.
`hoisted_dependency` builds the shape both hoist rounds want, with the
rationale for its two defaults in one place.
- `collect_deps` takes a `DependencyGroup` instead of a manifest key and
an `optional: bool`, and derives both from the group, so a caller
cannot pair a key with the wrong flag.
- `WorkspacePeerSettings` groups the three consecutive booleans of
`resolve_peers_workspace` into named fields.
- `NodeKind { Link, Leaf, Branch }` replaces the `is_link`/`is_leaf`
field pair, of which only three of the four combinations were states.
- `ReusableResolution { Registry, Git }` replaces the `bool` inside the
`Option<bool>` that `reusable_resolution_is_git` answered.
- `reuse.rs` calls the shared `register_peer_dep_names` instead of
keeping a verbatim copy of it.
In the lockfile's YAML emitter, four `bool`s threaded through every node
and were read positionally by ten functions: `CollectionStyle`,
`FirstEntry`, `EntrySpacing` and `ScalarLines` replace them, in
`yaml_emit/style.rs`.
In the default reporter, `BlockPlacement { Scrolling, Pinned }` replaces
`Frame::emit`'s `fixed: bool`.
In the filesystem crate, `LockMode { Exclusive, Shared }` replaces the
`exclusive: bool` of `lock_file` and `try_lock_file`, and every
implementation maps it with a match: the standard library's methods,
Android's `flock` operation, and the WebContainer host lease's flag.
In the hoisted linker, `DirectLink { Unchanged, Created,
RootShadowRemoved }` replaces the `(bool, bool)` that linking one direct
dependency returned, of which only three combinations occurred.
In pnpr:
- `ResponseBody { Sent, Omitted }` replaces the `head: bool` of three OCI
response builders and derives itself from the request method in one
place.
- `MetadataFreshness { Cached, Refetched }` replaces the `refresh: bool`
of the OIDC provider-metadata read, and separates the freshness
question from the retry rate limit that shared the flag. A preparatory
commit moves that cache into `oidc/provider_metadata.rs`, which also
brings `oidc.rs` back under the file-length limit.
- `OrgGrant { Read, Publish }` replaces the `publish: bool` that main's
organization consolidation left on `CallerOrgs::authorize` and on
`authorized_owner_key`, so the pipeline-run, compiler-cache and
shared-artifact paths ask for a grant with one named type.
- `GroupMatching { shadowed, allow_unresolved }` replaces the
`(bool, &HashSet<&str>)` tuple parameter of the directory-dependency
freshness check, whose call site read `(false, &peers.shadowed)`.
- `reject_removed_blocks` takes the parsed `packages:` and `groups:`
blocks, whose distinct types make the transposition its two same-typed
flags allowed a compile error.
- `ExistingDestination { Replace, Keep }` replaces the `replace: bool`
behind `write_atomic` and `write_atomic_new`.
- `StaleEntry { Delete, Keep }` replaces the `delete_stale: bool` of the
cargo index cache read.
- The resolution cache's eviction helpers lose a `private_first: bool`
that their callers always passed as `true`, and the fallback branch
that only ran when it was `false`;
`CandidateScope { PrivateOnly, Any }` names the choice that remains.
- `CrateDocument::max_version` reads its two passes through named
closures instead of a `yanked_allowed` flag.
Every branch on one of these enums is an exhaustive `match` that names
every variant, with no wildcard arm, no `matches!` and no `==` against a
variant, so adding a variant is a compile error at each site that has to
decide.
The criteria behind these choices are written down in
pnpm/CODE_STYLE_GUIDE.md under "Boolean parameters and fields": which
questions a `bool` may answer, how its use sites must read, what a
neighbouring flag does to it, and the exemptions that keep most of the
workspace's `bool`s as they are. This branch adds the section, including
the rule that a flag named for one of several alternatives wants an enum
of those alternatives rather than a better name, and the wildcard arm
that gives an unreachable combination away.
No behavior changes. The lockfile, resolver, package-manager, reporter,
fs, store-dir, deps-restorer, CLI and pnpr suites pass, and the first two
compare emitted lockfiles byte for byte.
|
||
|
|
43d0924777 |
fix(deps-restorer): keep an explicit copy import for built packages (#17196)
pnpm 12 imports a package that a lifecycle script or a patch will still write with `clone-or-copy`, whatever `packageImportMethod` says, so the build cannot write into the store through a hard link. That override also replaced an explicit `copy`, which already gives the build private files. On a filesystem that supports block cloning, such as ReFS or a Dev Drive on Windows, those packages were cloned even though the user asked for copies. Keep `copy` when it is the configured method. Every other method still becomes `clone-or-copy`. This matches pnpm 11, whose `createPackageImporter` keeps `copy` for packages that will be built. Closes pnpm/pnpm#17195 --------- Co-authored-by: Zoltan Kochan <z@kochan.io> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
01f0a52cdc |
fix(deps-restorer): leave present patched hoisted packages alone (#17189)
`package_is_reusable` excluded every reference containing `(patch_hash=`. That matches a patched package, and also any package whose peer suffix names a patched peer, such as `react-dom@19.2.0(react@19.2.0(patch_hash=...))`. Each install that reached the hoisted linker imported them all again. A React Native workspace with patched `react-native` and `expo` re-imported 115 packages per install. The exclusion is not needed. The patch hash is part of the dep path that `hoistedLocations` records, so a changed or removed patch gives the package another key, and the walker imports it again through that. pnpm 10 and 11 reuse a placed package on location and version alone, so this matches them. Closes pnpm/pnpm#17188 |
||
|
|
78365b06ce |
chore: require code-owner review for tasks.yaml (#17197)
The pipelines decide what each CI job runs, and `pnpm pipeline` reports a pipeline whose script does not exist as skipped and exits 0. The task settings also control ordering and the cache keys of the remotely cached jobs. An unowned tasks.yaml could therefore turn a CI check into a no-op without code-owner review. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9aa4a453af |
refactor(install): split directory_deps into the stages of its descent (#17194)
`directory_deps.rs` held 393 lines of code against the 400 the `perfectionist::overly_long_file` limit allows, so every addition to the directory-dependency freshness check had to be paid for elsewhere in the file. Its items are the three stages of one descent, each reached through a single entry point, plus the vocabulary all three share: - `importer_deps` reads the specs an importer declares, decides which of them name a directory dependency at all, answers a `link:` spec on its own, and resolves the rest to their lockfile entries. - `package` checks one directory package: the manifest the resolver would read for it, whether this scope has checked it already, and the peers its own dependencies shadow. - `children` compares the children the resolver walks for a package with the ones its snapshot records, and recurses into `package` for a child that is itself a directory. The module root keeps what the three share: `LocalDepContext`, the staleness error they raise, the test for a `file:` spec, and the directory set the spec comparisons resolve against. The move is otherwise verbatim. `check_single_directory_dep_freshness` and `check_local_dep_children_freshness` widen to `pub(super)` because their callers are now siblings rather than neighbours, and the root re-exports `check_directory_dependencies_freshness` so `manifest.rs` reaches it unchanged. No behavior changes. Claude-Session: https://claude.ai/code/session_01QYTBQo8HY7xD2iz6hjo1CW Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
82e235bacf |
chore: split workspace settings into policy, catalog, and task files (#17192)
* chore: move supply-chain settings to a protected config file Use the `include` setting from pnpm 12.15 to move the settings that decide what gets installed and what may run into .config/pnpm/security.yaml: allowBuilds, auditConfig, the minimumReleaseAge and trustPolicy settings, overrides, packageExtensions, patchedDependencies, patchesDir, and registries. pnpm-workspace.yaml delegates exactly that list, so no other file can define those settings. Patch paths are relative to the included file. pnpm 12.14 ignores `include` and would drop every moved setting, so the packageManager pin moves to 12.15.0. CODEOWNERS now names both workspace files, the pnpmfile, and the patches explicitly, and the CI path filters watch the new file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: move routine settings to a file without a code owner The catalog, the update settings, and the task-runner settings (pipelines, tasks, concurrencyGroups) move to .config/pnpm/routine.yaml, which CODEOWNERS leaves unowned. Because pnpm-workspace.yaml delegates only that list to the file, any other setting defined there fails with ERR_PNPM_SETTING_NOT_DELEGATED, so the file cannot be used to change the protected settings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: move the catalogs to their own file .config/pnpm/catalog.yaml owns `catalogs`, which covers the default catalog and any named catalog added later. Like routine.yaml, it has no code owner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: name the protected file policies.yaml and give tasks their own file security.yaml becomes policies.yaml. The task-runner settings (pipelines, tasks, concurrencyGroups) move to tasks.yaml, and `update` joins the catalogs in catalog.yaml, since its ignoreDeps list holds back catalog entries. routine.yaml is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: move catalogMode and catalogPrune to catalog.yaml Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: keep the patch list next to the patches patchedDependencies and patchesDir move to __patches__/patches.yaml, so the entries name the patch files without a directory prefix and patchesDir is `.`. CODEOWNERS already covers __patches__/. The CI path filters that did not watch __patches__/ now watch patches.yaml, since they watched the patch list when it lived in pnpm-workspace.yaml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: move the patches to .config/pnpm/patches Every pnpm configuration file now lives under .config/pnpm, so each CI path filter watches `.config/pnpm/**` instead of listing the files. CODEOWNERS keeps the patches protected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
87636f932c |
chore: update dependencies, Node.js, pnpm, and GitHub Actions (#17191)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> |
||
|
|
91774e72a0 |
chore(release): 11.28.7, pacquet 12.15.0, pnpr 0.1.0-alpha.16 (#17187)
* 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>pnpr@0.1.0-alpha.16 v11.28.7 v12.15.0 |
||
|
|
7b956715f0 |
feat(update): highlight the active row of the interactive update prompt (#17168)
`pnpm update --interactive` marked the row under the cursor only with the `❯` glyph. The dependency list previously turned off the prompt's cyan highlight (`highlight_active: false`), because repainting the row cyan with a single outer wrap would hide the colors that show which part of each target version changed. In a long list the cursor was hard to follow. `CheckboxPrompt` now always highlights the active row in cyan and re-applies the cyan foreground after every SGR foreground reset (`\x1b[39m` or `\x1b[0m`) inside the row, so embedded colors such as the target version diff survive while the rest of the row is cyan. `CheckboxTheme::highlight_active` is removed now that every prompt style uses the same cyan active-row highlight. The prompt for each PromptStyle is now built by PromptStyle::checkbox_prompt, outside the terminal-only branch of UpdatePrompt::select, so tests cover the theme each style uses. --------- Co-authored-by: Martin Olsson <martin@minimum.se> Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
e2a11f5611 |
perf(install): precalculate package bins and shebang runtimes (#17186)
Precalculate package binary entrypoints and shebang runtimes during tar streaming in pnpr (dist.pnpr.bins) and during cold tarball extraction in pnpm so .bin command shims can be linked without reading package.json, walking directories.bin, or opening bin scripts on disk. - Add PackageBin, ScriptRuntime, parse_shebang_from_bytes, resolve_package_bins, and verify_package_bins in pnpm-package-manifest with strict path normalization and shell-safe runtime validation. - Extend inspect_tar in pnpm-tarball to compute bins and shebang runtimes in the same streaming pass as requiresBuild when requiresBuild is false. - Emit dist.pnpr.bins in pnpr packument responses and cache it in pnpr-storage TarMetadataCache. - Propagate cold-extracted manifests and precalculated bins through CreateVirtualStoreOutput into LinkVirtualStoreBins and direct-dep bin linking, and verify warm-cache dist.pnpr.bins against the store index manifest and CAS file list before skipping shebang reads. Closes pnpm/tasks#178 |