@pnpm/installing.deps-installer 1101.9.0 → 1102.1.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.
@@ -2,13 +2,16 @@ import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { progressLogger, stageLogger, statsLogger, } from '@pnpm/core-loggers';
4
4
  import { calcDepState, findRuntimeNodeVersion } from '@pnpm/deps.graph-hasher';
5
+ import { readModulesDir } from '@pnpm/fs.read-modules-dir';
5
6
  import { symlinkDependency } from '@pnpm/fs.symlink-dependency';
7
+ import { isValidDependencyAlias, } from '@pnpm/installing.deps-resolver';
6
8
  import { linkDirectDeps } from '@pnpm/installing.linking.direct-dep-linker';
7
9
  import { hoist } from '@pnpm/installing.linking.hoist';
8
10
  import { prune } from '@pnpm/installing.linking.modules-cleaner';
9
11
  import { filterLockfileByImporters, } from '@pnpm/lockfile.filtering';
10
12
  import { logger } from '@pnpm/logger';
11
13
  import { symlinkAllModules } from '@pnpm/worker';
14
+ import { rimraf } from '@zkochan/rimraf';
12
15
  import pLimit from 'p-limit';
13
16
  import { pathExists } from 'path-exists';
14
17
  import { difference, equals, isEmpty, pick, pickBy, props } from 'ramda';
@@ -250,20 +253,47 @@ async function linkNewPackages(currentLockfile, wantedLockfile, depGraph, opts)
250
253
  });
251
254
  const existingWithUpdatedDeps = [];
252
255
  if (!opts.force && (currentLockfile.packages != null) && (wantedLockfile.packages != null)) {
256
+ const currentPackages = currentLockfile.packages;
257
+ const wantedPackages = wantedLockfile.packages;
253
258
  // add subdependencies that have been updated
254
- // TODO: no need to relink everything. Can be relinked only what was changed
255
- for (const depPath of wantedRelDepPaths) {
256
- if (currentLockfile.packages[depPath] &&
257
- (!equals(currentLockfile.packages[depPath].dependencies, wantedLockfile.packages[depPath].dependencies) ||
258
- !isEmpty(currentLockfile.packages[depPath].optionalDependencies ?? {}) ||
259
- !isEmpty(wantedLockfile.packages[depPath].optionalDependencies ?? {}))) {
259
+ await Promise.all(wantedRelDepPaths.map((depPath) => limitModulesDirReads(async () => {
260
+ if (currentPackages[depPath] &&
261
+ (!equals(currentPackages[depPath].dependencies, wantedPackages[depPath].dependencies) ||
262
+ !isEmpty(currentPackages[depPath].optionalDependencies ?? {}) ||
263
+ !isEmpty(wantedPackages[depPath].optionalDependencies ?? {}))) {
260
264
  // TODO: come up with a test that triggers the usecase of depGraph[depPath] undefined
261
265
  // see related issue: https://github.com/pnpm/pnpm/issues/870
262
266
  if (depGraph[depPath] && !newDepPathsSet.has(depPath)) {
263
- existingWithUpdatedDeps.push(depGraph[depPath]);
267
+ const { actualChildrenChanged, removedAliases: actualRemovedAliases } = await getActualChildrenDiff(depGraph[depPath], depGraph, opts.lockfileDir, opts.optional);
268
+ if (actualChildrenChanged) {
269
+ existingWithUpdatedDeps.push({
270
+ children: depGraph[depPath].children,
271
+ modules: depGraph[depPath].modules,
272
+ name: depGraph[depPath].name,
273
+ optionalDependencies: depGraph[depPath].optionalDependencies,
274
+ removedAliases: actualRemovedAliases,
275
+ });
276
+ return;
277
+ }
278
+ const { changedChildren, removedAliases } = getChangedChildren({
279
+ currentDependencies: currentPackages[depPath].dependencies,
280
+ currentOptionalDependencies: currentPackages[depPath].optionalDependencies,
281
+ wantedDependencies: wantedPackages[depPath].dependencies,
282
+ wantedOptionalDependencies: wantedPackages[depPath].optionalDependencies,
283
+ allChildren: depGraph[depPath].children,
284
+ });
285
+ if (!isEmpty(changedChildren) || removedAliases.length > 0) {
286
+ existingWithUpdatedDeps.push({
287
+ children: changedChildren,
288
+ modules: depGraph[depPath].modules,
289
+ name: depGraph[depPath].name,
290
+ optionalDependencies: depGraph[depPath].optionalDependencies,
291
+ removedAliases,
292
+ });
293
+ }
264
294
  }
265
295
  }
266
- }
296
+ })));
267
297
  }
268
298
  if (!newDepPathsSet.size && (existingWithUpdatedDeps.length === 0))
269
299
  return { newDepPaths: [], added };
@@ -317,6 +347,7 @@ async function selectNewFromWantedDeps(wantedRelDepPaths, currentLockfile, depGr
317
347
  return newDeps;
318
348
  }
319
349
  const limitLinking = pLimit(16);
350
+ const limitModulesDirReads = pLimit(16);
320
351
  async function linkAllPkgs(storeController, depNodes, opts) {
321
352
  // Resolved `engines.runtime` Node version (when present) so the
322
353
  // side-effects-cache key prefix tracks the script-runner Node
@@ -365,29 +396,83 @@ async function linkAllPkgs(storeController, depNodes, opts) {
365
396
  }));
366
397
  }
367
398
  async function linkAllModules(depNodes, depGraph, opts) {
399
+ await Promise.all(depNodes.flatMap((depNode) => (depNode.removedAliases ?? []).map(async (alias) => limitModulesDirReads(async () => removeObsoleteChild(depNode.modules, alias)))));
368
400
  await symlinkAllModules({
369
401
  deps: depNodes.map((depNode) => {
370
- const children = opts.optional
371
- ? depNode.children
372
- : pickBy((_, childAlias) => !depNode.optionalDependencies.has(childAlias), depNode.children);
373
- const childrenPaths = {};
374
- for (const [alias, childDepPath] of Object.entries(children ?? {})) {
375
- if (childDepPath.startsWith('link:')) {
376
- childrenPaths[alias] = path.resolve(opts.lockfileDir, childDepPath.slice(5));
377
- }
378
- else {
379
- const pkg = depGraph[childDepPath];
380
- if (!pkg || !pkg.installable && pkg.optional || alias === depNode.name)
381
- continue;
382
- childrenPaths[alias] = pkg.dir;
383
- }
384
- }
385
402
  return {
386
- children: childrenPaths,
403
+ children: getChildrenPaths(depNode, depGraph, opts.lockfileDir, opts.optional),
387
404
  modules: depNode.modules,
388
405
  name: depNode.name,
389
406
  };
390
407
  }),
391
408
  });
392
409
  }
410
+ function getChangedChildren(opts) {
411
+ const { currentOptionalDependencies, wantedOptionalDependencies, allChildren } = opts;
412
+ // Use null-prototype maps so a child literally named `constructor`,
413
+ // `toString`, `__proto__`, etc. is treated as a normal alias instead
414
+ // of colliding with an inherited `Object.prototype` key during the
415
+ // `in`/index lookups below.
416
+ const currentChildren = Object.assign(Object.create(null), opts.currentDependencies, currentOptionalDependencies);
417
+ const wantedChildren = Object.assign(Object.create(null), opts.wantedDependencies, wantedOptionalDependencies);
418
+ const changedChildren = {};
419
+ const removedAliases = [];
420
+ for (const [alias, wantedChildDepPath] of Object.entries(wantedChildren)) {
421
+ const optionalityChanged = hasOwn(wantedOptionalDependencies, alias) !== hasOwn(currentOptionalDependencies, alias);
422
+ if (currentChildren[alias] !== wantedChildDepPath || optionalityChanged) {
423
+ const resolvedChildDepPath = hasOwn(allChildren, alias) ? allChildren[alias] : undefined;
424
+ if (resolvedChildDepPath != null) {
425
+ changedChildren[alias] = resolvedChildDepPath;
426
+ }
427
+ }
428
+ }
429
+ for (const alias of Object.keys(currentChildren)) {
430
+ if (!hasOwn(wantedChildren, alias)) {
431
+ removedAliases.push(alias);
432
+ }
433
+ }
434
+ return { changedChildren, removedAliases };
435
+ }
436
+ function hasOwn(obj, key) {
437
+ return obj != null && Object.hasOwn(obj, key);
438
+ }
439
+ async function getActualChildrenDiff(depNode, depGraph, lockfileDir, optional) {
440
+ if (depNode.optionalDependencies.size === 0) {
441
+ return { actualChildrenChanged: false, removedAliases: [] };
442
+ }
443
+ const currentAliases = new Set((await readModulesDir(depNode.modules) ?? []).filter((alias) => alias !== depNode.name));
444
+ const nextAliases = new Set(Object.keys(getChildrenPaths(depNode, depGraph, lockfileDir, optional)));
445
+ const removedAliases = Array.from(currentAliases).filter((alias) => !nextAliases.has(alias));
446
+ const actualChildrenChanged = removedAliases.length > 0 ||
447
+ Array.from(nextAliases).some((alias) => !currentAliases.has(alias));
448
+ return { actualChildrenChanged, removedAliases };
449
+ }
450
+ async function removeObsoleteChild(modulesDir, alias) {
451
+ // Guard against an alias that would escape the modules directory (e.g. `../../x`).
452
+ if (!isValidDependencyAlias(alias))
453
+ return;
454
+ await rimraf(path.join(modulesDir, alias));
455
+ if (alias[0] === '@') {
456
+ await fs.rmdir(path.join(modulesDir, alias.split('/')[0])).catch(() => { });
457
+ }
458
+ }
459
+ function getChildrenPaths(depNode, depGraph, lockfileDir, optional) {
460
+ const children = optional
461
+ ? depNode.children
462
+ : pickBy((_, childAlias) => !depNode.optionalDependencies.has(childAlias), depNode.children);
463
+ const childrenPaths = {};
464
+ for (const [alias, childDepPath] of Object.entries(children ?? {})) {
465
+ if (alias === depNode.name)
466
+ continue;
467
+ if (childDepPath.startsWith('link:')) {
468
+ childrenPaths[alias] = path.resolve(lockfileDir, childDepPath.slice(5));
469
+ continue;
470
+ }
471
+ const pkg = depGraph[childDepPath];
472
+ if (!pkg || !pkg.installable && pkg.optional)
473
+ continue;
474
+ childrenPaths[alias] = pkg.dir;
475
+ }
476
+ return childrenPaths;
477
+ }
393
478
  //# sourceMappingURL=link.js.map
@@ -1,5 +1,5 @@
1
1
  import { hashObject } from '@pnpm/crypto.object-hasher';
2
- import { withResolutionShapeCacheIdentity } from './verifyLockfileResolutions.js';
2
+ import { withOfflineCheckCacheIdentities } from './verifyLockfileResolutions.js';
3
3
  import { recordVerification } from './verifyLockfileResolutionsCache.js';
4
4
  /**
5
5
  * Records the post-resolution lockfile as verified so the next install
@@ -18,7 +18,7 @@ export function recordLockfileVerified(opts) {
18
18
  return;
19
19
  recordVerification(opts.cacheDir, {
20
20
  lockfilePath: opts.lockfilePath,
21
- verifiers: withResolutionShapeCacheIdentity(opts.resolutionVerifiers),
21
+ verifiers: withOfflineCheckCacheIdentities(opts.resolutionVerifiers),
22
22
  hashLockfile: () => hashObject(opts.lockfile),
23
23
  });
24
24
  }
@@ -3,15 +3,19 @@ import type { ResolutionPolicyViolation, ResolutionVerifier } from '@pnpm/resolv
3
3
  import { type VerifierCacheIdentity } from './verifyLockfileResolutionsCache.js';
4
4
  export type { ResolutionPolicyViolation };
5
5
  export declare const RESOLUTION_SHAPE_MISMATCH_VIOLATION_CODE = "RESOLUTION_SHAPE_MISMATCH";
6
+ export declare const INVALID_DEPENDENCY_ALIAS_CODE = "INVALID_DEPENDENCY_NAME";
6
7
  /**
7
8
  * 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).
9
+ * the always-on offline structural checks' identities, so a record
10
+ * written before one of those rules existed cannot stat-fast-path around
11
+ * it its missing flag fails `canTrustPastCheck`, forcing a
12
+ * re-verification that runs the new check. Used by the gate itself and by
13
+ * {@link recordLockfileVerified}, whose freshly-resolved lockfile
14
+ * satisfies these invariants by construction (the resolver validates
15
+ * aliases at manifest-read time and derives every resolution key from the
16
+ * resolution it just produced).
13
17
  */
14
- export declare function withResolutionShapeCacheIdentity(verifiers: readonly VerifierCacheIdentity[]): VerifierCacheIdentity[];
18
+ export declare function withOfflineCheckCacheIdentities(verifiers: readonly VerifierCacheIdentity[]): VerifierCacheIdentity[];
15
19
  export interface VerifyLockfileResolutionsOptions {
16
20
  concurrency?: number;
17
21
  /**
@@ -1,6 +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 { isValidDependencyAlias } from '@pnpm/installing.deps-resolver';
4
5
  import { isGitHostedTarballUrl, nameVerFromPkgSnapshot } from '@pnpm/lockfile.utils';
5
6
  import pLimit from 'p-limit';
6
7
  import { recordVerification, tryLockfileVerificationCache, } from './verifyLockfileResolutionsCache.js';
@@ -13,20 +14,29 @@ const MAX_VIOLATIONS_TO_PRINT = 20;
13
14
  // verification pass doesn't push past what the rest of the install respects.
14
15
  const DEFAULT_CONCURRENCY = 64;
15
16
  export const RESOLUTION_SHAPE_MISMATCH_VIOLATION_CODE = 'RESOLUTION_SHAPE_MISMATCH';
17
+ // Same code the sink-level guards (`safeJoinModulesDir`) throw.
18
+ export const INVALID_DEPENDENCY_ALIAS_CODE = 'INVALID_DEPENDENCY_NAME';
16
19
  const RESOLUTION_SHAPE_CACHE_IDENTITY = {
17
20
  policy: { resolutionShapeCheck: true },
18
21
  canTrustPastCheck: (cached) => cached.resolutionShapeCheck === true,
19
22
  };
23
+ const DEPENDENCY_ALIAS_CACHE_IDENTITY = {
24
+ policy: { dependencyAliasCheck: true },
25
+ canTrustPastCheck: (cached) => cached.dependencyAliasCheck === true,
26
+ };
20
27
  /**
21
28
  * 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).
29
+ * the always-on offline structural checks' identities, so a record
30
+ * written before one of those rules existed cannot stat-fast-path around
31
+ * it its missing flag fails `canTrustPastCheck`, forcing a
32
+ * re-verification that runs the new check. Used by the gate itself and by
33
+ * {@link recordLockfileVerified}, whose freshly-resolved lockfile
34
+ * satisfies these invariants by construction (the resolver validates
35
+ * aliases at manifest-read time and derives every resolution key from the
36
+ * resolution it just produced).
27
37
  */
28
- export function withResolutionShapeCacheIdentity(verifiers) {
29
- return [...verifiers, RESOLUTION_SHAPE_CACHE_IDENTITY];
38
+ export function withOfflineCheckCacheIdentities(verifiers) {
39
+ return [...verifiers, RESOLUTION_SHAPE_CACHE_IDENTITY, DEPENDENCY_ALIAS_CACHE_IDENTITY];
30
40
  }
31
41
  /**
32
42
  * Policy-neutral pass that asks every resolver-supplied
@@ -65,7 +75,7 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
65
75
  const cache = options?.cacheDir && options?.lockfilePath
66
76
  ? { cacheDir: options.cacheDir, lockfilePath: options.lockfilePath }
67
77
  : undefined;
68
- const cacheVerifiers = withResolutionShapeCacheIdentity(verifiers);
78
+ const cacheVerifiers = withOfflineCheckCacheIdentities(verifiers);
69
79
  let cachePrecomputed;
70
80
  // hashObject streams and is key-order-stable, unlike JSON.stringify.
71
81
  let cachedHash;
@@ -104,7 +114,10 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
104
114
  // A degenerate lockfile where every snapshot fails the
105
115
  // name/version extraction (so candidates is empty) skips emission
106
116
  // entirely — no work, no noise.
107
- const { candidates, shapeViolations } = collectCandidates(lockfile);
117
+ const { candidates, shapeViolations, invalidAliases } = collectCandidates(lockfile);
118
+ if (invalidAliases.length > 0) {
119
+ throw buildInvalidAliasError(invalidAliases);
120
+ }
108
121
  if (shapeViolations.length > 0) {
109
122
  throw buildVerificationError(shapeViolations);
110
123
  }
@@ -158,6 +171,19 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
158
171
  });
159
172
  }
160
173
  }
174
+ function buildInvalidAliasError(aliases) {
175
+ const sorted = [...aliases].sort();
176
+ const visible = sorted.slice(0, MAX_VIOLATIONS_TO_PRINT);
177
+ const omitted = sorted.length - visible.length;
178
+ const breakdown = visible.map((alias) => ` ${JSON.stringify(alias)}`).join('\n');
179
+ const details = omitted > 0 ? `${breakdown}\n …and ${omitted} more` : breakdown;
180
+ const plural = aliases.length === 1 ? 'alias' : 'aliases';
181
+ return new PnpmError(INVALID_DEPENDENCY_ALIAS_CODE, `The lockfile contains ${aliases.length} dependency ${plural} that are not valid package names:\n${details}`, {
182
+ hint: 'A dependency alias becomes a directory under node_modules, so it must be a valid npm package name — a single `name` or `@scope/name` with no leading `.` or `_`, and not a reserved name such as `node_modules`. ' +
183
+ 'An alias containing path-traversal segments or a reserved name such as `.bin` or `.pnpm` could make an install write outside the intended directory or overwrite pnpm-owned layout. ' +
184
+ 'This usually means the lockfile was tampered with — inspect recent changes to pnpm-lock.yaml before trusting it.',
185
+ });
186
+ }
161
187
  function buildVerificationError(violations) {
162
188
  // Stable order so the error output is deterministic.
163
189
  violations.sort((a, b) => `${a.name}@${a.version}`.localeCompare(`${b.name}@${b.version}`));
@@ -266,7 +292,17 @@ function isRegistryShapedResolution(resolution) {
266
292
  function collectCandidates(lockfile) {
267
293
  const candidates = new Map();
268
294
  const shapeViolations = [];
295
+ // The importer alias maps are the one source not reached by the
296
+ // package loop below, so they're scanned here.
297
+ const invalidAliases = new Set();
298
+ for (const importer of Object.values(lockfile.importers ?? {})) {
299
+ pushInvalidAliases(importer.dependencies, invalidAliases);
300
+ pushInvalidAliases(importer.devDependencies, invalidAliases);
301
+ pushInvalidAliases(importer.optionalDependencies, invalidAliases);
302
+ }
269
303
  for (const [depPath, snapshot] of Object.entries(lockfile.packages ?? {})) {
304
+ pushInvalidAliases(snapshot.dependencies, invalidAliases);
305
+ pushInvalidAliases(snapshot.optionalDependencies, invalidAliases);
270
306
  const { name, version, nonSemverVersion } = nameVerFromPkgSnapshot(depPath, snapshot);
271
307
  if (!name || !version)
272
308
  continue;
@@ -292,7 +328,21 @@ function collectCandidates(lockfile) {
292
328
  resolution: snapshot.resolution,
293
329
  });
294
330
  }
295
- return { candidates, shapeViolations };
331
+ return { candidates, shapeViolations, invalidAliases: Array.from(invalidAliases) };
332
+ }
333
+ /**
334
+ * Add every key of `deps` that is not a valid {@link isValidDependencyAlias}
335
+ * to `invalid`. Only pass maps whose keys become `node_modules/<alias>`
336
+ * directories — not `overrides` (`foo>bar` selectors) or
337
+ * `patchedDependencies` (`name@version` keys).
338
+ */
339
+ function pushInvalidAliases(deps, invalid) {
340
+ if (deps == null)
341
+ return;
342
+ for (const alias of Object.keys(deps)) {
343
+ if (!isValidDependencyAlias(alias))
344
+ invalid.add(alias);
345
+ }
296
346
  }
297
347
  async function iterateLockfileViolations(candidates, verifiers, concurrency) {
298
348
  const violations = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/installing.deps-installer",
3
- "version": "1101.9.0",
3
+ "version": "1102.1.0",
4
4
  "description": "Fast, disk space efficient installation engine",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -52,7 +52,7 @@
52
52
  "dependencies": {
53
53
  "@inquirer/prompts": "^8.4.3",
54
54
  "@pnpm/npm-package-arg": "^2.0.0",
55
- "@pnpm/util.lex-comparator": "^3.0.2",
55
+ "@pnpm/util.lex-comparator": "^4.0.1",
56
56
  "@zkochan/rimraf": "^4.0.0",
57
57
  "is-inner-link": "^5.0.0",
58
58
  "is-subdir": "^2.0.0",
@@ -64,104 +64,105 @@
64
64
  "path-exists": "^5.0.0",
65
65
  "ramda": "npm:@pnpm/ramda@0.28.1",
66
66
  "run-groups": "^5.0.0",
67
- "semver": "^7.8.1",
68
- "@pnpm/bins.linker": "1100.0.13",
69
- "@pnpm/building.after-install": "1101.0.21",
70
- "@pnpm/building.during-install": "1101.0.18",
71
- "@pnpm/building.policy": "1100.0.9",
72
- "@pnpm/catalogs.resolver": "1100.0.0",
73
- "@pnpm/catalogs.types": "1100.0.0",
67
+ "semver": "^7.8.4",
68
+ "@pnpm/bins.linker": "1100.0.15",
69
+ "@pnpm/building.after-install": "1102.0.1",
70
+ "@pnpm/building.during-install": "1102.0.1",
71
+ "@pnpm/building.policy": "1100.0.10",
72
+ "@pnpm/catalogs.config": "1100.0.1",
74
73
  "@pnpm/catalogs.protocol-parser": "1100.0.0",
74
+ "@pnpm/catalogs.resolver": "1100.0.0",
75
+ "@pnpm/config.matcher": "1100.0.1",
76
+ "@pnpm/config.normalize-registries": "1100.0.8",
75
77
  "@pnpm/config.parse-overrides": "1100.0.1",
78
+ "@pnpm/core-loggers": "1100.2.1",
76
79
  "@pnpm/constants": "1100.0.0",
77
- "@pnpm/config.matcher": "1100.0.1",
78
- "@pnpm/config.normalize-registries": "1100.0.7",
79
- "@pnpm/core-loggers": "1100.2.0",
80
- "@pnpm/deps.graph-hasher": "1100.2.4",
81
- "@pnpm/crypto.hash": "1100.0.1",
80
+ "@pnpm/bins.remover": "1100.0.10",
81
+ "@pnpm/deps.graph-hasher": "1100.2.5",
82
+ "@pnpm/catalogs.types": "1100.0.0",
83
+ "@pnpm/crypto.object-hasher": "1100.0.0",
82
84
  "@pnpm/deps.graph-sequencer": "1100.0.0",
85
+ "@pnpm/deps.path": "1100.0.8",
83
86
  "@pnpm/error": "1100.0.0",
84
- "@pnpm/deps.path": "1100.0.7",
85
- "@pnpm/exec.lifecycle": "1100.0.17",
86
- "@pnpm/fs.symlink-dependency": "1100.0.9",
87
- "@pnpm/crypto.object-hasher": "1100.0.0",
88
- "@pnpm/hooks.read-package-hook": "1100.0.7",
89
- "@pnpm/hooks.types": "1100.0.11",
90
- "@pnpm/installing.deps-resolver": "1100.2.2",
91
- "@pnpm/bins.remover": "1100.0.9",
92
- "@pnpm/installing.context": "1100.0.17",
93
- "@pnpm/installing.linking.hoist": "1100.0.13",
94
- "@pnpm/installing.deps-restorer": "1101.1.11",
95
- "@pnpm/installing.linking.modules-cleaner": "1100.1.7",
96
- "@pnpm/installing.linking.direct-dep-linker": "1100.0.9",
97
- "@pnpm/installing.modules-yaml": "1100.0.8",
98
- "@pnpm/lockfile.filtering": "1100.1.6",
99
- "@pnpm/lockfile.fs": "1100.1.4",
100
- "@pnpm/installing.package-requester": "1101.1.0",
101
- "@pnpm/lockfile.preferred-versions": "1100.0.15",
87
+ "@pnpm/exec.lifecycle": "1100.1.0",
102
88
  "@pnpm/fs.read-modules-dir": "1100.0.1",
103
- "@pnpm/lockfile.settings-checker": "1100.0.17",
104
- "@pnpm/lockfile.to-pnp": "1100.0.13",
105
- "@pnpm/lockfile.pruner": "1100.0.10",
106
- "@pnpm/lockfile.utils": "1100.0.12",
107
- "@pnpm/lockfile.walker": "1100.0.10",
108
- "@pnpm/network.auth-header": "1101.1.1",
109
- "@pnpm/lockfile.verification": "1100.0.17",
110
- "@pnpm/patching.config": "1100.0.7",
111
- "@pnpm/pnpr.client": "1.2.0",
112
- "@pnpm/pkg-manifest.utils": "1100.2.4",
113
- "@pnpm/store.controller-types": "1100.1.4",
114
- "@pnpm/store.index": "1100.1.0",
115
- "@pnpm/resolving.resolver-base": "1100.4.1",
116
- "@pnpm/types": "1101.3.1",
89
+ "@pnpm/fs.symlink-dependency": "1100.0.10",
90
+ "@pnpm/hooks.types": "1100.0.12",
91
+ "@pnpm/installing.context": "1100.0.19",
92
+ "@pnpm/installing.deps-resolver": "1100.2.4",
93
+ "@pnpm/crypto.hash": "1100.0.1",
94
+ "@pnpm/installing.deps-restorer": "1102.1.0",
95
+ "@pnpm/installing.linking.hoist": "1100.0.15",
96
+ "@pnpm/installing.linking.modules-cleaner": "1100.1.8",
97
+ "@pnpm/installing.package-requester": "1102.0.0",
98
+ "@pnpm/installing.linking.direct-dep-linker": "1100.0.10",
99
+ "@pnpm/installing.modules-yaml": "1100.0.9",
100
+ "@pnpm/lockfile.filtering": "1100.1.7",
101
+ "@pnpm/lockfile.preferred-versions": "1100.0.16",
102
+ "@pnpm/lockfile.fs": "1100.1.6",
103
+ "@pnpm/lockfile.pruner": "1100.0.11",
104
+ "@pnpm/lockfile.to-pnp": "1100.1.0",
105
+ "@pnpm/lockfile.verification": "1100.0.19",
106
+ "@pnpm/lockfile.settings-checker": "1100.0.19",
107
+ "@pnpm/lockfile.utils": "1100.0.13",
108
+ "@pnpm/network.auth-header": "1101.1.2",
109
+ "@pnpm/patching.config": "1100.0.8",
110
+ "@pnpm/pkg-manifest.utils": "1100.2.5",
111
+ "@pnpm/pnpr.client": "1.2.2",
112
+ "@pnpm/resolving.resolver-base": "1100.4.2",
113
+ "@pnpm/store.controller-types": "1100.1.5",
117
114
  "@pnpm/resolving.parse-wanted-dependency": "1100.0.1",
118
- "@pnpm/workspace.project-manifest-reader": "1100.0.12"
115
+ "@pnpm/types": "1101.3.2",
116
+ "@pnpm/workspace.project-manifest-reader": "1100.0.13",
117
+ "@pnpm/hooks.read-package-hook": "1100.0.8",
118
+ "@pnpm/lockfile.walker": "1100.0.11",
119
+ "@pnpm/store.index": "1100.2.0"
119
120
  },
120
121
  "peerDependencies": {
121
- "@pnpm/logger": "^1001.0.1",
122
- "@pnpm/worker": "^1100.1.11"
122
+ "@pnpm/logger": "^1100.0.0",
123
+ "@pnpm/worker": "^1100.2.1"
123
124
  },
124
125
  "devDependencies": {
125
- "@jest/globals": "30.3.0",
126
+ "@jest/globals": "30.4.1",
126
127
  "@types/fs-extra": "^11.0.4",
127
128
  "@types/is-windows": "^1.0.2",
128
129
  "@types/normalize-path": "^3.0.2",
129
130
  "@types/ramda": "0.31.1",
130
131
  "@types/semver": "7.7.1",
131
- "@yarnpkg/core": "4.5.0",
132
+ "@yarnpkg/core": "4.8.0",
132
133
  "ci-info": "^4.4.0",
133
134
  "deep-require-cwd": "1.0.0",
134
135
  "execa": "npm:safe-execa@0.3.0",
135
136
  "exists-link": "2.0.0",
136
137
  "is-windows": "^1.0.2",
137
- "nock": "13.3.4",
138
138
  "path-name": "^1.0.0",
139
139
  "read-yaml-file": "^3.0.0",
140
140
  "resolve-link-target": "^3.0.0",
141
141
  "symlink-dir": "^10.0.1",
142
142
  "write-json-file": "^7.0.0",
143
143
  "write-yaml-file": "^6.0.0",
144
- "@pnpm/assert-project": "1100.0.15",
145
- "@pnpm/assert-store": "1100.0.15",
146
- "@pnpm/installing.deps-installer": "1101.9.0",
144
+ "@pnpm/assert-project": "1100.0.16",
145
+ "@pnpm/installing.deps-installer": "1102.1.0",
146
+ "@pnpm/assert-store": "1100.0.16",
147
+ "@pnpm/lockfile.types": "1100.0.11",
147
148
  "@pnpm/logger": "1100.0.0",
148
- "@pnpm/network.git-utils": "1100.0.1",
149
- "@pnpm/lockfile.types": "1100.0.10",
150
- "@pnpm/resolving.registry.types": "1100.1.2",
151
- "@pnpm/pkg-manifest.reader": "1100.0.7",
152
- "@pnpm/store.cafs": "1100.1.9",
149
+ "@pnpm/prepare": "1100.0.16",
150
+ "@pnpm/store.cafs": "1100.1.10",
151
+ "@pnpm/pkg-manifest.reader": "1100.0.8",
153
152
  "@pnpm/store.path": "1100.0.1",
154
- "@pnpm/prepare": "1100.0.15",
155
153
  "@pnpm/test-ipc-server": "1100.0.0",
156
154
  "@pnpm/test-fixtures": "1100.0.0",
157
- "@pnpm/testing.mock-agent": "1101.0.2",
158
- "@pnpm/testing.temp-store": "1100.1.8",
159
- "@pnpm/testing.registry-mock": "1100.0.5"
155
+ "@pnpm/network.git-utils": "1100.0.2",
156
+ "@pnpm/testing.mock-agent": "1101.0.3",
157
+ "@pnpm/resolving.registry.types": "1100.1.3",
158
+ "@pnpm/testing.temp-store": "1100.1.10",
159
+ "@pnpm/testing.registry-mock": "1100.0.6"
160
160
  },
161
161
  "engines": {
162
162
  "node": ">=22.13"
163
163
  },
164
164
  "jest": {
165
+ "collectCoverage": false,
165
166
  "preset": "@pnpm/jest-config/with-registry"
166
167
  },
167
168
  "scripts": {
@@ -170,6 +171,8 @@
170
171
  "test-with-preview": "preview && pnpm run test:e2e",
171
172
  "test": "pn compile && pn .test",
172
173
  "compile": "tsgo --build && pn lint --fix",
173
- ".test": "cross-env PNPM_REGISTRY_MOCK_PORT=7769 NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest"
174
+ ".test": "pn .test:heavy && pn .test:rest",
175
+ ".test:heavy": "cross-env PNPM_REGISTRY_MOCK_PORT=7769 NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest test/install/deepRecursive.ts",
176
+ ".test:rest": "cross-env PNPM_REGISTRY_MOCK_PORT=7769 NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest \"^(?!.*deepRecursive)\""
174
177
  }
175
178
  }