mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-12 10:11:42 -04:00
4d7cd56cccca0fc71a6097ade5f9e4c3ca534945
31 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4d7cd56ccc |
chore: upgrade @typescript/native-preview to 7.0.0-dev.20260421.2 (#11332)
* chore: upgrade @typescript/native-preview to 7.0.0-dev.20260421.2
- Add explicit `types: ["node"]` to the shared tsconfig because tsgo
20260421 no longer auto-acquires `@types/*` from `node_modules`.
- Refactor test files to explicitly import jest globals (`describe`,
`it`, `test`, `expect`, `beforeEach`, etc.) from `@jest/globals`
instead of relying on `@types/jest` ambient declarations. Under the
new tsgo build, `import { jest } from '@jest/globals'` shadows the
ambient `jest` namespace, breaking `@types/jest`'s `declare var
describe: jest.Describe;` globals.
- Add `@jest/globals` to each package's devDependencies where tests
now import from it, and add `@types/node` to packages that need it
but were relying on hoisted resolution.
- Replace `fail()` calls with `throw new Error(...)` since `fail` is
no longer globally available.
* chore: fix remaining tsgo type-strictness errors
- Strip `as <PnpmType>` casts on objects passed to toMatchObject /
toStrictEqual / toEqual; @jest/globals rejects the typed objects
(which include AsymmetricMatchers) vs. the repo-specific type.
- Type `jest.fn<...>()` explicitly where the mock's signature matters
for toHaveBeenCalledWith.
- Replace `beforeEach(() => X)` with `beforeEach(() => { X })` so the
return value is void, as the stricter jest typing requires.
- Use `expect.objectContaining({...})` in one place where the full
expected object triggered stricter type resolution.
- Cast `prompt.mock.calls` arg through `as unknown as Record<...>[]`
for patch.test.ts's nested-array matchers.
- Fix off-by-one `<reference path>` in pnpm/test/getConfig.test.ts
that only surfaced now.
- Move `@jest/globals` from devDependencies to dependencies in the
two `__utils__` packages that import it from `src/`.
- Clean up unused imports from the @jest/globals migration.
* chore: address Copilot review on #11332
- Move misplaced `@jest/globals` imports to the top import block in
checkEngine, run.ts, and workspace/root-finder tests where the
script dropped them below executable code.
- Replace `try { await x(); throw new Error('should have thrown') } catch`
in bins/linker, lockfile/fs, and resolving/local-resolver tests with
`await expect(x()).rejects.toMatchObject({...})`. The old pattern
swallowed an unrelated `throw` if the under-test call silently
succeeded, which would fail on the catch-block assertion with a
misleading message.
|
||
|
|
ccc606ed15 |
feat: pnpm agent — server-side resolution for faster installs (#11251)
## Summary
Adds an opt-in **pnpm agent** server that resolves dependencies server-side and streams only the files missing from the client's content-addressable store.
- **`@pnpm/agent.server`** — multi-process HTTP server (Node.js `cluster`) with SQLite-backed metadata and file caches
- **`@pnpm/agent.client`** — streams an NDJSON response, dispatches worker threads to fetch files while the server is still resolving
- **New config**: `agent` in `pnpm-workspace.yaml` (opt-in)
## How it works
1. Client reads integrity hashes from its local store index
2. Sends `POST /v1/install` with dependencies + store integrities
3. Server resolves the dependency tree using pnpm's `install({ lockfileOnly: true })`, with a SQLite-backed `PackageMetaCache` for fast repeat resolution
4. As each package resolves, a wrapped `storeController.requestPackage` looks up its files and immediately streams digests the client is missing (NDJSON `D` lines)
5. Client reads the stream line by line; digest batches fill up and dispatch worker threads to `POST /v1/files` — file downloads overlap with server-side resolution
6. After resolution, server sends index entries (`I` lines) and lockfile (`L` line)
7. Client writes index entries to store, then runs headless install with a wrapped `fetchPackage` that calls `readPkgFromCafs` with `verifyStoreIntegrity: false` (files are trusted from the agent)
8. `/v1/files` response is gzip-streamed (274MB → ~80MB) — server pipes through `createGzip`, worker pipes through `createGunzip`, parsing and writing files to CAFS as data arrives
## Performance
1351-package project, cold local store, warm server (localhost):
| Scenario | Time |
|----------|------|
| Vanilla pnpm install (cold OS cache) | ~48s |
| Vanilla pnpm install (warm OS cache) | ~34s |
| With pnpm agent (consistent) | **~33s** |
### Key optimizations
1. **SQLite metadata cache** — server-side resolution drops from ~3.4s to ~0.9s
2. **SQLite file store** — consistent read performance regardless of OS file cache state
3. **Streaming `/v1/install`** — file digests stream during resolution, downloads start before resolution finishes
4. **Gzip-streamed `/v1/files`** — whole-stream gzip (274MB → ~80MB), significant savings on remote servers
5. **Worker-thread streaming HTTP** — workers pipe gzip → parse → write to CAFS as data arrives, no buffering
6. **No rehashing** — server-provided digests used directly, skipping 33K SHA-512 computations
7. **No re-verification** — wrapped `fetchPackage` calls `readPkgFromCafs` with `verifyStoreIntegrity: false`
8. **Direct `writeFileSync` with `wx`** — no stat + temp + rename
9. **Pre-packed msgpack** — server sends raw store index buffers, client writes directly to SQLite
10. **WAL checkpoint** — ensures store index entries written by agent are visible to headless install's worker threads
## Usage
Start the server:
```bash
node agent/server/lib/bin.js
```
Configure in `pnpm-workspace.yaml`:
```yaml
agent: http://localhost:4873
```
|
||
|
|
7d25bc1136 |
fix: suppress packageManager/devEngines.packageManager conflict warning when values match exactly (#11307)
- Suppress the `Cannot use both "packageManager" and "devEngines.packageManager" in package.json. "packageManager" will be ignored` warning only when both fields specify the exact same package manager name and the exact same version string. Any other divergence (different name, range vs. exact version, prefixed versions like `v1.2.3`, etc.) still warns. - Lets projects keep both fields during migration (e.g. so v10 installs still auto-switch via `packageManager`, while v11 uses `devEngines.packageManager` and `npm install` still errors) without a noisy warning — as long as the two values are kept in sync. Closes #11301 |
||
|
|
9e0833c3cc |
feat: add minimumReleaseAgeIgnoreMissingTime setting (#11293)
Skips the minimumReleaseAge maturity check when the registry metadata lacks the "time" field, instead of throwing ERR_PNPM_MISSING_TIME. Defaults to true, and prints a warning once per affected package. |
||
|
|
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. |
||
|
|
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.
|
||
|
|
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
|
||
|
|
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
|
||
|
|
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. |
||
|
|
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>
|
||
|
|
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) |
||
|
|
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 |
||
|
|
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 |
||
|
|
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
|
||
|
|
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 |
||
|
|
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.
|
||
|
|
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
|
||
|
|
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> |
||
|
|
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.) |
||
|
|
2df8b71467 |
refactor(config): stop shelling out to npm for auth settings (#11146)
* refactor(config): stop shelling out to npm for auth settings Read and write auth-related settings (registry, tokens, credentials, scoped registries) directly to INI config files instead of delegating to `npm config`. Removes the @pnpm/exec.run-npm dependency from @pnpm/config.commands. * fix(config): give pnpm global rc priority over ~/.npmrc for auth settings Auth settings from the pnpm global rc file (e.g. ~/.config/pnpm/rc) now override ~/.npmrc in rawConfig. This ensures tokens written by `pnpm login` are correctly picked up by `pnpm publish`, since login writes to the pnpm global rc but ~/.npmrc previously took priority in the npm-conf chain. * chore: remove @pnpm/exec.run-npm package No longer used after removing npm config CLI delegation. * chore: remove accidentally committed __typecheck__/tsconfig.json * fix(config): narrow non-string rejection to credential keys, add priority test Non-string value rejection now only applies to credential keys (_auth, _authToken, _password, username), registry URLs, and scoped/registry- prefixed keys — not to INI settings like strict-ssl, proxy, or ca that can legitimately have boolean/null values. Added a test verifying that auth tokens from the pnpm global rc take priority over ~/.npmrc. |
||
|
|
0e8042e6dc |
revert: "feat: add allowBuildsOfTrustedDeps setting (true by default) (#11078)"
This reverts commit
|
||
|
|
5a3dc4ab2f |
feat: add allowBuildsOfTrustedDeps setting (true by default) (#11078)
* feat: load default trusted deps list from @pnpm/plugin-trusted-deps Add a new `use-default-trusted-deps` setting (default: true) that automatically loads a curated list of known-good packages into `allowBuilds` from @pnpm/plugin-trusted-deps. User-configured allowBuilds entries take precedence over the defaults. Set `use-default-trusted-deps=false` to disable. * fix: use catalog reference for @pnpm/plugin-trusted-deps * fix: use default import for @pnpm/plugin-trusted-deps CJS compat The package uses Object.defineProperty for DEFAULT_ALLOW_BUILDS, which Node.js/Jest ESM interop can't detect as a named export. Switch to a default import to fix test failures. * fix: use named ESM import from @pnpm/plugin-trusted-deps@0.3.0-1 The package now ships an ESM entry point with proper named exports, so we can use a clean named import instead of the default import workaround. * fix: update @pnpm/plugin-trusted-deps to 0.3.0-2 Uses static JSON import attributes in ESM entry, fixing the bundle issue where createRequire resolved paths relative to the bundle output instead of the original package. * refactor: rename setting to allow-builds-for-trusted-deps * test: disable default trusted deps in approveBuilds tests The tests assert exact allowBuilds contents, so the default trusted list must be disabled to avoid polluting the expected values. * fix: don't persist default trusted deps list to pnpm-workspace.yaml Track the user's original allowBuilds separately as userAllowBuilds before merging the default trusted list. Use userAllowBuilds when writing back to pnpm-workspace.yaml to avoid persisting the ~370 default entries from @pnpm/plugin-trusted-deps. * refactor: rename setting to allow-builds-of-trusted-deps * docs: use camelCase for setting name in changeset * fix: include userAllowBuilds in install command opts types Without this, userAllowBuilds wasn't passed through to handleIgnoredBuilds, causing the default trusted list to be written to pnpm-workspace.yaml during e2e tests. * fix: set userAllowBuilds to empty object when user has no config When the user has no allowBuilds configured, userAllowBuilds was undefined, causing handleIgnoredBuilds to fall back to the merged allowBuilds (with defaults). Use empty object instead so the fallback doesn't trigger. * fix: read allowBuilds from workspace manifest when writing back Instead of tracking userAllowBuilds separately (which gets stale when other code writes to pnpm-workspace.yaml mid-install), read the current allowBuilds directly from pnpm-workspace.yaml before writing. This avoids persisting the default trusted list and preserves entries written by --allow-build earlier in the flow. Also update e2e test expectation: esbuild is now in the default trusted list, so it builds instead of being ignored. * chore: update tsconfig references for new dependencies * test: disable default trusted deps in approveBuilds e2e install The execPnpmInstall helper runs the bundled CLI which picks up the default allowBuildsOfTrustedDeps=true. This causes extra placeholder entries in pnpm-workspace.yaml that break assertions. * fix: revert approveBuilds to use config-based allowBuilds approveBuilds.handler should use opts.allowBuilds from getConfig() (which excludes trusted deps defaults when disabled) rather than reading the workspace manifest. The handler's job is to write approve/deny decisions, not merge with auto-populated placeholders. * test: add config reader tests for allowBuildsOfTrustedDeps Cover: (1) default enabled with trusted defaults merged, (2) user allowBuilds overrides defaults, (3) setting allow-builds-of-trusted-deps=false disables the merge. |
||
|
|
606f53e78f |
feat: add dedupePeers option to reduce peer dependency duplication (#11071)
* feat: add `dedupePeers` option to reduce peer dependency duplication When enabled, this option applies two optimizations to peer dependency resolution: 1. Version-only peer suffixes: Uses name@version instead of full dep paths (including nested peer suffixes) when building peer identity hashes. This eliminates deeply nested suffixes like (foo@1.0.0(bar@2.0.0)). 2. Transitive peer pruning: Only directly declared peer dependencies are included in a package's suffix. Transitive peers from children are not propagated upward, preventing combinatorial explosion while maintaining correct node_modules layout. The option is scoped per-project: each workspace project defines a peer resolution environment, and all packages within that project's tree share that environment. Projects with different peer versions correctly produce different instances. Closes #11070 * fix: pass dedupePeers to getOutdatedLockfileSetting and use spread for lockfile write The frozen install path (used by approve-builds) calls getOutdatedLockfileSetting but was missing the dedupePeers parameter. This caused a false LOCKFILE_CONFIG_MISMATCH error because the lockfile had the key written (as undefined/null via YAML serialization) while the check function received undefined for the config value. Fix: pass dedupePeers to the settings check call, and use spread syntax to only write the dedupePeers key to lockfile settings when it's truthy (avoiding undefined keys). * fix: write dedupePeers to lockfile like other settings Write the value directly instead of spread syntax, and use the same != null guard pattern as autoInstallPeers in the settings checker. * test: add integration test for dedupePeers in peerDependencies.ts * fix: only write dedupePeers to lockfile when enabled When dedupePeers is false (default), don't write it to lockfile settings. This avoids adding a new key to every lockfile. * test: simplify dedupePeers test assertions * test: check exact snapshot keys in dedupePeers integration test * test: add workspace test for dedupePeers with different peer versions * fix: keep transitive peers in suffix with version-only IDs Instead of pruning transitive peers entirely (which prevented per-project differentiation), keep them but use version-only identifiers. This way: - Packages like abc-grand-parent still get a peer suffix when different projects provide different peer versions (correct per-project isolation) - But the suffixes use name@version instead of full dep paths, eliminating the nested parentheses that cause combinatorial explosion * refactor: extract peerNodeIdToPeerId helper in resolvePeers * refactor: simplify peerNodeIdToPeerId return * fix: pin peer-a dist tag in dedupePeers tests for CI stability * fix: address review comments - Register dedupe-peers in config schema, types, and defaults so .npmrc/pnpm-workspace.yaml settings are parsed correctly - Use Boolean() comparison in settings checker so enabling dedupePeers on a pre-existing lockfile triggers re-resolution - Fix changeset text and test names: transitive peers are still propagated, just with version-only IDs (no nested dep paths) |
||
|
|
9fc552d37a |
fix: update GVS symlinks after approve-builds by running install (#11043)
Fixes #11042 - **Root cause**: When `enableGlobalVirtualStore` is true and `allowBuilds` is not configured, `createAllowBuildFunction()` returned `undefined`, causing all GVS hashes to include `ENGINE_NAME`. When `approve-builds` later configured `allowBuilds`, the hash didn't change because the engine was already included. - **Fix**: Default `allowBuilds` to `{}` in GVS mode so hashes are engine-agnostic by default, and have `approve-builds` call `install.handler()` in GVS mode instead of the low-level `install()` function, so it properly handles workspaces and updates symlinks. - **Refactor**: Broke circular dependencies between `building/commands`, `installing/commands`, and `global/commands` using dependency injection via a `commands` map passed as the third argument to command handlers. Added `CommandHandler` and `CommandHandlerMap` types to `@pnpm/cli.command`. ## Changes ### Architecture - Command handlers now receive a `commands` map as an optional third argument `(opts, params, commands?)` - The CLI dispatcher in `main.ts` passes the full commands map to every handler - Handlers that need other commands (e.g., `globalAdd` needs `approve-builds`, `recursive` needs `rebuild`) access them from this map - This replaces direct cross-package imports that would create circular dependencies ### Packages changed - `@pnpm/cli.command` — new `CommandHandler` and `CommandHandlerMap` types - `@pnpm/building.commands` — `approve-builds` uses `install.handler` for GVS - `@pnpm/global.commands` — removed `building/commands` dependency; receives `approve-builds` via commands map - `@pnpm/installing.commands` — receives `rebuild` via commands map instead of direct import - `@pnpm/installing.deps-installer` / `@pnpm/installing.deps-restorer` — default `allowBuilds` to `{}` in GVS mode - `pnpm` CLI — dispatcher passes commands map to all handlers |
||
|
|
f0ae1b97d7 |
fix: store global binaries in PNPM_HOME/bin subdirectory (#11038)
Previously, globally installed binaries were placed directly in PNPM_HOME, which also contains internal directories (global/, store/). This polluted shell autocompletion with non-executable entries. Now binaries are stored in PNPM_HOME/bin, keeping the PATH clean. Closes #10986 |
||
|
|
cd0e887db3 |
refactor: remove unused @pnpm/fs.msgpack-file package and lockfile-directory setting (#11033)
Remove the @pnpm/fs.msgpack-file package which was never imported in source code (only in its own tests). Also remove the deprecated lockfile-directory CLI option alias — users should use lockfile-dir. |
||
|
|
0d88df854f |
chore: update all dependencies to latest versions (#11032)
* chore: update all dependencies to latest versions Update all outdated dependencies across the monorepo catalog and fix breaking changes from major version bumps. Notable updates: - ESLint 9 → 10 (fix custom rule API, disable new no-useless-assignment) - @stylistic/eslint-plugin 4 → 5 (auto-fixed indent changes) - @cyclonedx/cyclonedx-library 9 → 10 (adapt to removed SPDX API) - esbuild 0.25 → 0.27 - TypeScript 5.9.2 → 5.9.3 - Various @types packages, test utilities, and build tools Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update unified/remark/mdast imports for v11/v4 API changes Update imports in get-release-text for the new ESM named exports: - mdast-util-to-string: default → { toString } - unified: default → { unified } Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve typecheck errors from dependency updates - isexe v4: use named import { sync } instead of default export - remark-parse/remark-stringify v11: add vfile as packageExtension dependency so TypeScript can resolve type declarations - get-release-text: remove unused @ts-expect-error directives Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert runtime dependency major version bumps Revert major version bumps for runtime dependencies that are bundled into pnpm to fix test failures where pnpm add silently fails: - bin-links: keep ^5.0.0 (was ^6.0.0) - cli-truncate: keep ^4.0.0 (was ^5.2.0) - delay: keep ^6.0.0 (was ^7.0.0) - filenamify: keep ^6.0.0 (was ^7.0.1) - find-up: keep ^7.0.0 (was ^8.0.0) - isexe: keep 2.0.0 (was 4.0.0) - normalize-newline: keep 4.1.0 (was 5.0.0) - p-queue: keep ^8.1.0 (was ^9.1.0) - ps-list: keep ^8.1.1 (was ^9.0.0) - string-length: keep ^6.0.0 (was ^7.0.1) - symlink-dir: keep ^7.0.0 (was ^9.0.0) - terminal-link: keep ^4.0.0 (was ^5.0.0) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore runtime dependency major version bumps Re-apply all runtime dependency major version bumps that were previously reverted. All packages maintain their default exports except isexe v4 which needs named imports. Updated runtime deps: - bin-links: ^5.0.0 → ^6.0.0 - cli-truncate: ^4.0.0 → ^5.2.0 - delay: ^6.0.0 → ^7.0.0 - filenamify: ^6.0.0 → ^7.0.1 - find-up: ^7.0.0 → ^8.0.0 - isexe: 2.0.0 → 4.0.0 (fix: use named import { sync }) - normalize-newline: 4.1.0 → 5.0.0 - p-queue: ^8.1.0 → ^9.1.0 - ps-list: ^8.1.1 → ^9.0.0 - string-length: ^6.0.0 → ^7.0.1 - symlink-dir: ^7.0.0 → ^9.0.0 - terminal-link: ^4.0.0 → ^5.0.0 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert tempy to 3.0.0 to fix bundle hang tempy 3.2.0 pulls in temp-dir 3.0.0 which uses async fs.realpath() inside its module init. When bundled by esbuild into the __esm lazy init pattern, this causes a deadlock during module initialization, making the pnpm binary hang silently on startup. Keeping tempy at 3.0.0 which uses temp-dir 2.x (sync fs.realpathSync). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add comment explaining why tempy cannot be upgraded Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert nock to 13.3.4 for node-fetch compatibility nock 14 changed its HTTP interception mechanism in a way that doesn't properly intercept node-fetch requests, causing audit tests to hang waiting for responses that are never intercepted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add comment explaining why nock cannot be upgraded Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update symlink-dir imports for v10 ESM named exports symlink-dir v10 removed the default export and switched to named exports: { symlinkDir, symlinkDirSync }. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert @typescript/native-preview to working version Newer tsgo dev builds (>= 20260318) have a regression where @types/node cannot be resolved, breaking all node built-in types. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: vulnerabilities * fix: align comment indentation in runLifecycleHook Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: pin msgpackr to 1.11.8 for TypeScript 5.9 compatibility msgpackr 1.11.9 has broken type definitions that use Iterable/Iterator without required type arguments, causing compile errors with TS 5.9. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
6d0eeeeafb |
chore: remove npm_config_verify_deps_before_run from extraEnv (#11029)
This was marked for removal in v11. Only the pnpm_config_ prefixed version is kept. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
2e55dcf130 |
fix: default to ['.'] for workspacePackagePatterns when packages field is missing (#10996)
Commit |
||
|
|
303ca410f5 |
feat!: stop reading settings from the pnpm field of package.json (#10086)
Settings should be read from pnpm-workspace.yaml |
||
|
|
4a36b9a110 |
refactor: rename internal packages to @pnpm/<domain>.<leaf> convention (#10997)
## Summary Rename all internal packages so their npm names follow the `@pnpm/<domain>.<leaf>` convention, matching their directory structure. Also rename directories to remove redundancy and improve clarity. ### Bulk rename (94 packages) All `@pnpm/` packages now derive their name from their directory path using dot-separated segments. Exceptions: `packages/`, `__utils__/`, and `pnpm/artifacts/` keep leaf names only. ### Directory renames (removing redundant prefixes) - `cli/cli-meta` → `cli/meta`, `cli/cli-utils` → `cli/utils` - `config/config` → `config/reader`, `config/config-writer` → `config/writer` - `fetching/fetching-types` → `fetching/types` - `lockfile/lockfile-to-pnp` → `lockfile/to-pnp` - `store/store-connection-manager` → `store/connection-manager` - `store/store-controller-types` → `store/controller-types` - `store/store-path` → `store/path` ### Targeted renames (clarity improvements) - `deps/dependency-path` → `deps/path` (`@pnpm/deps.path`) - `deps/calc-dep-state` → `deps/graph-hasher` (`@pnpm/deps.graph-hasher`) - `deps/inspection/dependencies-hierarchy` → `deps/inspection/tree-builder` (`@pnpm/deps.inspection.tree-builder`) - `bins/link-bins` → `bins/linker`, `bins/remove-bins` → `bins/remover`, `bins/package-bins` → `bins/resolver` - `installing/get-context` → `installing/context` - `store/package-store` → `store/controller` - `pkg-manifest/manifest-utils` → `pkg-manifest/utils` ### Manifest reader/writer renames - `workspace/read-project-manifest` → `workspace/project-manifest-reader` (`@pnpm/workspace.project-manifest-reader`) - `workspace/write-project-manifest` → `workspace/project-manifest-writer` (`@pnpm/workspace.project-manifest-writer`) - `workspace/read-manifest` → `workspace/workspace-manifest-reader` (`@pnpm/workspace.workspace-manifest-reader`) - `workspace/manifest-writer` → `workspace/workspace-manifest-writer` (`@pnpm/workspace.workspace-manifest-writer`) ### Workspace package renames - `workspace/find-packages` → `workspace/projects-reader` - `workspace/find-workspace-dir` → `workspace/root-finder` - `workspace/resolve-workspace-range` → `workspace/range-resolver` - `workspace/filter-packages-from-dir` merged into `workspace/filter-workspace-packages` → `workspace/projects-filter` ### Domain moves - `pkg-manifest/read-project-manifest` → `workspace/project-manifest-reader` - `pkg-manifest/write-project-manifest` → `workspace/project-manifest-writer` - `pkg-manifest/exportable-manifest` → `releasing/exportable-manifest` ### Scope - 1206 files changed - Updated: package.json names/deps, TypeScript imports, tsconfig references, changeset files, renovate.json, test fixtures, import ordering |