Skip to content

Update npm-registry-client to version 7.3.0 🚀#437

Merged
zkochan merged 1 commit into
masterfrom
greenkeeper-npm-registry-client-7.3.0
Oct 20, 2016
Merged

Update npm-registry-client to version 7.3.0 🚀#437
zkochan merged 1 commit into
masterfrom
greenkeeper-npm-registry-client-7.3.0

Conversation

@greenkeeperio-bot

Copy link
Copy Markdown
Contributor

Hello lovely humans,

npm-registry-client just published its new version 7.3.0.

State Update 🚀
Dependency npm-registry-client
New version 7.3.0
Type dependency

This version is not covered by your current version range.

Without accepting this pull request your project will work just like it did before. There might be a bunch of new features, fixes and perf improvements that the maintainers worked on for you though.

I recommend you look into these changes and try to get onto the latest version of npm-registry-client.
Given that you have a decent test suite, a passing build is a strong indicator that you can take advantage of these changes by merging the proposed change into your project. Otherwise this branch is a great starting point for you to work on the update.

Do you have any ideas how I could improve these pull requests? Did I report anything you think isn’t right?
Are you unsure about how things are supposed to work?

There is a collection of frequently asked questions and while I’m just a bot, there is a group of people who are happy to teach me new things. Let them know.

Good luck with your project ✨

You rock!

🌴


This pull request was created by greenkeeper.io.

Tired of seeing this sponsor message? ⚡ greenkeeper upgrade

@zkochan zkochan merged commit 6eb4eb7 into master Oct 20, 2016
@zkochan zkochan deleted the greenkeeper-npm-registry-client-7.3.0 branch October 20, 2016 22:05
pull Bot pushed a commit to dwongdev/pnpm that referenced this pull request May 14, 2026
…ce A) (pnpm#457)

## Summary

Slice A of pnpm#437. Adds the lockfile types pnpm v11 writes for runtime dependencies (`node@runtime:`, `deno@runtime:`, `bun@runtime:`); the install pipeline doesn't dispatch them yet (subsequent slices). No new workspace deps.

- **`BinaryResolution { url, integrity, bin, archive, prefix? }`** — mirrors upstream's [`BinaryResolution`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L41-L49). `bin` is `BinarySpec::Single(String) | BinarySpec::Map(BTreeMap)` (untagged); `archive` is the lowercase `BinaryArchive::Tarball | BinaryArchive::Zip` discriminator; `prefix` (only set on the `.zip` branch upstream) skips serialization when `None`.
- **`PlatformAssetTarget { os, cpu, libc? }`** + **`PlatformAssetResolution { resolution, targets }`** per [`resolver-base`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L60-L69). `libc` is `Option<String>` (not an enum) so an upstream addition beyond the current `musl` value lands without a serde migration.
- **`VariationsResolution { variants }`** + the `LockfileResolution`/`TaggedResolution` arms routing both new shapes through the existing `from/into ResolutionSerde` round-trip.
- **Lockfile major stays at 9** — confirmed against [`core/constants/src/index.ts`](https://github.com/pnpm/pnpm/blob/94240bc046/core/constants/src/index.ts) (`LOCKFILE_MAJOR_VERSION = '9'`), so the existing `lockfile_version.major == 9` assertion holds for v11 lockfiles carrying runtime entries.

**Install-dispatch stubs.** `install_package_by_snapshot.rs` and `create_virtual_store.rs` raise `InstallPackageBySnapshotError::UnsupportedResolution { kind: "binary" | "variations" }` until Slice D wires the fetcher. Adding these arms keeps the workspace compiling without forcing the full install path to land in this slice. The warm-prefetch path returns `Ok(None)` for `Variations`, routing through the cold dispatcher where the unsupported-kind error fires; upstream unwraps `Variations` before this layer ever sees one, so the branch is unreachable on well-formed input.
pull Bot pushed a commit to dwongdev/pnpm that referenced this pull request May 14, 2026
 slice B) (pnpm#466)

* feat(lockfile): select_platform_variant + PlatformSelector (pnpm#437 slice B)

Add the variant-picking logic for `VariationsResolution` ahead of
Slice D's install-pipeline dispatch:

- `PlatformSelector { os, cpu, libc: Option<String> }` mirrors
  upstream's [`PlatformSelector`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L78-L83).
  The `libc` tri-state encodes pnpm's `string | null | undefined`
  shape: `None` (host doesn't care about libc — macOS/Windows/BSD)
  and `Some("glibc")` collapse to the same matching arm (variant
  must have no `libc:` annotation); `Some("musl")` requires an exact
  `libc: "musl"` annotation so the glibc default doesn't silently
  win on a musl host.

- `select_platform_variant(variants, selector)` ports
  [`selectPlatformVariant`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L92-L98).
  Iterates `variants` in declaration order and returns the first
  variant whose `targets[]` contains an `(os, cpu, libc)` triple
  matching the selector. Targets-array scan is linear because real
  archives ship 1–3 target entries per variant; quadratic cost is
  immaterial.

- `libc_matches(variant_libc, requested_libc)` is the asymmetric
  helper from
  [`libcMatches`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L100-L107).
  Crate-private so the matching policy isn't part of the public API.

Lives in `pacquet-lockfile` next to the variant types (mirrors
upstream's `resolver-base` package boundary). The host-platform side
(constructing the `PlatformSelector` from `pacquet-graph-hasher`'s
existing `host_platform/arch/libc` helpers) lands at the install
dispatcher in Slice D — keeps `pacquet-lockfile` free of the
`graph-hasher` dep and lets tests drive the picker with synthetic
hosts.

Tests: first-match wins, multi-target variant matches any host triple,
no-match returns `None`, musl-host rejects glibc-default variant,
musl-host matches musl-annotated variant, and a six-row `libc_matches`
truth table (None / "glibc" / "musl" / unknown-future-libc on both
sides) pinning the asymmetric contract from upstream.

Part of pnpm#437.

---
Written by an agent (Claude Code, claude-opus-4-7).

* docs: refresh stale references + add declaration-order tie-breaker test

Two follow-ups from Copilot review on PR pnpm#466:

- `resolution.rs:152-154` claimed "the variant picker checks at
  runtime that the resolved inner is atomic", but
  `select_platform_variant` does not. Reword to describe the actual
  contract: pacquet's `PlatformAssetResolution.resolution` is
  typed as the full `LockfileResolution` for serde uniformity, and
  the lockfile is trusted to honor upstream's atomic-inner
  invariant. No infinite-recursion risk because the install
  dispatcher doesn't call back into `select_platform_variant` for
  non-`Variations` inputs.

- `resolution.rs:169` referenced `pick_variant` in
  `pacquet-package-manager`, but the picker actually lives in this
  module as `select_platform_variant`. Updated the pointer.

- `pick_returns_first_when_multiple_variants_match` test: two
  variants both list the same `(darwin, arm64)` target; the test
  asserts the first one wins, pinning the `Array.prototype.find`
  semantics. Pnpm-written lockfiles can rely on declaration order
  (e.g., listing a preferred build before a fallback) — without
  this test, a future refactor that switched the iteration to a
  triple-keyed `BTreeMap` would silently break that.

No behavior change; doc-comment text + test addition.

---
Written by an agent (Claude Code, claude-opus-4-7).
pull Bot pushed a commit to dwongdev/pnpm that referenced this pull request May 14, 2026
…pm#472)

Slice C of [pnpm#437](pnpm/pacquet#437) — the zip half of pnpm's binary fetcher. Slices A (lockfile types) and B (variant picker) have landed; this slice adds the per-archive download and CAS-extraction pipeline. The install-dispatch site (slice D) and the `--no-runtime` flag (slice E) will follow.

Mirrors upstream's [`downloadAndUnpackZip` / `extractZipToTarget`](https://github.com/pnpm/pnpm/blob/94240bc046/fetching/binary-fetcher/src/index.ts):

- `extract_zip_entries` walks every entry. Path validation runs *before* the `is_dir()` early-skip so directory entries like `../evil/` still surface `PATH_TRAVERSAL`. The canonical `cas_paths` / `pkg_files_idx` key is built from `enclosed_name()` rebuilt as `Normal` components joined with `/` — `.` segments collapse, separators are cross-platform consistent, and the ignore filter sees the same string the map is keyed by.
- `DownloadZipArchiveToStore` is the public sibling of `DownloadTarballToStore` — same store-index lookup, prefetch cache reuse, store-index writer queue. The retry policy, network-permit shape, and post-download semaphore gating exactly match the tarball path.
- `ignore_file_pattern` is `Option<Arc<IgnoreEntryFilter>>` on both `DownloadTarballToStore` and `DownloadZipArchiveToStore`, so the Slice D dispatcher can construct a filter per fetch from runtime config without pinning to `'static`. Cloned per retry attempt; the inner trait object is shared.
- `run_with_mem_cache` doc-blocks the "stable filter per URL" invariant: the cache keys solely on `package_url` (matching pnpm's `tarballCache`), and callers keep the (URL, filter) relation functional. Upstream's `archiveFilters` is keyed by `pkg.name`, and tarball URLs encode `(name, version, integrity)`, so this naturally holds.
- New `TarballError::ReadZipEntries(std::io::Error)` for zip per-entry I/O failures (`try_reserve` / `read_to_end`); maps to `ERR_PACQUET_ZIP` in the retry classifier, distinct from the tar-specific `ERR_PACQUET_TARBALL_TAR`. Zip `unix_mode()` is masked to `0o777` before write so `CafsFileInfo.mode` stays permission-only (matches `store_dir::add_files_from_dir::file_mode_from`). `TarballError::TarballTooLarge` display generalized from "Tarball at …" to "Archive at …" so the zip pipeline's OOM path doesn't surface a misleading message.
- Adds `zip = "5", default-features = false, features = ["deflate"]` to the workspace; reused in `pacquet-tarball`.
pull Bot pushed a commit to dwongdev/pnpm that referenced this pull request May 14, 2026
…pnpm#437 slice D1) (pnpm#492)

Replace the cold-path `UnsupportedResolution` arms for
`LockfileResolution::{Binary, Variations}` with actual fetcher
dispatch. Slice C wired the per-archive download
(`DownloadTarballToStore` / `DownloadZipArchiveToStore`); this
slice routes lockfile resolutions through them.

- `Binary(b)` dispatches on `b.archive` (`Tarball` / `Zip`):
  - `Tarball` → `DownloadTarballToStore` (same shape as
    registry / tarball entries minus the `package_unpacked_size`
    hint).
  - `Zip` → `DownloadZipArchiveToStore` with
    `archive_prefix: b.prefix.as_deref()` so the runtime
    archive's top-level wrapper (e.g. `node-vX.Y.Z-darwin-arm64/`)
    is stripped before the CAS keys are written.
- `Variations(v)` runs `select_platform_variant` against a
  freshly-built `PlatformSelector` (`pacquet_graph_hasher`'s
  `host_platform` / `host_arch` / `host_libc`, mapping
  `"unknown"` → `None` per upstream's
  `process.platform === 'linux' ? family : null` convention).
  The picked variant's inner resolution must be `Binary`; any
  other shape (corrupt lockfile or a future shape pacquet hasn't
  learned about) raises the typed
  `VariantHasNonBinaryResolution` error rather than silently
  routing.
- `create_virtual_store::snapshot_cache_key` returns the proper
  warm-cache key for both new arms: `Binary` uses
  `store_index_key(integrity, pkg_id)`; `Variations` runs the
  same selector and keys off the picked variant. A miss on
  variant selection or a non-`Binary` inner shape returns
  `Ok(None)` and lets the cold path raise the typed error.
- Two new error variants on `InstallPackageBySnapshotError`:
  - `NoMatchingPlatformVariant { package_key, host_os, host_cpu,
    host_libc, available_targets }` — fires when no variant
    matches the host; carries the host triple + the rendered
    available target list so the user can see at a glance why.
  - `VariantHasNonBinaryResolution { package_key, inner_kind }`
    — defensive guard for malformed lockfiles.

The Node-runtime `NODE_EXTRAS_IGNORE_PATTERN` filter (strips
bundled `npm` / `corepack` from the archive) and bin-link
cmd-shims for runtime executables will land in Slice D2; the
filter slot stays `None` for now and `BinaryResolution::bin` is
read but not yet acted on. Slice E adds `--no-runtime` and
Slice F adds the end-to-end install fixtures.

Two new unit tests cover the helpers:
- `host_platform_selector_omits_libc_on_non_linux_hosts` —
  pins the `host_libc() == "unknown"` ⇔ `selector.libc.is_none()`
  relationship without needing platform-gated tests.
- `render_variant_targets_formats_each_triple_with_optional_libc`
  — locks in the `os/cpu[+libc]` rendering used by the
  `NoMatchingPlatformVariant` error message.
pull Bot pushed a commit to dwongdev/pnpm that referenced this pull request May 14, 2026
…pnpm#437 slice D2) (pnpm#496)

Construct the per-archive ignore filter at the install dispatcher.
For unscoped `node` the filter matches upstream's
[`NODE_EXTRAS_IGNORE_PATTERN`](https://github.com/pnpm/pnpm/blob/94240bc046/engine/runtime/node-resolver/src/index.ts),
which strips bundled `npm` / `corepack` from the Node.js runtime
archive during the CAS write — pnpm (and pacquet) install pnpm
itself as the package manager, so the bundled tooling is dead
weight and would shadow the user's pnpm via `node_modules/.bin/`.

Wiring matches upstream's
[`archiveFilters: { node: NODE_EXTRAS_IGNORE_PATTERN }`](https://github.com/pnpm/pnpm/blob/94240bc046/installing/client/src/index.ts):
the per-package-name table is keyed by `pkg.name`, so `@foo/node`
and other packages keep `None` and the full archive lands
unfiltered.

Pacquet uses a hand-coded matcher rather than the upstream regex
so `pacquet-tarball` doesn't have to pull in a regex engine. The
three branches mirror the regex alternation exactly:

1. `^(?:lib/)?node_modules/(?:npm|corepack)(?:/|$)`
2. `^bin/(?:npm|npx|corepack)$`
3. `^(?:npm|npx|corepack)(?:\.(?:cmd|ps1))?$`

A `OnceLock` caches the `Arc<IgnoreEntryFilter>` so per-snapshot
clones share one trait object.

Unit tests pin every branch of the alternation plus the negative
cases (e.g. `lib/node_modules/yarn/...` is *not* matched,
`bin/npm.cmd` is *not* matched — the regex's `$` and arm-specific
extension rules are deliberately asymmetric). Verified by
temporarily collapsing the `bin/` branch to a no-op — the test
fails as expected.

Bin-link cmd-shims for the runtime executables and `@runtime:`
substring handling in skip lists / reporter prefixes are Slice
D3. End-to-end runtime install fixtures land in Slice F.
pull Bot pushed a commit to dwongdev/pnpm that referenced this pull request May 14, 2026
…e D3 + E) (pnpm#506)

* feat(package-manager): runtime bin-link + --no-runtime (pnpm#437 slice D3 + E)

Closes the install-pipeline side of pnpm#437:

**Slice D3 — runtime bin-link integration.** Runtime archives
(`node@runtime:`, etc.) don't ship a `package.json`, so the
existing bin-link step had nothing to consume off the slot.
`fetch_binary_resolution_to_cas` now synthesizes one in the same
shape upstream's `appendManifest` does:

  { "name": "<pkg.name>", "version": "<pkg.version>", "bin": <BinarySpec> }

The bytes go through `StoreDir::write_cas_file` (same content-
addressing as every other CAS-imported file), the resulting cas-path
is inserted into the snapshot's `cas_paths` under `package.json`,
and the existing `link_bins_of_packages` flow picks it up.

`link_bins::build_has_bin_set` now includes `Binary` / `Variations`
resolutions unconditionally — pnpm v11 doesn't emit `hasBin: true`
on runtime metadata, but the synthesized manifest always carries
bins, so the lookup must not be gated on the metadata flag.

**Slice E — `--no-runtime` flag.** New `Config::skip_runtimes`
(default false) and `InstallArgs::no_runtime` CLI flag. The CLI
computes `config.skip_runtimes || --no-runtime` and threads it
through `Install` → `InstallFrozenLockfile`. When set, every
snapshot whose metadata resolution is `Binary` or `Variations` is
added to the install-time skip set (re-using
`add_optional_excluded` since the bucket count and
`.modules.yaml.skipped` semantics line up with `--no-optional`).

**Unit tests.**

- `synthesize_runtime_manifest_emits_name_version_and_bin_single` —
  pins `BinarySpec::Single` → JSON string.
- `synthesize_runtime_manifest_emits_name_version_and_bin_map` —
  pins `BinarySpec::Map` → JSON object with all bin entries.
- `synthesize_runtime_manifest_preserves_scoped_name` — `@scope/name`
  round-trips through the `name` field.
- `build_has_bin_set_includes_runtime_resolutions_even_when_has_bin_is_absent`
  — pins the runtime arm. Verified by removing the arm — test
  fails as expected.

End-to-end install fixtures (slice F's remaining items —
recorded-archive frozen-lockfile install, variant-mismatch
error, `--no-runtime` integration, integrity-mismatch path) need
a small recorded Node archive in the repo (or a mockable
`pnpm-resolution-mirror`). Tracking them as a separate
follow-up so this PR stays reviewable.

* fix(package-manager): narrow --no-runtime to importer-direct deps

Address CodeRabbit review on pnpm#506: my initial implementation
iterated `packages` (every metadata entry) and added all
`Binary` / `Variations` snapshots to the skip set, which widened
the behavior beyond pnpm's `skipRuntimes`. Per the cardinal rule
(CLAUDE.md: "Port behavior faithfully. ... Do not invent
behavior that pnpm does not have."), match upstream exactly.

Upstream's filter at
[`installing/deps-installer/src/install/index.ts`](https://github.com/pnpm/pnpm/blob/94240bc046/installing/deps-installer/src/install/index.ts#L1374-L1387)
iterates `dependenciesByProjectId` (per-importer direct deps) and
adds `depPath` to `ctx.skipped` when `depPath.includes('@runtime:')`.
Transitive runtime entries — unusual but possible — stay in the
install.

Pacquet now mirrors that exactly: walk each importer's
`dependencies` / `dev_dependencies` / `optional_dependencies`,
build the candidate snapshot key from `(alias, version)`, check
for the `@runtime:` substring, and only add the metadata-confirmed
`Binary` / `Variations` keys to `skipped`. Aliased runtime deps
(unusual) fall through the same way upstream does — pnpm's lookup
is by depPath, not by alias.
pull Bot pushed a commit to dwongdev/pnpm that referenced this pull request May 14, 2026
…m#512)

Pnpm v11 writes runtime depPaths with a scheme prefix in front of
the semver — e.g. `node@runtime:22.0.0` in `packages:` /
`snapshots:` keys, and `runtime:22.0.0` in importer `dependencies:`
values. Pacquet's `PkgVerPeer` parser previously accepted only
bare semver and rejected the prefix, so it couldn't load any v11
lockfile containing a runtime dependency.

Extend `PkgVerPeer` to recognise a leading `runtime:` (closed enum
`Prefix::{None, Runtime}`) and preserve it through `Display` /
`serde`. The version + peer parts continue to parse the same way
— the prefix-strip happens once up front, after which the
parenthesis-based peer-suffix detection is unchanged.

Touch points:
- Added `Prefix` enum with `as_str()` and `Display`.
- `PkgVerPeer::prefix()` exposes the variant so downstream
  consumers can discriminate runtime entries without
  substring-searching the depPath.
- `PkgVerPeer::into_tuple()` keeps the `(Version, String)` shape
  for backward compatibility — pre-runtime callers don't see the
  prefix.

5 new unit tests pin: bare-semver no-prefix, runtime no-peer,
runtime with peer-suffix, runtime-substring-mid-version is NOT a
prefix (anchored at start), and serde round-trip on the runtime
form. All 100 existing `pacquet-lockfile` tests still pass.

Unblocks pnpm#437 slice F (end-to-end install fixtures) — pacquet's
lockfile loader now accepts `node@runtime:X.Y.Z`-shaped keys.
pull Bot pushed a commit to dwongdev/pnpm that referenced this pull request May 14, 2026
…npm#437 slice F) (pnpm#516)

* test(package-manager): variant-mismatch + --no-runtime install tests (pnpm#437 slice F)

Two end-to-end install tests for the runtime-dependency pipeline,
now that pnpm#512 (`PkgVerPeer::Prefix::Runtime`) unblocked
fixture-lockfile parsing.

- `frozen_lockfile_install_errors_when_no_variant_matches_host`:
  Lockfile with a `VariationsResolution` whose only variant
  targets `aix/ppc64` (a platform pacquet CI never runs on).
  Install fails with `NoMatchingPlatformVariant` from the
  cold-batch dispatcher. Variant selection runs before any
  network fetch, so the bogus archive URL is never read — the
  test stays hermetic. Closes the variant-mismatch checkbox.
- `frozen_lockfile_install_skips_runtime_when_skip_runtimes_set`:
  Same lockfile, but with `skip_runtimes: true`. The
  `--no-runtime` filter iterates importer-direct deps, builds
  `node@runtime:22.0.0` from `(alias, version)`, sees the
  `@runtime:` substring, and adds the snapshot to the skip set
  — so variant selection never runs and the unmatchable variant
  doesn't fail the install. Asserts both the runtime slot and
  the direct-dep symlink are absent. Closes the `--no-runtime`
  checkbox.

Both tests were verified to catch their respective regressions
(disable the `skip_runtimes` filter → the second test fails with
`NoMatchingPlatformVariant` instead of succeeding).

Required a fix in `PkgNameVerPeer::without_peer`: the existing
implementation dropped the `Prefix::Runtime` when stripping the
peer-dependency suffix, so a runtime snapshot key like
`node@runtime:22.0.0(some@peer)` would resolve to the
non-existent `node@22.0.0` `packages:` entry. Now preserves the
prefix through the strip.

Slice F's remaining items (happy-path archive fetch, integrity
mismatch, NODE_EXTRAS verified end-to-end, TEST_PORTING.md
entries) need a mock HTTP server + recorded archive fixture, so
defer to a follow-up — keeping this PR reviewable.

* test(package-manager): use symlink_metadata for absent-entry checks

Address CodeRabbit review on pnpm#516: `Path::exists()` follows
symlinks, so a regression that created a *dangling* symlink at
`<modules_dir>/node` would still pass the
`!direct_dep.exists()` assertion — the install would have created
the entry the skip set was supposed to suppress, but the test
wouldn't notice.

Switch both "must NOT exist" assertions to
`std::fs::symlink_metadata(...).is_err()` so the entry-level
check catches dangling symlinks too.
github-actions Bot pushed a commit to Eyalm321/pnpm that referenced this pull request May 18, 2026
…ce A) (pnpm#457)

## Summary

Slice A of pnpm#437. Adds the lockfile types pnpm v11 writes for runtime dependencies (`node@runtime:`, `deno@runtime:`, `bun@runtime:`); the install pipeline doesn't dispatch them yet (subsequent slices). No new workspace deps.

- **`BinaryResolution { url, integrity, bin, archive, prefix? }`** — mirrors upstream's [`BinaryResolution`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L41-L49). `bin` is `BinarySpec::Single(String) | BinarySpec::Map(BTreeMap)` (untagged); `archive` is the lowercase `BinaryArchive::Tarball | BinaryArchive::Zip` discriminator; `prefix` (only set on the `.zip` branch upstream) skips serialization when `None`.
- **`PlatformAssetTarget { os, cpu, libc? }`** + **`PlatformAssetResolution { resolution, targets }`** per [`resolver-base`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L60-L69). `libc` is `Option<String>` (not an enum) so an upstream addition beyond the current `musl` value lands without a serde migration.
- **`VariationsResolution { variants }`** + the `LockfileResolution`/`TaggedResolution` arms routing both new shapes through the existing `from/into ResolutionSerde` round-trip.
- **Lockfile major stays at 9** — confirmed against [`core/constants/src/index.ts`](https://github.com/pnpm/pnpm/blob/94240bc046/core/constants/src/index.ts) (`LOCKFILE_MAJOR_VERSION = '9'`), so the existing `lockfile_version.major == 9` assertion holds for v11 lockfiles carrying runtime entries.

**Install-dispatch stubs.** `install_package_by_snapshot.rs` and `create_virtual_store.rs` raise `InstallPackageBySnapshotError::UnsupportedResolution { kind: "binary" | "variations" }` until Slice D wires the fetcher. Adding these arms keeps the workspace compiling without forcing the full install path to land in this slice. The warm-prefetch path returns `Ok(None)` for `Variations`, routing through the cold dispatcher where the unsupported-kind error fires; upstream unwraps `Variations` before this layer ever sees one, so the branch is unreachable on well-formed input.
github-actions Bot pushed a commit to Eyalm321/pnpm that referenced this pull request May 18, 2026
 slice B) (pnpm#466)

* feat(lockfile): select_platform_variant + PlatformSelector (pnpm#437 slice B)

Add the variant-picking logic for `VariationsResolution` ahead of
Slice D's install-pipeline dispatch:

- `PlatformSelector { os, cpu, libc: Option<String> }` mirrors
  upstream's [`PlatformSelector`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L78-L83).
  The `libc` tri-state encodes pnpm's `string | null | undefined`
  shape: `None` (host doesn't care about libc — macOS/Windows/BSD)
  and `Some("glibc")` collapse to the same matching arm (variant
  must have no `libc:` annotation); `Some("musl")` requires an exact
  `libc: "musl"` annotation so the glibc default doesn't silently
  win on a musl host.

- `select_platform_variant(variants, selector)` ports
  [`selectPlatformVariant`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L92-L98).
  Iterates `variants` in declaration order and returns the first
  variant whose `targets[]` contains an `(os, cpu, libc)` triple
  matching the selector. Targets-array scan is linear because real
  archives ship 1–3 target entries per variant; quadratic cost is
  immaterial.

- `libc_matches(variant_libc, requested_libc)` is the asymmetric
  helper from
  [`libcMatches`](https://github.com/pnpm/pnpm/blob/94240bc046/resolving/resolver-base/src/index.ts#L100-L107).
  Crate-private so the matching policy isn't part of the public API.

Lives in `pacquet-lockfile` next to the variant types (mirrors
upstream's `resolver-base` package boundary). The host-platform side
(constructing the `PlatformSelector` from `pacquet-graph-hasher`'s
existing `host_platform/arch/libc` helpers) lands at the install
dispatcher in Slice D — keeps `pacquet-lockfile` free of the
`graph-hasher` dep and lets tests drive the picker with synthetic
hosts.

Tests: first-match wins, multi-target variant matches any host triple,
no-match returns `None`, musl-host rejects glibc-default variant,
musl-host matches musl-annotated variant, and a six-row `libc_matches`
truth table (None / "glibc" / "musl" / unknown-future-libc on both
sides) pinning the asymmetric contract from upstream.

Part of pnpm#437.

---
Written by an agent (Claude Code, claude-opus-4-7).

* docs: refresh stale references + add declaration-order tie-breaker test

Two follow-ups from Copilot review on PR pnpm#466:

- `resolution.rs:152-154` claimed "the variant picker checks at
  runtime that the resolved inner is atomic", but
  `select_platform_variant` does not. Reword to describe the actual
  contract: pacquet's `PlatformAssetResolution.resolution` is
  typed as the full `LockfileResolution` for serde uniformity, and
  the lockfile is trusted to honor upstream's atomic-inner
  invariant. No infinite-recursion risk because the install
  dispatcher doesn't call back into `select_platform_variant` for
  non-`Variations` inputs.

- `resolution.rs:169` referenced `pick_variant` in
  `pacquet-package-manager`, but the picker actually lives in this
  module as `select_platform_variant`. Updated the pointer.

- `pick_returns_first_when_multiple_variants_match` test: two
  variants both list the same `(darwin, arm64)` target; the test
  asserts the first one wins, pinning the `Array.prototype.find`
  semantics. Pnpm-written lockfiles can rely on declaration order
  (e.g., listing a preferred build before a fallback) — without
  this test, a future refactor that switched the iteration to a
  triple-keyed `BTreeMap` would silently break that.

No behavior change; doc-comment text + test addition.

---
Written by an agent (Claude Code, claude-opus-4-7).
github-actions Bot pushed a commit to Eyalm321/pnpm that referenced this pull request May 18, 2026
…pm#472)

Slice C of [pnpm#437](pnpm/pacquet#437) — the zip half of pnpm's binary fetcher. Slices A (lockfile types) and B (variant picker) have landed; this slice adds the per-archive download and CAS-extraction pipeline. The install-dispatch site (slice D) and the `--no-runtime` flag (slice E) will follow.

Mirrors upstream's [`downloadAndUnpackZip` / `extractZipToTarget`](https://github.com/pnpm/pnpm/blob/94240bc046/fetching/binary-fetcher/src/index.ts):

- `extract_zip_entries` walks every entry. Path validation runs *before* the `is_dir()` early-skip so directory entries like `../evil/` still surface `PATH_TRAVERSAL`. The canonical `cas_paths` / `pkg_files_idx` key is built from `enclosed_name()` rebuilt as `Normal` components joined with `/` — `.` segments collapse, separators are cross-platform consistent, and the ignore filter sees the same string the map is keyed by.
- `DownloadZipArchiveToStore` is the public sibling of `DownloadTarballToStore` — same store-index lookup, prefetch cache reuse, store-index writer queue. The retry policy, network-permit shape, and post-download semaphore gating exactly match the tarball path.
- `ignore_file_pattern` is `Option<Arc<IgnoreEntryFilter>>` on both `DownloadTarballToStore` and `DownloadZipArchiveToStore`, so the Slice D dispatcher can construct a filter per fetch from runtime config without pinning to `'static`. Cloned per retry attempt; the inner trait object is shared.
- `run_with_mem_cache` doc-blocks the "stable filter per URL" invariant: the cache keys solely on `package_url` (matching pnpm's `tarballCache`), and callers keep the (URL, filter) relation functional. Upstream's `archiveFilters` is keyed by `pkg.name`, and tarball URLs encode `(name, version, integrity)`, so this naturally holds.
- New `TarballError::ReadZipEntries(std::io::Error)` for zip per-entry I/O failures (`try_reserve` / `read_to_end`); maps to `ERR_PACQUET_ZIP` in the retry classifier, distinct from the tar-specific `ERR_PACQUET_TARBALL_TAR`. Zip `unix_mode()` is masked to `0o777` before write so `CafsFileInfo.mode` stays permission-only (matches `store_dir::add_files_from_dir::file_mode_from`). `TarballError::TarballTooLarge` display generalized from "Tarball at …" to "Archive at …" so the zip pipeline's OOM path doesn't surface a misleading message.
- Adds `zip = "5", default-features = false, features = ["deflate"]` to the workspace; reused in `pacquet-tarball`.
github-actions Bot pushed a commit to Eyalm321/pnpm that referenced this pull request May 18, 2026
…pnpm#437 slice D1) (pnpm#492)

Replace the cold-path `UnsupportedResolution` arms for
`LockfileResolution::{Binary, Variations}` with actual fetcher
dispatch. Slice C wired the per-archive download
(`DownloadTarballToStore` / `DownloadZipArchiveToStore`); this
slice routes lockfile resolutions through them.

- `Binary(b)` dispatches on `b.archive` (`Tarball` / `Zip`):
  - `Tarball` → `DownloadTarballToStore` (same shape as
    registry / tarball entries minus the `package_unpacked_size`
    hint).
  - `Zip` → `DownloadZipArchiveToStore` with
    `archive_prefix: b.prefix.as_deref()` so the runtime
    archive's top-level wrapper (e.g. `node-vX.Y.Z-darwin-arm64/`)
    is stripped before the CAS keys are written.
- `Variations(v)` runs `select_platform_variant` against a
  freshly-built `PlatformSelector` (`pacquet_graph_hasher`'s
  `host_platform` / `host_arch` / `host_libc`, mapping
  `"unknown"` → `None` per upstream's
  `process.platform === 'linux' ? family : null` convention).
  The picked variant's inner resolution must be `Binary`; any
  other shape (corrupt lockfile or a future shape pacquet hasn't
  learned about) raises the typed
  `VariantHasNonBinaryResolution` error rather than silently
  routing.
- `create_virtual_store::snapshot_cache_key` returns the proper
  warm-cache key for both new arms: `Binary` uses
  `store_index_key(integrity, pkg_id)`; `Variations` runs the
  same selector and keys off the picked variant. A miss on
  variant selection or a non-`Binary` inner shape returns
  `Ok(None)` and lets the cold path raise the typed error.
- Two new error variants on `InstallPackageBySnapshotError`:
  - `NoMatchingPlatformVariant { package_key, host_os, host_cpu,
    host_libc, available_targets }` — fires when no variant
    matches the host; carries the host triple + the rendered
    available target list so the user can see at a glance why.
  - `VariantHasNonBinaryResolution { package_key, inner_kind }`
    — defensive guard for malformed lockfiles.

The Node-runtime `NODE_EXTRAS_IGNORE_PATTERN` filter (strips
bundled `npm` / `corepack` from the archive) and bin-link
cmd-shims for runtime executables will land in Slice D2; the
filter slot stays `None` for now and `BinaryResolution::bin` is
read but not yet acted on. Slice E adds `--no-runtime` and
Slice F adds the end-to-end install fixtures.

Two new unit tests cover the helpers:
- `host_platform_selector_omits_libc_on_non_linux_hosts` —
  pins the `host_libc() == "unknown"` ⇔ `selector.libc.is_none()`
  relationship without needing platform-gated tests.
- `render_variant_targets_formats_each_triple_with_optional_libc`
  — locks in the `os/cpu[+libc]` rendering used by the
  `NoMatchingPlatformVariant` error message.
github-actions Bot pushed a commit to Eyalm321/pnpm that referenced this pull request May 18, 2026
…pnpm#437 slice D2) (pnpm#496)

Construct the per-archive ignore filter at the install dispatcher.
For unscoped `node` the filter matches upstream's
[`NODE_EXTRAS_IGNORE_PATTERN`](https://github.com/pnpm/pnpm/blob/94240bc046/engine/runtime/node-resolver/src/index.ts),
which strips bundled `npm` / `corepack` from the Node.js runtime
archive during the CAS write — pnpm (and pacquet) install pnpm
itself as the package manager, so the bundled tooling is dead
weight and would shadow the user's pnpm via `node_modules/.bin/`.

Wiring matches upstream's
[`archiveFilters: { node: NODE_EXTRAS_IGNORE_PATTERN }`](https://github.com/pnpm/pnpm/blob/94240bc046/installing/client/src/index.ts):
the per-package-name table is keyed by `pkg.name`, so `@foo/node`
and other packages keep `None` and the full archive lands
unfiltered.

Pacquet uses a hand-coded matcher rather than the upstream regex
so `pacquet-tarball` doesn't have to pull in a regex engine. The
three branches mirror the regex alternation exactly:

1. `^(?:lib/)?node_modules/(?:npm|corepack)(?:/|$)`
2. `^bin/(?:npm|npx|corepack)$`
3. `^(?:npm|npx|corepack)(?:\.(?:cmd|ps1))?$`

A `OnceLock` caches the `Arc<IgnoreEntryFilter>` so per-snapshot
clones share one trait object.

Unit tests pin every branch of the alternation plus the negative
cases (e.g. `lib/node_modules/yarn/...` is *not* matched,
`bin/npm.cmd` is *not* matched — the regex's `$` and arm-specific
extension rules are deliberately asymmetric). Verified by
temporarily collapsing the `bin/` branch to a no-op — the test
fails as expected.

Bin-link cmd-shims for the runtime executables and `@runtime:`
substring handling in skip lists / reporter prefixes are Slice
D3. End-to-end runtime install fixtures land in Slice F.
github-actions Bot pushed a commit to Eyalm321/pnpm that referenced this pull request May 18, 2026
…e D3 + E) (pnpm#506)

* feat(package-manager): runtime bin-link + --no-runtime (pnpm#437 slice D3 + E)

Closes the install-pipeline side of pnpm#437:

**Slice D3 — runtime bin-link integration.** Runtime archives
(`node@runtime:`, etc.) don't ship a `package.json`, so the
existing bin-link step had nothing to consume off the slot.
`fetch_binary_resolution_to_cas` now synthesizes one in the same
shape upstream's `appendManifest` does:

  { "name": "<pkg.name>", "version": "<pkg.version>", "bin": <BinarySpec> }

The bytes go through `StoreDir::write_cas_file` (same content-
addressing as every other CAS-imported file), the resulting cas-path
is inserted into the snapshot's `cas_paths` under `package.json`,
and the existing `link_bins_of_packages` flow picks it up.

`link_bins::build_has_bin_set` now includes `Binary` / `Variations`
resolutions unconditionally — pnpm v11 doesn't emit `hasBin: true`
on runtime metadata, but the synthesized manifest always carries
bins, so the lookup must not be gated on the metadata flag.

**Slice E — `--no-runtime` flag.** New `Config::skip_runtimes`
(default false) and `InstallArgs::no_runtime` CLI flag. The CLI
computes `config.skip_runtimes || --no-runtime` and threads it
through `Install` → `InstallFrozenLockfile`. When set, every
snapshot whose metadata resolution is `Binary` or `Variations` is
added to the install-time skip set (re-using
`add_optional_excluded` since the bucket count and
`.modules.yaml.skipped` semantics line up with `--no-optional`).

**Unit tests.**

- `synthesize_runtime_manifest_emits_name_version_and_bin_single` —
  pins `BinarySpec::Single` → JSON string.
- `synthesize_runtime_manifest_emits_name_version_and_bin_map` —
  pins `BinarySpec::Map` → JSON object with all bin entries.
- `synthesize_runtime_manifest_preserves_scoped_name` — `@scope/name`
  round-trips through the `name` field.
- `build_has_bin_set_includes_runtime_resolutions_even_when_has_bin_is_absent`
  — pins the runtime arm. Verified by removing the arm — test
  fails as expected.

End-to-end install fixtures (slice F's remaining items —
recorded-archive frozen-lockfile install, variant-mismatch
error, `--no-runtime` integration, integrity-mismatch path) need
a small recorded Node archive in the repo (or a mockable
`pnpm-resolution-mirror`). Tracking them as a separate
follow-up so this PR stays reviewable.

* fix(package-manager): narrow --no-runtime to importer-direct deps

Address CodeRabbit review on pnpm#506: my initial implementation
iterated `packages` (every metadata entry) and added all
`Binary` / `Variations` snapshots to the skip set, which widened
the behavior beyond pnpm's `skipRuntimes`. Per the cardinal rule
(CLAUDE.md: "Port behavior faithfully. ... Do not invent
behavior that pnpm does not have."), match upstream exactly.

Upstream's filter at
[`installing/deps-installer/src/install/index.ts`](https://github.com/pnpm/pnpm/blob/94240bc046/installing/deps-installer/src/install/index.ts#L1374-L1387)
iterates `dependenciesByProjectId` (per-importer direct deps) and
adds `depPath` to `ctx.skipped` when `depPath.includes('@runtime:')`.
Transitive runtime entries — unusual but possible — stay in the
install.

Pacquet now mirrors that exactly: walk each importer's
`dependencies` / `dev_dependencies` / `optional_dependencies`,
build the candidate snapshot key from `(alias, version)`, check
for the `@runtime:` substring, and only add the metadata-confirmed
`Binary` / `Variations` keys to `skipped`. Aliased runtime deps
(unusual) fall through the same way upstream does — pnpm's lookup
is by depPath, not by alias.
github-actions Bot pushed a commit to Eyalm321/pnpm that referenced this pull request May 18, 2026
…m#512)

Pnpm v11 writes runtime depPaths with a scheme prefix in front of
the semver — e.g. `node@runtime:22.0.0` in `packages:` /
`snapshots:` keys, and `runtime:22.0.0` in importer `dependencies:`
values. Pacquet's `PkgVerPeer` parser previously accepted only
bare semver and rejected the prefix, so it couldn't load any v11
lockfile containing a runtime dependency.

Extend `PkgVerPeer` to recognise a leading `runtime:` (closed enum
`Prefix::{None, Runtime}`) and preserve it through `Display` /
`serde`. The version + peer parts continue to parse the same way
— the prefix-strip happens once up front, after which the
parenthesis-based peer-suffix detection is unchanged.

Touch points:
- Added `Prefix` enum with `as_str()` and `Display`.
- `PkgVerPeer::prefix()` exposes the variant so downstream
  consumers can discriminate runtime entries without
  substring-searching the depPath.
- `PkgVerPeer::into_tuple()` keeps the `(Version, String)` shape
  for backward compatibility — pre-runtime callers don't see the
  prefix.

5 new unit tests pin: bare-semver no-prefix, runtime no-peer,
runtime with peer-suffix, runtime-substring-mid-version is NOT a
prefix (anchored at start), and serde round-trip on the runtime
form. All 100 existing `pacquet-lockfile` tests still pass.

Unblocks pnpm#437 slice F (end-to-end install fixtures) — pacquet's
lockfile loader now accepts `node@runtime:X.Y.Z`-shaped keys.
github-actions Bot pushed a commit to Eyalm321/pnpm that referenced this pull request May 18, 2026
…npm#437 slice F) (pnpm#516)

* test(package-manager): variant-mismatch + --no-runtime install tests (pnpm#437 slice F)

Two end-to-end install tests for the runtime-dependency pipeline,
now that pnpm#512 (`PkgVerPeer::Prefix::Runtime`) unblocked
fixture-lockfile parsing.

- `frozen_lockfile_install_errors_when_no_variant_matches_host`:
  Lockfile with a `VariationsResolution` whose only variant
  targets `aix/ppc64` (a platform pacquet CI never runs on).
  Install fails with `NoMatchingPlatformVariant` from the
  cold-batch dispatcher. Variant selection runs before any
  network fetch, so the bogus archive URL is never read — the
  test stays hermetic. Closes the variant-mismatch checkbox.
- `frozen_lockfile_install_skips_runtime_when_skip_runtimes_set`:
  Same lockfile, but with `skip_runtimes: true`. The
  `--no-runtime` filter iterates importer-direct deps, builds
  `node@runtime:22.0.0` from `(alias, version)`, sees the
  `@runtime:` substring, and adds the snapshot to the skip set
  — so variant selection never runs and the unmatchable variant
  doesn't fail the install. Asserts both the runtime slot and
  the direct-dep symlink are absent. Closes the `--no-runtime`
  checkbox.

Both tests were verified to catch their respective regressions
(disable the `skip_runtimes` filter → the second test fails with
`NoMatchingPlatformVariant` instead of succeeding).

Required a fix in `PkgNameVerPeer::without_peer`: the existing
implementation dropped the `Prefix::Runtime` when stripping the
peer-dependency suffix, so a runtime snapshot key like
`node@runtime:22.0.0(some@peer)` would resolve to the
non-existent `node@22.0.0` `packages:` entry. Now preserves the
prefix through the strip.

Slice F's remaining items (happy-path archive fetch, integrity
mismatch, NODE_EXTRAS verified end-to-end, TEST_PORTING.md
entries) need a mock HTTP server + recorded archive fixture, so
defer to a follow-up — keeping this PR reviewable.

* test(package-manager): use symlink_metadata for absent-entry checks

Address CodeRabbit review on pnpm#516: `Path::exists()` follows
symlinks, so a regression that created a *dangling* symlink at
`<modules_dir>/node` would still pass the
`!direct_dep.exists()` assertion — the install would have created
the entry the skip set was supposed to suppress, but the test
wouldn't notice.

Switch both "must NOT exist" assertions to
`std::fs::symlink_metadata(...).is_err()` so the entry-level
check catches dangling symlinks too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants