Commit Graph

11945 Commits

Author SHA1 Message Date
John-David Dalton
2dd79e372c feat(pacquet): honor NODE_EXTRA_CA_CERTS for custom CA trust (#12508)
* feat(pacquet): honor NODE_EXTRA_CA_CERTS for custom CA trust

pacquet's reqwest client trusts only its bundled webpki roots plus the
.npmrc ca/cafile material — it ignores NODE_EXTRA_CA_CERTS. pnpm running
on Node picks that variable up transitively via Node's TLS runtime, so a
native port has to read it explicitly to keep real-world parity for
users behind a corporate MITM proxy or a self-signed registry.

Load the named PEM bundle as additional trust roots in
default_client_builder (the single chokepoint every client routes
through), keeping the .npmrc-derived TlsConfig env-free. Additive and
lowest-priority; a missing, unreadable, or malformed file is silently
ignored, matching pnpm's silent treatment of a missing cafile. Documented
as the one deliberate exception to the TlsConfig no-env-vars parity note.

* perf(pacquet): load NODE_EXTRA_CA_CERTS once per for_installs

Addresses review on the NODE_EXTRA_CA_CERTS change:

- Read and parse the bundle once in for_installs via
  load_node_extra_ca_certs(), then clone the parsed certs into the
  default client and each per-registry client. Previously
  default_client_builder re-read and re-parsed the file on every call,
  i.e. once per per-registry override.
- Use the existing EnvGuard test helper (process-wide lock + restore on
  drop, panic-safe) instead of a hand-rolled lock with manual restore.
  The test now also asserts a valid bundle parses to one root and a
  missing file yields none.

* test(pacquet): align NODE_EXTRA_CA_CERTS test with aube's

Make the pacquet and aube tests mirror each other in form and coverage:
exercise the same four cases in the same order — empty value, valid
bundle (asserting one parsed root plus a successful client build), a
readable non-PEM file, and a missing file — each asserting
load_node_extra_ca_certs() yields the expected roots. Also align the
env-var read in load_node_extra_ca_certs to the same let-else + filter
form aube uses (no behavior change).

* docs(pacquet): fix broken intra-doc links in load_node_extra_ca_certs

The free function's doc used [`Self::for_installs`], but `Self` only
resolves in impl/trait contexts, so rustdoc flagged it as an unresolved
intra-doc link and the Rust CI Doc job (RUSTDOCFLAGS=-D warnings) failed.
Reference [`ThrottledClient::for_installs`] instead.
2026-06-18 21:38:13 +00:00
Zoltan Kochan
511e74200d fix: update vulnerable dependencies (#12503) 2026-06-18 19:42:35 +02:00
Zoltan Kochan
33745b892b fix(ci): grant pull-requests: write so the review label step can label PRs (#12501) 2026-06-18 16:11:04 +02:00
Zoltan Kochan
cb3d8e75bd test(exec): fix Windows failure in PnP require option test (#12499)
The 'exec should merge node options with PnP require option' test (added in
#12430) hardcoded an unquoted '--require=<path>' expectation. On Windows the
.pnp.cjs path contains backslashes, which makeNodeRequireOption quotes and
escapes for Node's NODE_OPTIONS tokenizer, so the assertion mismatched and the
test failed on Windows runners.

Derive the expected NODE_OPTIONS from makeNodeRequireOption itself so the
expectation matches the implementation's quoting on every platform.
2026-06-18 14:02:12 +00:00
Zoltan Kochan
8d17c7b8cd chore: update lockfile, Node.js, pnpm, and pacquet versions (#12441)
* chore: update lockfile, Node.js, pnpm, and pacquet versions

* fix: node range

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-18 14:35:34 +02:00
Zoltan Kochan
6ee484e3c5 fix(ci): run PR review automation for fork PRs via workflow_run (#12493)
Approvals on PRs from forks never got the `reviewed: coderabbit` label or a
Discord announcement. A pull_request_review run triggered by a forked PR is
granted a read-only token and no secrets, so the label step failed with HTTP
403 and the Discord step had no webhook.

Split the automation in two: the pull_request_review workflow now only records
the approval as an artifact (no token, no secrets), and a new workflow_run
companion runs from the default branch in base-repo context — where it has the
write-scoped token and secrets — to add the label and post to Discord.

The privileged half never checks out or executes PR content: it reads inert
data files, validates the PR number is an integer, maps the reviewer to a fixed
label, requests only actions:read + issues:write, surfaces a failed (vs absent)
artifact download instead of passing as a silent no-op, and scopes the Discord
webhook secret to the announce step.

Also documents that agents must open PRs using .github/pull_request_template.md,
since gh pr create does not apply it automatically.
2026-06-18 14:25:52 +02:00
Zoltan Kochan
93458600a8 chore(release): 11.8.0 (#12492)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
v11.8.0
2026-06-18 12:17:52 +02:00
Maël Nison
0474a9c3b1 feat: add support for package maps (#12430)
Generate a Node.js package map at `node_modules/.package-map.json` on every
isolated or hoisted install, including under the global virtual store, so that
third-party tooling can start experimenting with package maps. The file is
serialized compactly.

Two settings control how the map is consumed:

- `node-experimental-package-map` (default: off): inject
  `--experimental-package-map` into `NODE_OPTIONS` for the Node.js scripts pnpm
  runs — dependency lifecycle scripts, `pnpm exec`, and `pnpm run` (including
  recursive runs).
- `node-package-map-type` (`standard` | `loose`): choose between a strict map
  and one that tolerates hoisting-like access.

Covered by both the pnpm CLI and the pacquet (Rust) implementation.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-18 11:51:50 +02:00
Alessio Attilio
c2414e8236 docs: add PR template and clarify changeset purpose (#11290)
Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-18 11:47:53 +02:00
Zoltan Kochan
fe66535046 fix: converge incremental install by refreshing stale transitive pins (#12472)
When a package is reused from the lockfile, its child edges are taken
verbatim and bypass the preferred-versions walk, so a transitive
dependency can stay pinned to an older version even after a direct
dependency resolved to a higher version that satisfies the same range —
leaving the lockfile non-convergent (an incremental install keeps a
duplicate that a fresh install would not).

The resolver now refreshes such a stale pin to the higher
direct-dependency version during resolution, via `preferredVersion`
(singular), which overrides the EXISTING_VERSION_SELECTOR_WEIGHT stability
bias. The older version is never resolved or fetched, and the incremental
result converges to what a fresh install produces. The pick is anchored to
direct dependencies (which resolve first), so it restores the automatic
dedupe removed in pnpm/pnpm#11110 without reintroducing its
non-determinism, and unlike the post-pass in pnpm/pnpm#11502 it does not
over-fetch.

pacquet is ported in the same change. Its full-subtree lockfile reuse is
coarser than pnpm's per-edge reuse, so it records per importer which direct
deps changed and their resolved versions, declines full-subtree reuse for a
parent that depends on a changed direct dep, and forces the higher version
in the child walk. Range satisfaction uses plain semver (not
prerelease-inclusive), matching pnpm's semver.satisfies(.., true).
2026-06-18 10:59:15 +02:00
Juan Picado
ff9a1cfa1d feat(pnpr): per-uplink verdaccio settings (maxage, timeout, max_fails, fail_timeout, cache) (#12375)
Add verdaccio's per-uplink tuning knobs to pnpr's proxy uplinks, parsed
from the same config.yaml shape verdaccio uses:

- maxage:       per-uplink packument freshness window; overrides the
                global packument_ttl when set, otherwise defers to it.
- timeout:      per-request deadline applied to every upstream fetch
                (default 30s).
- max_fails /   circuit breaker: after max_fails consecutive failures the
  fail_timeout  uplink short-circuits with a 503 until the fail_timeout
                cooldown lapses, then one probe per window is allowed
                through (defaults 2 / 5m). max_fails: 0 disables it.
- cache:        when false, tarballs stream through without being written
                to the local mirror (default true).

Interval values accept verdaccio's format ("2m", "30s", "1h30m", or a
bare number of seconds, as either a YAML string or a numeric scalar);
an unparsable or out-of-range value is a named InvalidConfig error
rather than a silent fallback or a panic. Defaults match verdaccio
(timeout 30s, max_fails 2, fail_timeout 5m, cache true).

Circuit breaker behavior:
- Only a 5xx or transport error counts as a failure; a non-404 4xx
  (auth / rate-limit / bad request) surfaces verbatim and never opens
  the circuit.
- The half-open probe is gated on the cooldown window, so exactly one
  probe runs per fail_timeout and a cancelled or dropped probe cannot
  stick the breaker open.
- An open breaker reports the configured uplink name, not its upstream
  URL, so the 503 does not disclose internal endpoints.

Part of the verdaccio-parity tracking issue pnpm/pnpm#11973.

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-17 23:44:03 +00:00
Zoltan Kochan
bee4bf41ca fix: reject path-traversal config dependency names from the env lockfile (#12470)
Config dependency names and versions are read from the committed env lockfile
(pnpm-lock.yaml) and the legacy inline-integrity format in pnpm-workspace.yaml,
and both become path segments of the directories pnpm creates during install
(node_modules/.pnpm-config/<name> and the global virtual store's
<name>/<version>/<hash>). They were used unvalidated, so a malicious repository
could commit a traversal-shaped name (../../PWNED) or version (../../../PWNED)
and make `pnpm install` create symlinks or write package files outside those
roots — triggered on install, even with --ignore-scripts.

Add verifyEnvLockfile, an offline structural gate that validates every config
dependency and optional-subdependency name (must be a valid npm package name)
and version (must be an exact semver version) before any path is built from it.
It runs at the install boundary and, through a single writeVerifiedEnvLockfile
seam, before the env lockfile is ever persisted, so an invalid entry is rejected
with no write side effect. __proto__ names are rejected too (the validation
accumulators use null-prototype objects so the key can't slip past Object.keys).

The same fix and structure land in pacquet to keep the two stacks in sync.

Fixes GHSA-qrv3-253h-g69c.
2026-06-17 23:03:38 +00:00
Zoltan Kochan
6e218db8cb fix(pacquet): resolve package-manager bootstrap through trusted registries (#12471)
Port the GHSA-j2hc-m6cf-6jm8 fix (pnpm/pnpm#12296) to pacquet.

When pnpm auto-switches to the version requested by `packageManager` /
`devEngines.packageManager`, the bootstrap (`pnpm` / `@pnpm/exe`) must be
resolved through trusted registries only. Pacquet was resolving it through
`config.resolved_registries()`, which a malicious repository controls via
the workspace `.npmrc` or `pnpm-workspace.yaml` `registries:` block.

Add `Config::package_manager_bootstrap`, built in `Config::current()` from a
trusted-only fold of the URL-scoped env, `auth.ini`, and user `.npmrc`
sources (the project `.npmrc` is excluded), reusing the existing
registry/proxy/TLS/auth application logic. It defaults to the public npm
registry, and `PNPM_CONFIG_REGISTRY` still overrides the default because it
is user-controlled.

`EnvInstallerContext::for_package_manager` routes only the package-manager
bootstrap path (`sync_package_manager_dependencies`) through this trusted
config; project `configDependencies` resolution keeps the project
registries, matching the narrow scope of the upstream TypeScript fix.
2026-06-18 00:46:15 +02:00
subaru
223d060a0f docs: document --cpu, --os and --libc flags in pnpm install --help (#12478)
These flags are already supported via rcOptionsTypes but were only
documented on the website, not in the CLI help output.

Close pnpm/pnpm#12359
2026-06-17 23:20:15 +02:00
Zoltan Kochan
83f06a6046 refactor(pacquet): trim comments that restate code or record history (#12482)
## What

A repository-wide sweep of the Rust source under `pacquet/` to bring comments in line with the comment policy in `AGENTS.md` / `pacquet/AGENTS.md`: **comments are for the non-obvious _why_, not a translation of the _what_, and behavior already proven by a test should not be re-narrated in code.**

Two focused commits:

**1. Trim comments that restate code or record history** (`refactor(pacquet): trim comments that restate code or record history`)
- comments that merely restate the adjacent code;
- history / refactor-shape narration (`used to`, `before this PR`, `Copilot flagged`, "the old X", removed-type references);
- call-site comments duplicating a callee's own doc comment;
- test prose that just re-describes what a test's name and assertions demonstrate.

**2. Drop comments that narrate test-covered behavior** (`refactor(pacquet): drop comments that narrate test-covered behavior`) — enforcing the "tests are documentation" rule. Each implementation file was paired with its co-located test module so the duplication was visible, then comments enumerating tested scenarios, edge cases, failure modes, or worked examples were removed — **including when they carried a pnpm-parity reference/link**, since that context lives in the test names and git history.

Preserved throughout: genuine `///`/`//!` contracts (guarantees, preconditions, postconditions, panics), hidden invariants, workarounds, ordering/safety reasons, parity notes that aren't behavior-restatement, and `SAFETY:`/`TODO:` notes.

## Scope

Comments only — **no code, identifiers, or string literals were modified** in either commit. ~500 file edits in total; several thousand comment lines removed.

## How it was done

Multi-agent sweeps: one agent per batch applied the policy, and a second agent diff-checked each edited batch for over-deletion (lost contracts) or accidental code changes. Over-trimmed contracts were restored; zero code changes were introduced (independently re-verified: every added line is identical to a removed line modulo a stripped trailing comment).
2026-06-17 23:05:55 +02:00
Zoltan Kochan
2bb1fe7ecd feat: let Qodo auto-approve clean PRs (#12481)
Auto-approval was enabled (enable_auto_approval + auto_approve_for_no_suggestions)
but never fired because it is evaluated by the /improve tool, which was not in
pr_commands — only /agentic_describe and /agentic_review ran. Add /improve to
pr_commands so the approval check runs, and add auto_approve_for_low_review_effort
= 2 so small PRs are approved even when /improve has minor suggestions.

/improve is added to pr_commands only (not push_commands) to keep Qodo's per-PR
usage low; it runs once when a PR opens rather than on every commit.
2026-06-17 20:45:00 +02:00
dependabot[bot]
23c8efeffc chore(cargo): bump dialoguer from 0.11.0 to 0.12.0 (#12355)
Bumps [dialoguer](https://github.com/console-rs/dialoguer) from 0.11.0 to 0.12.0.
- [Release notes](https://github.com/console-rs/dialoguer/releases)
- [Changelog](https://github.com/console-rs/dialoguer/blob/main/CHANGELOG-OLD.md)
- [Commits](https://github.com/console-rs/dialoguer/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: dialoguer
  dependency-version: 0.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 20:29:26 +02:00
Khải
7e8abe26a0 refactor(pacquet): replace OnceLock with LazyLock where init is self-contained (#12477) 2026-06-17 18:06:54 +00:00
Zoltan Kochan
c18cbd4cad fix: enable CodeRabbit approvals via request_changes_workflow (#12476)
CodeRabbit's effective config (from the `@coderabbitai configuration`
command) showed request_changes_workflow: false, which is why it only
ever commented and never approved — CodeRabbit submits approving reviews
only when this is true. The dashboard toggle wasn't taking effect, so
declare it in-repo.

Also explicitly disable pre_merge_checks: they're enabled by default and
in the dashboard (removing the block from the config did not turn them
off), and they add a status block without review value here. Title
enforcement stays covered by the commit-msg hook and squash-title
convention.
2026-06-17 14:51:08 +02:00
Zoltan Kochan
7a313077fe fix: restore CodeRabbit and Qodo PR auto-approval (#12474)
* chore: stop configuring CodeRabbit pre-merge checks

Defining any pre-merge check turned on the whole pre-merge-checks
subsystem, which under Request Changes Workflow replaces the
auto-approve flow: CodeRabbit emits a pre-merge-checks status block
instead of approving once review threads are resolved, leaving PRs at
REVIEW_REQUIRED even with all threads cleared and all checks green.

Remove the title check (added in pnpm/pnpm#12460) to restore
auto-approval and document why the block is intentionally absent.
Conventional Commits on the squash title stay covered by the commit-msg
hook and the squash-title convention.

* fix: declare Qodo auto-approval under [config]

enable_auto_approval and auto_approve_for_no_suggestions were declared
under [pr_reviewer], where Qodo never reads them, so auto-approval never
ran. Per the Qodo docs both keys belong under [config]. Move them and
drop the now-empty [pr_reviewer] section.

* chore: stop applying labels from Qodo

Product labels are applied by CodeRabbit (auto_apply_labels +
labeling_instructions in .coderabbit.yaml). Having Qodo also publish
labels via enable_custom_labels/publish_labels/custom_labels meant two
tools managing the same labels. Drop the Qodo label config and leave
labeling to CodeRabbit.

* ci: tag Qodo-approved PRs and fix the label step

Add an on-qodo-approval job that applies the 'reviewed: qodo' label when
qodo-free-for-open-source-projects[bot] approves, mirroring the existing
CodeRabbit job.

The label step used 'gh pr edit --add-label', which is broken (it errors
on the deprecated Projects-v1 GraphQL field), so adding the label failed
on approval. Switch all three jobs to the issues REST API
(POST /repos/{owner}/{repo}/issues/{number}/labels) instead.
2026-06-17 14:15:46 +02:00
dependabot[bot]
de74c58f58 chore(cargo): bump object_store from 0.12.5 to 0.13.2 (#12354)
* chore(cargo): bump object_store from 0.12.5 to 0.13.2

Bumps [object_store](https://github.com/apache/arrow-rs-object-store) from 0.12.5 to 0.13.2.
- [Changelog](https://github.com/apache/arrow-rs-object-store/blob/main/CHANGELOG-old.md)
- [Commits](https://github.com/apache/arrow-rs-object-store/compare/v0.12.5...v0.13.2)

---
updated-dependencies:
- dependency-name: object_store
  dependency-version: 0.13.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(pnpr): import ObjectStoreExt for object_store 0.13 method move

object_store 0.13 moved get/put/delete off the ObjectStore trait onto
the new ObjectStoreExt trait. Import it so the S3 hosted-store backend
keeps compiling.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-17 11:37:36 +00:00
Ben Scholzen
96bdd57bf4 fix: dedupe workspace deps with children in single-project mode (#11448)
## Issue

When `injectWorkspacePackages: true` is set and a workspace package depends on another workspace package that has its own dependencies, running `pnpm rm` from inside the dependent package's directory switches the lockfile protocol from `link:` to `file:`.

Reproduction (workspace where `a` depends on workspace `b`, and `b` has any dependency of its own):

```
cd packages/a
pnpm add redis
pnpm rm redis
# pnpm-lock.yaml: a's "b" entry switched from link:../b to file:packages/b
```

## Root Cause

The fix in #10575 added a defensive guard in `dedupeInjectedDeps` that skipped deduplication whenever the target workspace project's children weren't in `dependenciesByProjectId`:

```ts
if (!targetProjectDeps) {
  if (children.length > 0) continue
}
```

In single-project operations (`mutateModulesInSingleProject`, used by `pnpm rm` from inside a package directory) only the operated-on project is resolved. `dependenciesByProjectId` then only has that one project, so the guard fires for any workspace dependency whose target has children, and the protocol stays `file:`.

## Solution

In single-project mode the injected dep is resolved against the same workspace package source, so dedupe is safe — *except* for peer-suffixed depPaths, whose resolution depends on the importer's peer context (a plain `link:` would lose it). The new code dedupes whenever `targetProjectDeps` is missing for a known workspace project and the depPath has no peer suffix. The peer-suffix check compares the depPath against its peer-free `pkgIdWithPatchHash` (depPaths are built as `${pkgIdWithPatchHash}${peerDepGraphHash}`), so it's exact rather than a `(`-substring heuristic.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-17 13:37:09 +02:00
Victor Sumner
1c0587698d fix: narrow warm install relinking (#11169)
## Problem
During warm installs, pnpm relinked existing packages more broadly than necessary when only some child dependencies changed.

In the narrowed relinking path, removed child aliases could also remain behind as stale links after dependency updates.

## Solution
Only pass changed child edges through the relinking path for existing packages.

When a child alias is no longer present in the updated dependency set, remove the obsolete link before relinking. Added regression tests for both cases:
- unchanged child dependencies are not relinked unnecessarily
- deleted child dependencies do not remain as stale links after a warm install

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-17 13:36:01 +02:00
Zoltan Kochan
c86559c9bc docs: add new silver sponsor (#12473) 2026-06-17 13:31:01 +02:00
Hiten Shah
1b02b47669 fix: remove macOS Gatekeeper quarantine xattr from native binaries (#11095)
Fixes #11056

## Problem

On macOS, pnpm imports files from its content-addressable store into `node_modules` via copy, reflink/clone, or hardlink. All three preserve extended attributes, including `com.apple.quarantine`. If a store blob carries that xattr — e.g. it was first written under a Gatekeeper-enabled app such as a Git client (`LSFileQuarantineEnabled=YES`) — the quarantine propagates into `node_modules`. Gatekeeper then blocks ad-hoc-signed native binaries (`.node`, `.dylib`, `.so`) from loading, even though pnpm has already verified each file's integrity against `pnpm-lock.yaml`.

## Solution

After importing a package from the store, strip `com.apple.quarantine` from its native binaries — mirroring Homebrew's behaviour of dropping quarantine from downloads after checksum verification.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-17 10:05:37 +00:00
Khải
268c97d122 refactor(pacquet): remove redundant Arc (#12461)
* perf(package-manager): drop redundant Arc around process-global compat extender

The built-in `@yarnpkg/extensions` compatibility database is built once
per process and cached in a `OnceLock`. Wrapping it in an `Arc` added an
allocation plus per-install refcount churn for no benefit: a `OnceLock`
already hands out a stable `&'static` reference, and every consumer
(`PackageExtender::apply` / `apply_to_arc`, both `&self`) is satisfied by
`&'static PackageExtender` — which is `Copy + Send + Sync + 'static` and
so moves cleanly into the resolver's `ManifestHook` closure.

Store the extender as `OnceLock<PackageExtender>` returning a
`&'static` reference. The per-install user-provided extender keeps its
`Arc`, since it is constructed fresh each install and shared into the
resolver hook.

* refactor(store-dir): borrow &StoreIndexWriter in upload instead of &Arc

`upload` only reads through the writer — a single `queue_side_effects_upload`
call, no `Arc::clone` — so it never needed shared ownership. Borrowing
`&Arc<StoreIndexWriter>` forced the signature to advertise reference
counting it does not use. Take `&StoreIndexWriter` instead; the sole
caller passes a `&Arc<StoreIndexWriter>` that deref-coerces at the call
site, so it is unchanged.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-17 11:30:00 +02:00
Ayush Baluni
9d0a3006ff fix(version): honor workspace selection for recursive version bumps (#11425)
## Problem
`pnpm version --recursive` did not bump the workspace packages the user selected. In recursive mode the command re-derived the workspace selection itself (via `filterProjectsFromDir`) using an incomplete set of options, so it could resolve a different set of packages than the CLI's actual `--filter`/`--recursive` resolution.

Fixes #11348.

## Change
- In recursive mode, bump the projects in `selectedProjectsGraph` — the selection the pnpm CLI (`main.ts`) already computes from the workspace filter, exactly the way `pnpm publish --recursive` works.
- Remove the in-handler `filterProjectsFromDir` fallback. It duplicated the CLI's filtering logic and was unreachable in production (`main.ts` always passes `selectedProjectsGraph` for a recursive run). `@pnpm/workspace.projects-filter` becomes a dev-only dependency of `@pnpm/releasing.commands`.
- No global/config plumbing is needed for `--recursive`: `@pnpm/cli.parse-cli-args` already recognizes `recursive` (and the `-r` shorthand) for every command, the config reader passes it through to the handler, and the `version` command already declares `recursive` in its own `cliOptionsTypes`.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-17 11:22:15 +02:00
Zoltan Kochan
c1f9cdfe95 ci: remove TS CI / Success aggregate gate job (#12466) 2026-06-17 10:03:40 +02:00
Zoltan Kochan
9de161c386 chore: keep test fixtures out of GitHub's dependency graph (#12465)
Mark fixture trees (package.json, lockfiles) as linguist-generated in
.gitattributes so GitHub's dependency graph ignores them. This stops
Dependabot from raising alerts and opening automatic security-update PRs
for packages that appear in fixtures only as test data — e.g. the
js-cookie bump in pnpm/pnpm#11840.

The `@pnpm/test-fixtures` helper package is real source and is
intentionally left unmarked.
2026-06-17 09:47:14 +02:00
Allan Kimmer Jensen
1495cb080c feat(sbom): per-package SBOM generation with --out and --split (#12097)
* feat(sbom): add monorepo workspace support for SBOM generation

When --filter selects a single workspace, the SBOM root component now
uses that workspace's name, version, description, license, and author
instead of the workspace root's metadata. Author, repository, and
license fall back to the root manifest when the workspace package
doesn't define them.

Workspace inter-dependencies (workspace: protocol) and their transitive
dependencies are now included in the SBOM. Dev-only workspace deps are
correctly excluded when --prod is used, and lockfile-only mode skips
workspace resolution entirely to avoid unexpected disk reads.

* fix(sbom): add recursiveByDefault so --filter works in workspaces

Without recursiveByDefault, the sbom command in a workspace didn't
enter the recursive code path that populates selectedProjectsGraph from
--filter. The handler received no filter info and always used the
workspace root manifest for the root component.

* feat(sbom): add --out and --split for per-package SBOM generation

Add --out <path> to write SBOM to a file instead of stdout. Supports
%s (package name) and %v (version) placeholders. When %s is present,
generates one SBOM per workspace package automatically.

Add --split to output NDJSON (one compact JSON per line) to stdout,
for piping into tooling that processes multiple SBOMs.

Add compact option to CycloneDX and SPDX serializers so NDJSON lines
are single-line JSON without re-parsing.

Sanitize %s and %v values to prevent path traversal in output paths.

* fix: use sbom-out instead of sboms to pass spellcheck

* fix(sbom): expand %v in single-output path and fix workspace dep parent relationships

Two bugs found by CodeRabbit:

1. --out with %v but no %s wrote a literal %v filename. Now both %s
   and %v are expanded in the single-output path using the SBOM root
   component's name and version.

2. The lockfile walker used rootPurl as parent for every importer,
   including additional workspace dep importers. This caused
   shared-lib's external deps (like is-odd) to appear as direct
   deps of the root package. Now each importer's walk uses the
   correct parent PURL based on whether it's an original or
   workspace dep importer.

* fix(sbom): fall back to workspace root manifest for filtered package metadata

Read the root manifest from rootProjectManifest/rootProjectManifestDir instead
of opts.dir so license/author/repository/description fall back to the workspace
root when a filtered package omits them. Add the missing rootDescription
fallback. Fix the per-package CycloneDX test assertions to match the standard
group/name split for scoped names.

* fix(sbom): harden path handling and fix lockfile-only/versionless workspace deps

- Neutralize '.', '..' and blank segments in --out path templates so a crafted
  package name/version cannot escape the output directory.
- Skip lockfile link: targets that resolve outside the workspace root, and guard
  the workspace manifest reads with a containment check, preventing arbitrary
  package.json reads from a malicious lockfile.
- Honor --lockfile-only inside collectSbomComponents so workspace links and their
  transitive deps are no longer traversed.
- Include workspace packages that omit a version (default to 0.0.0, matching the
  root component).
- Bound workspace manifest reads with p-limit to avoid EMFILE on large monorepos.

* fix(sbom): error on per-package output path collisions and use O(n) workspace BFS

- Throw SBOM_OUT_PATH_COLLISION when two workspace packages sanitize to the same
  --out path instead of silently overwriting one SBOM with another.
- Replace the resolveWorkspaceDeps BFS queue.shift() (O(n) per dequeue) with a
  moving head index for O(n) traversal on large workspaces.

* fix(sbom): strip control chars from output paths and skip unreadable workspace importers

- Strip ASCII control characters (incl. newlines) in sanitizePathSegment so a
  crafted package name/version cannot inject lines into the --split --out summary
  or produce confusing filenames.
- Skip walking a reachable workspace importer when its package info is missing
  (e.g. unreadable manifest) instead of misattributing its deps to the root.
- Bound importer-walker fan-out with p-limit to avoid resource pressure on large
  workspaces.

* perf(sbom): reuse workspace manifests across split outputs and use a Set for path collisions

- Read workspace package manifests once into SharedContext (keyed by importer id)
  from the project graph, so split mode no longer re-reads them from disk for
  every emitted SBOM.
- Track written --out paths in a Set instead of Array#includes to make collision
  detection O(1) per package rather than O(n2) overall.

* fix(sbom): support %s in single-project --out, harden importer lookup, cache root license

- Only treat %s in --out as per-package (split) mode when a workspace project
  graph is present; in a single-project repo %s/%v interpolate from the root
  component on the single-output path. Adds a regression test.
- Use an own-property check for lockfile importer existence so a crafted link:
  target cannot follow inherited keys (e.g. toString) via the prototype chain.
- Fast-path resolveRootLicense when the manifest already declares an SPDX license
  and cache the workspace-root license in SharedContext, avoiding redundant
  on-disk LICENSE probing per emitted SBOM.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-17 09:43:27 +02:00
Orgad Shaneh
9d79ba181e fix: expose update --no-save in CLI help (#12091)
The update command already honors --no-save and the docs already
mention it, but the flag was missing from the update command
metadata.

Add the option entry so pnpm update --help shows it and the CLI
surface matches the documented behavior.
2026-06-17 08:22:09 +02:00
Mayank Maurya
3f0fb219b6 fix(default-reporter): erase trailing characters on progress line (#12351)
External processes like SSH passphrase prompts can write to the terminal between progress updates. The previous renderer used `ansi-diff`, which only overwrites the characters it knows changed, so leftover characters from the external output stayed visible on the progress line — e.g. `added 0sa':`, where `sa':` is a fragment of `Enter passphrase for key '.../.ssh/id_rsa':`.

Closes https://github.com/pnpm/pnpm/issues/12350

## Summary

The interactive (non-append-only) reporter now redraws the whole frame in place on each update instead of incrementally diffing it:

- return the cursor to the top-left of the previous frame (`ESC[<rows>A` followed by a carriage return, so the redraw starts at column 0 even if an external process left the cursor mid-line),
- erase from there to the end of the display (`ESC[0J`),
- reprint the frame — all in a single atomic write, so there is no flicker.

Because the whole region is erased on every frame, any characters an external process wrote in between are cleared. This matches pacquet's `Output::Frame` rendering (the column-reset hardening was applied to both stacks). The now-unused `ansi-diff` dependency has been removed.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-17 08:17:15 +02:00
MK (fengmk2)
3b54d79521 fix(deps-installer): keep catalog-referencing overrides in sync on update (#12158)
* fix(deps-installer): re-resolve catalog-referencing overrides on update

When `pnpm.overrides` reference a catalog (e.g. `overrides: { foo: 'catalog:' }`),
`pnpm update` bumped the catalog entry during resolution but left the resolved
`overrides` in the lockfile pointing at the old version. The lockfile's
`catalogs` advanced while `overrides` stayed stale, producing an internally
inconsistent lockfile that fails a later `pnpm install --frozen-lockfile` with
ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.

After resolution, re-resolve the overrides against the catalog merged with the
update's `updatedCatalogs`, so the lockfile `overrides` track the bumped catalog
just like `catalogs` and direct catalog dependencies do.

* fix(deps-installer): re-resolve catalog overrides before afterAllResolved

Address review feedback:

- Run the catalog-override re-resolution before the `afterAllResolved`
  pnpmfile hook instead of after it, so a hook that edits `lockfile.overrides`
  still sees and can amend the final value (the block previously ran after the
  hook and would clobber its edits whenever a catalog entry was updated).
- Drop the dead `opts.catalogs ?? {}` fallback; `opts.catalogs` is required on
  the install options and always defaulted to `{}`, so it is never nullish here.

* test(pacquet): cover catalog-referencing override sync on update --latest

Mirrors pnpm's regression test for keeping lockfile overrides that resolve
through a catalog in sync when `update --latest` bumps that catalog. pacquet
already behaves correctly (it threads the bumped catalogs through to override
parsing), so this is a guard against a future refactor reintroducing the
inconsistency that pnpm/pnpm#12158 fixes on the TypeScript side.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-17 08:10:12 +02:00
Zoltan Kochan
c7950e7fdb fix(ci): grant issues: write so the approval label step can run (#12464)
The pr-review-automation workflow merged in pnpm/pnpm#12462 fails at the
label step with 'Resource not accessible by integration
(addLabelsToLabelable)'. gh pr edit --add-label uses the GraphQL
addLabelsToLabelable mutation, which requires issues: write even when
labeling a PR; pull-requests: write alone is insufficient. Grant it per
job, matching pacquet-integrated-benchmark-comment.yml.
2026-06-17 08:07:08 +02:00
Abdullah Alaqeel
5c12968ad6 fix(update): handle mixed direct and transitive selectors (#12105)
* fix(update): handle mixed direct and transitive selectors

* test(update): strengthen regression test and port to pacquet

The pnpm regression test passed with and without the fix: the fixture's
`latest` dist-tag made a fresh install of `^100.0.0` already resolve to
100.1.0, so the assertion was trivially true. Pin the transitive
dep-of-pkg-with-1-dep to 100.0.0 before install so the test genuinely
fails without the fix and passes with it.

Add pacquet parity regression tests for the same mixed direct/transitive
selector scenario (exact-name and glob forms). pacquet has no equivalent
source change to make — its `update` matches every bare-name/glob
selector against direct deps and locked snapshot names in one pass, so a
direct selector never gates the transitive one — but the behavior is
guarded by tests to lock in pnpm/pnpm#12103 parity.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-17 07:29:57 +02:00
morning-verlu
531f2a307c fix: preserve workspace specs on update (#12140)
## What
- preserve existing `workspace:` dependency specifiers when `updateProjectManifest` saves updated direct dependencies and `preserveWorkspaceProtocol` is enabled
- keep catalog specifiers taking precedence over resolver-normalized specs
- add focused coverage for preserved and normalized local spec behavior
- add a changeset for the published `@pnpm/installing.deps-resolver` change

### pacquet parity
Ported the same fix to pacquet's `update` command. Previously `pacquet update --latest` routed every direct dependency through a registry `latest` lookup, so a `workspace:` local-path dependency (e.g. `workspace:../packages/foo/dist`) was rewritten into a registry version — corrupting the manifest (in the regression test it became `0.0.1-security`). Both `--latest` rewrite sites now skip registry resolution for such specs via `is_workspace_local_path_specifier`, a faithful port of pnpm's `isWorkspaceLocalPathSpecifier`. The gate is unconditional in the `--latest` path because `preserveWorkspaceProtocol` is always on there (its only override derives from `linkWorkspacePackages` under `--workspace`, which cannot be combined with `--latest`).

Fixes #3902

---------

Co-authored-by: morning-verlu <258725120+morning-verlu@users.noreply.github.com>
Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-16 23:38:05 +00:00
Zoltan Kochan
c38f68aecc ci: PR review automation (CodeRabbit approval + maintainer automerge) (#12462)
* ci: label and notify on CodeRabbit approval

Add a workflow that fires on pull_request_review and, when CodeRabbit
submits an approving review, applies the informational "reviewed:
coderabbit" label and (if a DISCORD_WEBHOOK secret is configured) posts
a notice to Discord.

PR fields reach the steps through the environment rather than script
interpolation, so an attacker-controlled PR title cannot inject shell
commands. The Discord step is skipped when the webhook secret is unset.

* ci: flag maintainer-approved PRs for automerge

Rename the approval workflow to cover both review automations and add a
second job: when zkochan submits an approving review, apply the existing
"state: automerge" label.

Like the CodeRabbit job, the PR number is passed through the environment
rather than interpolated into the run script.

* ci: harden PR review automation per review feedback

- Scope permissions to each job (top-level permissions: {}) instead of
  granting pull-requests: write workflow-wide (zizmor).
- Set allowed_mentions to {parse: []} on the Discord payload so a PR
  title containing `@everyone`/`@here` cannot ping the server.
- Add connect/overall timeouts and retries to the Discord curl call.
2026-06-17 01:33:50 +02:00
btea
293921a788 feat(view): support searching package.json upward when package name is omitted (#11696)
* feat(view): support searching package.json upward when package name is omitted

* Potential fix for pull request finding

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

* fix: apply review

* fix: apply review

* Potential fix for pull request finding

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

* fix: update

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-17 01:04:25 +02:00
Zoltan Kochan
cc4cadad8d chore: tune CodeRabbit config (#12460)
Add several CodeRabbit settings:

- path_filters: skip generated/vendored/snapshot files (lockfiles, dist,
  fixtures, snapshots, changelogs) so reviews focus on hand-written code.
- pre_merge_checks.title: enforce Conventional Commits on the PR title,
  which becomes the commit message under squash merge (the commit-msg
  hook only validates per-commit messages).
- knowledge_base.learnings scoped to local so maintainer corrections are
  remembered without crossing repo boundaries.
- issue_enrichment labeling: auto-apply the durable area/type taxonomy to
  issues, where long-lived labels actually aid triage and search.
- poem disabled.

Secret scanners (gitleaks, semgrep, trufflehog) are already enabled by
default, so they are not added explicitly.
2026-06-17 01:00:10 +02:00
Abdullah Alaqeel
61969fbddf fix(deps-status): detect lockfile-only changes (#12106)
## Summary

Fixes `pnpm install` with `optimisticRepeatInstall` incorrectly returning `Already up to date` when `pnpm-lock.yaml` changed but project manifests did not.

Fixes #12100.

## Root Cause

`checkDepsStatus` used modified manifest mtimes as the only signal for whether it needed to validate dependency status. If no manifest was newer than `workspaceState.lastValidatedTimestamp`, it returned `upToDate: true` before checking whether the wanted lockfile had changed.

That skipped lockfile validation for workflows like:

- `git checkout HEAD~1 -- pnpm-lock.yaml`
- restoring only `pnpm-lock.yaml` from a stash
- external tools rewriting the lockfile without touching manifests

## Changes

- Check wanted lockfile mtimes before taking the optimistic fast path.
- If any wanted lockfile is missing or newer than the workspace state timestamp, validate all projects instead of only modified manifests.
- Add a regression test proving a lockfile-only change does not skip wanted-lockfile validation.
- Add a patch changeset for `@pnpm/deps.status` and `pnpm`.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-16 22:04:07 +00:00
Zoltan Kochan
c9db42dcd8 chore: add product labels in CodeRabbit (#12459)
Mirror the three product labels (product: pnpm, product: pacquet,
product: pnpr) from the Qodo config into CodeRabbit, auto-applied based
on which stack the diff touches. Enables auto_apply_labels so the
labels are attached to the PR rather than only suggested.
2026-06-16 23:55:15 +02:00
Zoltan Kochan
b1a28e03f6 chore: auto-approve issue-free PRs and add product labels in Qodo (#12457)
Enable Qodo Merge auto-approval when the review finds no suggestions, and
add three custom labels (product: pnpm, product: pacquet, product: pnpr)
assigned by the describe tool based on which stack the diff touches.
2026-06-16 23:26:26 +02:00
Marvin Hagemeister
8081aacbd2 fix(pacquet): limit settings-only workspaces to root project (#12133)
Fixes pacquet workspace project enumeration when `pnpm-workspace.yaml` exists but does not define `packages`.

A settings-only workspace manifest like this:

```yaml
allowBuilds:
  esbuild: false
```

should still make the project a workspace, but it should only enumerate the root importer. Pacquet was passing the missing packages field through to the lower-level workspace project finder, which uses the recursive `['.', '**']` default. That could accidentally treat vendored fixture packages as workspace projects.

This maps absent packages to `['.']` in the install path, matching pnpm’s config-reader `workspacePackagePatterns` default:

```ts
cliOptions['workspace-packages'] ?? workspaceManifest?.packages ?? ['.']
```

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-16 23:23:43 +02:00
Truffle
6d35338691 fix: detect changes inside file: dependencies on repeat install (pacquet + pnpm) (#12317)
## Summary

- `pnpm install` reports "Already up to date" after edits inside a `file:` dependency's directory or after repacking a `file:` tarball. This is a v11 regression from the `optimisticRepeatInstall` default flip in pnpm/pnpm#11158. Fixes pnpm/pnpm#11795.
- `checkDepsStatus` gains a `treatLocalFileDepsAsOutdated` option: when set, any project manifest declaring a local file dependency makes the check report not up to date. `installDeps` sets it on the optimistic fast path, so projects with local file dependencies always run a real install, which refetches those dependencies (the v10 behavior).
- The predicate covers `file:` specs, path-prefixed specs (`./`, `../`, `~/`, absolute POSIX paths, and Windows drive paths including drive-relative ones like `C:dir`, matching the local resolver's `isFilespec`), and bare tarball file names (`vendor/pkg.tgz`). It is deliberately narrower than the local resolver's bare-path matching: a bare `user/repo` is statically indistinguishable from a git shorthand at this layer, and matching it would kill the fast path for every project with git dependencies, so protocol-carrying and URL specs stay on the fast path.
- `pnpm.overrides` entries are scanned with the same predicate: an override mapping to a local file spec redirects every matching dependency in the graph to that directory, so it has the same blind spot as a direct local file dependency. Registry and `link:` overrides keep the fast path.
- The option is caller-scoped on purpose. `verifyDepsBeforeRun` also consumes `checkDepsStatus`, and treating `file:` deps as always stale there would force a reinstall before every `pnpm run`. Its behavior is unchanged, and a regression test pins that.
- pacquet port in the same commit: `check_optimistic_repeat_install` bails unconditionally on `file:` specifiers, because its only caller is the install command, the one consumer that sets the flag upstream. `link:` specifiers are excluded on both sides: they are symlinked, so changes inside them flow through without a reinstall.

## Why

Both branches of `checkDepsStatus` are blind to content changes inside a `file:` dependency. The workspace branch exits early with `upToDate: true` when no project manifest's mtime moved, without ever reaching `linkedPackagesAreUpToDate`. The non-workspace branch exits at the manifest-vs-lockfile mtime gate the same way. Editing a source file inside a `file:` dependency bumps neither, so the fast path can never see it; the fix has to bail before those gates rather than refine them. This is the fix shape (a) I proposed in my diagnosis on the issue thread ([comment](https://github.com/pnpm/pnpm/issues/11795#issuecomment-4504177744)): the cost is a full resolution on repeat installs only for projects that declare `file:` dependencies, which is exactly what v10 did.

The manifest-only comparison in `@pnpm/lockfile.verification` (`allProjectsAreUpToDate`) is intentional for the install-proper path and asserted by its tests, so this PR leaves it untouched.

## Checks

- `pnpm --filter @pnpm/deps.status test test/checkDepsStatus.test.ts` (31 passed, 13 new)
- `pnpm --filter @pnpm/deps.status run compile` and `pnpm --filter @pnpm/installing.commands run compile` (tsgo + eslint clean)
- `cargo test -p pacquet-package-manager optimistic_repeat_install` (51 passed, 7 new; run in a rust:1.95.0 container)
- `cargo fmt --check -p pacquet-package-manager`
- `RUSTDOCFLAGS="-D warnings" cargo doc -p pacquet-package-manager --no-deps`

---
Written by an agent (Claude Code, claude-fable-5).

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-16 22:31:45 +02:00
Scarab Systems
30c7590a26 fix: shorten CAFS temp directory names (#12327)
## Summary
- use `fs.mkdtemp()` with a short `_tmp_` prefix for CAFS temporary package directories
- remove `path-temp` from `@pnpm/store.create-cafs-store`
- add git-fetcher regression coverage for short CAFS temp directories during git package preparation
- add a patch changeset for `@pnpm/store.create-cafs-store` and `pnpm`

## Why
This leaves more Unix socket path budget for lifecycle tools that create IPC sockets under `TMPDIR` during git-hosted dependency preparation.

Closes pnpm/pnpm#12222.

## Pacquet
No pacquet change is included because the Rust git fetcher already uses `tempfile::tempdir()` instead of the TypeScript CAFS `path-temp` suffix changed here.
2026-06-16 22:31:03 +02:00
Allan Kimmer Jensen
b4194aa5e0 feat(pacquet): prune surplus virtual-store dirs on install (#12419)
Ports the virtual-store sweep from pnpm's `prune` to pacquet's isolated linker.
After an install, `node_modules/.pnpm` (pacquet: `.pacquet`) entries the wanted
lockfile no longer references are removed, instead of accumulating across branch
switches and downgrades. Before this, only the hoisted linker removed orphans
(`remove_orphans`); the default isolated linker never pruned its virtual store.

- **Sweep** — `prune_virtual_store`: needed set = `node_modules` + one
  `depPathToFilename` per non-skipped snapshot key; readdir the virtual store;
  rimraf the rest. Mirrors `prune.ts` L180-190.
- **Throttle** — `should_prune_virtual_store` mirrors the `pruneVirtualStore` gate
  (`index.ts` L471-473) and `cacheExpired` (L1180-1182): skip under the global
  virtual store; otherwise prune unless `prunedAt` is within `modulesCacheMaxAge`.
  An unparseable `prunedAt` is treated as not-expired, matching pnpm's
  `NaN > maxAge == false`.
- **`prunedAt` lifecycle** — now stamped only when a sweep ran or on first install,
  else the prior value is preserved (`index.ts` L1828). Pacquet previously stamped
  it every install, which would have wedged the throttle off.

## Notable deviation

The sweep keeps `lock.yaml` (the current lockfile), whereas upstream deletes it and
unconditionally rewrites it. Pacquet's current-lockfile write is *conditional*
(skipped when `config.lockfile` is off), so deleting it in the sweep could orphan
it. Preserving it is end-state-equivalent whenever the rewrite runs and strictly
safer when it doesn't. Making pacquet's current-lockfile write unconditional (full
parity) is a separate, pre-existing concern.

## Scope

Only the virtual-store sweep slice of `prune()`. Deferred (other roadmap items /
follow-ups): changed-direct-dep removal, the subset-importer orphan calculation,
and the `pnpm:stats` `removed` count + `pnpm:removal` debug channel.

## Tests

- Unit: sweep (keeps needed + `lock.yaml`; removes surplus dir and skipped
  snapshot; missing-dir no-op) and throttle (both directions; empty/unparseable/
  future timestamps; global-store exclusion).
- Integration: a first install with a pre-seeded surplus `.pacquet` dir asserts the
  dir is swept while the installed package's slot survives. Confirmed it fails when
  the wiring is disabled.

Refs #11633 (Rust roadmap, Stage 1 Tier 4).

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-16 22:30:34 +02:00
Allan Kimmer Jensen
dcededcd27 feat(sbom): mark dev-only components with CycloneDX scope "excluded" (#12442)
Components reachable only through devDependencies now get
`scope: "excluded"` plus the `cdx:npm:package:development` property in
CycloneDX output. The `excluded` scope documents non-runtime/test usage
(valid in every exported spec version, 1.5/1.6/1.7); the property is the
CycloneDX npm-taxonomy marker emitted by `@cyclonedx/cyclonedx-npm`, so
both modern and existing consumers are covered. Runtime-reachable
components (ProdOnly, DevAndProd, and installed optionalDependencies)
omit both and default to `required`.
2026-06-16 20:29:31 +02:00
minbang
3188ae7026 fix: accept loose peer ranges in peers check (#12150)
Run peer dependency range checks with semver loose parsing while preserving
prerelease inclusion.

This prevents pnpm peers check from reporting react-native-reanimated@4.4.0 as
unmet for ranges like >=3.16.0 || >=4.0.0-.

Add a lockfile regression fixture for #12149 and a patch changeset for the peer
checker and pnpm CLI.

Closes #12149.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-06-16 20:20:53 +02:00
Felipe Santos
13815ade71 fix: warnings in store|config should be printed to stderr (#12434)
* fix: warnings in `store|config` should be printed to `stderr`

* refactor: extract stderr-reporter command list into a named set

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-16 19:16:50 +02:00
Ruben Nogueira
9e0c375dcb fix: record the global virtual store dir in .modules.yaml during the post-install build step (#12308)
* fix: invalid gvs workspace

* test: cover GVS workspace virtual-store-dir + add changeset

Add an e2e regression test for the post-install build step preserving the
global virtual store directory in a workspace package's .modules.yaml, and
the missing changeset for the fix (pnpm/pnpm#12307).

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-16 19:13:40 +02:00