Commit Graph

10667 Commits

Author SHA1 Message Date
Zoltan Kochan
62f760ec3d fix(dlx): fix race conditions in parallel dlx calls sharing Global Virtual Store (#10939)
## Summary

Fixes intermittent failures in the `parallel dlx calls of the same package` test, especially on Windows CI. Multiple race conditions were discovered when concurrent `pnpm dlx` processes share the same Global Virtual Store (GVS):

- **Content-verified skip in GVS mode**: When `safeToSkip` is true and a rename fails because the target already exists (ENOTEMPTY/EEXIST/EPERM), verify all files match (inode or content comparison) before skipping. Falls through to `renameOverwriteSync` if content doesn't match.
- **Tolerate EPERM during bin creation on Windows**: `cmd-shim`'s `chmod` can fail with EPERM when another process holds the `.bin` file. Warn instead of crashing.
- **Handle EPERM in DLX cache symlink**: Added EPERM to the list of tolerated errors when creating the DLX cache symlink, as Windows can throw this when another process has the symlink open.

## Test plan

- [x] `parallel dlx calls of the same package` test passes on Windows CI
- [x] Full test suite passes on both Ubuntu and Windows
2026-03-12 21:06:32 +01:00
Zoltan Kochan
39afb24ce6 chore: add worktree helper script and shell functions (#10938)
Adds a `pnpm worktree:new <branch>` script that creates a git worktree
as a sibling directory of the repo root, with branch slashes replaced by
dashes (e.g. `feat/foo` → `../feat-foo`). Shell helpers in `shell/wt.fish`
and `shell/wt.sh` wrap the script to also `cd` into the new worktree.
Documents setup and usage in CONTRIBUTING.md.
2026-03-11 22:59:42 +01:00
Zoltan Kochan
edbe1bcbf9 chore: add Husky hooks to prevent Claude Code from bad git practices (#10937)
- Adds two Husky hooks to prevent Claude Code from bad git practices:
  - \`prepare-commit-msg\`: blocks \`git commit --amend\` (detected via the \`commit\` source argument)
  - \`pre-commit\`: blocks committing directly to \`main\`
- Both hooks detect Claude Code sessions via the \`CLAUDECODE\` environment variable, so regular users are unaffected
- Error messages explicitly tell Claude what to do instead
2026-03-11 20:28:25 +01:00
Zoltan Kochan
178f2210e3 fix: don't run pnpm install in the fixture dir, use a temp copy instead (#10936) 2026-03-11 20:09:57 +01:00
Zoltan Kochan
0fda5eaf0b fix: skip entries that disappear between readdirSync and lstatSync on Windows CI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 19:41:40 +01:00
Zoltan Kochan
bb177242df feat: support devEngines.packageManager for pnpm version management (#10932)
## Summary

- Support specifying the pnpm version via `devEngines.packageManager` in `package.json`, as an alternative to the `packageManager` field
- Unlike `packageManager`, `devEngines.packageManager` supports semver ranges — the resolved version is stored in `pnpm-lock.env.yaml` and reused if it still satisfies the range
- The `onFail` field determines behavior: `download` (auto-download), `error` (default), `warn`, or `ignore`
- `devEngines.packageManager` takes precedence over `packageManager` when both are present (with a warning)
- For array notation, default `onFail` is `ignore` for non-last elements and `error` for the last
- For the legacy `packageManager` field, `onFail` is derived from existing config settings (`managePackageManagerVersions`, `packageManagerStrict`, `packageManagerStrictVersion`), so `main.ts` uses `onFail` as the single source of truth
- Reuses `EngineDependency` type from `@pnpm/types` instead of a custom `WantedPackageManager` type

## Test plan

- [x] 10 tests in `switchingVersions.test.ts` — version switching with `packageManager` field, `devEngines.packageManager` with `onFail=download` (exact + range), env lockfile reuse, corrupt binary
- [x] 15 tests in `packageManagerCheck.test.ts` — version checks with `engines.pnpm`, `packageManager` field, `devEngines.packageManager` with all `onFail` values, array notation, range matching, precedence

close https://github.com/pnpm/pnpm/issues/8153
2026-03-11 18:49:09 +01:00
Zoltan Kochan
6d56db2aad feat(audit): audit dependencies from pnpm-lock.env.yaml (#10933)
The `pnpm audit` command now also audits dependencies from
`pnpm-lock.env.yaml`, including `configDependencies` and
`packageManagerDependencies` along with their transitive dependencies.

They appear as separate groups in the audit tree so that vulnerability
paths clearly indicate their origin (e.g. configDependencies>pkg>...).
2026-03-11 18:48:16 +01:00
Zoltan Kochan
3a5bfaa94f chore: update zkochan packages to latest versions (#10930)
Update all packages from zkochan/packages to their latest major versions
and exclude them from minimumReleaseAge requirement. This includes
updating catalog entries, adapting to breaking API changes (default
exports replaced with named exports, sync functions renamed with Sync
suffix), and updating type declarations.
2026-03-11 13:47:46 +01:00
Zoltan Kochan
56b065dd47 chore: update pnpm-lock.yaml (#10905) 2026-03-11 08:53:47 +01:00
Zoltan Kochan
6470c17172 chore: update pnpm to v11 alpha 14 2026-03-11 01:17:27 +01:00
Zoltan Kochan
4b4a7f5bce chore(release): 11.0.0-alpha.14 v11.0.0-alpha.14 2026-03-11 01:09:05 +01:00
Zoltan Kochan
a8f016ca59 feat: store config deps and package manager integrities in pnpm-lock.env.yaml (#10912)
## Summary

Store config dependency and package manager integrity info in a separate `pnpm-lock.env.yaml` lockfile instead of inlining it in `pnpm-workspace.yaml`. The workspace manifest now contains only clean version specifiers for `configDependencies`, while the resolved versions, integrity hashes, and tarball URLs are recorded in the new env lockfile.

### Key changes

- **New `pnpm-lock.env.yaml` lockfile**: Uses the standard lockfile format (`importers`, `packages`, `snapshots`) to store resolved config dependencies and package manager dependencies with integrity hashes and tarball URLs.
- **Automatic migration**: Projects using the old inline-hash format in `pnpm-workspace.yaml` are automatically migrated on install.
- **Global Virtual Store (GVS) for version switching**: When switching pnpm versions via the `packageManager` field, pnpm is installed to the global virtual store (`$STORE_DIR/links/`) instead of `globalPkgDir`, reusing the content-addressable store for deduplication.
- **Self-update uses headless install**: `pnpm self-update` performs frozen headless installs using integrity hashes from the env lockfile, then links bins to `PNPM_HOME`.
- **`packageManagerDependencies`**: The env lockfile also stores resolved `packageManagerDependencies` during version switching and self-update.
- **`@pnpm/exe` support**: Replicates `@pnpm/exe`'s postinstall script (linking platform-specific binaries) since install scripts are disabled.
- **`pnpm setup` refactored**: Uses `pnpm add -g` instead of copying the CLI binary directly.
- **Extracted `toLockfileResolution`** to `@pnpm/lockfile.utils` and **deduplicated `iteratePkgMeta`** into `@pnpm/calc-dep-state`.
- **Removed unused `@pnpm/tools.path` package**.
2026-03-11 00:39:37 +01:00
Zoltan Kochan
2b68ae123b chore: add --provenance to publish commands in release script 2026-03-09 23:12:12 +01:00
Rohan Santhosh
78c2faabec docs: fix after-install warning typo (#10919)
Co-authored-by: rohan436 <rohan.santhoshkumar@googlemail.com>
2026-03-09 10:56:33 +01:00
Zoltan Kochan
2fccb03fbe refactor: consolidate build-related packages into building/ domain (#10918)
* refactor: rename rebuildSelectedPkgs/rebuildProjects to buildSelectedPkgs/buildProjects

The "rebuild" prefix is redundant now that these functions live in
@pnpm/building.after-install.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: rename Rebuild option types to Build (RebuildOptions → BuildOptions, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: rename plugin-commands-rebuild and exec.build-commands to building domain

- @pnpm/plugin-commands-rebuild → @pnpm/building.build-commands
- @pnpm/exec.build-commands → @pnpm/building.policy-commands

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: move build-modules and pkg-requires-build to building domain

- @pnpm/build-modules → @pnpm/building.during-install
- @pnpm/exec.pkg-requires-build → @pnpm/building.pkg-requires-build

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: alphabetically sort imports after package renames

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add changeset

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:56:13 +01:00
Zoltan Kochan
7354e6bb19 refactor: extract rebuild implementation into @pnpm/building.after-install (#10916)
* refactor: extract rebuild implementation into @pnpm/building.after-install

Move the rebuild implementation (rebuildProjects, rebuildSelectedPkgs)
from @pnpm/plugin-commands-rebuild into a new @pnpm/building.after-install
package. This breaks the circular dependency chain:
config/deps-installer → core → plugin-commands-rebuild → cli-utils → config/deps-installer

The CLI command layer stays in plugin-commands-rebuild, while the core
rebuild logic now lives in building/after-install with no dependency
on cli-utils.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add README for @pnpm/building.after-install

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: rename @pnpm/builder.policy to @pnpm/building.policy

Move builder/policy to building/policy, consolidating all build-related
packages under the building/ domain. Update all consumers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add changeset

* refactor: remove old implementation files from plugin-commands-rebuild

These files were copied to @pnpm/building.after-install but not removed
from the original location.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: remove unused deps from plugin-commands-rebuild

Dependencies that were only needed by the implementation (now in
@pnpm/building.after-install) are removed. Deps used only in tests
are moved to devDependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:20:47 +01:00
Zoltan Kochan
aeb06caae9 refactor: simplify patchedDependencies lockfile format (#10911)
* refactor: simplify patchedDependencies lockfile format to map selectors to hashes

Remove the `path` field from patchedDependencies in the lockfile, changing the
format from `Record<string, { path: string, hash: string }>` to
`Record<string, string>` (selector → hash). The path was never consumed from
the lockfile — patch file paths come from user config, not the lockfile.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: migrate old patchedDependencies format when reading lockfile

When reading a lockfile with the old `{ path, hash }` format for
patchedDependencies, extract just the hash string. This ensures
backwards compatibility with existing lockfiles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: carry patchFilePath through patch groups for runtime patch application

The previous commit removed `path` from the lockfile format but also
accidentally dropped it from the runtime PatchInfo type. This broke
patch application since `applyPatchToDir` needs the file path.

- Add optional `patchFilePath` to `PatchInfo` for runtime use
- Build patch groups with resolved file paths in install
- Fix `build-modules` to use `patchFilePath` instead of `file.path`
- Fix `calcPatchHashes` call site in `checkDepsStatus` (extra arg)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update remaining references to old PatchFile type

- Update getPatchInfo tests to use { hash, key } instead of { file, key }
- Fix createDeployFiles to handle patchedDependencies as hash strings
- Fix configurationalDependencies test assertion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: throw when patch exists but patchFilePath is missing

Also guard against undefined patchedDependencies entry when
ignorePackageManifest is true.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: don't join lockfileDir with already-absolute patch file paths

opts.patchedDependencies values are already absolute paths, so
path.join(opts.lockfileDir, absolutePath) created invalid doubled
paths like /project/home/runner/work/pnpm/...

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use path.resolve for patch file paths and address Copilot review

- Use path.resolve instead of path.join to correctly handle both
  relative and absolute patch file paths
- Use PnpmError instead of plain Error for missing patch file path
- Only copy patchedDependencies to deploy output when manifest
  provides the patch file paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: pass rootProjectManifest in deploy patchedDependencies test

The test was missing rootProjectManifest, so createDeployFiles could
not find the manifest's patchedDependencies to propagate to the
deploy output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:26:48 +01:00
Zoltan Kochan
821b36ac20 feat: install config dependencies into the global virtual store (#10910)
* feat: install config dependencies into the global virtual store

Config dependencies are now imported into {storeDir}/links/ following the
same path convention as regular packages (@scope/name/version/hash), then
symlinked into node_modules/.pnpm-config/. When the package already exists
in the GVS, the fetch and import are skipped entirely.

* refactor: extract shared GVS path computation into @pnpm/calc-dep-state

Move the leaf node hash computation from config deps-installer into
calcLeafGlobalVirtualStorePath in @pnpm/calc-dep-state to avoid
duplicating the hash logic.
2026-03-08 13:49:26 +01:00
Ryan Johnson
60d3a328bc test: strip ANSI before assertions in outdated and formatError (same brittleness as list) (#10894)
* test: strip ANSI before assertions in outdated and formatError tests

Same brittleness as in list tests: exact comparison with chalk output
fails when chalk is disabled or TTY differs. Strip control characters
on both sides before toBe/toContain so tests pass in any environment.

- reviewing/plugin-commands-outdated: renderLatest.test.ts — assert
  on '(deprecated)' after stripVTControlCharacters(output).
- pnpm/test: formatError.test.ts — strip both actual and expected
  before toBe().

* fix: use stripAnsi alias and simplify test assertions

Address review comments: use `stripVTControlCharacters as stripAnsi`
alias for codebase consistency, and simplify formatError tests by
comparing against plain text expected values instead of stripping
both sides.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 00:46:03 +01:00
Zoltan Kochan
df9d1dccbb chore: update pnpm-lock.yaml (#10856) 2026-03-08 00:10:16 +01:00
Brandon Cheng
01914345d5 build: enable @typescript-eslint/no-import-type-side-effects (#10630)
* build: enable `@typescript-eslint/no-import-type-side-effects`

* build: disable `@typescript-eslint/consistent-type-imports`

* chore: apply fixes for `no-import-type-side-effects`

pnpm exec eslint "**/src/**/*.ts" "**/test/**/*.ts" --fix
2026-03-08 00:02:48 +01:00
Zoltan Kochan
c096e48090 fix: show absolute path instead of ??? when globally linking from cwd (#10899)
When running `pnpm add -g .`, the linked-from path equals the cwd,
so path.relative() returns an empty string which is falsy, causing
the fallback to '???'. Use the absolute path as fallback instead.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 15:49:20 +01:00
Zoltan Kochan
1ec944a31b fix: fix mount-modules test when fuse-native is not installed (#10900)
* fix: avoid static type import that triggers fuse-native resolution in test

The static `import type` from createFuseHandlers.ts caused Jest to resolve
the entire module graph, including fuse-native, before the mock was set up.
Derive the type from the function's return type instead.

* fix: fix mount-modules test when fuse-native is not installed

The static `import type` from createFuseHandlers.ts caused Jest to resolve
the entire module graph, including fuse-native, before the mock was set up.
Derive the type from the function's return type instead.

Also add a moduleNameMapper and mock file for fuse-native so Jest's resolver
can find the module even when the optional dependency is not installed.

* fix: preserve existing jest config in meta-updater

The meta-updater was overwriting the entire jest config with just the
preset, dropping any additional config like moduleNameMapper.
2026-03-07 15:48:59 +01:00
Zoltan Kochan
77165a6fca ci: change pnpm installation to standalone mode 2026-03-07 14:58:43 +01:00
Zoltan Kochan
64db713fa5 chore: update pnpm to v11 alpha 13 2026-03-07 14:35:42 +01:00
Zoltan Kochan
5669dbfd87 chore(release): 11.0.0-alpha.13 v11.0.0-alpha.13 2026-03-07 14:22:59 +01:00
Zoltan Kochan
ab9eb59e9c fix: improve global install output to show clean summary (#10897)
Hide the ugly temporary directory path prefix from progress output
for global installs (same approach as dlx). Show a proper install
summary with "global:" heading instead of the temp directory path.
2026-03-07 14:05:26 +01:00
Zoltan Kochan
112a2964c4 fix: race condition with global-virtual store (#10896) 2026-03-07 14:04:58 +01:00
Zoltan Kochan
5d999f2ceb fix: update globalVirtualStore test to use StoreIndex instead of removed getIndexFilePathInCafs
The store index was migrated from msgpack files to SQLite in #10827,
but this test still referenced the removed getIndexFilePathInCafs function
and readMsgpackFileSync, causing a compilation error.
2026-03-07 10:43:35 +01:00
Zoltan Kochan
cd743ef57f fix(gvs): engine-agnostic hashes and build failure recovery (#10846)
* feat(calc-dep-state): use allowBuilds to compute engine-agnostic GVS hashes

Use the allowBuilds config to determine which packages need ENGINE_NAME
in their GVS hash. Packages that are not allowed to build (and don't
transitively depend on packages that are) now get engine-agnostic hashes,
so they survive Node.js upgrades and architecture changes without
re-import.

Closes #10837

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(modules-yaml): persist allowBuilds and re-link GVS on change

Store the allowBuilds config in modules.yaml so that when it changes
between installs, the headless installer detects the difference and
re-processes all packages with updated GVS hashes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: deduplicate computeBuiltDepPaths into iterateHashedGraphNodes

Move builtDepPaths computation inside iterateHashedGraphNodes, which now
accepts an AllowBuild function instead of a precomputed Set. This
eliminates duplicate logic in iteratePkgsForVirtualStore and
resolve-dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(gvs): recover from failed or interrupted builds using .pnpm-needs-build marker

When a GVS package needs building, a .pnpm-needs-build marker file is added
to the filesMap before import. The import pipeline treats it as a regular file,
so it's atomically included in the staged directory and renamed with the
package. On the next install, GVS fast paths detect the marker and force a
re-fetch/re-import/re-build.

On build success, the marker is removed. On build failure, the entire hash
directory is removed so the next install starts fresh.

The marker is only checked for packages that are allowed to build (via
allowBuild), minimizing filesystem operations. It is also skipped when cached
side effects will be applied, since the package is already built.

Closes #10837

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove .pnpm-needs-build marker before uploading side effects

Move the marker removal before the side effects upload so the marker
file is not included in the side effects diff. Add a test assertion
that verifies the marker does not appear in the cached side effects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(calc-dep-state): return undefined from computeBuiltDepPaths when allowBuild is not configured

Previously, computeBuiltDepPaths returned an empty Set when allowBuild
was undefined, causing all GVS hashes to become engine-agnostic even
without allowBuilds configured. Now the function is only called when
allowBuild is provided, and iterateHashedGraphNodes avoids materializing
the iterator when it's not needed.

Also restore upfront filtering in extendGraph so non-GVS installs only
hash runtime dep paths, and only pass allowBuild when GVS is on.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:03:39 +01:00
Zoltan Kochan
5a15a32d84 feat: use JSON for npm registry metadata cache instead of msgpack (#10886)
* feat: use JSON for npm registry metadata cache instead of msgpack

Switch the on-disk package metadata cache from msgpack (.mpk) to JSON (.json).
When metadata is not filtered, the raw JSON response from the registry is written
directly to disk with cachedAt injected, avoiding a parse-then-serialize round-trip.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update lockfileOnly test to use .json metadata extension

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update resolving/npm-resolver/src/pickPackage.ts

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-06 17:39:54 +01:00
Zoltan Kochan
b7f0f21582 feat: use SQLite for storing package index in the content-addressable store (#10827)
## Summary

Replace individual `.mpk` (MessagePack) files under `$STORE/index/` with a single SQLite database at `$STORE/index.db` using Node.js 22's built-in `node:sqlite` module. This reduces filesystem syscall overhead and improves space efficiency for small metadata entries.

Closes #10826

## Design

### New package: `@pnpm/store.index`

A new `StoreIndex` class wraps a SQLite database with a simple key-value API (`get`, `set`, `delete`, `has`, `entries`). Data is serialized with msgpackr and stored as BLOBs. The table uses `WITHOUT ROWID` for compact storage.

Key design decisions:

- **WAL mode** enables concurrent reads from workers while the main process writes.
- **`busy_timeout=5000`** plus a retry loop with `Atomics.wait`-based `sleepSync` handles `SQLITE_BUSY` errors from concurrent access.
- **Performance PRAGMAs**: `synchronous=NORMAL`, `mmap_size=512MB`, `cache_size=32MB`, `temp_store=MEMORY`, `wal_autocheckpoint=10000`.
- **Write batching**: `queueWrites()` batches pre-packed entries from tarball extraction and flushes them in a single transaction on `process.nextTick`. `setRawMany()` writes immediate batches (e.g. from `addFilesFromDir`).
- **Lifecycle**: `close()` auto-flushes pending writes, runs `PRAGMA optimize`, and closes the DB. A `process.on('exit')` handler ensures cleanup even on unexpected exits.
- **`VACUUM` after `deleteMany`** (used by `pnpm store prune`) to reclaim disk space.

### Key format

Keys are `integrity\tpkgId` (tab-separated). Git-hosted packages use `pkgId\tbuilt` or `pkgId\tnot-built`.

### Shared StoreIndex instance

A single `StoreIndex` instance is threaded through the entire install lifecycle — from `createNewStoreController` through the fetcher chain, package requester, license scanner, SBOM collector, and dependencies hierarchy. This replaces the previous pattern of each component creating its own file-based index access.

### Worker architecture

Index writes are performed in the main process, not in worker threads. Workers send pre-packed `{ key, buffer }` pairs back to the main process via `postMessage`, where they are batched and flushed to SQLite. This avoids SQLite write contention between threads.

### SQLite ExperimentalWarning suppression

`node:sqlite` emits an `ExperimentalWarning` on first load. This is suppressed via a `process.emitWarning` override injected through esbuild's `banner` option, which runs on line 1 of both `dist/pnpm.mjs` and `dist/worker.js` — before any module that loads `node:sqlite`.

### No migration from `.mpk` files

Old `.mpk` index files are not migrated. Packages missing from the new SQLite index are re-fetched on demand (the same behavior as a fresh store).

## Changed packages

121 files changed across these areas:

- **`store/index/`** — New `@pnpm/store.index` package
- **`worker/`** — Write batching moved from worker module into `StoreIndex` class; workers send pre-packed buffers to main process
- **`store/package-store/`** — StoreIndex creation and lifecycle management
- **`store/cafs/`** — Removed `getFilePathInCafs` index-file utilities (no longer needed)
- **`store/pkg-finder/`** — Reads from StoreIndex instead of `.mpk` files
- **`store/plugin-commands-store/`** — `store status` uses StoreIndex
- **`store/plugin-commands-store-inspecting/`** — `cat-index` and `find-hash` use StoreIndex
- **`fetching/tarball-fetcher/`** — Threads StoreIndex through fetchers; git-hosted fetcher flushes before reading
- **`fetching/git-fetcher/`, `binary-fetcher/`, `pick-fetcher/`** — Accept StoreIndex parameter
- **`pkg-manager/`** — `client`, `core`, `headless`, `package-requester` thread StoreIndex
- **`reviewing/`** — `license-scanner`, `sbom`, `dependencies-hierarchy` accept StoreIndex
- **`cache/api/`** — Cache view uses StoreIndex
- **`pnpm/bundle.ts`** — esbuild banner for ExperimentalWarning suppression

## Test plan

- [x] `pnpm --filter @pnpm/store.index test` — Unit tests for StoreIndex CRUD and batching
- [x] `pnpm --filter @pnpm/package-store test` — Store controller lifecycle
- [x] `pnpm --filter @pnpm/package-requester test` — Package requester reads from SQLite index
- [x] `pnpm --filter @pnpm/tarball-fetcher test` — Tarball and git-hosted fetcher writes
- [x] `pnpm --filter @pnpm/headless test` — Headless install
- [x] `pnpm --filter @pnpm/core test` — Core install, side effects, patching
- [x] `pnpm --filter @pnpm/plugin-commands-rebuild test` — Rebuild reads from index
- [x] `pnpm --filter @pnpm/license-scanner test` — License scanning
- [x] e2e tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-06 12:59:04 +01:00
Brandon Cheng
0bf7051fb3 build: enable TypeScript verbatimModuleSyntax compiler option (#10628)
* build: enable TypeScript `verbatimModuleSyntax`

* fix: use `import type` to elide import errors during bundling

```
> pnpm dlx node@runtime:24.6.0 bundle.ts

Error: R] Could not resolve "@npm/types"

    ../pkg-manifest/exportable-manifest/lib/transform/index.js:1:15:
      1 │ import {} from '@npm/types';
        ╵                ~~~~~~~~~~~~

  You can mark the path "@npm/types" as external to exclude it from the bundle, which will remove this error and leave the unresolved path in the bundle.

Error: Build failed with 1 error:
../pkg-manifest/exportable-manifest/lib/transform/index.js:1:15: ERROR: Could not resolve "@npm/types"
    at failureErrorWithLog (/home/runner/work/pnpm/pnpm/node_modules/.pnpm/esbuild@0.25.12/node_modules/esbuild/lib/main.js:1467:15)
```

* fix: use `import type` to elide import errors in tests
2026-03-05 12:07:28 +01:00
Zoltan Kochan
d49aa63cdf fix: update tar to 7.5.10 to fix hardlink path traversal vulnerability
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 12:00:56 +01:00
Zoltan Kochan
b20d3fc486 ci: increase timeout on node.js install 2026-03-04 22:59:40 +01:00
Zoltan Kochan
35d5318ed7 fix: resolve relative path params in global add against CWD (#10848)
* fix: resolve relative path params in global add against CWD

When running `pnpm -g add .`, the "." was resolved relative to the
temporary install directory instead of the user's working directory.
This happened because handleGlobalAdd switches opts.dir to a fresh
temp directory before the dependency selectors are resolved.

Now relative path params (., ./foo, ../bar, file:./foo, link:../bar)
are resolved to absolute paths before the directory is switched.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve relative local selectors against opts.dir instead of process.cwd()

This fixes `pnpm -C <dir> -g add .` where the relative selector would
incorrectly resolve against process.cwd() instead of the user's intended
directory. Also adds test coverage for file: and link: prefixed selectors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(config): stop overwriting dir to globalPkgDir for global commands

Previously, `pnpmConfig.dir` was set to `globalPkgDir` when `--global`
was used. This caused `opts.dir` to point to the global packages
directory instead of the user's CWD, breaking `pnpm -g add .` because
relative paths resolved against the wrong directory.

Now `pnpmConfig.dir` is always set to the user's CWD. Global command
handlers already use `opts.globalPkgDir` where they need the global
packages directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use globalPkgDir in pnpm root -g handler

The root command handler was using opts.dir which no longer points to
the global packages directory. Use opts.globalPkgDir instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 21:52:53 +01:00
Zoltan Kochan
00e7787097 feat: allow global install to override bins owned by the new package (#10828)
* feat: allow global install to override bins owned by the new package

When a package name matches the bin name (e.g., `npm` package providing
`npm` bin), the new package gets priority and can override an existing
bin from another global package. The `npx` bin is also treated as owned
by the `npm` package via a hardcoded override map.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: run benchmarks on node.js 25

* feat: skip linking bins owned by existing global packages instead of failing

When installing a package globally (e.g. node) whose bins conflict with
an already-installed package that owns those bins (e.g. npm owns npm/npx),
the install now succeeds and skips linking the conflicting bins rather
than aborting with GLOBAL_BIN_CONFLICT.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: improve global bin conflict checks for aliased packages and multi-provider bins

Track all new bin providers (not just the last one) so ownership is
correctly resolved when multiple new packages provide the same bin.
Use manifest.name instead of the dependency alias when checking
existing package ownership, fixing incorrect decisions for aliased
installs. Use symlink-dir in tests for Windows compatibility and
remove a non-null assertion in link-bins.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use Set for conflicting bins lookup and fix error hint to use alias

Convert the conflicting bins collection from an array to a Set for O(1)
lookups. Use the dependency alias in the `pnpm remove -g` hint since
that is what the user originally installed with, showing both alias and
manifest name when they differ.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: remove changeset

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:42:04 +01:00
Zoltan Kochan
5979bb7b0c test: replace sinon with built-in jest spying across the repo (#10849)
Replace all sinon.spy/match/withArgs usage with jest.fn, toHaveBeenCalledWith,
mockClear, and mock.calls filtering. Remove sinon and @types/sinon from all
package.json files and the workspace catalog.
2026-03-04 15:00:25 +01:00
Jason Paulos
a540007a26 test: fix additional isRepoPublic flaky tests (#10825)
* test: fix additional isRepoPublic flaky tests

* test: fix nock cleanup order
2026-03-03 22:49:03 +01:00
Victor Sumner
d5fcecac82 test(fetch): verify pnpm fetch populates global virtual store links/ (#10835)
* test(fetch): verify pnpm fetch populates global virtual store links/

pnpm fetch flows through headlessInstall → lockfileToDepGraph →
iteratePkgsForVirtualStore, which respects enableGlobalVirtualStore.
When enabled, packages are written to the store's links/ directory
instead of only the CAS. This behavior is relied upon by tools that
pre-build stores (Docker multistage builds, Nix, CI caching) but was
not covered by any test.

Also adds enableGlobalVirtualStore to FetchCommandOptions type to
match the runtime behavior where the option flows through via the
opts spread.

* test(fetch): use lockfileOnly instead of full install in GVS test

The test only needs a lockfile for the fetch step, so a full install
followed by rimraf of node_modules is unnecessary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 21:40:24 +01:00
Copilot
8b6dae8533 fix(tests): pin real npm packages to prevent moving target test failures (#10844)
* Initial plan

* fix(tests): pin real npm packages in moving target tests

Co-authored-by: KSXGitHub <11488886+KSXGitHub@users.noreply.github.com>

* fix(tests): remove unnecessary addDistTag for controlled @zkochan scope

Co-authored-by: KSXGitHub <11488886+KSXGitHub@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: KSXGitHub <11488886+KSXGitHub@users.noreply.github.com>
2026-03-03 17:47:02 +01:00
Zoltan Kochan
e91ced5c88 docs: update sponsors 2026-03-03 17:34:17 +01:00
Zoltan Kochan
38ad99d6f5 chore: update pnpm-lock.yaml (#10843)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-03 16:24:02 +01:00
Victor Sumner
9f5c0e391f fix(config): respect explicit enableGlobalVirtualStore in CI environments (#10836)
Previously, `ci: true` (auto-detected or configured) unconditionally
set `enableGlobalVirtualStore` to `false`, even when the user had
explicitly enabled it in `pnpm-workspace.yaml` or via CLI. This forced
users to add `ci: false` as a workaround in their workspace config
whenever they wanted GVS in CI-like environments (Nix builds, CI systems
with persistent caches, Docker multistage builds).

Now, the CI override only applies when `enableGlobalVirtualStore` was
not explicitly set (i.e., is `null` or `undefined`). This preserves the
default behavior for ephemeral CI while respecting explicit user
configuration.

Also removes the `ci: false` workarounds from existing tests that
were documenting this limitation.

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-03-03 01:59:57 +01:00
Zoltan Kochan
14b3a46d0b chore: update pnpm-lock.yaml (#10818) 2026-03-03 01:09:23 +01:00
dependabot[bot]
28daa2cb2e build(deps): bump the github-actions group across 1 directory with 4 updates (#10833)
Bumps the github-actions group with 4 updates in the / directory: [actions/upload-artifact](https://github.com/actions/upload-artifact), [actions/download-artifact](https://github.com/actions/download-artifact), [github/codeql-action](https://github.com/github/codeql-action) and [cbrgm/mastodon-github-action](https://github.com/cbrgm/mastodon-github-action).


Updates `actions/upload-artifact` from 6.0.0 to 7.0.0
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](b7c566a772...bbbca2ddaa)

Updates `actions/download-artifact` from 7.0.0 to 8.0.0
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](37930b1c2a...70fc10c6e5)

Updates `github/codeql-action` from 4.31.11 to 4.32.5
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](19b2f06db2...c793b717bc)

Updates `cbrgm/mastodon-github-action` from 2.1.23 to 2.1.26
- [Release notes](https://github.com/cbrgm/mastodon-github-action/releases)
- [Commits](3ebdc72dcd...fc8b40e2ec)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: github/codeql-action
  dependency-version: 4.32.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: cbrgm/mastodon-github-action
  dependency-version: 2.1.26
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-02 23:53:39 +01:00
Zoltan Kochan
b07c371047 fix: remove not needed tsconfig.json 2026-03-02 23:20:04 +01:00
Zoltan Kochan
4793a9380a ci: run benchmarks on node.js 25 2026-03-02 17:10:46 +01:00
Zoltan Kochan
8ab576281d ci: don't install npm separately from node 2026-03-01 20:36:28 +01:00
Zoltan Kochan
7480179239 chore: update pnpm to v11 alpha 12 2026-03-01 20:04:34 +01:00