Replace the external `@pnpm/registry-mock` (Verdaccio) test dependency with an in-repo, in-process registry that serves package fixtures to **both** the pacquet Rust tests and the pnpm CLI (Jest) tests. No separately managed registry process is needed.
### How it works
- **Fixtures** live at `registry/.fixtures/packages/<name>/<version>/…`, moved verbatim from [`pnpm/registry-mock`](https://github.com/pnpm/registry-mock) (keyed by each `package.json`'s `name`+`version`).
- **`pnpm-registry-fixtures`** builds verdaccio-shaped storage from those fixtures; the in-tree **`pnpm-registry`** crate serves it.
- Files whose names differ only by case (`@pnpm.e2e/with-same-file-in-different-cases`) and `bundleDependencies` trees are composed **in memory** by the builder, since neither can be committed to the working tree.
- **pacquet**: `pacquet-testing-utils`' `TestRegistry` starts the server lazily (once per process) in proxy mode, serving `@pnpm.e2e` fixtures locally and falling through to the npm uplink for real packages (`is-positive`, `is-negative`, …) — matching how registry-mock behaved.
- **pnpm CLI**: the `with-registry` Jest `globalSetup` builds storage from the fixtures via the new `pnpm-registry-prepare` binary (built from source in the Test CI job) and serves it with `pnpm-registry`. `REGISTRY_MOCK_PORT` / `REGISTRY_MOCK_CREDENTIALS` / `getIntegrity` now come from `@pnpm/testing.registry-mock`.
### Result
`@pnpm/registry-mock` is removed from every manifest, the catalog, and `packageExtensions`; `cargo test` / `cargo nextest run` / `just test` and the pnpm CLI Jest suites all run registry-backed tests without launching Verdaccio.
Extends `configDependencies` to resolve and install one level of `optionalDependencies`, with `os` / `cpu` / `libc` platform filtering applied at install time. Closes the prerequisite called out in #11723: this is what makes the esbuild/swc-style platform-binary pattern viable for config dependencies (e.g. shipping pacquet as a config dep with native binaries via `optionalDependencies`).
### What lands
- **Resolution** (`resolveOptionalSubdeps.ts`, wired into `resolveConfigDeps` and `resolveAndInstallConfigDeps`): after each top-level config dep resolves, walks one level of `optionalDependencies`, resolves each, and records them in the env lockfile with `os`/`cpu`/`libc` preserved. The parent's snapshot gets `optionalDependencies: { … }`. All variants are recorded regardless of host platform, so the env lockfile stays portable across machines.
- **Install** (`installConfigDeps.ts`): after the parent is installed into its GVS leaf, fetches each platform-compatible subdep into its own GVS leaf and creates a sibling symlink inside the parent leaf's `node_modules/`. Node's `realpath`-based resolution then makes `require('pkg-platform-arch')` from inside the parent resolve correctly. Stale siblings are pruned, so platform changes between runs produce a clean layout.
- **GVS hash** (new `calcGlobalVirtualStorePathWithSubdeps` in `graph-hasher`): the parent's GVS leaf hash now folds in the optional subdeps' full pkg ids. Without this, changing a subdep version while keeping the parent pinned would land in the same leaf and silently overwrite the sibling symlinks. The leaf function keeps its original "no children" contract; the new function is a separate entry point that pacquet can mirror cleanly.
- **Re-install detection**: the "skip if already installed" check compares the existing `.pnpm-config/{name}` symlink's `realpath` against the expected GVS leaf, not the package.json's name/version. With subdep versions now feeding the leaf hash, name/version alone isn't sufficient. The check only short-circuits the parent's re-import and re-symlink — `installOptionalSubdeps` always runs so platform-specific siblings get pruned and relinked when the host's effective platform changes (Rosetta x64 ↔ arm64, etc.).
- **Exact versions only**: subdep specifiers must be valid semver exact versions (e.g. `"1.2.3"`). Ranges (`"^1.0.0"`) and tags (`"latest"`) are rejected up-front with a `CONFIG_DEP_OPTIONAL_NOT_EXACT` error. With the parent pinned by integrity, the subdep's resolved version mustn't drift between machines.
- **Error handling**: optional-subdep resolution failures are logged via `skippedOptionalDependencyLogger` with `reason: 'resolution_failure'` (same shape as `installing/deps-resolver`) and the install continues — except for `ERR_PNPM_TRUST_DOWNGRADE`, which is a security signal that must still abort the install.
### Scope
Only one level deep. Transitive `dependencies` and lifecycle scripts remain unsupported — pacquet doesn't need them yet, and they carry meaningful security and complexity tradeoffs that deserve a separate discussion.
The env lockfile schema needs no changes: `LockfilePackageInfo` already carries `os`/`cpu`/`libc`, and `LockfilePackageSnapshot.optionalDependencies` already exists for recording the parent→child edge.
## Known limitation
If a workspace already had a resolved config dep in the env lockfile (`snapshots[pkgKey] = {}`) before this PR, optional subdeps won't be retroactively discovered on subsequent installs. Workaround: `pnpm update <pkg>` (or remove + re-add). In practice no published package today relies on `optionalDependencies` in a config dep — they couldn't, since the feature didn't exist — so the practical exposure is narrow. See the inline review thread for the design rationale.
* chore: upgrade @typescript/native-preview to 7.0.0-dev.20260421.2
- Add explicit `types: ["node"]` to the shared tsconfig because tsgo
20260421 no longer auto-acquires `@types/*` from `node_modules`.
- Refactor test files to explicitly import jest globals (`describe`,
`it`, `test`, `expect`, `beforeEach`, etc.) from `@jest/globals`
instead of relying on `@types/jest` ambient declarations. Under the
new tsgo build, `import { jest } from '@jest/globals'` shadows the
ambient `jest` namespace, breaking `@types/jest`'s `declare var
describe: jest.Describe;` globals.
- Add `@jest/globals` to each package's devDependencies where tests
now import from it, and add `@types/node` to packages that need it
but were relying on hoisted resolution.
- Replace `fail()` calls with `throw new Error(...)` since `fail` is
no longer globally available.
* chore: fix remaining tsgo type-strictness errors
- Strip `as <PnpmType>` casts on objects passed to toMatchObject /
toStrictEqual / toEqual; @jest/globals rejects the typed objects
(which include AsymmetricMatchers) vs. the repo-specific type.
- Type `jest.fn<...>()` explicitly where the mock's signature matters
for toHaveBeenCalledWith.
- Replace `beforeEach(() => X)` with `beforeEach(() => { X })` so the
return value is void, as the stricter jest typing requires.
- Use `expect.objectContaining({...})` in one place where the full
expected object triggered stricter type resolution.
- Cast `prompt.mock.calls` arg through `as unknown as Record<...>[]`
for patch.test.ts's nested-array matchers.
- Fix off-by-one `<reference path>` in pnpm/test/getConfig.test.ts
that only surfaced now.
- Move `@jest/globals` from devDependencies to dependencies in the
two `__utils__` packages that import it from `src/`.
- Clean up unused imports from the @jest/globals migration.
* chore: address Copilot review on #11332
- Move misplaced `@jest/globals` imports to the top import block in
checkEngine, run.ts, and workspace/root-finder tests where the
script dropped them below executable code.
- Replace `try { await x(); throw new Error('should have thrown') } catch`
in bins/linker, lockfile/fs, and resolving/local-resolver tests with
`await expect(x()).rejects.toMatchObject({...})`. The old pattern
swallowed an unrelated `throw` if the under-test call silently
succeeded, which would fail on the catch-block assertion with a
misleading message.
Library packages had `prepublishOnly: pn compile`, which expands to
`tsgo --build && pn lint --fix`. During `pn release` that runs eslint
against ~150 packages for no benefit — the code has already been linted
in CI and the release flow's upfront compile has already built dist/.
Switch lib prepublishOnly to a bare `tsgo --build` so the safety-net
compile stays but the per-package eslint cost is gone.
* fix: ensure PNPM_HOME/bin is in PATH during pnpm setup
When upgrading from old pnpm (global bin = PNPM_HOME) to new pnpm
(global bin = PNPM_HOME/bin), `pnpm setup` would fail because the
spawned `pnpm add -g` checks that the global bin dir is in PATH.
Prepend PNPM_HOME/bin to PATH in the spawned process env so the
check passes during the transition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update pnpm to v11 beta 2
* chore: update pnpm to v11 beta 2
* chore: update pnpm to v11 beta 2
* chore: update pnpm to v11 beta 2
* fix: lint
* refactor: rename _-prefixed scripts to .-prefixed scripts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update root package.json to use .test instead of _test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: update action-setup
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: reduce noisy warnings in test output
- Suppress ExperimentalWarning and DEP0169 via --disable-warning in NODE_OPTIONS
- Fix MaxListenersExceededWarning by raising limit in StoreIndex when adding exit listeners
- Update meta-updater to generate the new _test scripts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: stop streaming pnpm subprocess output during CLI tests
Buffer stdout/stderr from execPnpm instead of writing to the parent
process in real time. Output is still included in the error message on
failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: pipe all subprocess output in CLI tests
Use stdio: 'pipe' for all pnpm/pnpx spawn helpers so subprocess output
is buffered instead of printed. Output is still included in error
messages on failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove duplicate @pnpm/installing.env-installer in pnpm/package.json
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: use pipe stdio in dlx and errorHandler tests
Replace stdio: 'inherit' and [null, 'pipe', 'inherit'] with 'pipe' to
prevent subprocess output from leaking into test output.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: skip maxListeners adjustment when set to unlimited (0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>