Commit Graph
12566 Commits
Author SHA1 Message Date
Zoltan Kochanandgithub-actions[bot] 4c85370bf7 chore: update dependencies, Node.js, pnpm, and GitHub Actions (#13515)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-31 01:24:21 +02:00
Zoltan Kochanandgithub-actions[bot] 6c0b2ed636 chore(release): pacquet 12.0.0-beta.2 (#13514)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
v12.0.0-beta.2
2026-07-31 00:50:05 +02:00
Zoltan Kochan 53ba59e882 feat(napi): expose readConfig for embedders (#13513)
Bit parses .npmrc through @pnpm/config.reader only to hand the result
back to the engine, and that JS package chain is what keeps Bit's
registry mirror problems alive (its mirror caps @pnpm/config.env-replace
below the version config.reader needs). The engine already resolves the
full config cascade for its own installs; readConfig projects the
subset an embedder consumes: registries with resolved Authorization
headers, authHeaderByUri, proxy, TLS, network limits, directories, and
install behavior settings.
2026-07-31 00:43:23 +02:00
Zoltan Kochan e62e24beef fix(pacquet): ignore a broken wanted lockfile on regular installs (#13511)
A pnpm-lock.yaml that fails to parse aborted every install flavor,
matching upstream only for --frozen-lockfile. A regular install now
warns ("Ignoring broken lockfile at ..."), resolves from the
manifests, and rewrites the file - the lockfile is regenerable state,
and failing left no way to install at all. Found via Bit: a broken
lockfile aborted the install after node_modules had already been
purged for layout drift, leaving the workspace with neither packages
nor a usable lockfile.

Ports deps-installer/test/lockfile.ts:1288 ("a lockfile with duplicate
keys is fixed") as the regression test.
2026-07-30 23:33:35 +02:00
Zoltan Kochan 59554bef71 fix(deps-resolver): don't download unsupported-platform optional deps on fresh install (#13510)
On a fresh install, extract_children collected a resolved package's
dependencies and optionalDependencies without deduplicating. npm merges
optionalDependencies into dependencies at publish time, so registry
manifests list every platform-specific optional dependency in both maps,
and each got two resolve edges: one optional, one non-optional. The
non-optional edge bypassed the platform gate in
PrefetchingResolver::should_skip_prefetch (it only applies to optional
edges), so fresh installs prefetched the tarball of every platform
variant, e.g. all seven `@typescript/native-preview-*` packages on a
linux-x64 host. The lockfile came out correct regardless; only the
downloads were wasted.

Fold the duplicate into one optional edge keeping the dependencies
range, mirroring the TypeScript resolver's
{...optionalDependencies, ...dependencies} merge in
getNonDevWantedDependencies. The TypeScript CLI is unaffected: its
object-spread merge already collapses the duplicate key, confirmed by
byte-identical lockfiles between the two stacks on the repro.
2026-07-30 22:33:29 +02:00
Zoltan Kochanandgithub-actions[bot] 13f5820de9 chore: update dependencies, Node.js, pnpm, and GitHub Actions (#13480)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-30 21:27:43 +02:00
Zoltan KochanandClaude Fable 5 078d4be193 ci: let update-lockfile self-update pnpm to same-day releases (#13509)
Bump pnpm/update to the release that adds the
update-pnpm-minimum-release-age input, and set it to 0. pnpm 12
defaults minimumReleaseAge to 24 hours and self-update deliberately
ignores the repo's minimumReleaseAgeExclude, so the job could never
bump the pin to a pnpm release published the same day: the next-12
dist-tag silently resolved to the newest mature version instead.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:20:35 +02:00
Zoltan Kochanandgithub-actions[bot] 866d6175bd chore(release): pacquet 12.0.0-beta.1 (#13508)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
v12.0.0-beta.1
2026-07-30 20:16:09 +02:00
Zoltan KochanandClaude Fable 5 cbf8fbae0d fix(scripts): accept the leading -- forwarded by pnpm run bump (#13507)
pnpm run forwards everything after the script name to the script verbatim,
including the -- separator itself, so the create-release-pr workflow's
`pnpm run bump -- --release <product>` invocations delivered a literal --
as the first argument and the fail-closed parser rejected it.

parseSelectedProducts now skips a single leading --; a -- in any other
position, and any other unrecognized token, still fails closed.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 20:14:42 +02:00
Alessio AttilioandZoltan Kochan b287582858 fix(git-resolver): pass GIT_TERMINAL_PROMPT=0 to prevent interactive prompts in git ls-remote (#13471)
The git ls-remote command spawned by the GitHub Actions checker now includes GIT_TERMINAL_PROMPT=0 in its environment. This ensures that the command fails immediately instead of blocking the user on an interactive prompt when the repository is private and the user lacks HTTPS credentials. Closes pnpm/pnpm#13421

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-07-30 20:13:51 +02:00
Abdullah Alaqeel 9a4f51c6fa fix(install-summary): suppress '(X is available)' when latest is held back by minimumReleaseAge (#13214)
fix(install-summary): suppress '(X is available)' when latest is held back by minimumReleaseAge

When minimumReleaseAge (default: 24h) left the registry's
dist-tags.latest immature, the install summary still printed
'(X is available)' for it — advertising the exact version the policy
had just refused to install.

The hint only ever names the actual latest tag: the resolvers now
surface dist-tags.latest on the resolve result only when the active
policy would allow installing it (latest_allowed_by_policy /
latestAllowedByPolicy — an O(1) check of the tag's publish timestamp
against the cutoff, honoring publishedByExclude full-name and exact-
version entries). An immature latest suppresses the hint instead of
being rewritten to an older mature version, so the hint never names a
non-latest version as latest. Suppression requires positive evidence
of immaturity: a missing or unparsable timestamp keeps the raw tag,
matching the pick itself, which only enforces the policy when the
packument's time map is usable.

Also fixes a pre-existing divergence in the pacquet reporter: it gated
the hint on 'latest != version' rather than 'latest > version', so it
would suggest downgrading when the installed version was newer than
the latest tag. Now uses node-semver comparison, matching the
TypeScript reporter's semver.lt check.

Closes pnpm/pnpm#11698.
2026-07-30 20:00:23 +02:00
Zoltan Kochan 3eec70fdea perf(resolver): cut large-workspace peer resolution roughly in half (#13506)
An importer whose required-peer round converged skips re-discovery
until its inputs change; later rounds walk only newly added direct
deps (the full direct set still defines the provider context, and
earlier subtrees' scope-filtered missing reports replay from an
accumulator); a children-ownership handover with an unchanged
peer-shadowed context no longer flips sibling occurrences lazy or
bumps the rewrite counter; and resolver-internal maps use rustc-hash.

Peers provided by multiple candidate versions may bind to a different
(still range-valid) provider than before; output stays deterministic.
Measured on a 114-importer workspace: resolution 77s -> 36s, discovery
walks 802 -> ~400, node visits 2.6M -> ~1.0M.
Profile and remaining work: https://github.com/pnpm/pnpm/issues/13505
2026-07-30 19:47:43 +02:00
Zoltan Kochan 2a40b48dab fix(resolver): stop replaying cached subtrees' peer providers across importers (#13502)
The peers-cache hit path replayed the cached walk's
auto_install_resolved_peers, so a subtree first resolved under one
importer handed the peer providers it bound to every importer sharing
it. Combined with the owner-scope miss suppression, the sharing
importer neither hoisted the peer from the workspace root nor bound it
in its own context — it inherited the owner context's provider, even
one the consumer's declared range rejects. pnpm's resolver gives a
not-new package resolvedPeers: {} (resolveDependencies.ts), so only
the walk that first resolves a subtree promotes its providers; match
that by replaying no providers on a cache hit.

In a bit.cloud workspace with 114 importers this halved peer-variant
fanout: 25,534 -> 20,791 snapshots (TypeScript CLI: 20,219), with
`@testing-library/react@13.4.0`-suffixed variants dropping from 535 to
43 (TypeScript: 41).
2026-07-30 17:11:36 +02:00
Zoltan Kochan 18e5e4c42d fix(lockfile): emit over-long dependency-path keys in explicit form (#13500)
YAML caps a simple key at 1024 characters (the ':' must appear within
that lookahead), and the parser enforces it — but the emitter wrote
peer-suffixed snapshot keys of any length inline. A large workspace
whose dep paths exceed the limit got a lockfile the engine could not
re-read, so every subsequent install silently fell back to a full
re-link. Port js-yaml's explicit-pair rule: keys whose rendered form
exceeds 1024 characters are emitted as '? <key>' with the value on the
following ': ' line, which round-trips and matches what the TypeScript
CLI writes.
2026-07-30 14:57:00 +02:00
Zoltan KochanandClaude Fable 5 9e26fcb0be fix(resolver): let the dependencies range win over devDependencies (#13501)
importer_direct_wanted_specs merged the manifest's dependency groups
with devDependencies overriding dependencies, while the TypeScript
CLI's filterDependenciesByType spreads {...dev, ...prod, ...optional}.
For a name declared in both groups pacquet resolved the dev range but
recorded the importer entry under dependencies with the prod
specifier, producing a lockfile that fails its own up-to-date check
(version doesn't satisfy range) and re-resolves on every install.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 14:47:38 +02:00
Zoltan Kochan 5b063b9e32 docs(contributing): address duplicate and unvetted AI-assisted PRs (#13499)
Strengthen the duplicate-PR guidance to point at the issue's
automatically linked PRs, add an AI-assisted contributions section
(agents are welcome, but contributors own the output — check linked
PRs, understand the change, run the tests, keep the diff focused,
disclose agent authorship with the standard footer), and document that
human review starts only after CodeRabbit approves and CI is green.
CodeRabbit is the single required AI approval — Qodo ignores
Dependabot/Renovate PRs, so a two-reviewer gate could never be
satisfied for dependency updates; the PR template is aligned
accordingly.
2026-07-30 13:48:58 +02:00
Zoltan Kochan b9603f69b4 docs(github): ask PR authors to confirm no linked PR solves the issue (#13498)
Multiple contributors keep submitting parallel PRs for the same issue
(e.g. pnpm/pnpm#13114 was superseded by pnpm/pnpm#13124 for issue
pnpm/pnpm#13108). GitHub already cross-links every PR that references
an issue on the issue's timeline, so add a checklist item prompting
authors to check those links before submitting.
2026-07-30 13:38:12 +02:00
Minha KangandZoltan Kochan be871d7340 fix(resolver): keep the explicit = operator when updating an exact pin (#13184)
pnpm update wrote the new version of an `=`-pinned dependency back as the
bare version, dropping the explicit operator (`=3.5.1` became `3.5.2`).
The two spellings are the same semver range, but the `=` form marks the
pin as deliberate, so the update should keep it.

The save-style enum (formerly PinnedVersion) gains an `exact` variant
and is renamed to RangeSpecStyle: it selects the operator a specifier is
saved with, not a pin granularity. inferRangeSpecStyle (formerly
whichVersionIsPinned) classifies a bare `=` before a full version as
`exact` (partial `=1.0` / `=1` keep pinning like the plain version they
prefix), and the specifier formatters emit `=` for it. A granularity
projection (rangeSpecGranularity / RangeSpecStyle::granularity) collapses
`exact` to `patch` for consumers that only care about range width, such
as the save-workspace-protocol: rolling mapping, where an `=` pin maps
to `workspace:*` like other exact pins. `@pnpm/types` keeps PinnedVersion
as a deprecated alias and stays declaration-only; the shared helpers
live in `@pnpm/pkg-manifest.utils`. save-prefix now accepts `=`, saving
new dependencies as `=x.y.z`; --save-exact still wins and saves the
bare version. Implemented in both the TypeScript CLI and the Rust port.

Closes pnpm/pnpm#13168

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-07-30 13:37:36 +02:00
c7bab86a22 fix(resolver): bound minimumReleaseAge latest fallback (#13044)
When minimumReleaseAge filtered the current latest target, the fallback scanned mature releases across every major. A chronologically older but SemVer-greater release could therefore replace the registry's authoritative latest selection.

Bound latest fallback candidates to versions at or below the original tag target in both resolver implementations. Leave the tag unset when no safe fallback exists, and keep pacquet's generic version-filter behavior unchanged for package-version guards.

Closes pnpm/pnpm#13034.

---------

Co-authored-by: ychampion <ychampion@users.noreply.github.com>
Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-07-30 13:26:31 +02:00
Zoltan Kochan b234e56853 fix(lockfile): parse lockfiles above the default YAML scalar budget (#13497)
The node/event budgets already scale with the document's byte length
(pnpm/pnpm#13485), but a ~100 MB lockfile still failed with "budget
breached: ScalarBytes" at serde-saphyr's 64 MiB scalar-text default.
Scale every size-proportional budget dimension (scalar bytes, comment
bytes, reader input bytes) the same way: none of them can exceed the
size of an input that is already in memory, so a valid lockfile can
never trip them. The remaining defaults (aliases, anchors, depth,
documents) bound YAML shapes the lockfile emitter never produces and
stay as security caps.

Add a regression test that parses a generated lockfile above the
64 MiB scalar budget.
2026-07-30 13:24:36 +02:00
Omri LuzonandZoltan Kochan d36e0fdb1a fix(deps-resolver): preserve workspace link: on re-resolution (fixes pnpm/pnpm#10433) (#13112)
A workspace dependency that deduped to `link:` on install could be rewritten to
a peer-suffixed `file:` by any run that re-resolves it without deduping back to
`link:` — e.g. `pnpm update <other-pkg>` (with or without --recursive), a
root/catalog bump, or a plain install that hits a genuine peer-context
divergence.

When finalizing importer refs, keep the previous `link:` for a workspace
dependency the run doesn't target: not new, specifier unchanged, and not matched
by `pnpm update <name>`. `updateSpec` is intentionally excluded from the
criterion because a plain install marks every manifest dependency with it. A
workspace `link:` dependency has no version to update, so a run that doesn't
target it must not flip it to `file:`; targeted dependencies still resolve
freely.

Adds regression tests for the update-recursive and plain-install paths (both
fail without the fix) and ports the guard to the Rust package-manager crate.

Completes the fix for pnpm/pnpm#10433 — the first mechanism was fixed in #12800.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-07-30 12:34:52 +02:00
Zoltan KochanandClaude Fable 5 2b23eebb85 ci: run all TS test jobs in parallel without a smoke gate (#13496)
The ubuntu Node 24 smoke job previously gated the Node 22/26 test runs,
delaying them by a full test cycle. Fold the Node 24 (garnet) leg into
the test matrix and gate every test job only on compile-and-lint plus
its platform's pnpr build, so all Node.js versions and all Windows
chunks start in parallel. The trade-off is that a broken build now
surfaces on all test jobs at once instead of costing a single smoke run.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 12:09:05 +02:00
zhangli091011andZoltan Kochan 2325c32c42 fix(lockfile): omit empty bundled dependencies (#13148)
Tarball manifests can contain an empty bundledDependencies array while registry
metadata for the same package omits the field. Treat empty arrays as absent so
lockfile generation does not depend on the manifest source used during
resolution.

Preserve nonempty arrays and true for both field spellings. Continue preferring
bundledDependencies when meaningful, but allow an empty value to fall back to
a meaningful bundleDependencies value.

Mirror the change in pacquet: BundledDependencies::from_manifest no longer
records an empty list and falls through to the legacy spelling instead.

Closes pnpm/pnpm#13123.
---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-07-30 12:08:31 +02:00
Neil de CarteretandZoltan Kochan 3729d83d4c fix: approve git-hosted tarball builds by repository url (#12985)
Normalizes git-hosted tarball dep paths back to the canonical
`name@git+https://host/org/repo.git` key that a clone of the same repository produces, so one
hashless entry approves the package whether pnpm clones it or downloads a tarball. GitHub
(codeload), GitLab archive, and Bitbucket download URLs are covered. The host is part of each
derived key, and the GitHub and Bitbucket download hosts are matched against a literal value
(GitLab's is captured generically to allow self-hosted instances), so a look-alike host cannot
be rewritten into an unrelated repository key. Approving or denying a specific resolved commit
by its full tarball dep path continues to work.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-07-30 12:07:02 +02:00
4d4736aa87 fix(projects-graph): resolve workspace deps declared with a relative path (#13189)
* fix(projects-graph): resolve workspace deps declared with a relative path

A dependency declared as `workspace:../some/path` was silently dropped
from the project graph. `workspacePrefToNpm` turns it into the bare
string `../some/path`, and because the registry argument is empty every
string satisfies the `startsWith(registry)` check, so `parseBareSpecifier`
tries to parse it as a tarball URL and throws. The surrounding
`catch { return '' }` swallowed the error and `filter(Boolean)` removed
the edge, so the dependency disappeared without any warning, affecting
`--filter` selection and recursive install ordering.

Fall back to resolving the un-prefixed spec as a directory dependency
via the existing `npa.resolve` path, the same way plain relative-path
deps are already handled. This matches the behaviour the Rust engine
already implements, which classifies a path-like workspace version as a
directory before attempting bare-specifier parsing.

* refactor(projects-graph): classify path-like workspace specs before parsing

Route a workspace spec with a relative path (workspace:./foo,
workspace:../foo) straight to directory resolution instead of relying on
parseBareSpecifier throwing and being caught. The try/catch remains only
as a backstop for other malformed specs. Behaviour is unchanged; this
addresses review feedback about using a thrown exception as control flow
for the common path-like case.

* chore: add changeset

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 09:32:21 +00:00
dependabot[bot] 8a4d60b9a5 chore(cargo): bump bytes from 1.12.0 to 1.12.1 (#13100)
Bumps [bytes](https://github.com/tokio-rs/bytes) from 1.12.0 to 1.12.1.
- [Release notes](https://github.com/tokio-rs/bytes/releases)
- [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/bytes/compare/v1.12.0...v1.12.1)

---
updated-dependencies:
- dependency-name: bytes
  dependency-version: 1.12.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-30 10:58:33 +02:00
Zoltan Kochan 0f1c46762e fix(napi): deterministic overrides, hook-free install options, and large-workspace install fixes (#13492)
The napi install options deserialized `overrides` into a HashMap, so
the JS object's key order was lost and pnpm-lock.yaml#overrides was
rewritten in a random order on every install driven through the addon
(the freshness comparison is order-insensitive, so this churned the
file without invalidating it). Deserialize into an IndexMap via napi's
object_indexmap feature - matching the order the TypeScript engine and
the pacquet CLI record - and collect the getPeerDependencyIssues JSON
path through the same order-preserving shape.

Also wire pacquet_diagnostics::enable_tracing_by_env into the addon's
module init so the TRACE env var works for napi consumers like it does
for the CLI, and log the staleness reason when a preferred-frozen
install falls through to a fresh resolve - both were needed to diagnose
a Bit workspace that re-resolved on every install.

Batch the readPackage hook dispatch (per-manifest threadsafe calls cost
roughly one event-loop tick each, serializing large resolutions), expose
dedupePeers in the install options (its absence made the freshness gate
treat every Bit lockfile as outdated), and try pick_package's read-only
mirror fast paths before the per-name fetch semaphore so version-pinned
picks stop queueing behind concurrent refreshes of the same package.

Resolve workspace importers concurrently - children-ownership claims
are rank-ordered, so the result is arrival-order independent - which
absorbs the JS hook round-trip latency on large workspaces.

Add projects[].dependencyManifest so hosts can express their readPackage
hook logic engine-side with zero JS round trips (per-manifest deletions
use the existing overrides removal syntax; neverBuiltDependencies stays
rejected in favor of allowBuilds), and apply overrides after the
readPackage hook to match the TypeScript engine's createReadPackageHook
order - a hook that replaced a manifest wholesale (raw-manifest
substitution for injected workspace instances) previously erased the
overrides from that manifest.

Stop repeating the release-age abbreviated->full metadata upgrade fetch
per dependency edge: coalesce it on a per-document permit and remember a
304 Not Modified for the rest of the install, and share the resolver's
workspace-packages map behind an Arc instead of deep-copying every
project manifest on each ResolveOptions clone. Full resolution of a
345-importer workspace: 105 s -> 36 s.

Cut the peer walker's per-node map churn: share ParentRefs behind Arc
copy-on-write, build the collision overlay lazily, and reuse the
per-child parent-package snapshots when the context is unchanged
(peer-heavy 331-importer benchmark: 3.95 s -> 2.78 s).

Share the run-resolved preferred-versions fold across importers: each
importer replayed the whole workspace history into a private map every
hoist round; the fold now lives once on the workspace context and the
hoist call sites materialize merged buckets for just the names they
query (full-workspace benchmark: 886 ms -> 424 ms).
2026-07-30 10:10:39 +02:00
Zoltan Kochan 91fb66344c perf(resolver): make multi-importer peer-hoist discovery linear (#13491)
The auto-install-peers hoist loop cloned the workspace tree and ran a
full peer pass - DependenciesGraph construction included - once per
importer per round, making a from-scratch resolve of a large component
workspace quadratic in both time and memory (the regression Bit hit
through the napi engine).

Replace the per-round snapshot + full pass with a PeerHoistDiscovery
engine shared by every hoist round of one workspace resolve:

- discovery walks share one persistent tree view, incrementally synced
  from the workspace context; a children-ownership handover that
  rewrites existing occurrence nodes (tracked by a rewrite counter), a
  re-recorded child list, or a changed peer split discards the whole
  view instead of merging;
- the walker's purePkgs / peersCache / parent-context maps persist
  across rounds and importers, so a subtree settled by one importer's
  pass short-circuits every compatible revisit - the same sharing the
  final resolve_peers_workspace pass already applies within one call;
- discovery skips graph construction entirely: the hoist loop only
  consumes missing peers and resolved providers, and each peers-cache
  item carries its subtree's per-package missing breakdown so the
  owner-scope report stays exact under cross-round cache hits.

Also drop the remaining per-importer O(workspace) costs in the initial
barrier: record_first_walk_missing no longer deep-clones the
children-owner map, the preferred-versions fold reads an append-only
resolved-versions log through a per-importer cursor, and the
owner-scope maps are snapshotted once per barrier. The CLI deploy
tests' stderr assertions are also made robust to miette's
path-length-dependent line wrapping.

cargo bench -p pacquet-resolving-deps-resolver --bench workspace_full_resolution
(331 importers, 5,000 shared packages, peer hoisting, no lockfile):
before 65.1s / 3.76 GiB peak RSS; after 1.55s / 1.14 GiB.
2026-07-29 17:06:51 +02:00
Zoltan Kochan 6eed03f88a fix(pacquet): align deprecated package reporting (#13490)
Pacquet re-emitted deprecated packages when the same package was encountered at a shallower depth. This could change a transitive warning into a direct warning and required Rust-only deduplication in the default reporter. Emit only when inserting a package into the shared resolved-package map, matching the TypeScript resolver's packageIsNew gate.

Thread the recursive command state into the default reporter so direct dependency warnings use pnpm's workspace-relative form and omit the deprecation reason during recursive installs. Remove reporter-side deduplication so event folding matches the TypeScript reporter.

Serialize package-manager environment resolution before reading or replacing its shared lockfile, acquiring blocking filesystem locks on Tokio’s blocking pool. Treat incomplete or broken cache entries as misses both before and after the engine-install lock so installation can recover safely.

Keep package statistics from prematurely flushing the pending frozen-install message, preserving the lockfile-policy verdict order while verification and materialization run concurrently.
2026-07-29 14:29:57 +02:00
Zoltan Kochan 44e0ebfb83 fix(napi): support trustLockfile option (#13489) 2026-07-29 13:20:56 +02:00
Zoltan Kochan 0ae2001072 fix(napi): respect in-memory manifest changes (#13488)
NAPI consumers provide authoritative project manifests in memory. The optimistic repeat-install path uses package.json mtimes as its freshness signal, so it could report an install as already up to date when only the supplied manifest changed.

Disable that shortcut for NAPI installs so the normal lockfile freshness checks compare the supplied manifests and materialize newly requested dependencies. Rebuilds retain the existing fast-path behavior.
2026-07-29 12:14:57 +02:00
Zoltan Kochan 54abb6a7f9 fix(lockfile): parse large lockfiles (#13485)
Pacquet parsed lockfiles with serde-saphyr's default structural budgets, which reject valid large lockfiles after 250,000 YAML nodes.

Set the event and node budgets to the greater of their defaults and the lockfile's byte length. This keeps the structural work allowance proportional to an input that is already in memory while retaining the parser's other resource limits.

Add a regression test that parses a generated lockfile above the previous node limit.

Fixes pnpm/pnpm#12857.
2026-07-29 12:12:41 +02:00
John-David Dalton b9e7b8d88d feat(login): support web login without an interactive terminal (#13479)
pnpm login refused to run whenever stdin or stdout was not a TTY, even
though the registry web-auth flow only prints an authentication URL and
polls the done endpoint until the browser approval completes - neither
needs a terminal. Agent- and CI-adjacent tooling had to wrap pnpm in a
pseudo-terminal (script -q /dev/null pnpm login) to use the web flow.

Move the non-interactive guard from the top of the login command into
the classic username/password fallback, the only path that prompts on
the terminal. Without a TTY the web flow now prints the authentication
URL and polls as before; the URL is printed without the QR code (a
piped stdout cannot render the block art), and the press-ENTER browser
prompt was already skipped for a non-TTY stdin. A registry without web
login support still fails with ERR_PNPM_LOGIN_NON_INTERACTIVE.

Harden the TypeScript web-login path to match pacquet while touching
it: narrow the attacker-controlled response body at runtime (a missing,
empty, or non-string loginUrl/doneUrl is an invalid response), and
reject URLs containing Unicode control characters with pacquet's
ERR_PNPM_AUTH_COMMANDS_LOGIN_UNSAFE_URL before anything is printed or
polled. The shared error message now says "authentication URL" in both
stacks, since the check covers loginUrl and doneUrl alike.

Implemented in both stacks: the TypeScript CLI moves the guard into
classicLogin and prints a URL-only message via the new
formatAuthUrlOnlyMessage export of the web-auth package; pacquet moves
the same guard into classic_login and selects AuthUrlMessage::UrlOnly
when stdout is not a TTY. The pacquet CLI adapter unit test and the
CLI-tier integration tests now drive the guard through a 404 web-login
probe so it exercises the classic fallback, and a new integration test
covers the headless web flow end-to-end against a mock registry.

Also acknowledge a pre-existing zizmor ref-version-mismatch finding on
the winget-releaser pin in update-latest.yml with the repository's
usual inline ignore: the pinned commit is no longer reachable from any
named ref upstream, so no version comment can describe it accurately.
2026-07-29 12:12:10 +02:00
Zoltan Kochan 95b1fd1ab2 chore: update pnpm to 12 beta 0 (#13486) 2026-07-29 11:40:18 +02:00
Zoltan Kochan 02f8807f56 fix(release): resume pacquet beta publish (#13483)
The first pnpm 12 beta release used pnpm 12.0.0-alpha.21 to publish a wrapper whose workspace name is `pacquet`. That version predates `publishConfig.name`, so npm trusted publishing was attempted for `pacquet` instead of `pnpm` and the root package failed after all native packages had already been published.

Use pnpm 11.18.0, the released TypeScript CLI that supports `publishConfig.name`, for release tooling. Update every `pnpm/setup` consumer to the revision that can install v11 from GitHub release archives. Make the Rust publishing loop query each effective published name and skip versions already on npm, while preserving hard failures for registry errors other than 404. This allows a moved beta tag to resume the partial release and reach the dependent GitHub release job.
v12.0.0-beta.0
2026-07-29 10:54:15 +02:00
Zoltan Kochanandgithub-actions[bot] 925c33d780 chore(release): 11.18.0, pacquet 12.0.0-beta.0 (#13481)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
v11.18.0
2026-07-29 09:29:53 +02:00
Zoltan Kochan 627476dd51 perf: reuse lockfile for compatible dependency ranges (#13477)
Direct dependency and catalog range edits currently force the dependency graph
to be resolved even when every locked version remains valid.

Add transactional fast paths that rewrite only compatible importer specifiers
or catalog snapshots, then validate importer state and lockfile resolutions
before committing them. Stale catalog snapshots may be removed when no
importer references them. Ambiguous or resolution-sensitive cases continue
through the normal resolver.

Implement the same conservative rules in the TypeScript CLI and Rust pnpm
implementation. Tests cover compatible and incompatible direct and catalog
ranges, stale and missing snapshots, simultaneous settings changes, atomic
rollback, malformed versions, peer-bearing packages, and operation while the
registry is unavailable.

Related to pnpm/pnpm#13474.
2026-07-29 09:25:13 +02:00
Zoltan Kochan 3e6f9306f7 test: make override status fixture platform-independent (#13476)
Make the registry and link override status test independent of whether
platform-specific timestamp and path handling reaches lockfile validation.
Keep the mocked workspace state, lockfiles, and manifest mutually consistent
so the test isolates local-file override classification.

Related to pnpm/pnpm#13464.
2026-07-29 00:54:40 +02:00
Zoltan Kochan 2b03ceaba5 perf: reuse lockfile entries for safe override updates (#13464)
An overrides mismatch previously forced a complete dependency graph
resolution even when the lockfile could be updated safely in place.

Add conservative fast paths to both pnpm implementations for exact registry
version replacements and dependency-removal overrides. Replacements resolve
only their package metadata, retain satisfying child resolutions, prune dropped
edges, and attach added edges only when one safe compatible snapshot is already
locked. Independent replacements and removals can be processed together, and
parent-scoped removals match only the selected locked parent snapshots.

Fall back to the full resolver for peer-sensitive, ambiguous, patched, aliased,
exotic, policy-violating, or custom-hook cases. Remove obsolete virtual-store child links
while materializing the rewritten lockfile.
2026-07-28 23:51:22 +02:00
Zoltan Kochan 0457ad9a98 chore(release): move pacquet to the beta lane (#13458) 2026-07-28 22:33:29 +02:00
Zoltan Kochan b2f42eee2c fix(pacquet): preserve locked peer contexts during dedupe (#13473)
Scope importer-hoisted optional peers to each reused direct dependency's
locked peer suffix. This preserves distinct peer contexts for dependencies
that share an importer without preventing optional-peer deduplication.

Track the hoisted providers per importer so a shared leaf NodeId cannot affect
an unrelated workspace project.

Related to pnpm/pnpm#13305.
2026-07-28 22:31:05 +02:00
Zoltan Kochan 1e6f6f4ae7 fix(pacquet): align remaining parity output (#13472)
Finish the strict output-parity work for pacquet's license, dedupe, and
pre-run verification commands.

Use pnpm-compatible package-name collation and keep license versions aligned
with their single representative paths. Teach dedupe progress to account for
the store and reusable skipped optional subdependencies, restore the added
counter, and preserve pnpm's report spacing. Buffer the frozen-lockfile status
until the lockfile-policy verdict and render zero-change stats as already up to
date.

Closes pnpm/pnpm#13457.
2026-07-28 18:21:58 +02:00
Zoltan Kochan 539c83ce56 fix(pacquet): preserve lockfile before scripts (#13469)
Keep verifier-triggered installs on the normal prefer-frozen dispatch path. A regenerated lockfile that is fresh for the current configuration can then be materialized without re-resolving and rewriting its peer graph.

Continue bypassing both optimistic and frozen repeat-install exits for this mode so root lifecycle scripts still run before the requested command.

Fixes pnpm/pnpm#13466.
2026-07-28 14:48:17 +02:00
Zoltan Kochan 09b7bbecb0 fix(catalogs): preserve valid resolutions during dedupe (#13468)
Keep catalog snapshots available while dedupe rebuilds importer and package dependency edges. The resolver validates that the recorded catalog specifier still matches before reusing its version, so changed catalog ranges continue to re-resolve while valid pins remain stable.

Closes pnpm/pnpm#13465.
2026-07-28 14:33:34 +02:00
Zoltan Kochanandgithub-actions[bot] e7ec77654a chore: update dependencies, Node.js, pnpm, and GitHub Actions (#13413)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-28 14:07:17 +02:00
Zoltan Kochan 7d4d91cfaf fix(pacquet): verify dependencies before run (#13463)
The Rust lockfile-only dedupe path bypassed both ordinary resolution progress and pnpm-style error reporting. Attach a lightweight resolution observer to emit resolved-package progress, then report check failures as a structured pnpm event that each reporter can handle appropriately.

This makes default, NDJSON, and silent output match the TypeScript CLI while retaining the existing exit status and leaving the lockfile untouched.

Related to item 2 of pnpm/pnpm#13457.
2026-07-28 12:01:50 +02:00
Zoltan Kochan 7fd21977a1 fix(cli): align dedupe check reporting (#13462)
The Rust lockfile-only dedupe path bypassed both ordinary resolution progress and pnpm-style error reporting. Attach a lightweight resolution observer to emit resolved-package progress, then report check failures as a structured pnpm event that each reporter can handle appropriately.

This makes default, NDJSON, and silent output match the TypeScript CLI while retaining the existing exit status and leaving the lockfile untouched.

Related to item 2 of pnpm/pnpm#13457.
2026-07-28 12:01:20 +02:00
Zoltan Kochan d472ffb4d5 fix(pacquet): align license report metadata (#13461)
Normalize author strings and derive repository-based homepages when pacquet
builds license reports. Preserve the license-group insertion order produced by
the TypeScript CLI's package-first traversal.

Addresses item 1 of pnpm/pnpm#13457.
2026-07-28 11:59:51 +02:00
Zoltan Kochan 982116209f fix(cli): print filtered script command (#13460)
Pacquet's recursive runner hardcoded every script execution as silent, so a
filtered run inherited the script output but omitted the preceding `$ <script>`
line. Pass the reporter's silent state through the recursive execution path so
normal filtered runs echo the command while `--silent` continues to suppress it.

The TypeScript CLI already has the intended behavior, so no TypeScript change is
needed.

Related to pnpm/pnpm#13457, item 3.
2026-07-28 11:59:22 +02:00
Zoltan Kochan 3563b1320e fix(pacquet): preserve locked peers during dedupe (#13459)
Pacquet's cross-importer missing-peer suppression treated all preferred
versions alike. During lockfile re-resolution, this could suppress optional
peer providers that were already encoded in peer suffixes in the wanted
lockfile.

Derive the names of locked peer providers from wanted-lockfile snapshot
suffixes and exempt those names from cross-importer suppression. This keeps
importer-local peer contexts stable during dedupe and aligns the affected n8n
jsdom and vitest variants with the TypeScript CLI.

Related to pnpm/pnpm#13305.
2026-07-28 10:38:59 +02:00