@pnpm/installing.deps-installer 1101.6.1 → 1101.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -229,8 +229,11 @@ export interface StrictInstallOptions {
229
229
  * summary at the very end instead of one summary per install.
230
230
  */
231
231
  omitSummaryLog: boolean;
232
- /** URL of a pnpm agent server. See the pnpm-agent README. */
233
- agent?: string;
232
+ /**
233
+ * URL of a pnpr server that resolves dependencies server-side and serves
234
+ * only the files missing from the client's store.
235
+ */
236
+ pnprServer?: string;
234
237
  }
235
238
  export type InstallOptions = Partial<StrictInstallOptions> & Pick<StrictInstallOptions, 'storeDir' | 'storeController'>;
236
239
  export interface ProcessedInstallOptions extends StrictInstallOptions {
@@ -15,7 +15,7 @@ import { getContext } from '@pnpm/installing.context';
15
15
  import { getWantedDependencies, resolveDependencies, } from '@pnpm/installing.deps-resolver';
16
16
  import { extendProjectsWithTargetDirs, headlessInstall } from '@pnpm/installing.deps-restorer';
17
17
  import { writeModulesManifest } from '@pnpm/installing.modules-yaml';
18
- import { cleanGitBranchLockfiles, getWantedLockfileName, isEmptyLockfile, readWantedLockfile, writeCurrentLockfile, writeLockfiles, writeWantedLockfile, } from '@pnpm/lockfile.fs';
18
+ import { cleanGitBranchLockfiles, getWantedLockfileName, isEmptyLockfile, readWantedLockfileFile, writeCurrentLockfile, writeLockfiles, writeWantedLockfile, } from '@pnpm/lockfile.fs';
19
19
  import { getPreferredVersionsFromLockfileAndManifests } from '@pnpm/lockfile.preferred-versions';
20
20
  import { calcPatchHashes, createOverridesMapFromParsed, getOutdatedLockfileSetting, } from '@pnpm/lockfile.settings-checker';
21
21
  import { writePnpFile } from '@pnpm/lockfile.to-pnp';
@@ -55,10 +55,10 @@ const BROKEN_LOCKFILE_INTEGRITY_ERRORS = new Set([
55
55
  const DEV_PREINSTALL = 'pnpm:devPreinstall';
56
56
  export async function install(manifest, opts) {
57
57
  const rootDir = (opts.dir ?? process.cwd());
58
- // When a pnpm agent is configured, use server-side resolution
58
+ // When a pnpr server is configured, use server-side resolution
59
59
  // instead of the normal resolution flow.
60
- if (opts.agent) {
61
- return installFromPnpmRegistry(manifest, rootDir, opts);
60
+ if (opts.pnprServer) {
61
+ return installViaPnprServer(manifest, rootDir, opts);
62
62
  }
63
63
  const { updatedCatalogs, updatedProjects: projects, ignoredBuilds, resolutionPolicyViolations } = await mutateModules([
64
64
  {
@@ -120,14 +120,14 @@ export async function mutateModules(projects, maybeOpts) {
120
120
  streamParser.on('data', reporter);
121
121
  }
122
122
  const opts = extendOptions(maybeOpts);
123
- // When a pnpm agent is configured, use server-side resolution. The agent
123
+ // When a pnpr server is configured, use server-side resolution. The pnpr server
124
124
  // path supports `install`, `installSome` (pnpm add), and `uninstallSome`
125
125
  // (pnpm remove). Mutations that need full client-side resolution (update
126
126
  // flags) still fall through to the normal flow.
127
- if (opts.agent && canUseAgentForMutations(projects)) {
128
- const agentResult = await mutateModulesViaAgent(projects, opts);
129
- if (agentResult)
130
- return agentResult;
127
+ if (opts.pnprServer && canUsePnprForMutations(projects)) {
128
+ const pnprResult = await mutateModulesViaPnpr(projects, opts);
129
+ if (pnprResult)
130
+ return pnprResult;
131
131
  }
132
132
  const allowBuild = createAllowBuildFunction(opts);
133
133
  if (!opts.include.dependencies && opts.include.optionalDependencies) {
@@ -262,7 +262,7 @@ export async function mutateModules(projects, maybeOpts) {
262
262
  }
263
263
  let ignoredBuilds = result.ignoredBuilds;
264
264
  if (!opts.ignoreScripts && ignoredBuilds?.size) {
265
- ignoredBuilds = await runUnignoredDependencyBuilds(opts, ignoredBuilds, allowBuild);
265
+ ignoredBuilds = await runUnignoredDependencyBuilds(opts, ignoredBuilds, ctx.wantedLockfile, allowBuild);
266
266
  }
267
267
  // Detect packages whose build approval was revoked between the previous
268
268
  // and current install. A package is considered revoked when it was
@@ -277,10 +277,13 @@ export async function mutateModules(projects, maybeOpts) {
277
277
  for (const depPath of Object.keys(ctx.wantedLockfile.packages)) {
278
278
  if (ignoredBuilds?.has(depPath))
279
279
  continue;
280
- const { name, version } = dp.parse(depPath);
281
- if (!name || !version)
280
+ // The old policy is evaluated with identity trust overridden so that
281
+ // package-name approvals count as they did when they were granted,
282
+ // even for git/tarball artifacts that the current policy no longer
283
+ // approves by name.
284
+ if (oldAllowBuild(depPath, { trustPackageIdentity: true }) !== true)
282
285
  continue;
283
- if (oldAllowBuild(name, version) === true && allowBuild?.(name, version) === undefined) {
286
+ if (allowBuild?.(depPath) === undefined) {
284
287
  ignoredBuilds ??= new Set();
285
288
  ignoredBuilds.add(depPath);
286
289
  }
@@ -536,14 +539,21 @@ export async function mutateModules(projects, maybeOpts) {
536
539
  });
537
540
  if (opts.catalogMode !== 'manual') {
538
541
  for (const wantedDep of wantedDeps) {
542
+ // A `runtime:` specifier (e.g. node from `devEngines.runtime` or
543
+ // `pnpm runtime set`) round-trips to `devEngines.runtime` through the
544
+ // manifest writer, which only recognizes the `runtime:` protocol.
545
+ // Promoting it into a catalog rewrites the entry to `catalog:`, which
546
+ // breaks that round-trip and strands it in `devDependencies`.
547
+ if (wantedDep.bareSpecifier?.startsWith('runtime:'))
548
+ continue;
539
549
  const perDepCatalogName = getPerDepCatalogName(wantedDep, opts.saveCatalogName);
540
550
  const catalogBareSpecifier = `catalog:${perDepCatalogName === 'default' ? '' : perDepCatalogName}`;
541
551
  const catalog = resolveFromCatalog(opts.catalogs, { ...wantedDep, bareSpecifier: catalogBareSpecifier });
542
552
  const catalogDepSpecifier = matchCatalogResolveResult(catalog, pickCatalogSpecifier);
543
553
  if (!catalogDepSpecifier ||
544
554
  wantedDep.bareSpecifier === catalogBareSpecifier ||
545
- semver.validRange(wantedDep.bareSpecifier) &&
546
- semver.validRange(catalogDepSpecifier) &&
555
+ semver.valid(wantedDep.bareSpecifier) &&
556
+ semver.valid(catalogDepSpecifier) &&
547
557
  semver.eq(wantedDep.bareSpecifier, catalogDepSpecifier)) {
548
558
  wantedDep.saveCatalogName = perDepCatalogName;
549
559
  continue;
@@ -789,19 +799,17 @@ Note that in CI environments, this setting is enabled by default.`,
789
799
  }
790
800
  }
791
801
  }
792
- async function runUnignoredDependencyBuilds(opts, previousIgnoredBuilds, allowBuild) {
802
+ async function runUnignoredDependencyBuilds(opts, previousIgnoredBuilds, currentLockfile, allowBuild) {
793
803
  if (!allowBuild) {
794
804
  return previousIgnoredBuilds;
795
805
  }
796
806
  const pkgsToBuild = [];
797
807
  for (const ignoredPkg of previousIgnoredBuilds) {
798
- const parsed = dp.parse(ignoredPkg);
799
- if (!parsed.name || !parsed.version)
808
+ if (currentLockfile.packages?.[ignoredPkg] == null)
800
809
  continue;
801
- const allowed = allowBuild(parsed.name, parsed.version);
802
- if (allowed === true) {
810
+ if (allowBuild(ignoredPkg) === true) {
803
811
  // Package is explicitly allowed - rebuild it
804
- pkgsToBuild.push(`${parsed.name}@${parsed.version}`);
812
+ pkgsToBuild.push(dp.getPkgIdWithPatchHash(ignoredPkg));
805
813
  }
806
814
  }
807
815
  if (pkgsToBuild.length) {
@@ -1409,7 +1417,7 @@ function allMutationsAreInstalls(projects) {
1409
1417
  * Run the pacquet binary if it's configured, otherwise run the JS
1410
1418
  * `headlessInstall`. Callers can hand off any code path that materializes
1411
1419
  * an already-resolved lockfile (workspace partial install, hoisted
1412
- * linker, agent-server install, frozen install) without restating the
1420
+ * linker, pnpr server install, frozen install) without restating the
1413
1421
  * delegation choice.
1414
1422
  *
1415
1423
  * Pacquet reads the wanted lockfile from disk and produces its own
@@ -1423,7 +1431,7 @@ function allMutationsAreInstalls(projects) {
1423
1431
  async function materializeOrDelegate(opts, runHeadlessInstall) {
1424
1432
  if (opts.runPacquet != null) {
1425
1433
  // Reached only from the resolve-then-materialize call sites
1426
- // (workspace-partial, hoisted-linker, agent install). Each ran a
1434
+ // (workspace-partial, hoisted-linker, pnpr server install). Each ran a
1427
1435
  // lockfileOnly resolve pass that emitted one
1428
1436
  // `pnpm:progress status:resolved` per package, so pacquet's
1429
1437
  // duplicate `resolved` events would double the reporter's count.
@@ -1565,7 +1573,7 @@ export class IgnoredBuildsError extends PnpmError {
1565
1573
  }
1566
1574
  }
1567
1575
  function dedupePackageNamesFromIgnoredBuilds(ignoredBuilds) {
1568
- return Array.from(new Set(Array.from(ignoredBuilds ?? []).map(dp.removeSuffix))).sort(lexCompare);
1576
+ return Array.from(new Set(Array.from(ignoredBuilds ?? []).map(depPath => dp.getPkgIdWithPatchHash(depPath)))).sort(lexCompare);
1569
1577
  }
1570
1578
  /**
1571
1579
  * Build injectionTargetsByDepPath from the dependenciesGraph for injected workspace packages
@@ -1588,13 +1596,13 @@ function getProjectsWithTargetDirs(projects, lockfile, dependenciesGraph) {
1588
1596
  return extendProjectsWithTargetDirs(projects, injectionTargetsByDepPath);
1589
1597
  }
1590
1598
  /**
1591
- * Whether the agent path can handle this batch of mutations. The agent flow
1599
+ * Whether the pnpr server path can handle this batch of mutations. The pnpr server flow
1592
1600
  * supports installing the manifest as-is (`install`), adding new deps
1593
1601
  * (`installSome`), and removing deps (`uninstallSome`). It cannot model the
1594
1602
  * client-side update-flag behavior (`update`/`updateMatching`/`updateToLatest`)
1595
1603
  * yet, so those still go through the normal client-side resolver.
1596
1604
  */
1597
- function canUseAgentForMutations(projects) {
1605
+ function canUsePnprForMutations(projects) {
1598
1606
  if (projects.length === 0)
1599
1607
  return false;
1600
1608
  return projects.every((p) => {
@@ -1607,26 +1615,26 @@ function canUseAgentForMutations(projects) {
1607
1615
  });
1608
1616
  }
1609
1617
  /**
1610
- * Pre-process projects for the agent flow:
1618
+ * Pre-process projects for the pnpr server flow:
1611
1619
  * - `install`: send the manifest as-is.
1612
1620
  * - `uninstallSome`: drop the named deps from the manifest before sending,
1613
- * so the agent's resolution naturally produces a lockfile without them.
1621
+ * so the pnpr server's resolution naturally produces a lockfile without them.
1614
1622
  * - `installSome`: parse selectors and merge them into the manifest. The
1615
- * agent server then resolves the merged manifest, and we read the resolved
1623
+ * pnpr server then resolves the merged manifest, and we read the resolved
1616
1624
  * specifiers (with the right save-prefix applied server-side) back from
1617
1625
  * the lockfile importer entries to update the client-side manifest.
1618
1626
  *
1619
1627
  * Returns null if the projects don't map cleanly to allProjects (caller
1620
1628
  * should fall through to the normal flow).
1621
1629
  */
1622
- async function prepareAgentProjects(projects, opts) {
1630
+ async function preparePnprProjects(projects, opts) {
1623
1631
  const allProjects = opts.allProjects ?? [];
1624
1632
  const mutationByRootDir = new Map();
1625
1633
  for (const p of projects) {
1626
1634
  mutationByRootDir.set(p.rootDir, p);
1627
1635
  }
1628
1636
  // Include every workspace project, not just the mutated ones — otherwise
1629
- // the agent's resulting lockfile would only contain the targeted importer
1637
+ // the pnpr server's resulting lockfile would only contain the targeted importer
1630
1638
  // and `headlessInstall` (or a later install) would crash on the missing
1631
1639
  // entries for the other workspace projects. Projects without a mutation
1632
1640
  // are sent with their current manifest (no-op for resolution).
@@ -1685,7 +1693,7 @@ async function prepareAgentProjects(projects, opts) {
1685
1693
  * dependency field per the mutation's `targetDependenciesField` (or the
1686
1694
  * existing field if the dep is already in the manifest, defaulting to
1687
1695
  * `dependencies`). Selectors without a version use `'latest'` so the
1688
- * agent's resolver picks the newest matching release.
1696
+ * pnpr server's resolver picks the newest matching release.
1689
1697
  */
1690
1698
  function mergeInstallSelectors(manifest, mutation) {
1691
1699
  const target = mutation.targetDependenciesField;
@@ -1729,10 +1737,10 @@ function findExistingSpec(alias, manifest) {
1729
1737
  manifest.optionalDependencies?.[alias];
1730
1738
  }
1731
1739
  /**
1732
- * After the agent resolves, copy the lockfile importer's per-dep specifier
1740
+ * After the pnpr server resolves, copy the lockfile importer's per-dep specifier
1733
1741
  * (which the server's resolver computed with the right save-prefix) back
1734
1742
  * into the client manifest for any newly added aliases. We rely on the
1735
- * lockfile because the agent server applies catalog substitution,
1743
+ * lockfile because the pnpr server applies catalog substitution,
1736
1744
  * normalizedBareSpecifier, and save-prefix logic during resolution.
1737
1745
  */
1738
1746
  function applyResolvedSpecsFromLockfile(manifest, importerSnapshot, newDeps, pinnedVersion) {
@@ -1751,7 +1759,7 @@ function applyResolvedSpecsFromLockfile(manifest, importerSnapshot, newDeps, pin
1751
1759
  const resolvedVersion = importerSnapshot[field]?.[dep.alias];
1752
1760
  if (!resolvedVersion || manifest[field]?.[dep.alias] == null)
1753
1761
  continue;
1754
- // The agent server resolved the tree but, on the plain-install path, it
1762
+ // The pnpr server resolved the tree but, on the plain-install path, it
1755
1763
  // writes the user's raw spec (`'latest'`) into the lockfile specifier
1756
1764
  // rather than normalizing to a save-prefix range. Compute the
1757
1765
  // save-prefix spec client-side from the resolved version.
@@ -1762,24 +1770,24 @@ function applyResolvedSpecsFromLockfile(manifest, importerSnapshot, newDeps, pin
1762
1770
  return manifest;
1763
1771
  }
1764
1772
  /**
1765
- * Drives the agent path for a `mutateModules` call across one or more
1766
- * projects. Returns null if the call can't be served by the agent (e.g. one
1773
+ * Drives the pnpr server path for a `mutateModules` call across one or more
1774
+ * projects. Returns null if the call can't be served by the pnpr server (e.g. one
1767
1775
  * of the projects isn't in `allProjects`).
1768
1776
  */
1769
- async function mutateModulesViaAgent(projects, opts) {
1770
- const agentProjects = await prepareAgentProjects(projects, opts);
1771
- if (!agentProjects)
1777
+ async function mutateModulesViaPnpr(projects, opts) {
1778
+ const pnprProjects = await preparePnprProjects(projects, opts);
1779
+ if (!pnprProjects)
1772
1780
  return null;
1773
- // installFromPnpmRegistry runs the headless install for the first
1781
+ // installViaPnprServer runs the headless install for the first
1774
1782
  // project's root and the workspace path for the rest. Pass the
1775
1783
  // pre-processed manifests so resolution sees the post-mutation state.
1776
- const result = await installFromPnpmRegistry(agentProjects[0].manifest, agentProjects[0].rootDir, opts, agentProjects.map((p) => ({ rootDir: p.rootDir, manifest: p.manifest })));
1784
+ const result = await installViaPnprServer(pnprProjects[0].manifest, pnprProjects[0].rootDir, opts, pnprProjects.map((p) => ({ rootDir: p.rootDir, manifest: p.manifest })));
1777
1785
  // For installSome projects, copy resolved specs from the lockfile importer
1778
1786
  // entries back into the client manifest so save-prefix/catalog/etc. take
1779
1787
  // effect (the server applies these during its resolution step).
1780
1788
  const lockfileDir = opts.lockfileDir ?? projects[0].rootDir;
1781
1789
  const mutatedRootDirs = new Set(projects.map((p) => p.rootDir));
1782
- const updatedProjects = agentProjects
1790
+ const updatedProjects = pnprProjects
1783
1791
  .filter((p) => mutatedRootDirs.has(p.rootDir))
1784
1792
  .map((p) => {
1785
1793
  if (p.mutation === 'installSome' && p.newDeps.length > 0) {
@@ -1798,69 +1806,62 @@ async function mutateModulesViaAgent(projects, opts) {
1798
1806
  };
1799
1807
  }
1800
1808
  /**
1801
- * When a pnpm agent is configured, resolve dependencies server-side
1802
- * and download only the missing files. Then run a headless install to link
1803
- * packages into node_modules.
1809
+ * When a pnpr server is configured, resolve dependencies server-side,
1810
+ * then run a headless install that fetches tarballs from the registries
1811
+ * and links packages into node_modules — like a normal install.
1804
1812
  */
1805
- async function installFromPnpmRegistry(manifest, rootDir, opts, allInstallProjects) {
1806
- // The agent path skips client-side resolution, so resolver-side policies
1813
+ async function installViaPnprServer(manifest, rootDir, opts, allInstallProjects) {
1814
+ // The pnpr server path skips client-side resolution, so resolver-side policies
1807
1815
  // can't be enforced locally. `minimumReleaseAge` is forwarded to the
1808
- // agent and enforced server-side. `trustPolicy` has no server-side
1816
+ // pnpr server and enforced server-side. `trustPolicy` has no server-side
1809
1817
  // counterpart yet, so refuse to run under it instead of silently
1810
1818
  // letting through a lockfile the local verifier would reject.
1811
1819
  if (opts.trustPolicy === 'no-downgrade') {
1812
- throw new PnpmError('TRUST_POLICY_INCOMPATIBLE_WITH_AGENT', 'The pnpm agent does not yet enforce `trustPolicy: no-downgrade`, so running an install through the agent under this policy would produce a lockfile that the local verifier rejects.', { hint: 'Unset `trustPolicy` for this install, or disable the agent (unset `--agent` / `agent` in pnpm-workspace.yaml) so resolution runs locally and the trust check applies.' });
1820
+ throw new PnpmError('TRUST_POLICY_INCOMPATIBLE_WITH_PNPR', 'The pnpr server does not yet enforce `trustPolicy: no-downgrade`, so running an install through it under this policy would produce a lockfile that the local verifier rejects.', { hint: 'Unset `trustPolicy` for this install, or disable the pnpr server (unset `--pnpr-server` / `pnprServer` in pnpm-workspace.yaml) so resolution runs locally and the trust check applies.' });
1813
1821
  }
1814
- const { fetchFromPnpmRegistry } = await import('@pnpm/agent.client');
1815
- const { StoreIndex } = await import('@pnpm/store.index');
1816
- const { setImportConcurrency } = await import('@pnpm/worker');
1817
- // Raise import concurrency for this install only the agent path has no
1818
- // concurrent fetching competing for workers. Restore afterwards so we
1819
- // don't leak a process-wide mutation to other installs (e.g. tests).
1820
- const restoreImportConcurrency = setImportConcurrency(6);
1822
+ const { resolveViaPnprServer } = await import('@pnpm/pnpr.client');
1823
+ const { createGetAuthHeaderByURI, getAuthHeadersFromCreds } = await import('@pnpm/network.auth-header');
1824
+ // Forward the whole credential map (the registries a graph touches
1825
+ // aren't known up front), so the server attaches the right token per
1826
+ // URL. `authorization` also identifies the caller to pnpr's gate.
1827
+ const configByUri = opts.configByUri ?? {};
1828
+ const forwardedAuthHeaders = getAuthHeadersFromCreds(configByUri);
1829
+ const pnprAuthorization = createGetAuthHeaderByURI(configByUri)(opts.pnprServer);
1821
1830
  try {
1822
1831
  const lockfileDir = opts.lockfileDir ?? rootDir;
1823
- // Read existing lockfile if available
1824
- const existingLockfile = await readWantedLockfile(lockfileDir, {
1832
+ // Read the existing lockfile (if any) in its on-disk shape — that's
1833
+ // what the pnpr server protocol carries, so no conversion is needed before
1834
+ // sending it.
1835
+ const existingLockfile = await readWantedLockfileFile(lockfileDir, {
1825
1836
  ignoreIncompatible: true,
1826
1837
  }).catch(() => null);
1827
- logger.info({ message: 'Resolving dependencies via pnpm agent', prefix: rootDir });
1828
- // Open the store index to read integrities and write new entries.
1829
- // Close it in a finally so a failure in fetchFromPnpmRegistry doesn't
1830
- // leak an open SQLite handle (on Windows that also blocks store cleanup).
1831
- const storeIndex = new StoreIndex(opts.storeDir);
1832
- let lockfile, agentStats, fileDownloads, indexEntries;
1833
- try {
1834
- // Build projects list for workspace support.
1835
- // Normalize separators to POSIX — on Windows `path.relative` returns
1836
- // backslashes, which the agent server rejects (it treats `\` as an
1837
- // unsafe/YAML-injection character and normalizes paths as POSIX).
1838
- const projectsList = allInstallProjects && allInstallProjects.length > 1
1839
- ? allInstallProjects.map(p => ({
1840
- dir: (path.relative(lockfileDir, p.rootDir) || '.').split(path.sep).join('/'),
1841
- dependencies: p.manifest.dependencies,
1842
- devDependencies: p.manifest.devDependencies,
1843
- }))
1844
- : undefined;
1845
- ({ lockfile, stats: agentStats, fileDownloads, indexEntries } = await fetchFromPnpmRegistry({
1846
- registryUrl: opts.agent,
1847
- storeDir: opts.storeDir,
1848
- storeIndex,
1849
- dependencies: projectsList ? undefined : manifest.dependencies,
1850
- devDependencies: projectsList ? undefined : manifest.devDependencies,
1851
- projects: projectsList,
1852
- overrides: opts.overrides,
1853
- minimumReleaseAge: opts.minimumReleaseAge,
1854
- lockfile: existingLockfile ?? undefined,
1855
- }));
1856
- // Write store index entries so headless install finds them.
1857
- const { writeRawIndexEntries } = await import('@pnpm/agent.client');
1858
- writeRawIndexEntries(indexEntries, storeIndex);
1859
- storeIndex.checkpoint();
1860
- }
1861
- finally {
1862
- storeIndex.close();
1863
- }
1838
+ logger.info({ message: 'Resolving dependencies via the pnpr server', prefix: rootDir });
1839
+ // Build projects list for workspace support.
1840
+ // Normalize separators to POSIX on Windows `path.relative` returns
1841
+ // backslashes, which the pnpr server rejects (it treats `\` as an
1842
+ // unsafe/YAML-injection character and normalizes paths as POSIX).
1843
+ const projectsList = allInstallProjects && allInstallProjects.length > 1
1844
+ ? allInstallProjects.map(p => ({
1845
+ dir: (path.relative(lockfileDir, p.rootDir) || '.').split(path.sep).join('/'),
1846
+ dependencies: p.manifest.dependencies,
1847
+ devDependencies: p.manifest.devDependencies,
1848
+ optionalDependencies: p.manifest.optionalDependencies,
1849
+ }))
1850
+ : undefined;
1851
+ const { lockfile, stats: pnprStats } = await resolveViaPnprServer({
1852
+ registryUrl: opts.pnprServer,
1853
+ dependencies: projectsList ? undefined : manifest.dependencies,
1854
+ devDependencies: projectsList ? undefined : manifest.devDependencies,
1855
+ optionalDependencies: projectsList ? undefined : manifest.optionalDependencies,
1856
+ projects: projectsList,
1857
+ registry: opts.registries?.default,
1858
+ namedRegistries: opts.namedRegistries,
1859
+ authHeaders: forwardedAuthHeaders,
1860
+ authorization: pnprAuthorization,
1861
+ overrides: opts.overrides,
1862
+ minimumReleaseAge: opts.minimumReleaseAge,
1863
+ lockfile: existingLockfile ?? undefined,
1864
+ });
1864
1865
  await writeWantedLockfileAndRecordVerified({
1865
1866
  lockfileDir,
1866
1867
  lockfile,
@@ -1870,46 +1871,28 @@ async function installFromPnpmRegistry(manifest, rootDir, opts, allInstallProjec
1870
1871
  mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
1871
1872
  });
1872
1873
  logger.info({
1873
- message: `Resolved ${agentStats.totalPackages} packages: ${agentStats.alreadyInStore} cached, ${agentStats.filesToDownload} files to download`,
1874
+ message: `Resolved ${pnprStats.totalPackages} packages`,
1874
1875
  prefix: rootDir,
1875
1876
  });
1876
- // Wrap fetchPackage to:
1877
- // 1. Wait for agent file downloads before checking the store
1878
- // 2. Skip integrity verification — files just written from the agent
1879
- // are guaranteed correct (server verified, no rehashing needed)
1880
- const { readPkgFromCafs } = await import('@pnpm/worker');
1881
- const { storeIndexKey: _storeIndexKey } = await import('@pnpm/store.index');
1882
- const wrappedStoreController = {
1883
- ...opts.storeController,
1884
- fetchPackage: async (fetchOpts) => {
1885
- await fileDownloads;
1886
- const resolution = fetchOpts.pkg.resolution;
1887
- const integrity = resolution?.integrity;
1888
- // Fall through to the regular store controller for git-hosted tarballs.
1889
- // Their cached entry lives under gitHostedStoreIndexKey (preserves the
1890
- // built/not-built dimension), not the integrity-keyed path the agent
1891
- // uses for npm tarballs. See @pnpm/store.pkg-finder for the rationale.
1892
- if (integrity && !resolution?.gitHosted) {
1893
- const filesIndexFile = _storeIndexKey(integrity, fetchOpts.pkg.id);
1894
- const result = await readPkgFromCafs({ storeDir: opts.storeDir, verifyStoreIntegrity: false }, filesIndexFile, { readManifest: true, expectedPkg: { name: fetchOpts.pkg.name, version: fetchOpts.pkg.version } });
1895
- return {
1896
- fetching: () => Promise.resolve({
1897
- files: result.files,
1898
- bundledManifest: result.bundledManifest,
1899
- integrity,
1900
- }),
1901
- filesIndexFile,
1902
- };
1903
- }
1904
- return opts.storeController.fetchPackage(fetchOpts);
1905
- },
1906
- };
1877
+ // `--lockfile-only`: the pnpr server resolved and we wrote the lockfile, but
1878
+ // pnpm fetches nothing and links nothing in this mode — stop before the
1879
+ // headless install. See https://github.com/pnpm/pnpm/issues/12146.
1880
+ if (opts.lockfileOnly) {
1881
+ return {
1882
+ updatedCatalogs: undefined,
1883
+ updatedManifest: manifest,
1884
+ ignoredBuilds: undefined,
1885
+ stats: { added: 0, removed: 0, linkedToRoot: 0 },
1886
+ lockfile,
1887
+ resolutionPolicyViolations: [],
1888
+ };
1889
+ }
1890
+ // The pnpr server only resolves; it serves no file content. Fetch every
1891
+ // tarball from the registries with the regular store controller, in
1892
+ // parallel, exactly like a normal install. See
1893
+ // https://github.com/pnpm/pnpm/issues/12230.
1907
1894
  const headlessOpts = {
1908
1895
  ...opts,
1909
- // Skip re-verifying files just written from the agent — they're
1910
- // guaranteed correct (server verified, no rehashing needed).
1911
- verifyStoreIntegrity: false,
1912
- storeController: wrappedStoreController,
1913
1896
  dir: rootDir,
1914
1897
  lockfileDir,
1915
1898
  engineStrict: opts.engineStrict ?? false,
@@ -1948,13 +1931,13 @@ async function installFromPnpmRegistry(manifest, rootDir, opts, allInstallProjec
1948
1931
  updatedManifest: manifest,
1949
1932
  ignoredBuilds,
1950
1933
  // Pacquet doesn't surface a structured stats return; default to
1951
- // zeros so the agent-path's non-optional `stats` slot is filled.
1934
+ // zeros so the pnpr server's non-optional `stats` slot is filled.
1952
1935
  // The reporter still renders accurate counts from pacquet's
1953
1936
  // `pnpm:stats` log events.
1954
1937
  stats: stats ?? { added: 0, removed: 0, linkedToRoot: 0 },
1955
1938
  lockfile,
1956
- // Server-side resolution (pnpm agent) enforces `minimumReleaseAge`
1957
- // itself — the agent picks only mature versions and the lockfile
1939
+ // Server-side resolution (pnpr server) enforces `minimumReleaseAge`
1940
+ // itself — the pnpr server picks only mature versions and the lockfile
1958
1941
  // can't contain immature entries to auto-collect. `trustPolicy` is
1959
1942
  // guarded above (we refuse to enter this path when it's set), so
1960
1943
  // there's nothing for the install command to react to here.
@@ -1966,7 +1949,6 @@ async function installFromPnpmRegistry(manifest, rootDir, opts, allInstallProjec
1966
1949
  // normal install path does the same; skipping it here would leave
1967
1950
  // pending writes on disk and diverge from lifecycle expectations.
1968
1951
  await opts.storeController.close();
1969
- restoreImportConcurrency();
1970
1952
  }
1971
1953
  }
1972
1954
  //# sourceMappingURL=index.js.map
@@ -328,7 +328,7 @@ async function linkAllPkgs(storeController, depNodes, opts) {
328
328
  depNode.requiresBuild = files.requiresBuild;
329
329
  let sideEffectsCacheKey;
330
330
  if (opts.sideEffectsCacheRead && files.sideEffectsMaps && !isEmpty(files.sideEffectsMaps)) {
331
- if (opts.allowBuild?.(depNode.name, depNode.version) === true) {
331
+ if (opts.allowBuild?.(depNode.depPath) === true) {
332
332
  sideEffectsCacheKey = calcDepState(opts.depGraph, opts.depsStateCache, depNode.depPath, {
333
333
  includeDepGraphHash: !opts.ignoreScripts && depNode.requiresBuild, // true when is built
334
334
  patchFileHash: depNode.patch?.hash,
@@ -1,4 +1,5 @@
1
1
  import { hashObject } from '@pnpm/crypto.object-hasher';
2
+ import { withResolutionShapeCacheIdentity } from './verifyLockfileResolutions.js';
2
3
  import { recordVerification } from './verifyLockfileResolutionsCache.js';
3
4
  /**
4
5
  * Records the post-resolution lockfile as verified so the next install
@@ -17,7 +18,7 @@ export function recordLockfileVerified(opts) {
17
18
  return;
18
19
  recordVerification(opts.cacheDir, {
19
20
  lockfilePath: opts.lockfilePath,
20
- verifiers: opts.resolutionVerifiers,
21
+ verifiers: withResolutionShapeCacheIdentity(opts.resolutionVerifiers),
21
22
  hashLockfile: () => hashObject(opts.lockfile),
22
23
  });
23
24
  }
@@ -1,6 +1,17 @@
1
1
  import type { LockfileObject } from '@pnpm/lockfile.fs';
2
2
  import type { ResolutionPolicyViolation, ResolutionVerifier } from '@pnpm/resolving.resolver-base';
3
+ import { type VerifierCacheIdentity } from './verifyLockfileResolutionsCache.js';
3
4
  export type { ResolutionPolicyViolation };
5
+ export declare const RESOLUTION_SHAPE_MISMATCH_VIOLATION_CODE = "RESOLUTION_SHAPE_MISMATCH";
6
+ /**
7
+ * Every verifier list that flows into the verification cache must carry
8
+ * the resolution-shape identity, so records written before the shape rule
9
+ * existed cannot stat-fast-path around it. Used by the gate itself and by
10
+ * {@link recordLockfileVerified}, whose freshly-resolved lockfile satisfies
11
+ * the shape invariant by construction (the writer derives every key from
12
+ * the resolution it just produced).
13
+ */
14
+ export declare function withResolutionShapeCacheIdentity(verifiers: readonly VerifierCacheIdentity[]): VerifierCacheIdentity[];
4
15
  export interface VerifyLockfileResolutionsOptions {
5
16
  concurrency?: number;
6
17
  /**
@@ -1,7 +1,7 @@
1
1
  import { lockfileVerificationLogger } from '@pnpm/core-loggers';
2
2
  import { hashObject } from '@pnpm/crypto.object-hasher';
3
3
  import { PnpmError } from '@pnpm/error';
4
- import { nameVerFromPkgSnapshot } from '@pnpm/lockfile.utils';
4
+ import { isGitHostedTarballUrl, nameVerFromPkgSnapshot } from '@pnpm/lockfile.utils';
5
5
  import pLimit from 'p-limit';
6
6
  import { recordVerification, tryLockfileVerificationCache, } from './verifyLockfileResolutionsCache.js';
7
7
  // Cap the per-entry breakdown so a verifier rejecting hundreds of entries
@@ -12,6 +12,22 @@ const MAX_VIOLATIONS_TO_PRINT = 20;
12
12
  // (Math.min(64, Math.max(workers*3, 16))); keep them aligned so the
13
13
  // verification pass doesn't push past what the rest of the install respects.
14
14
  const DEFAULT_CONCURRENCY = 16;
15
+ export const RESOLUTION_SHAPE_MISMATCH_VIOLATION_CODE = 'RESOLUTION_SHAPE_MISMATCH';
16
+ const RESOLUTION_SHAPE_CACHE_IDENTITY = {
17
+ policy: { resolutionShapeCheck: true },
18
+ canTrustPastCheck: (cached) => cached.resolutionShapeCheck === true,
19
+ };
20
+ /**
21
+ * Every verifier list that flows into the verification cache must carry
22
+ * the resolution-shape identity, so records written before the shape rule
23
+ * existed cannot stat-fast-path around it. Used by the gate itself and by
24
+ * {@link recordLockfileVerified}, whose freshly-resolved lockfile satisfies
25
+ * the shape invariant by construction (the writer derives every key from
26
+ * the resolution it just produced).
27
+ */
28
+ export function withResolutionShapeCacheIdentity(verifiers) {
29
+ return [...verifiers, RESOLUTION_SHAPE_CACHE_IDENTITY];
30
+ }
15
31
  /**
16
32
  * Policy-neutral pass that asks every resolver-supplied
17
33
  * {@link ResolutionVerifier} to check every entry in a lockfile loaded
@@ -40,8 +56,6 @@ const DEFAULT_CONCURRENCY = 16;
40
56
  * the lookup logic.
41
57
  */
42
58
  export async function verifyLockfileResolutions(lockfile, verifiers, options) {
43
- if (verifiers.length === 0)
44
- return;
45
59
  if (!lockfile.packages)
46
60
  return;
47
61
  // Caching kicks in only when the caller surfaced both a writable
@@ -51,6 +65,7 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
51
65
  const cache = options?.cacheDir && options?.lockfilePath
52
66
  ? { cacheDir: options.cacheDir, lockfilePath: options.lockfilePath }
53
67
  : undefined;
68
+ const cacheVerifiers = withResolutionShapeCacheIdentity(verifiers);
54
69
  let cachePrecomputed;
55
70
  // hashObject streams and is key-order-stable, unlike JSON.stringify.
56
71
  let cachedHash;
@@ -62,7 +77,7 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
62
77
  if (cache) {
63
78
  const result = tryLockfileVerificationCache(cache.cacheDir, {
64
79
  lockfilePath: cache.lockfilePath,
65
- verifiers,
80
+ verifiers: cacheVerifiers,
66
81
  hashLockfile,
67
82
  });
68
83
  if (result.hit)
@@ -76,12 +91,17 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
76
91
  // A degenerate lockfile where every snapshot fails the
77
92
  // name/version extraction (so candidates is empty) skips emission
78
93
  // entirely — no work, no noise.
79
- const candidates = collectCandidates(lockfile);
94
+ const { candidates, shapeViolations } = collectCandidates(lockfile);
95
+ if (shapeViolations.length > 0) {
96
+ throw buildVerificationError(shapeViolations);
97
+ }
98
+ if (verifiers.length === 0)
99
+ return;
80
100
  if (candidates.size === 0) {
81
101
  if (cache) {
82
102
  recordVerification(cache.cacheDir, {
83
103
  lockfilePath: cache.lockfilePath,
84
- verifiers,
104
+ verifiers: cacheVerifiers,
85
105
  hashLockfile,
86
106
  }, cachePrecomputed);
87
107
  }
@@ -108,7 +128,7 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
108
128
  if (cache) {
109
129
  recordVerification(cache.cacheDir, {
110
130
  lockfilePath: cache.lockfilePath,
111
- verifiers,
131
+ verifiers: cacheVerifiers,
112
132
  hashLockfile,
113
133
  }, cachePrecomputed);
114
134
  }
@@ -174,38 +194,97 @@ export async function collectResolutionPolicyViolations(lockfile, verifiers, opt
174
194
  return [];
175
195
  if (!lockfile.packages)
176
196
  return [];
177
- return iterateLockfileViolations(collectCandidates(lockfile), verifiers, options?.concurrency);
197
+ // Shape violations are deliberately not collected here: they are hard
198
+ // tampering failures, not policy picks a caller may auto-exclude.
199
+ return iterateLockfileViolations(collectCandidates(lockfile).candidates, verifiers, options?.concurrency);
200
+ }
201
+ function isRegistryShapedResolution(resolution) {
202
+ if (resolution == null)
203
+ return true;
204
+ if (typeof resolution !== 'object')
205
+ return false;
206
+ const { type, gitHosted, tarball, variants } = resolution;
207
+ if (type === 'variations') {
208
+ return Array.isArray(variants) && variants.every((variant) => isRegistryShapedResolution(variant?.resolution));
209
+ }
210
+ // Custom resolver protocols (`type: 'custom:*'`) are a legitimate
211
+ // non-registry source the user opted into. They can only be materialized by
212
+ // a project-configured custom fetcher — an unrecognized custom type throws at
213
+ // fetch time (see @pnpm/fetching.pick-fetcher) — so a forged custom type
214
+ // cannot launder an artifact past this gate into a build.
215
+ if (typeof type === 'string' && type.startsWith('custom:'))
216
+ return true;
217
+ if (type != null)
218
+ return false;
219
+ // Plain tarball / registry resolution. The lockfile is parsed from YAML
220
+ // without schema validation, so the `gitHosted` flag is not trustworthy on
221
+ // its own: a tampered entry could set a non-boolean (dodging a strict
222
+ // `=== true`) or an explicit `false` on a git-host URL (the loader only
223
+ // backfills the flag when absent). Treat any non-boolean flag as git-hosted
224
+ // and gate on the URL so the verdict never depends on the flag alone.
225
+ if (gitHosted != null && (typeof gitHosted !== 'boolean' || gitHosted))
226
+ return false;
227
+ // A registry resolution reconstructs its tarball URL from name+version, so
228
+ // an absent/empty `tarball` is registry-shaped. When a URL is present it
229
+ // must be an http(s) registry artifact: the npm verifier's tarball-URL
230
+ // binding skips non-http(s) schemes (file:, etc.), so a `file:` tarball
231
+ // under a name@semver key would otherwise be trusted with no safety net.
232
+ if (typeof tarball === 'string' && tarball !== '') {
233
+ if (!/^https?:\/\//i.test(tarball))
234
+ return false;
235
+ if (isGitHostedTarballUrl(tarball))
236
+ return false;
237
+ }
238
+ return true;
178
239
  }
179
240
  // depPath can include peer-dependency and patch_hash suffixes (e.g.
180
241
  // `react@18.0.0(peer)(patch_hash=…)`); the same (name, version) pair may
181
242
  // therefore appear multiple times. Dedupe so we issue at most one
182
243
  // verification per package version.
183
244
  //
184
- // Include a serialization of `resolution` in the key so two entries that
185
- // share a (name, version) but differ in *what* was resolved (e.g. one
186
- // pinned via npm, another via a git URL under the same alias) don't
187
- // collapse: if the wrong shape wins the dedup, a protocol-scoped
188
- // verifier short-circuits on the surviving entry and the real one is
245
+ // Include a serialization of `resolution` and `nonSemverVersion` in the key
246
+ // so two entries that share a (name, version) but differ in *what* was
247
+ // resolved (e.g. one pinned via npm, another a URL-keyed tarball whose
248
+ // snapshot copied the same semver `version` from its manifest) don't
249
+ // collapse: `nonSemverVersion` flips whether the npm verifier enforces or
250
+ // skips the tarball/policy checks, so if the wrong shape wins the dedup the
251
+ // surviving entry is verified under the wrong rules and the real one is
189
252
  // never checked.
190
253
  function collectCandidates(lockfile) {
191
254
  const candidates = new Map();
255
+ const shapeViolations = [];
192
256
  for (const [depPath, snapshot] of Object.entries(lockfile.packages ?? {})) {
193
- const { name, version } = nameVerFromPkgSnapshot(depPath, snapshot);
257
+ const { name, version, nonSemverVersion } = nameVerFromPkgSnapshot(depPath, snapshot);
194
258
  if (!name || !version)
195
259
  continue;
196
- const key = `${name}@${version}@${JSON.stringify(snapshot.resolution)}`;
260
+ // A registry-style depPath (name@semver) must be backed by a
261
+ // registry-shaped resolution: the allowBuilds policy derives a
262
+ // trusted package identity from that key shape, which is only sound
263
+ // while this invariant holds. The check is offline, so it applies
264
+ // even when no policy verifiers are active.
265
+ if (nonSemverVersion == null && !isRegistryShapedResolution(snapshot.resolution)) {
266
+ shapeViolations.push({
267
+ name,
268
+ version,
269
+ resolution: snapshot.resolution,
270
+ code: RESOLUTION_SHAPE_MISMATCH_VIOLATION_CODE,
271
+ reason: 'a registry-style dependency path is backed by a non-registry resolution',
272
+ });
273
+ }
274
+ const key = `${name}@${version}@${nonSemverVersion ?? ''}@${JSON.stringify(snapshot.resolution)}`;
197
275
  candidates.set(key, {
198
276
  name,
199
277
  version,
278
+ nonSemverVersion,
200
279
  resolution: snapshot.resolution,
201
280
  });
202
281
  }
203
- return candidates;
282
+ return { candidates, shapeViolations };
204
283
  }
205
284
  async function iterateLockfileViolations(candidates, verifiers, concurrency) {
206
285
  const violations = [];
207
286
  const limit = pLimit(concurrency ?? DEFAULT_CONCURRENCY);
208
- await Promise.all(Array.from(candidates.values(), ({ name, version, resolution }) => limit(async () => {
287
+ await Promise.all(Array.from(candidates.values(), ({ name, version, nonSemverVersion, resolution }) => limit(async () => {
209
288
  // Fan out across every active verifier; each handles its own
210
289
  // protocol short-circuit (e.g. the npm verifier returns ok:true for
211
290
  // git resolutions). We stop at the first failure per entry so a
@@ -213,7 +292,7 @@ async function iterateLockfileViolations(candidates, verifiers, concurrency) {
213
292
  // same (name, version).
214
293
  for (const verifier of verifiers) {
215
294
  // eslint-disable-next-line no-await-in-loop
216
- const result = await verifier.verify(resolution, { name, version });
295
+ const result = await verifier.verify(resolution, { name, version, nonSemverVersion });
217
296
  if (!result.ok) {
218
297
  violations.push({ name, version, resolution, code: result.code, reason: result.reason });
219
298
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/installing.deps-installer",
3
- "version": "1101.6.1",
3
+ "version": "1101.8.0",
4
4
  "description": "Fast, disk space efficient installation engine",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -28,7 +28,10 @@
28
28
  ],
29
29
  "license": "MIT",
30
30
  "funding": "https://opencollective.com/pnpm",
31
- "repository": "https://github.com/pnpm/pnpm/tree/main/installing/deps-installer",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/pnpm/pnpm/tree/main/installing/deps-installer"
34
+ },
32
35
  "homepage": "https://github.com/pnpm/pnpm/tree/main/installing/deps-installer#readme",
33
36
  "bugs": {
34
37
  "url": "https://github.com/pnpm/pnpm/issues"
@@ -62,60 +65,61 @@
62
65
  "ramda": "npm:@pnpm/ramda@0.28.1",
63
66
  "run-groups": "^5.0.0",
64
67
  "semver": "^7.8.1",
65
- "@pnpm/agent.client": "1.0.8",
66
- "@pnpm/bins.remover": "1100.0.6",
67
- "@pnpm/building.after-install": "1101.0.18",
68
- "@pnpm/bins.linker": "1100.0.10",
69
- "@pnpm/building.during-install": "1101.0.15",
68
+ "@pnpm/bins.linker": "1100.0.12",
69
+ "@pnpm/building.after-install": "1101.0.20",
70
70
  "@pnpm/catalogs.protocol-parser": "1100.0.0",
71
- "@pnpm/building.policy": "1100.0.7",
71
+ "@pnpm/building.during-install": "1101.0.17",
72
+ "@pnpm/building.policy": "1100.0.9",
73
+ "@pnpm/catalogs.types": "1100.0.0",
72
74
  "@pnpm/catalogs.resolver": "1100.0.0",
75
+ "@pnpm/config.normalize-registries": "1100.0.7",
73
76
  "@pnpm/config.matcher": "1100.0.1",
74
- "@pnpm/config.normalize-registries": "1100.0.5",
75
- "@pnpm/catalogs.types": "1100.0.0",
76
- "@pnpm/config.parse-overrides": "1100.0.1",
77
- "@pnpm/deps.graph-hasher": "1100.2.2",
77
+ "@pnpm/core-loggers": "1100.1.4",
78
+ "@pnpm/constants": "1100.0.0",
78
79
  "@pnpm/crypto.hash": "1100.0.1",
80
+ "@pnpm/deps.graph-hasher": "1100.2.4",
79
81
  "@pnpm/crypto.object-hasher": "1100.0.0",
80
82
  "@pnpm/deps.graph-sequencer": "1100.0.0",
81
- "@pnpm/deps.path": "1100.0.5",
82
- "@pnpm/exec.lifecycle": "1100.0.14",
83
- "@pnpm/hooks.read-package-hook": "1100.0.5",
84
- "@pnpm/fs.symlink-dependency": "1100.0.6",
85
- "@pnpm/fs.read-modules-dir": "1100.0.1",
86
- "@pnpm/installing.deps-resolver": "1100.1.6",
87
- "@pnpm/installing.deps-restorer": "1101.1.8",
83
+ "@pnpm/config.parse-overrides": "1100.0.1",
88
84
  "@pnpm/error": "1100.0.0",
89
- "@pnpm/constants": "1100.0.0",
90
- "@pnpm/hooks.types": "1100.0.9",
91
- "@pnpm/installing.context": "1100.0.14",
92
- "@pnpm/installing.linking.direct-dep-linker": "1100.0.6",
93
- "@pnpm/installing.modules-yaml": "1100.0.6",
94
- "@pnpm/installing.linking.modules-cleaner": "1100.1.4",
95
- "@pnpm/lockfile.filtering": "1100.1.3",
96
- "@pnpm/installing.package-requester": "1101.0.10",
97
- "@pnpm/installing.linking.hoist": "1100.0.10",
98
- "@pnpm/lockfile.fs": "1100.1.2",
99
- "@pnpm/lockfile.pruner": "1100.0.8",
100
- "@pnpm/lockfile.settings-checker": "1100.0.14",
101
- "@pnpm/lockfile.preferred-versions": "1100.0.12",
102
- "@pnpm/lockfile.to-pnp": "1100.0.11",
103
- "@pnpm/lockfile.verification": "1100.0.14",
104
- "@pnpm/lockfile.walker": "1100.0.8",
105
- "@pnpm/lockfile.utils": "1100.0.10",
106
- "@pnpm/patching.config": "1100.0.5",
107
- "@pnpm/core-loggers": "1100.1.2",
85
+ "@pnpm/fs.read-modules-dir": "1100.0.1",
86
+ "@pnpm/exec.lifecycle": "1100.0.16",
87
+ "@pnpm/fs.symlink-dependency": "1100.0.8",
88
+ "@pnpm/hooks.read-package-hook": "1100.0.7",
89
+ "@pnpm/installing.deps-restorer": "1101.1.10",
90
+ "@pnpm/hooks.types": "1100.0.11",
91
+ "@pnpm/installing.context": "1100.0.16",
92
+ "@pnpm/installing.deps-resolver": "1100.2.1",
93
+ "@pnpm/installing.linking.direct-dep-linker": "1100.0.8",
94
+ "@pnpm/deps.path": "1100.0.7",
95
+ "@pnpm/installing.linking.modules-cleaner": "1100.1.6",
96
+ "@pnpm/installing.linking.hoist": "1100.0.12",
97
+ "@pnpm/bins.remover": "1100.0.8",
98
+ "@pnpm/installing.modules-yaml": "1100.0.8",
99
+ "@pnpm/lockfile.fs": "1100.1.4",
100
+ "@pnpm/installing.package-requester": "1101.0.12",
101
+ "@pnpm/lockfile.pruner": "1100.0.10",
102
+ "@pnpm/lockfile.settings-checker": "1100.0.16",
103
+ "@pnpm/lockfile.preferred-versions": "1100.0.14",
104
+ "@pnpm/lockfile.filtering": "1100.1.5",
105
+ "@pnpm/lockfile.to-pnp": "1100.0.13",
106
+ "@pnpm/lockfile.verification": "1100.0.16",
107
+ "@pnpm/lockfile.utils": "1100.0.12",
108
+ "@pnpm/lockfile.walker": "1100.0.10",
109
+ "@pnpm/network.auth-header": "1101.1.1",
108
110
  "@pnpm/resolving.parse-wanted-dependency": "1100.0.1",
109
- "@pnpm/pkg-manifest.utils": "1100.2.1",
110
- "@pnpm/resolving.resolver-base": "1100.3.1",
111
+ "@pnpm/pkg-manifest.utils": "1100.2.3",
112
+ "@pnpm/patching.config": "1100.0.7",
113
+ "@pnpm/pnpr.client": "1.2.0",
114
+ "@pnpm/resolving.resolver-base": "1100.4.1",
115
+ "@pnpm/store.controller-types": "1100.1.4",
116
+ "@pnpm/workspace.project-manifest-reader": "1100.0.11",
111
117
  "@pnpm/store.index": "1100.1.0",
112
- "@pnpm/types": "1101.2.0",
113
- "@pnpm/workspace.project-manifest-reader": "1100.0.9",
114
- "@pnpm/store.controller-types": "1100.1.2"
118
+ "@pnpm/types": "1101.3.1"
115
119
  },
116
120
  "peerDependencies": {
117
121
  "@pnpm/logger": "^1001.0.1",
118
- "@pnpm/worker": "^1100.1.8"
122
+ "@pnpm/worker": "^1100.1.10"
119
123
  },
120
124
  "devDependencies": {
121
125
  "@jest/globals": "30.3.0",
@@ -137,22 +141,22 @@
137
141
  "symlink-dir": "^10.0.1",
138
142
  "write-json-file": "^7.0.0",
139
143
  "write-yaml-file": "^6.0.0",
140
- "@pnpm/assert-project": "1100.0.12",
141
- "@pnpm/installing.deps-installer": "1101.6.1",
142
- "@pnpm/assert-store": "1100.0.12",
143
- "@pnpm/lockfile.types": "1100.0.8",
144
+ "@pnpm/assert-project": "1100.0.14",
145
+ "@pnpm/lockfile.types": "1100.0.10",
146
+ "@pnpm/installing.deps-installer": "1101.8.0",
147
+ "@pnpm/assert-store": "1100.0.14",
144
148
  "@pnpm/network.git-utils": "1100.0.1",
145
- "@pnpm/pkg-manifest.reader": "1100.0.5",
146
149
  "@pnpm/logger": "1100.0.0",
147
- "@pnpm/prepare": "1100.0.12",
148
- "@pnpm/store.path": "1100.0.1",
149
- "@pnpm/resolving.registry.types": "1100.1.0",
150
+ "@pnpm/pkg-manifest.reader": "1100.0.7",
151
+ "@pnpm/resolving.registry.types": "1100.1.2",
152
+ "@pnpm/prepare": "1100.0.14",
150
153
  "@pnpm/test-fixtures": "1100.0.0",
151
- "@pnpm/store.cafs": "1100.1.7",
152
- "@pnpm/testing.mock-agent": "1100.0.8",
153
- "@pnpm/testing.temp-store": "1100.1.5",
154
+ "@pnpm/store.cafs": "1100.1.9",
155
+ "@pnpm/testing.mock-agent": "1101.0.1",
154
156
  "@pnpm/test-ipc-server": "1100.0.0",
155
- "@pnpm/testing.registry-mock": "1100.0.2"
157
+ "@pnpm/store.path": "1100.0.1",
158
+ "@pnpm/testing.temp-store": "1100.1.7",
159
+ "@pnpm/testing.registry-mock": "1100.0.4"
156
160
  },
157
161
  "engines": {
158
162
  "node": ">=22.13"