Commit Graph

11458 Commits

Author SHA1 Message Date
Zoltan Kochan
96ece9d736 chore(release): 11.0.0-rc.2 v11.0.0-rc.2 2026-04-17 18:21:35 +02:00
Zoltan Kochan
ea2a7fb244 feat: skip lockfile writes for legacy packageManager field (#11284)
* feat: skip lockfile writes for legacy packageManager field

When pnpm is pinned via the `packageManager` field in `package.json`, the
resolved pnpm integrity info is no longer written to `pnpm-lock.yaml`
unless the pinned version is pnpm v12 or newer. `devEngines.packageManager`
still populates and reuses `packageManagerDependencies` as before. This
keeps the v10 -> v11 transition quiet by avoiding unrelated lockfile
churn for projects that pin pnpm the legacy way.

* fix: address Copilot review and CI failure

- Update `configurationalDependencies.test.ts` to assert the new behavior:
  the `packageManager` field no longer writes pnpm resolution info to the
  env lockfile while config dependencies still are.
- Fast-path in `switchCliVersion`: when the lockfile is not persisted and
  the running CLI already matches `pm.version`, skip store access and
  integrity resolution entirely.
- Clarify the `resolvePackageManagerIntegrities` docstring to describe
  the conditional `save` behavior.

* test: add unit tests for shouldPersistLockfile

Extract the decision logic for persisting pnpm resolution info to the env
lockfile into a dedicated helper so the branches — devEngines source,
legacy `packageManager` field with v11 or older, v12+, and invalid/missing
version — can all be covered without needing an actual pnpm v12 tarball
on the registry.
2026-04-17 14:45:51 +02:00
Zoltan Kochan
2dd8712c87 fix: pnpm init skips devEngines.packageManager in workspace subpackages (#11285)
The devEngines.packageManager field should only live at the workspace root.
When init runs inside a subpackage, the field is now omitted.
2026-04-17 14:30:28 +02:00
Zoltan Kochan
1c95cb6392 feat: pnpm init writes devEngines.packageManager field (#11283)
* feat: pnpm init writes devEngines.packageManager field

Replaces the flat "packageManager" field with a "devEngines.packageManager"
entry, using a caret range and onFail: "download" so the declared pnpm
version auto-updates on install.

* fix: update workspace/commands/src/init.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-17 13:52:19 +02:00
Zoltan Kochan
ff7733ce21 feat: add runtimeOnFail setting (#11277)
* feat: add runtimeOnFail setting

Adds a `runtimeOnFail` config setting ('ignore' | 'warn' | 'error' |
'download') that overrides the `onFail` field on `devEngines.runtime`
and `engines.runtime` in the root project's package.json. This makes
it possible to opt into (or out of) runtime auto-download without
changing the project manifest.

* fix: skip runtime download when version is missing

Without a version, convertEnginesRuntimeToDependencies would write
`runtime:undefined` into the manifest. Warn and skip instead.

* feat: apply runtimeOnFail override during install

The config reader override only mutates the context's rootProjectManifest,
but installDeps reads the manifest fresh via tryReadProjectManifest and
findWorkspaceProjects. Apply the override there too so `runtimeOnFail`
actually affects what gets installed. Adds an e2e test covering both
download and ignore overrides through the real CLI bundle.
2026-04-17 12:00:17 +02:00
Alessio Attilio
75942bfac5 feat: implement native star, stars, unstar, and whoami commands (#11242)
This PR implements native pnpm commands for starring and unstarring packages, listing stars, and finding the current user (whoami). It follows the project standards and provides fallbacks for various registry API versions.
2026-04-17 11:02:12 +02:00
Zoltan Kochan
cee550a57d feat!: remove deprecated managePackageManagerVersions / packageManagerStrict / packageManagerStrictVersion (#11278)
* feat!: remove managePackageManagerVersions / packageManagerStrict / packageManagerStrictVersion

These three settings existed only to derive the `onFail` behavior for
the legacy `packageManager` field. The `pmOnFail` setting introduced
in #11275 subsumes all three — it directly sets `onFail` for both
`packageManager` and `devEngines.packageManager`.

Legacy `packageManager` now defaults to `onFail: 'download'` when no
override is set. `COREPACK_ENABLE_STRICT` is no longer read (it only
gated `packageManagerStrict`); `pmOnFail` is the replacement.

Also drops pass-through `packageManagerStrict*` option fields from
cli.utils / workspace.projects-reader (they were unused) and the
unused `managePackageManagerVersions` Pick in engine.pm.commands'
`SelfUpdateCommandOptions`.

* fix: use kebab-case setting name in BAD_PM_VERSION hint

Copilot review feedback: user-facing error hints for configuration keys
conventionally use the kebab-case form that matches both the CLI flag
(`--pm-on-fail`) and the `.npmrc` key, consistent with the prior hint
text that referenced `package-manager-strict`. The `pnpm-workspace.yaml`
field (`pmOnFail`) is camelCase but that mapping is documented
elsewhere.

* Revert "fix: use kebab-case setting name in BAD_PM_VERSION hint"

This reverts commit e03c29b17. pnpm-workspace.yaml uses camelCase
(`pmOnFail`) — the primary config location for pnpm 11 — so the
hint keeps the camelCase form. The CLI flag is already shown
alongside.
2026-04-17 00:57:33 +02:00
Khải
4ab3d9ba9e feat(dlx): accept more local configs (#11240)
* feat(config): make dlx inherit security and trust policy settings from local config

Previously, `pnpm dlx` and `pnpm create` only inherited auth/registry
settings from the local project config, ignoring all other settings.
This meant security policy settings like `minimumReleaseAge` and
`trustPolicy` configured in a project's `pnpm-workspace.yaml` were
silently dropped.

Now these commands inherit two categories of local settings:
1. Registry & auth (existing) — needed to reach the same package sources
2. Security & trust policy (new) — settings that gate what is allowed
   to be downloaded, reflecting the org's security posture

Project-structural settings (hoisting, linking, workspace layout, etc.)
remain correctly excluded.

Closes #11183

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* refactor(config): rename auth.ts to localConfig.ts and clean up tests

Addresses review feedback:
- Rename auth.ts / auth.test.ts to localConfig.ts / localConfig.test.ts
  to reflect the broader scope (auth + security/trust policy + npmrc utils)
- Remove unnecessary `as any` casts from tests; the types already work
- Consolidate individual expect() assertions into toMatchObject

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* fix(config): sort imports and exports after rename

Fixes simple-import-sort/imports and simple-import-sort/exports lint
errors introduced when localConfig.js replaced auth.js; the previous
position was correct for auth.* but not for localConfig.*.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* refactor(config): remove dead RAW_POLICY_CFG_KEYS handling

Policy keys (minimum-release-age*, trust-policy*) are filtered out of
.npmrc by isNpmrcReadableKey, so they can never appear in authConfig.
The RAW_POLICY_CFG_KEYS / isRawPolicyCfgKey / pickRawDlxConfig branch
for those keys was unreachable in production.

inheritDlxConfig now uses pickRawAuthConfig directly for the raw config
pick. The test assertion that placed minimum-release-age in authConfig
(an impossible state) is also dropped.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* test(dlx): respect minimumReleaseAge from pnpm-workspace.yaml

Integration test for #11183 — verifies that pnpm dlx, invoked via the
bundled CLI, picks up minimumReleaseAge from the project's
pnpm-workspace.yaml and rejects packages that don't meet the cutoff.

Uses the public npm registry (matching the existing minimumReleaseAge
tests in exec/commands/test/dlx.e2e.ts:391) because verdaccio includes
the 'time' field in abbreviated metadata, which short-circuits the
publish-date check.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* fix(test): allow pnpm-workspace.yaml to override minimumReleaseAge in tests

The execPnpmSync test helper hardcoded
  pnpm_config_minimum_release_age: '0'
which forced the value via env var (highest priority) for every test,
overriding any minimumReleaseAge set via pnpm-workspace.yaml.

This was inconsistent with the other settings in the helper (registry,
hoist, storeDir, fetchRetries) which use a `fallback()` reading from
the workspace manifest if present and falling back to a default
otherwise. Apply the same pattern for minimumReleaseAge.

Restores the integration test added in 6bc965b — without this fix the
test passes through dlx without applying the workspace's
minimumReleaseAge, making it not fail as the test expected.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* refactor(config,test): address review feedback

localConfig.ts doc comment:
- Drop redundant "(camelCase, from Config type)" parenthetical
- Replace em-dash-sandwiched paragraph with two flat sentences
- Switch list-item em dashes to colons (label: definition form)

pnpm/test/dlx.ts:
- Switch em dash in registry-override comment to colon
- Group the minimumReleaseAge tests into a describe block
- Add positive test: dlx succeeds when the pinned version is older
  than the computed minimumReleaseAge cutoff
- Add range-resolution test: dlx resolves `shx@0.3.x` to 0.3.2 when
  the cutoff is positioned between 0.3.2 (2018-07-11) and 0.3.3
  (2020-10-26). The ~2.3 year gap leaves ample room for CI variance;
  0.3.2's publish date is hardcoded (npm policy forbids unpublishing
  past 72h).

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* fix(test,config): address Copilot review feedback

- execPnpm.ts: only set pnpm_config_minimum_release_age env var when
  the workspace manifest does not specify minimumReleaseAge, so tests
  that verify dlx's local-config inheritance exercise the real config
  path instead of being masked by the env var
- dlx.ts: fix "~19 years" comment to "~27.4 years" (10,000 days)
- dlx.ts: add pnpm create test verifying minimumReleaseAge from
  pnpm-workspace.yaml (create delegates to dlx internally)
- changeset: bump @pnpm/config.reader to major (the rename of
  ignoreNonAuthSettingsFromLocal → onlyInheritDlxSettingsFromLocal
  is a breaking change to the published getConfig API)

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* refactor(test): add noDefaultMinimumReleaseAge option to execPnpmSync

Replace the implicit workspace-yaml auto-detection with an explicit
opt-in flag. Tests that verify dlx/create inherits minimumReleaseAge
from pnpm-workspace.yaml pass `noDefaultMinimumReleaseAge: true` so
the env var default doesn't mask the real inheritance path.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* refactor(test): use omitEnvDefaults instead of noDefaultMinimumReleaseAge

Replace the single-purpose boolean flag with a general-purpose
`omitEnvDefaults: string[]` option on ExecPnpmSyncOpts. Tests pass the
env var name(s) to skip, e.g.
`omitEnvDefaults: ['pnpm_config_minimum_release_age']`.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* refactor(test): type omitEnvDefaults as PnpmEnvDefault[] literal union

Provides autocomplete and prevents typos by constraining the array
to known pnpm_config_* env var names set by the test helper.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* refactor(test): make omitEnvDefaults honor all listed env var names

Previously the code only checked for 'pnpm_config_minimum_release_age',
but the PnpmEnvDefault type listed 7 names, making the option silently
ineffective for the other 6. Now all defaults are set unconditionally
and any listed in omitEnvDefaults are deleted after, so every member
of PnpmEnvDefault actually works.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* docs(config): remove 'proxies' from inherited-settings examples

dlx does not actually inherit proxy settings (httpProxy / httpsProxy
etc. are neither in AUTH_CFG_KEYS nor RAW_AUTH_CFG_KEYS). The doc
comment in localConfig.ts listed 'proxies' as an example, which
mismatched the code. Drop the mention.

Behavior is unchanged; this is a docs-only fix.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* fix(dlx): fetch full metadata when minimumReleaseAge is set

Including minimumReleaseAge in the fullMetadata condition (alongside
the existing resolution-mode=time-based and trustPolicy=no-downgrade
triggers) bypasses the abbreviated→full metadata upgrade path in
pickPackage.ts for this case. That upgrade path is fragile on Windows:
the integration test at pnpm/test/dlx.ts:112 was failing with
ERR_PNPM_MISSING_TIME only on windows-latest runners, even though
the registry response is identical across platforms.

When minimumReleaseAge is set, pnpm always needs per-version
timestamps to decide which versions are mature enough. The original
condition only handled the two other time-dependent features
(resolution-mode=time-based and trust-policy=no-downgrade), missing
minimumReleaseAge. Adding it here eliminates an unnecessary round
trip plus the flaky upgrade, and matches the intent of the existing
siblings in the condition.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* style(test): avoid 'verdaccio: verdaccio' repetition in test comment

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* refactor(config): rename POLICY_CFG_KEYS to SECURITY_POLICY_CFG_KEYS

'POLICY_CFG_KEYS' was too vague — reading it cold didn't convey what
kind of policy. Renamed to match the doc comment's 'security policy'
wording. Also renamed 'isPolicyCfgKey' → 'isSecurityPolicyCfgKey'.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

* test(config): drop impossible 'cache-dir' key from inheritAuthConfig test

Addressing @zkochan's review: 'cache-dir' can never appear in
authConfig in production (pickIniConfig filters it out at .npmrc
load), so the assertion was testing an impossible state. Removed
from both the target's authConfig and the expected assertion.

https://claude.ai/code/session_01NumMLsTvswMVJpbWp3YJrH

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-17 00:38:41 +02:00
Zoltan Kochan
9af708a613 feat: add pnpm with <version|current> command (#11275)
## Summary

- **New command `pnpm with <version|current> <args...>`** — runs pnpm at a specific version (or the currently active one) for a single invocation, bypassing the project's `packageManager` and `devEngines.packageManager` pins. Uses the same install mechanism as `pnpm self-update`, caching the downloaded pnpm in the global virtual store for reuse.
- **New config setting `pmOnFail`** — overrides the `onFail` behavior of both `packageManager` and `devEngines.packageManager`. Accepted values: `download`, `error`, `warn`, `ignore`. Readable from CLI flag, env var, `pnpm-workspace.yaml`, or `.npmrc` — useful when version management is handled by an external tool (asdf, mise, Volta, etc.) and the project wants pnpm itself to skip the check.

```
pnpm with current install                    # one-shot, use running pnpm
pnpm with 11.0.0-rc.1 install                # one-shot, use specific version
pnpm install --pm-on-fail=ignore             # direct CLI flag
pnpm install --config.pm-on-fail=ignore      # equivalent via --config.* sugar
pnpm_config_pm_on_fail=ignore pnpm install   # env var
# or in pnpm-workspace.yaml: pmOnFail: ignore
```

## Implementation notes

- Command handler lives in `@pnpm/engine.pm.commands` (next to `self-update` and `setup`).
- `'with'` added to `SPECIALLY_ESCAPED_CMDS` in `cli/parse-cli-args` so args after `<spec>` pass through opaquely like `dlx`/`run`.
- `pnpm with current <cmd> [args]` is rewritten in `pnpm/src/parseCliArgs.ts` to an in-process dispatch — argv is rebuilt in place so any global flags the user put before `with` (e.g. `--dir`, `--filter`) are preserved. `process.env.pnpm_config_pm_on_fail=ignore` is set so the override survives `parseCliArgsLib`'s `-v` / `--help` short-circuits (which discard other parsed options).
- `main.ts` treats `skipPackageManagerCheck: true` as bypassing both the auto-download and the warn/error check (previously only the check). Also skips when `cmd='help'` and the help target is itself a skip-check command, so `pnpm with -h` works in pinned projects without downloading the pinned version first.
- Errors reported to stderr for `with` (aligned with `dlx`/`create`/`sbom`).
- `pmOnFail` wired in `config/reader/src/index.ts`: added to `types`, `Config`, and `pnpmConfigFileKeys`; applied as an override in the `onFail` resolution block.
- The `with <version>` child process sets both `COREPACK_ROOT` (honored by every pnpm release via `isExecutedByCorepack()`) and `pnpm_config_pm_on_fail=ignore` (principled override on new releases that ship the setting). This gives graceful behavior when `pnpm with 9.3.0 install` spawns an older pnpm that predates the new setting.
- Store controller lifecycle in the handler wrapped in `try/finally` to prevent leaks on install errors. Signal-induced child exits return a non-zero exit code so interrupted runs aren't masked as success.
2026-04-16 22:34:34 +02:00
Zoltan Kochan
1f82123a80 chore: update pnpm to v11 rc 1 2026-04-16 01:43:24 +02:00
Zoltan Kochan
f7c23231a9 chore(release): 11.0.0-rc.1 v11.0.0-rc.1 2026-04-16 01:18:55 +02:00
John van Leeuwen
ff28085997 fix: adapt audit client to npmjs /advisories/bulk endpoint (#11268)
The legacy `/-/npm/v1/security/audits{,/quick}` endpoints have been retired by npmjs.org. This PR rewires the audit client to the replacement `/-/npm/v1/security/advisories/bulk` endpoint.

The new endpoint is not a drop-in rename — the request and response contracts are both different:

- **Request**: a flat `{ pkgName: [versions] }` map. `lockfileToAuditRequest` walks the lockfile once and builds the POST body directly; there is no more nested `AuditTree`.
- **Response**: only `id`, `url`, `title`, `severity`, `vulnerable_versions`, and `cwe` per advisory. Everything else the old endpoint returned is computed locally:
  - `findings[].paths` are walked from the lockfile (skipped entirely when the response is empty; the second walk intentionally avoids `@pnpm/lockfile.walker`'s global dedup so alternate install chains to the same shared dep aren't dropped).
  - `metadata.vulnerabilities` counts advisories per severity.
  - `metadata.dependencies` / `devDependencies` / `optionalDependencies` / `totalDependencies` come from a classified lockfile walk; the classifier respects `--prod`/`--dev` include flags when deciding whether a subgraph is reachable non-optionally.
  - `patched_versions` is inferred from the vulnerable range for common `<X.Y.Z` / `<=X.Y.Z` shapes so `audit --fix` can still produce usable overrides; left `undefined` when inference fails.
  - `github_advisory_id` is parsed from the advisory URL and canonicalized to the github.com form (uppercase `GHSA-` prefix, lowercase suffix).
  - `info` severity is now supported end-to-end (severity type, `--audit-level`, filters, colors).

## Breaking changes (v11)

- Private registries that do not implement `/advisories/bulk` now fail with `AuditEndpointNotExistsError`.
- CVE-based filtering is replaced with GHSA-based filtering, since the bulk endpoint does not return CVE identifiers:
  - `auditConfig.ignoreCves` → `auditConfig.ignoreGhsas` (the old key is no longer recognized).
  - `pnpm audit --ignore <id>` and `--ignore-unfixable` now read and write GHSAs.
  - Migration: replace each `CVE-YYYY-NNNNN` in `auditConfig.ignoreCves` with the matching `GHSA-xxxx-xxxx-xxxx` (visible in the `More info` column of `pnpm audit` output) under `auditConfig.ignoreGhsas`.
- `--ignore-unfixable` now only targets advisories whose patched range couldn't be inferred — the only "no fix available" signal the bulk endpoint provides.
- `AuditReport` and `AuditAdvisory` are trimmed to just the fields the audit client actually populates:
  - `AuditReport`: `advisories` + `metadata` only (`actions` and `muted` removed).
  - `AuditAdvisory`: `findings`, `id`, `title`, `module_name`, `vulnerable_versions`, `patched_versions?`, `severity`, `cwe`, `github_advisory_id`, `url`. Dropped: `cves`, `created`, `updated`, `deleted`, `access`, `overview`, `recommendation`, `references`, `found_by`, `reported_by`, `metadata`.
  - `AuditAction`, `AuditResolution`, `AuditActionRecommendation` removed (no consumers).

## Hardening

- Response body validated: non-object / malformed JSON / non-array package buckets all surface as `ERR_PNPM_AUDIT_BAD_RESPONSE` with a body excerpt. Advisory `id` must be a finite number and `severity` must be a known value before being indexed.
- Name-keyed records use `Object.create(null)` so a hostile/unusual package name can't trigger prototype pollution.
- GHSA ids canonicalized on both read and write so casing drift between config and registry doesn't mask ignores.
- `findings[].paths` are deduped and capped per (name, version) to keep pathologically shared graphs from blowing up memory.

## Internals

- `AuditTree` / `AuditNode` / `lockfileToAuditTree` removed. `lockfileToAuditIndex.ts` exports `lockfileToAuditRequest` (flat POST body + counts) and `buildAuditPathIndex` (only invoked when the response has advisories).
- `AuditAdvisory.findings` is now `AuditFinding[]` (was an unintended 1-tuple).
- Top-level test fixtures regenerated from real `registry.npmjs.org` responses; synthetic `update-*` fixtures converted in place to bulk shape.

---------

Co-authored-by: John van Leeuwen <john.van.leeuwen@priva.com>
Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-04-16 01:07:48 +02:00
Alessio Attilio
b738043ab1 feat: implement native pnpm ping command (#11258)
Add native `pnpm ping` command to test connectivity to registries.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-04-14 21:21:55 +00:00
Alessio Attilio
f2083f4d7a feat(cli): implement native pnpm search (#11243)
This PR implements the native `pnpm search` command and its aliases (`s`, `se`, `find`), removing the fallback to npm CLI. It follows the project standards by being root-cause and avoiding type duplications.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-04-14 19:58:11 +00:00
Zoltan Kochan
b989a4a1f4 fix: prevent store prune from breaking globally installed pnpm (#11253)
When pnpm self-updates via the headless install path, the install
directory was not registered in the store's project registry. This
caused `pnpm store prune` to treat its global virtual store packages
as unreachable and remove them, breaking the global pnpm binary.

Register the install dir after headless install in installPnpmToGlobalDir
2026-04-14 17:30:41 +02:00
Zoltan Kochan
73fd47667a fix: update dependencies 2026-04-14 16:25:21 +02:00
Alessio Attilio
2410cf4092 feat: add pnpm docs command and home alias (#11244)
* feat: add pnpm docs command and home alias

* chore: update manifests

* fix: address review comments for pnpm docs command

- Remove 'docs' and 'home' from NOT_IMPLEMENTED_COMMANDS to prevent
  not-implemented handlers from overriding the real implementations
- Change fallback URL from npmjs.com to npmx.dev
- Remove bugs URL as a docs fallback (it's an issue tracker, not docs)
- Fix ESM mock in tests: use jest.unstable_mockModule instead of jest.mock

* refactor: use open package for browser opening in promptBrowserOpen

Replace the hand-rolled platform-specific execFile logic with the open
npm package, which handles WSL, Docker-in-WSL, and Windows edge cases
better. This removes the execFile dependency injection from
promptBrowserOpen, OtpContext, LoginContext, and SharedContext.

* fix: address copilot review comments

- docs: use www.npmjs.com fallback (not npmx.dev) and validate homepage
  URL is http(s) before opening
- promptBrowserOpen: validate authUrl protocol before passing to open(),
  and guard against synchronous throws from open()

* fix: restore npmx.dev fallback for pnpm docs

* chore: expand changeset with web-auth refactor impact

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-04-14 16:20:40 +02:00
Zoltan Kochan
33af23bca2 docs(README): update links 2026-04-10 22:43:51 +02:00
Zoltan Kochan
673c09240d chore: update pnpm to rc 0 2026-04-10 19:57:01 +02:00
Zoltan Kochan
3bac9f52d0 docs: update changelog 2026-04-10 19:56:15 +02:00
Zoltan Kochan
06d6c2d405 chore(release): 11.0.0-rc.0 v11.0.0-rc.0 2026-04-10 18:30:33 +02:00
Zoltan Kochan
ac944ef1d9 feat: add minimumReleaseAgeStrict setting (#11234)
- Adds a new `minimumReleaseAgeStrict` setting (default: `false`)
- When `false` (default), pnpm falls back to versions that don't meet the `minimumReleaseAge` constraint if no mature versions satisfy the range being resolved
- Set to `true` to preserve the previous strict behavior (error when no mature version matches)
2026-04-10 17:49:02 +02:00
Zoltan Kochan
dedbb7617e fix(test): include LICENSE in case-insensitive filesystem test expectation (#11228)
The test expected only ['foo.js', 'package.json'] on case-insensitive
filesystems, but LICENSE has no case collision and should still be present.
2026-04-08 20:07:07 +02:00
Zoltan Kochan
68a951006e test: update registry-mock to 6.0.0 stable and use pnpm view in tests (#11227)
* test: update registry-mock to 6.0.0 stable and use pnpm view in tests

Update @pnpm/registry-mock from 6.0.0-6 to 6.0.0 stable release.
Replace npm view with pnpm view in test helpers now that pnpm has
native view/dist-tag commands. Unskip the nodeRuntime test that was
blocked on the registry-mock republish.

* chore: update pnpm to beta 8

* feat: support versions, dist-tags, and time field selectors in pnpm view

The view command now exposes versions (as an array of version strings),
dist-tags, and time from registry metadata. Single-field --json output
returns the raw value instead of wrapping it in an object, matching npm
behavior. This allows tests to use pnpm view instead of npm view.
2026-04-08 15:14:53 +02:00
Zoltan Kochan
0298caa9b1 chore(release): 11.0.0-beta.8 v11.0.0-beta.8 2026-04-08 12:15:42 +02:00
Zoltan Kochan
70fa43562e feat: add registry as supported flag of the view command 2026-04-08 11:55:24 +02:00
Zoltan Kochan
94b846ec8a feat: add dist-tags to aliases of dist-tag 2026-04-08 11:55:00 +02:00
Zoltan Kochan
fb58648b00 test: update registry-mock to v6 and fix test fixtures (#11223)
- Update `@pnpm/registry-mock` from 5.2.4 to 6.0.0-6
- Fix auth tests to use bearer token from `globalSetup` instead of hardcoding credentials
- Replace hardcoded integrity checksums with `getIntegrity()` from registry-mock in `customResolvers` tests
- Add `prepareFixtureWithIntegrity()` helper in deps-restorer tests to dynamically patch `@pnpm.e2e` integrity values in fixture lockfiles at runtime, so they don't go stale when registry-mock is updated
- Fix `workspace-external-depends-deep` fixture's current lockfile (was missing `packages/f` and `packages/g` importers)
- Remove unnecessary credentials from `gitChecks` tests (they reject before any registry interaction)
2026-04-08 11:45:38 +02:00
Dami Oyeniyi
a03ab1d745 fix: reject empty files in isEmptyDirOrNothing (#11182)
* fix: reject empty files in isEmptyDirOrNothing

* test(fs.is-empty-dir-or-nothing): isolate empty-file test in its own temp dir
2026-04-08 10:26:30 +02:00
Brandon Cheng
9b0a460c8d fix: non-deterministic resolution causing pnpm dedupe --check to unexpectedly fail (#11110)
* test: ensure prerelease weighting is correct

* fix: use higher weight for package versions already in lockfile

* test: remove fundamentally incompatible test

* fix(test): use undici MockAgent instead of nock for HTTP mocking

nock only patches Node's built-in http/https modules, but pnpm uses
undici for HTTP requests. Replace nock with @pnpm/testing.mock-agent
(which wraps undici's MockAgent) so the regression test actually
intercepts registry metadata requests.

* fix(benchmarks): show errors from store populate step

The populate step redirected both stdout and stderr to /dev/null,
hiding the actual error when pnpm install fails during benchmarks.

* fix(benchmarks): replace deprecated packages in benchmark fixture

The old fixture used deprecated babel 6, gulp, and other legacy
packages whose transitive dependencies (e.g. es-abstract) are missing
the "time" field in registry metadata, causing ERR_PNPM_MISSING_TIME
with time-based resolution mode.

Replace with modern equivalents (babel 7, webpack 5, MUI, Redux
Toolkit, etc.) that maintain a similar dependency tree size (~1300
packages) while using well-maintained packages with proper registry
metadata.

* fix(benchmarks): drop eslint plugins that pull in es-abstract

eslint-plugin-react, eslint-plugin-import, and eslint-plugin-jsx-a11y
transitively depend on es-abstract, whose registry metadata lacks the
"time" field. Replace them with eslint-plugin-prettier to avoid
ERR_PNPM_MISSING_TIME with time-based resolution.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-04-07 23:46:27 +02:00
Zoltan Kochan
7721d2e7f0 feat(audit): add patched versions to minimumReleaseAgeExclude on audit --fix (#11216)
When `pnpm audit --fix` adds overrides to fix vulnerabilities, it now
also adds the minimum patched version for each advisory to
`minimumReleaseAgeExclude` in pnpm-workspace.yaml. This allows
`pnpm install` to install the security fix without waiting for it to
satisfy the minimumReleaseAge constraint.

Closes #10263
2026-04-07 16:38:57 +02:00
Zoltan Kochan
51b04c3e9a refactor!: remove ignoreDepScripts and neverBuiltDependencies (#11220)
* refactor: remove ignoreDepScripts and neverBuiltDependencies settings

These settings are redundant in v11:
- `ignore-dep-scripts` is superseded by the default behavior of `allowBuilds`
- `neverBuiltDependencies` was already dead code, replaced by `allowBuilds`

* chore: add changeset for removed ignore-dep-scripts setting
2026-04-07 13:41:13 +02:00
Khải
16cfde66ec feat: pnpm logout (#11213)
* feat(auth): implement `pnpm logout` command

Adds a new `pnpm logout` command that logs users out of npm registries.
The command revokes the authentication token on the registry via
DELETE /-/user/token/{token}, then removes it from the local auth.ini
config file. Token revocation is best-effort: local cleanup always
proceeds even if the registry is unreachable or doesn't support
revocation.

Uses the same dependency injection pattern as `pnpm login` for
comprehensive testability.

https://claude.ai/code/session_016fw5sdGFtBiB9QapMKEuXa

* fix(auth): address review feedback on pnpm logout

- Rename revokeToken to tryRevokeToken for self-documenting code
- Extract token removal into removeTokenFromAuthIni function
- Remove redundant comments that restate function names
- Fix toHaveProperty to use array syntax for keys containing dots
  (avoids Jest property path parsing pitfall)
- Add globalWarn when token is found in authConfig but not in auth.ini,
  informing the user it must be removed manually from .npmrc
- Add tests for the .npmrc-only warning case

https://claude.ai/code/session_016fw5sdGFtBiB9QapMKEuXa

* fix(auth): fix Windows CI failure in logout test

Use path.join in test expectations for the warning message path,
since path.join produces backslashes on Windows.

https://claude.ai/code/session_016fw5sdGFtBiB9QapMKEuXa

* refactor(auth): use jest.fn() for fetch assertions in logout tests

Replace manual fetchedUrls arrays with jest.fn() mocks and use
toHaveBeenCalledWith for cleaner, more idiomatic assertions.

https://claude.ai/code/session_016fw5sdGFtBiB9QapMKEuXa

* refactor: destructure `context`

* refactor: literal types for `method`

* refactor(auth): test cleanup per review feedback

- Rename mockFetch to fetch for shorthand property syntax
- Use platform-aware configDir in warning tests instead of
  path.join on Unix-style paths

https://claude.ai/code/session_016fw5sdGFtBiB9QapMKEuXa

* style(auth): remove redundant return in createMockResponse arrow

Single-statement return-with-braces arrow function converted to
expression-body form.

https://claude.ai/code/session_016fw5sdGFtBiB9QapMKEuXa

* fix(auth): address Copilot review on pnpm logout

- Send Authorization: Bearer header in the DELETE token revocation
  request, otherwise the registry returns 401 and the token is not
  actually revoked
- Make tryRevokeToken return a boolean indicating whether the token
  was actually revoked, and use it to choose the right warning when
  the token is not in auth.ini
- Drop the misleading "(token removed locally)" suffix from the
  registry-failure log messages, since the local removal may not
  happen
- Extract getRegistryConfigKey and safeReadIniFile from login.ts and
  logout.ts into a shared module to prevent the two commands from
  drifting apart over time
- Add tests asserting the Authorization header is sent and that the
  warning correctly distinguishes between revoked and not-revoked
  cases

https://claude.ai/code/session_016fw5sdGFtBiB9QapMKEuXa

* fix(auth): throw on logout when nothing actually happened

When the registry rejects the token revocation AND the token is not
in auth.ini, neither side effect of logout actually happened — the
user is still authenticated locally and on the registry. Throwing
an ERR_PNPM_LOGOUT_FAILED error in this case avoids the misleading
"Logged out of ..." success message and gives a non-zero exit code.

https://claude.ai/code/session_016fw5sdGFtBiB9QapMKEuXa

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-07 11:19:42 +02:00
Zoltan Kochan
853be661d8 feat: add native pnpm dist-tag command (#11218)
Implement dist-tag ls, add, and rm subcommands natively instead of
delegating to npm. Follows the same pattern as the recently added
deprecate and unpublish commands.
2026-04-07 10:22:03 +02:00
Alessio Attilio
d401ff2500 feat: add native pnpm unpublish command (#11128)
- Implements the `pnpm unpublish` command natively instead of passing through to npm
- Supports unpublishing specific versions or version ranges using semver
- Supports unpublishing entire packages with `--force` flag (with protection against accidental unpublish)
- Supports OTP authentication via `--otp` flag
- Supports custom registry via `--registry` flag
- Reuses existing data structures from the deprecate command

## Usage

```bash
# Unpublish a specific version
pnpm unpublish my-package@1.0.0

# Unpublish multiple versions matching a range
pnpm unpublish my-package@">1.0.0 <2.0.0"

# Unpublish entire package (requires --force)
pnpm unpublish my-package --force

# With custom registry
pnpm unpublish my-package --registry https://my-registry.com
```

## Changes

- Added `unpublish.ts` command in `releasing/plugin-commands-publishing/`
- Removed unpublish from npm pass-through list in `pnpm.ts`
- Added required dependencies: `@pnpm/fetch`, `semver`, `@types/semver`

Follows npm unpublish behavior and is aligned with the existing `pnpm deprecate` implementation.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-04-06 17:01:40 +02:00
Alessio Attilio
2c90d4061d feat: add native pnpm deprecate and undeprecate commands (#11120)
Adds native `pnpm deprecate` and `pnpm undeprecate` commands that interact with the npm registry directly instead of delegating to the npm CLI.

## Changes

- **New package `@pnpm/registry-access.commands`** — a new top-level domain for commands that manage packages on the registry
- **`pnpm deprecate <package>[@<version>] <message>`** — sets a deprecation message on matching versions
- **`pnpm undeprecate <package>[@<version>]`** — removes deprecation from already-deprecated versions
- Updated `pnpm-workspace.yaml`, CLI registration, and meta-updater config
- Removed `deprecate` from the not-implemented commands list
- Added changeset

## Implementation Details

- `registry-access/commands/src/deprecation/common.ts` — shared logic (auth, registry fetch, version matching)
- `registry-access/commands/src/deprecation/deprecate.ts` — deprecate command handler
- `registry-access/commands/src/deprecation/undeprecate.ts` — undeprecate command handler
- Uses `pickRegistryForPackage` for per-scope registry support
- Uses `createFetchFromRegistry`/`fetchWithDispatcher` for proxy/TLS support
- Uses `@pnpm/npm-package-arg` for package spec parsing
- Reuses `PackageMeta`/`PackageInRegistry` types from `@pnpm/resolving.registry.types`
- Sends minimal payload (only `name` + modified `versions.deprecated` fields) to the registry

## Usage

```bash
# Deprecate all versions
pnpm deprecate my-package "This package is deprecated"

# Deprecate a specific version
pnpm deprecate my-package@1.0.0 "Use v2 instead"

# Undeprecate all versions
pnpm undeprecate my-package

# Undeprecate a specific version
pnpm undeprecate my-package@1.0.0
```

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-04-06 15:30:11 +02:00
Zoltan Kochan
b65204762a refactor(config): move network settings from .npmrc to YAML config (#11209)
Proxy settings (httpProxy, httpsProxy, noProxy), local-address,
strict-ssl, and git-shallow-hosts are now written to config.yaml
(global) or pnpm-workspace.yaml (local) instead of auth.ini/.npmrc.

They are still readable from .npmrc for easier migration from npm CLI.
The canonical YAML key names (httpProxy, httpsProxy, noProxy) match
Yarn Berry's naming convention.

- Add httpProxy, httpsProxy, noProxy to PnpmSettings type
- Add http-proxy to pnpmTypes and pnpmConfigFileKeys
- Separate network keys from auth keys in config routing
- Add isNpmrcReadableKey for backward-compatible .npmrc reading
2026-04-06 11:36:08 +02:00
Zoltan Kochan
705e3fe3cc fix(auth): enable shell for .bat/.cmd token helpers on Windows
The refactor in 45a6cb6b dropped `shell: true` from token helper
execution. On Windows, .bat/.cmd files require a shell to run,
causing all tokenHelper tests to fail.
2026-04-06 02:04:37 +02:00
Zoltan Kochan
5384a0675c chore: update pnpm to beta 7 2026-04-05 22:55:27 +02:00
Zoltan Kochan
9a78e59382 chore(release): 11.0.0-beta.7 v11.0.0-beta.7 2026-04-05 22:45:34 +02:00
Zoltan Kochan
7f6d356e65 fix(test): add pnpm-workspace.yaml to multiple-scripts-error-exit fixture
The errorHandler test runs pnpm from this fixture directory. Without its
own workspace manifest, pnpm walked up and discovered the monorepo root,
triggering verifyDepsBeforeRun which modified unrelated files.
2026-04-05 22:45:33 +02:00
Zoltan Kochan
b103439d9a refactor(test): extract shared DEFAULT_OPTS into @pnpm/testing.command-defaults (#11208)
12 command test suites had near-identical ~50-field DEFAULT_OPTS objects
copy-pasted between them. Extract the common fields into a single shared
package so each suite only declares its overrides.
2026-04-05 21:22:24 +02:00
Zoltan Kochan
45a6cb6b2a refactor(auth): unify auth/SSL into structured configByUri (#11201)
Replaces the dual `authConfig` (raw .npmrc) + `authInfos` (parsed auth) + `sslConfigs` (parsed SSL) pattern with a single structured `configByUri: Record<string, RegistryConfig>` field on Config.

### New types (`@pnpm/types`)
- **`RegistryConfig`** — per-registry config: `{ creds?: Creds, tls?: TlsConfig }`
- **`Creds`** — auth credentials: `{ authToken?, basicAuth?, tokenHelper? }`
- **`TlsConfig`** — TLS config: `{ cert?, key?, ca? }`

### Key changes
- Rewrite `createGetAuthHeaderByURI` to accept `Record<string, RegistryConfig>` instead of raw .npmrc key-value pairs
- Eliminate duplicate auth parsing between `getAuthHeadersFromConfig` and `getNetworkConfigs`
- Remove `authConfig` from the install pipeline (`StrictInstallOptions`, `HeadlessOptions`), replaced by `configByUri`
- Remove `sslConfigs` from Config — SSL fields now live in `configByUri[uri].tls`
- Remove `authConfig['registry']` mutation in `extendInstallOptions` (default registry now passed directly to `createGetAuthHeaderByURI`)
- `authConfig` remains on Config only for raw .npmrc access (config commands, error reporting, config inheritance)

### Security
- tokenHelper in project .npmrc now throws instead of being silently stripped
- tokenHelper execution uses `shell: false` to prevent shell metacharacter injection
- Basic auth uses `Buffer.from().toString('base64')` instead of `btoa()` for Unicode safety
- Dispatcher only creates custom agents when entries actually have TLS fields
2026-04-05 20:15:10 +02:00
Zoltan Kochan
b5d93c6ba9 refactor(config): remove rawLocalConfig and force* hoist flags (#11199)
rawLocalConfig detected whether hoist settings were explicitly
set. In v11, config values are always authoritative.

- Remove rawLocalConfig from ConfigContext, config reader,
  inheritPickedConfig, UniversalOptions
- Remove forceHoistPattern, forcePublicHoistPattern,
  forceShamefullyHoist — validateModules always checks now
- Simplify save-workspace-protocol check
- Remove dead rawLocalConfig overrides in deploy/patchCommit
2026-04-05 11:36:36 +02:00
Zoltan Kochan
3033bee430 refactor(config): split Config interface into settings + runtime context (#11197)
* refactor(config): split Config interface into settings + runtime context

Create ConfigContext for runtime state (hooks, finders, workspace graph,
CLI metadata) and keep Config for user-facing settings only. Functions
use Pick<Config, ...> & Pick<ConfigContext, ...> to express which fields
they need from each interface.

getConfig() now returns { config, context, warnings }. The CLI wrapper
returns { config, context } and spreads both when calling command
handlers (to be refactored to separate params in follow-up PRs).

Closes #11195

* fix: address review feedback

- Initialize cliOptions on pnpmConfig so context.cliOptions is never undefined
- Move rootProjectManifestDir assignment before ignoreLocalSettings guard
- Add allProjectsGraph to INTERNAL_CONFIG_KEYS

* refactor: remove INTERNAL_CONFIG_KEYS from configToRecord

configToRecord now accepts Config and ConfigContext separately, so
context fields are never in scope. Only auth-related Config fields
(authConfig, authInfos, sslConfigs) need filtering.

* refactor: eliminate INTERNAL_CONFIG_KEYS from configToRecord

configToRecord now receives the clean Config object and explicitlySetKeys
separately (via opts.config and opts.context), so context fields are
never in scope. main.ts passes the original split objects alongside
the spread for command handlers that need them.

* fix: spelling

* fix: import sorting

* fix: --config.xxx nconf overrides conflicting with --config CLI flag

When `pnpm add` registers `config: Boolean`, nopt captures
--config.xxx=yyy as the --config flag value instead of treating it
as a nconf-style config override. Fix by extracting --config.xxx args
before nopt parsing and re-parsing them separately.

Also rename the split config/context properties on the command opts
object to _config/_context to avoid clashing with the --config CLI option.
2026-04-04 23:44:25 +02:00
Zoltan Kochan
96704a1c58 refactor(config): rename rawConfig to authConfig, add nodeDownloadMirrors, simplify config reader (#11194)
Major cleanup of the config system after migrating settings from `.npmrc` to `pnpm-workspace.yaml`.

### Config reader simplification
- Remove `checkUnknownSetting` (dead code, always `false`)
- Trim `npmConfigTypes` from ~127 to ~67 keys (remove unused npm config keys)
- Replace `rcOptions` iteration over all type keys with direct construction from defaults + auth overlay
- Remove `rcOptionsTypes` parameter from `getConfig()` and its assembly chain

### Rename `rawConfig` to `authConfig`
- `rawConfig` was a confusing mix of auth data and general settings
- Non-auth settings are already on the typed `Config` object — stop duplicating them in `rawConfig`
- Rename `rawConfig` → `authConfig` across the codebase to clarify it only contains auth/registry data from `.npmrc`

### Remove `rawConfig` from non-auth consumers
- **Lifecycle hooks**: replace `rawConfig: object` with `userAgent?: string` — only user-agent was read
- **Fetchers**: remove unused `rawConfig` from git fetcher, binary fetcher, tarball fetcher, prepare-package
- **Update command**: use `opts.production/dev/optional` instead of `rawConfig.*`
- **`pnpm init`**: accept typed init properties instead of parsing `rawConfig`

### Add `nodeDownloadMirrors` setting
- New `nodeDownloadMirrors?: Record<string, string>` on `PnpmSettings` and `Config`
- Replaces the `node-mirror:<channel>` pattern that was stored in `rawConfig`
- Configured in `pnpm-workspace.yaml`:
  ```yaml
  nodeDownloadMirrors:
    release: https://my-mirror.example.com/download/release/
  ```
- Remove unused `rawConfig` from deno-resolver and bun-resolver

### Refactor `pnpm config get/list`
- New `configToRecord()` builds display data from typed Config properties on the fly
- Excludes sensitive internals (`authInfos`, `sslConfigs`, etc.)
- Non-types keys (e.g., `package-extensions`) resolve through `configToRecord` instead of direct property access
- Delete `processConfig.ts` (replaced by `configToRecord.ts`)

### Pre-push hook improvement
- Add `compile-only` (`tsgo --build`) to pre-push hook to catch type errors before push
2026-04-04 20:33:43 +02:00
sotanengel
c7203b99ad feat!: set default minimumReleaseAge to 1 day (1440 minutes) (#11158)
set default minimumReleaseAge to 1 day

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-04-04 13:26:22 +02:00
Zoltan Kochan
3d67773188 docs: add instruction to never ignore test failures in AGENTS.md 2026-04-04 12:10:34 +02:00
Zoltan Kochan
8bba5c3858 refactor(config): only read auth/registry from .npmrc, add registries to pnpm-workspace.yaml (#11189)
Replace the unmaintained @pnpm/npm-conf package with a purpose-built
module that reads only auth/registry-related settings from .npmrc files
using read-ini-file + @pnpm/config.env-replace (both already deps).

All non-registry settings (hoist-pattern, node-linker, etc.) are now
only read from pnpm-workspace.yaml, CLI options, or environment
variables. Registry-related settings (auth tokens, registry URLs,
SSL certs, proxy settings) continue to be read from .npmrc for
migration compatibility, and can also be set in pnpm-workspace.yaml.

New modules:
- loadNpmrcFiles.ts: reads .npmrc from standard locations, filters to
  auth/registry keys, returns structured layers
- npmConfigTypes.ts: inlined npm config type definitions
- npmDefaults.ts: inlined npm defaults (registry, unsafe-perm, etc.)
2026-04-04 02:44:12 +02:00
Zoltan Kochan
2554264fdd perf: use NDJSON format for metadata cache (#11188)
The metadata cache files now use a two-line NDJSON format:
- Line 1: cache headers (etag, modified, cachedAt) ~100 bytes
- Line 2: raw registry metadata JSON (unchanged)

This allows loadMetaHeaders to read only the first 1 KB of the file
to extract conditional-request headers (etag, modified), avoiding
the cost of reading and parsing multi-MB metadata files when the
registry returns 200 and the old metadata would be discarded.

Also moves cache directories to v11/ namespace (v11/metadata,
v11/metadata-full, v11/metadata-full-filtered) since the format
is not backwards compatible.
2026-04-04 01:24:05 +02:00