@pnpm/installing.deps-resolver 1100.2.7 → 1100.2.9

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.
@@ -1,5 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import normalize from 'normalize-path';
3
+ import { isCompatibleAndHasMoreDeps } from './depPathCompatibility.js';
3
4
  export function dedupeInjectedDeps(opts) {
4
5
  const injectedDepsByProjects = getInjectedDepsByProjects(opts);
5
6
  const dedupeMap = getDedupeMap(injectedDepsByProjects, opts);
@@ -46,7 +47,25 @@ function getDedupeMap(injectedDepsByProjects, opts) {
46
47
  // The injected project in the workspace may have dev deps
47
48
  const children = Object.entries(node.children);
48
49
  const isSubset = children
49
- .every(([alias, depPath]) => targetProjectDeps.get(alias) === depPath);
50
+ .every(([alias, depPath]) => {
51
+ const targetDepPath = targetProjectDeps.get(alias);
52
+ if (targetDepPath === depPath)
53
+ return true;
54
+ if (targetDepPath == null)
55
+ return false;
56
+ // A shared dep can resolve peer-suffixed on one side and peer-free on
57
+ // the other (e.g. an existing lockfile pinned debug's optional
58
+ // supports-color for the target project but not the injected
59
+ // occurrence). Accept the target's variant when it's the same package
60
+ // identity and a compatible superset. See pnpm/pnpm#10433.
61
+ const targetNode = opts.depGraph[targetDepPath];
62
+ const injectedChildNode = opts.depGraph[depPath];
63
+ if (targetNode == null || injectedChildNode == null)
64
+ return false;
65
+ if (targetNode.pkgIdWithPatchHash !== injectedChildNode.pkgIdWithPatchHash)
66
+ return false;
67
+ return isCompatibleAndHasMoreDeps(opts.depGraph, targetDepPath, depPath);
68
+ });
50
69
  if (isSubset) {
51
70
  dedupedInjectedDeps.set(alias, dep.id);
52
71
  }
@@ -0,0 +1,4 @@
1
+ import type { DepPath } from '@pnpm/types';
2
+ import type { GenericDependenciesGraphNodeWithResolvedChildren, GenericDependenciesGraphWithResolvedChildren, PartialResolvedPackage } from './resolvePeers.js';
3
+ export declare function nodeDepsCount(node: GenericDependenciesGraphNodeWithResolvedChildren): number;
4
+ export declare function isCompatibleAndHasMoreDeps<T extends PartialResolvedPackage>(depGraph: GenericDependenciesGraphWithResolvedChildren<T>, depPath1: DepPath, depPath2: DepPath): boolean;
@@ -0,0 +1,28 @@
1
+ // Shared helpers used by both resolvePeers (dedupePeerDependents) and
2
+ // dedupeInjectedDeps. Lives in its own module so neither consumer has to import
3
+ // the other, which would create a runtime cycle. The type imports above are
4
+ // erased at build time, so no cycle exists at runtime.
5
+ export function nodeDepsCount(node) {
6
+ return Object.keys(node.children).length + node.resolvedPeerNames.size;
7
+ }
8
+ // Whether `depPath1` is a superset-or-equal of `depPath2`: same-or-more resolved
9
+ // children and peers. Compares dependency/peer *sets* only, not package
10
+ // identity, so callers must pass depPaths already known to share a
11
+ // `pkgIdWithPatchHash` — otherwise two unrelated leaf packages (both with empty
12
+ // sets) would count as compatible.
13
+ export function isCompatibleAndHasMoreDeps(depGraph, depPath1, depPath2) {
14
+ const node1 = depGraph[depPath1];
15
+ const node2 = depGraph[depPath2];
16
+ if (nodeDepsCount(node1) < nodeDepsCount(node2))
17
+ return false;
18
+ const node1DepPathsSet = new Set(Object.values(node1.children));
19
+ const node2DepPaths = Object.values(node2.children);
20
+ if (!node2DepPaths.every((depPath) => node1DepPathsSet.has(depPath)))
21
+ return false;
22
+ for (const depPath of node2.resolvedPeerNames) {
23
+ if (!node1.resolvedPeerNames.has(depPath))
24
+ return false;
25
+ }
26
+ return true;
27
+ }
28
+ //# sourceMappingURL=depPathCompatibility.js.map
@@ -1,9 +1,14 @@
1
1
  import type { PreferredVersions } from '@pnpm/resolving.resolver-base';
2
- import type { PkgAddressOrLink } from './resolveDependencies.js';
2
+ /** One workspace-root dependency that a missing peer can be satisfied with. */
3
+ export interface HoistableRootDep {
4
+ alias: string;
5
+ pkgName: string;
6
+ normalizedBareSpecifier?: string;
7
+ }
3
8
  export declare function hoistPeers(opts: {
4
9
  autoInstallPeers: boolean;
5
10
  allPreferredVersions?: PreferredVersions;
6
- workspaceRootDeps: PkgAddressOrLink[];
11
+ workspaceRootDeps: HoistableRootDep[];
7
12
  }, missingRequiredPeers: Array<[string, {
8
13
  range: string;
9
14
  }]>): Record<string, string>;
package/lib/hoistPeers.js CHANGED
@@ -9,7 +9,7 @@ export function hoistPeers(opts, missingRequiredPeers) {
9
9
  continue;
10
10
  }
11
11
  const rootDep = opts.workspaceRootDeps
12
- .filter((rootDep) => rootDep.pkg.name === peerName)
12
+ .filter((rootDep) => rootDep.pkgName === peerName)
13
13
  .sort((rootDep1, rootDep2) => lexCompare(rootDep1.alias, rootDep2.alias))[0];
14
14
  if (rootDep?.normalizedBareSpecifier) {
15
15
  dependencies[peerName] = rootDep.normalizedBareSpecifier;
@@ -27,22 +27,28 @@ export function hoistPeers(opts, missingRequiredPeers) {
27
27
  nonVersions.push(spec);
28
28
  }
29
29
  }
30
- // When the range is an exact version (e.g. pinned by an override like "4.3.0"),
31
- // try to find a preferred version that satisfies it. This prevents a stale
32
- // higher version from the lockfile being picked over the overridden version.
33
- // For regular semver ranges (e.g. "^1.0.0"), use the highest preferred
34
- // version for deduplication.
35
- const isExactVersion = semver.valid(range) != null;
36
- const satisfyingVersion = isExactVersion
30
+ // Dedupe onto a preferred version only when it actually satisfies the
31
+ // wanted peer range. Picking the highest preferred version regardless of
32
+ // the range lets a version resolved for one importer be auto-installed as
33
+ // another importer's peer even though nothing in that importer's closure
34
+ // accepts it, silently producing a peer graph that mixes incompatible
35
+ // majors. Ranges that are not semver (workspace:, npm: aliases, dist-tags)
36
+ // cannot be checked, so they keep the dedupe-to-highest behavior.
37
+ const isSemverRange = semver.validRange(range, { includePrerelease: true }) != null;
38
+ const satisfyingVersion = isSemverRange
37
39
  ? semver.maxSatisfying(versions, range, { includePrerelease: true })
38
40
  : null;
39
41
  if (satisfyingVersion) {
40
42
  dependencies[peerName] = [satisfyingVersion, ...nonVersions].join(' || ');
41
43
  }
42
- else if (isExactVersion && opts.autoInstallPeers) {
43
- // No preferred version satisfies the exact override version.
44
- // Use the range directly so pnpm resolves it from the registry.
45
- dependencies[peerName] = range;
44
+ else if (isSemverRange && versions.length > 0) {
45
+ // Preferred versions exist but none satisfies the wanted range.
46
+ // Use the range directly so pnpm resolves it from the registry rather
47
+ // than installing a version the peer explicitly rejects. Without
48
+ // autoInstallPeers, hoist nothing and leave the peer missing.
49
+ if (opts.autoInstallPeers) {
50
+ dependencies[peerName] = range;
51
+ }
46
52
  }
47
53
  else {
48
54
  dependencies[peerName] = [semver.maxSatisfying(versions, '*', { includePrerelease: true }), ...nonVersions]
@@ -54,7 +54,7 @@ export async function resolveRootDependencies(ctx, importers) {
54
54
  let workspaceRootDeps;
55
55
  if (ctx.resolvePeersFromWorkspaceRoot) {
56
56
  const rootImporterIndex = importers.findIndex(({ options }) => options.parentIds[0] === '.');
57
- workspaceRootDeps = pkgAddressesByImportersWithoutPeers[rootImporterIndex]?.pkgAddresses ?? [];
57
+ workspaceRootDeps = getHoistableRootDeps(importers[rootImporterIndex], pkgAddressesByImportersWithoutPeers[rootImporterIndex]?.pkgAddresses ?? []);
58
58
  }
59
59
  else {
60
60
  workspaceRootDeps = [];
@@ -150,6 +150,38 @@ export async function resolveRootDependencies(ctx, importers) {
150
150
  time,
151
151
  };
152
152
  }
153
+ /**
154
+ * Lists the workspace-root dependencies that `hoistPeers` may satisfy a
155
+ * missing peer with. A root dependency reused from the lockfile skips full
156
+ * resolution, so it has no address (or an address without a
157
+ * normalizedBareSpecifier); its wanted specifier is used instead, so that
158
+ * re-resolving with a lockfile hoists the same version as a fresh install of
159
+ * the same manifest.
160
+ */
161
+ function getHoistableRootDeps(rootImporter, rootPkgAddresses) {
162
+ const wantedSpecifierByAlias = new Map();
163
+ for (const wantedDep of rootImporter?.wantedDependencies ?? []) {
164
+ if (wantedDep.alias && wantedDep.bareSpecifier) {
165
+ wantedSpecifierByAlias.set(wantedDep.alias, wantedDep.bareSpecifier);
166
+ }
167
+ }
168
+ const rootDeps = rootPkgAddresses.map((pkgAddress) => ({
169
+ alias: pkgAddress.alias,
170
+ pkgName: pkgAddress.pkg.name,
171
+ normalizedBareSpecifier: pkgAddress.normalizedBareSpecifier ?? wantedSpecifierByAlias.get(pkgAddress.alias),
172
+ }));
173
+ const coveredAliases = new Set(rootDeps.map(({ alias }) => alias));
174
+ for (const [alias, bareSpecifier] of wantedSpecifierByAlias) {
175
+ if (coveredAliases.has(alias))
176
+ continue;
177
+ rootDeps.push({
178
+ alias,
179
+ pkgName: unwrapPackageName(alias, bareSpecifier).pkgName,
180
+ normalizedBareSpecifier: bareSpecifier,
181
+ });
182
+ }
183
+ return rootDeps;
184
+ }
153
185
  async function resolveDependenciesOfImporters(ctx, importers) {
154
186
  const pickLowestVersion = ctx.resolutionMode === 'time-based' || ctx.resolutionMode === 'lowest-direct';
155
187
  const resolveResults = await Promise.all(importers.map(async (importer) => {
@@ -1074,7 +1106,9 @@ async function resolveDependency(wantedDependency, ctx, options) {
1074
1106
  currentPkg.dependencyLockfile &&
1075
1107
  currentPkg.name &&
1076
1108
  await pathExists(path.join(ctx.virtualStoreDir, dp.depPathToFilename(currentPkg.depPath, ctx.virtualStoreDirMaxLength), 'node_modules', currentPkg.name, 'package.json')));
1077
- if (!options.update && !options.proceed && (currentPkg.resolution != null) && depIsLinked) {
1109
+ if (!options.update && !options.proceed &&
1110
+ options.currentDepth === Math.max(0, options.updateDepth) &&
1111
+ (currentPkg.resolution != null) && depIsLinked) {
1078
1112
  return null;
1079
1113
  }
1080
1114
  let pkgResponse;
@@ -7,12 +7,16 @@ import pDefer, {} from 'p-defer';
7
7
  import { partition, pick } from 'ramda';
8
8
  import semver from 'semver';
9
9
  import { dedupeInjectedDeps } from './dedupeInjectedDeps.js';
10
+ import { isCompatibleAndHasMoreDeps, nodeDepsCount } from './depPathCompatibility.js';
10
11
  import { linkPathToPeerVersion } from './linkPathToPeerVersion.js';
11
12
  import { mergePeers } from './mergePeers.js';
12
13
  export async function resolvePeers(opts) {
13
14
  const depGraph = {};
14
15
  const pathsByNodeId = new Map();
15
16
  const pathsByNodeIdPromises = new Map();
17
+ const awaitedPeerNodeIdsByNodeId = new Map();
18
+ const peersCacheOwnerByNodeId = new Map();
19
+ const cycleBrokenNodeIds = new Set();
16
20
  const depPathsByPkgId = new Map();
17
21
  const nodeIdsByPreviousDepPath = opts.resolvedPeerProviderPaths == null
18
22
  ? new Map()
@@ -76,6 +80,9 @@ export async function resolvePeers(opts) {
76
80
  parentDepPathsChain: [],
77
81
  pathsByNodeId,
78
82
  pathsByNodeIdPromises,
83
+ awaitedPeerNodeIdsByNodeId,
84
+ peersCacheOwnerByNodeId,
85
+ cycleBrokenNodeIds,
79
86
  depPathsByPkgId,
80
87
  nodeIdsByPreviousDepPath,
81
88
  resolvedPeerProviderPaths: opts.resolvedPeerProviderPaths,
@@ -102,6 +109,9 @@ export async function resolvePeers(opts) {
102
109
  // its cycle analysis only sees the children of one call, and providers
103
110
  // frequently peer-depend on each other, so resolving them one by one
104
111
  // would leave their dep path calculations awaiting each other forever.
112
+ // A peer cycle that spans this call and the traversal above is still
113
+ // invisible here; breakDepPathAwaitCycles resolves those before the
114
+ // finishing promises are awaited.
105
115
  const prunedProviderChildren = {};
106
116
  for (const [alias, nodeId] of Object.entries(hoistedProviderChildren)) {
107
117
  if (parentPkgsOfNode.has(nodeId))
@@ -122,6 +132,14 @@ export async function resolvePeers(opts) {
122
132
  };
123
133
  }
124
134
  }
135
+ breakDepPathAwaitCycles({
136
+ awaitedPeerNodeIdsByNodeId,
137
+ peersCacheOwnerByNodeId,
138
+ cycleBrokenNodeIds,
139
+ pathsByNodeId,
140
+ pathsByNodeIdPromises,
141
+ dependenciesTree: opts.dependenciesTree,
142
+ });
125
143
  await Promise.all(finishingList);
126
144
  const depGraphWithResolvedChildren = resolveChildren(depGraph);
127
145
  function resolveChildren(depGraph) {
@@ -168,8 +186,61 @@ export async function resolvePeers(opts) {
168
186
  pathsByNodeId,
169
187
  };
170
188
  }
171
- function nodeDepsCount(node) {
172
- return Object.keys(node.children).length + node.resolvedPeerNames.size;
189
+ // Cycle analysis inside resolvePeersOfChildren only sees the children of a
190
+ // single call, but a dep path calculation may await a node resolved in a
191
+ // different call or at a different tree level (a hoisted peer provider and
192
+ // a consumer that peer-depend on each other, https://github.com/pnpm/pnpm/issues/12921).
193
+ // Such an await cycle never settles on its own, so before the finishing
194
+ // promises are awaited, walk the recorded await edges and resolve every
195
+ // dep path promise on a cycle to the peer's `name@version` — the same
196
+ // collapse in-call cycle detection applies (see calculateDepPath).
197
+ // A node that hit the peers cache awaits its dep path from the node that
198
+ // created the cache entry, so it borrows that owner's await edges.
199
+ function breakDepPathAwaitCycles(opts) {
200
+ const isSettled = (nodeId) => opts.pathsByNodeId.has(nodeId) || opts.cycleBrokenNodeIds.has(nodeId);
201
+ const keysByNodeId = new Map();
202
+ const nodeIdsByKey = new Map();
203
+ function keyOf(nodeId) {
204
+ let key = keysByNodeId.get(nodeId);
205
+ if (key == null) {
206
+ key = String(keysByNodeId.size);
207
+ keysByNodeId.set(nodeId, key);
208
+ nodeIdsByKey.set(key, nodeId);
209
+ }
210
+ return key;
211
+ }
212
+ const graphEntries = [];
213
+ const awaitingNodeIds = new Set([
214
+ ...opts.awaitedPeerNodeIdsByNodeId.keys(),
215
+ ...opts.peersCacheOwnerByNodeId.keys(),
216
+ ]);
217
+ for (const nodeId of awaitingNodeIds) {
218
+ if (isSettled(nodeId))
219
+ continue;
220
+ const cacheOwnerNodeId = opts.peersCacheOwnerByNodeId.get(nodeId);
221
+ const awaitedNodeIds = opts.awaitedPeerNodeIdsByNodeId.get(nodeId) ??
222
+ (cacheOwnerNodeId == null ? undefined : opts.awaitedPeerNodeIdsByNodeId.get(cacheOwnerNodeId));
223
+ if (awaitedNodeIds == null)
224
+ continue;
225
+ const liveTargets = [];
226
+ for (const awaitedNodeId of awaitedNodeIds) {
227
+ if (!isSettled(awaitedNodeId)) {
228
+ liveTargets.push(keyOf(awaitedNodeId));
229
+ }
230
+ }
231
+ if (liveTargets.length > 0) {
232
+ graphEntries.push([keyOf(nodeId), liveTargets]);
233
+ }
234
+ }
235
+ if (graphEntries.length === 0)
236
+ return;
237
+ const { cycles } = analyzeGraph(graphEntries);
238
+ for (const key of new Set(cycles.flat())) {
239
+ const nodeId = nodeIdsByKey.get(key);
240
+ const { name, version } = opts.dependenciesTree.get(nodeId).resolvedPackage;
241
+ opts.cycleBrokenNodeIds.add(nodeId);
242
+ opts.pathsByNodeIdPromises.get(nodeId)?.resolve(`${name}@${version}`);
243
+ }
173
244
  }
174
245
  function deduplicateAll(depGraph, duplicates) {
175
246
  const { depPathsMap, remainingDuplicates } = deduplicateDepPaths(duplicates, depGraph);
@@ -234,21 +305,6 @@ function deduplicateDepPaths(duplicates, depGraph) {
234
305
  remainingDuplicates,
235
306
  };
236
307
  }
237
- function isCompatibleAndHasMoreDeps(depGraph, depPath1, depPath2) {
238
- const node1 = depGraph[depPath1];
239
- const node2 = depGraph[depPath2];
240
- if (nodeDepsCount(node1) < nodeDepsCount(node2))
241
- return false;
242
- const node1DepPathsSet = new Set(Object.values(node1.children));
243
- const node2DepPaths = Object.values(node2.children);
244
- if (!node2DepPaths.every((depPath) => node1DepPathsSet.has(depPath)))
245
- return false;
246
- for (const depPath of node2.resolvedPeerNames) {
247
- if (!node1.resolvedPeerNames.has(depPath))
248
- return false;
249
- }
250
- return true;
251
- }
252
308
  function createPkgsByName(dependenciesTree, { directNodeIdsByAlias, topParents }) {
253
309
  const parentRefs = toPkgByName(Array.from(directNodeIdsByAlias.entries())
254
310
  .map(([alias, nodeId]) => ({
@@ -389,6 +445,7 @@ async function resolvePeersOfNode(currentAlias, nodeId, parentParentPkgs, ctx) {
389
445
  wantedRange,
390
446
  });
391
447
  }
448
+ ctx.peersCacheOwnerByNodeId.set(nodeId, hit.ownerNodeId);
392
449
  return {
393
450
  missingPeers: hit.missingPeers,
394
451
  finishing: (async () => {
@@ -451,6 +508,7 @@ async function resolvePeersOfNode(currentAlias, nodeId, parentParentPkgs, ctx) {
451
508
  missingPeers: allMissingPeers,
452
509
  depPath: pDefer(),
453
510
  resolvedPeers: allResolvedPeers,
511
+ ownerNodeId: nodeId,
454
512
  };
455
513
  if (ctx.peersCache.has(resolvedPackage.pkgIdWithPatchHash)) {
456
514
  ctx.peersCache.get(resolvedPackage.pkgIdWithPatchHash).push(cache);
@@ -512,6 +570,7 @@ async function resolvePeersOfNode(currentAlias, nodeId, parentParentPkgs, ctx) {
512
570
  if (cyclicPeerAliases.has(pendingPeer.alias)) {
513
571
  const { name, version } = ctx.dependenciesTree.get(pendingPeer.nodeId)?.resolvedPackage;
514
572
  const id = `${name}@${version}`;
573
+ ctx.cycleBrokenNodeIds.add(pendingPeer.nodeId);
515
574
  ctx.pathsByNodeIdPromises.get(pendingPeer.nodeId)?.resolve(id);
516
575
  return id;
517
576
  }
@@ -521,6 +580,12 @@ async function resolvePeersOfNode(currentAlias, nodeId, parentParentPkgs, ctx) {
521
580
  return { name: peerNode.resolvedPackage.name, version: peerNode.resolvedPackage.version };
522
581
  }
523
582
  }
583
+ let awaitedPeerNodeIds = ctx.awaitedPeerNodeIdsByNodeId.get(nodeId);
584
+ if (awaitedPeerNodeIds == null) {
585
+ awaitedPeerNodeIds = new Set();
586
+ ctx.awaitedPeerNodeIdsByNodeId.set(nodeId, awaitedPeerNodeIds);
587
+ }
588
+ awaitedPeerNodeIds.add(pendingPeer.nodeId);
524
589
  return ctx.pathsByNodeIdPromises.get(pendingPeer.nodeId).promise;
525
590
  })),
526
591
  ], ctx.peersSuffixMaxLength);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/installing.deps-resolver",
3
- "version": "1100.2.7",
3
+ "version": "1100.2.9",
4
4
  "description": "Resolves dependency graph of a package",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -27,7 +27,32 @@
27
27
  "!*.map"
28
28
  ],
29
29
  "dependencies": {
30
+ "@pnpm/catalogs.resolver": "1100.0.0",
31
+ "@pnpm/catalogs.types": "1100.0.0",
32
+ "@pnpm/config.version-policy": "1100.1.7",
33
+ "@pnpm/constants": "1100.0.0",
34
+ "@pnpm/core-loggers": "1100.2.2",
35
+ "@pnpm/deps.graph-hasher": "1100.2.10",
36
+ "@pnpm/deps.path": "1100.0.9",
37
+ "@pnpm/deps.peer-range": "1100.0.2",
38
+ "@pnpm/error": "1100.0.1",
39
+ "@pnpm/fetching.pick-fetcher": "1100.1.1",
40
+ "@pnpm/fs.symlink-dependency": "1100.0.11",
41
+ "@pnpm/hooks.types": "1100.2.1",
42
+ "@pnpm/lockfile.preferred-versions": "1100.0.20",
43
+ "@pnpm/lockfile.pruner": "1100.0.14",
44
+ "@pnpm/lockfile.types": "1100.0.14",
45
+ "@pnpm/lockfile.utils": "1100.1.3",
46
+ "@pnpm/patching.config": "1100.0.10",
47
+ "@pnpm/patching.types": "1100.0.0",
48
+ "@pnpm/pkg-manifest.reader": "1100.0.10",
49
+ "@pnpm/pkg-manifest.utils": "1100.2.7",
50
+ "@pnpm/resolving.npm-resolver": "1102.1.3",
51
+ "@pnpm/resolving.resolver-base": "1100.5.2",
52
+ "@pnpm/store.controller-types": "1100.1.8",
53
+ "@pnpm/types": "1101.4.0",
30
54
  "@pnpm/util.lex-comparator": "^4.0.1",
55
+ "@pnpm/workspace.spec-parser": "1100.0.0",
31
56
  "@yarnpkg/core": "4.8.0",
32
57
  "graph-cycles": "3.0.0",
33
58
  "is-inner-link": "^5.0.0",
@@ -42,44 +67,19 @@
42
67
  "semver": "^7.8.4",
43
68
  "semver-range-intersect": "^0.3.1",
44
69
  "validate-npm-package-name": "7.0.2",
45
- "version-selector-type": "^3.0.0",
46
- "@pnpm/catalogs.resolver": "1100.0.0",
47
- "@pnpm/config.version-policy": "1100.1.6",
48
- "@pnpm/catalogs.types": "1100.0.0",
49
- "@pnpm/constants": "1100.0.0",
50
- "@pnpm/deps.graph-hasher": "1100.2.8",
51
- "@pnpm/core-loggers": "1100.2.1",
52
- "@pnpm/deps.peer-range": "1100.0.2",
53
- "@pnpm/deps.path": "1100.0.8",
54
- "@pnpm/error": "1100.0.1",
55
- "@pnpm/fetching.pick-fetcher": "1100.0.14",
56
- "@pnpm/hooks.types": "1100.1.1",
57
- "@pnpm/lockfile.preferred-versions": "1100.0.18",
58
- "@pnpm/lockfile.pruner": "1100.0.13",
59
- "@pnpm/fs.symlink-dependency": "1100.0.10",
60
- "@pnpm/lockfile.types": "1100.0.13",
61
- "@pnpm/lockfile.utils": "1100.1.1",
62
- "@pnpm/patching.types": "1100.0.0",
63
- "@pnpm/pkg-manifest.reader": "1100.0.9",
64
- "@pnpm/pkg-manifest.utils": "1100.2.6",
65
- "@pnpm/resolving.npm-resolver": "1102.1.2",
66
- "@pnpm/resolving.resolver-base": "1100.5.1",
67
- "@pnpm/store.controller-types": "1100.1.7",
68
- "@pnpm/types": "1101.3.2",
69
- "@pnpm/patching.config": "1100.0.9",
70
- "@pnpm/workspace.spec-parser": "1100.0.0"
70
+ "version-selector-type": "^3.0.0"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "@pnpm/logger": "^1100.0.0"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@jest/globals": "30.4.1",
77
+ "@pnpm/installing.deps-resolver": "1100.2.9",
78
+ "@pnpm/logger": "1100.0.0",
77
79
  "@types/normalize-path": "^3.0.2",
78
80
  "@types/ramda": "0.31.1",
79
81
  "@types/semver": "7.7.1",
80
- "@types/validate-npm-package-name": "^4.0.2",
81
- "@pnpm/installing.deps-resolver": "1100.2.7",
82
- "@pnpm/logger": "1100.0.0"
82
+ "@types/validate-npm-package-name": "^4.0.2"
83
83
  },
84
84
  "engines": {
85
85
  "node": ">=22.13"