@pnpm/installing.env-installer 1101.0.10 → 1101.1.1

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,12 +1,12 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { checkPackage } from '@pnpm/config.package-is-installable';
3
4
  import { pickRegistryForPackage } from '@pnpm/config.pick-registry-for-package';
4
- import { installingConfigDepsLogger } from '@pnpm/core-loggers';
5
- import { calcLeafGlobalVirtualStorePath } from '@pnpm/deps.graph-hasher';
5
+ import { installingConfigDepsLogger, skippedOptionalDependencyLogger } from '@pnpm/core-loggers';
6
+ import { calcGlobalVirtualStorePathWithSubdeps, calcLeafGlobalVirtualStorePath } from '@pnpm/deps.graph-hasher';
6
7
  import { PnpmError } from '@pnpm/error';
7
8
  import { readModulesDir } from '@pnpm/fs.read-modules-dir';
8
9
  import { readEnvLockfile } from '@pnpm/lockfile.fs';
9
- import { safeReadPackageJsonFromDir } from '@pnpm/pkg-manifest.reader';
10
10
  import { rimraf } from '@zkochan/rimraf';
11
11
  import getNpmTarballUrl from 'get-npm-tarball-url';
12
12
  import { symlinkDir } from 'symlink-dir';
@@ -21,25 +21,44 @@ export async function installConfigDeps(configDepsOrLockfile, opts) {
21
21
  const globalVirtualStoreDir = path.join(opts.storeDir, 'links');
22
22
  const configModulesDir = path.join(opts.rootDir, 'node_modules/.pnpm-config');
23
23
  const existingConfigDeps = await readModulesDir(configModulesDir) ?? [];
24
+ let startedEmitted = false;
25
+ const reportStarted = () => {
26
+ if (startedEmitted)
27
+ return;
28
+ startedEmitted = true;
29
+ installingConfigDepsLogger.debug({ status: 'started' });
30
+ };
24
31
  await Promise.all(existingConfigDeps.map(async (existingConfigDep) => {
25
32
  if (!normalizedDeps[existingConfigDep]) {
33
+ reportStarted();
26
34
  await rimraf(path.join(configModulesDir, existingConfigDep));
27
35
  }
28
36
  }));
29
37
  const installedConfigDeps = [];
30
38
  await Promise.all(Object.entries(normalizedDeps).map(async ([pkgName, pkg]) => {
31
39
  const configDepPath = path.join(configModulesDir, pkgName);
32
- const existingPkgJson = existingConfigDeps.includes(pkgName)
33
- ? await safeReadPackageJsonFromDir(configDepPath)
34
- : null;
35
- if (existingPkgJson != null && existingPkgJson.name === pkgName && existingPkgJson.version === pkg.version) {
36
- return;
37
- }
38
- installingConfigDepsLogger.debug({ status: 'started' });
39
40
  const fullPkgId = `${pkgName}@${pkg.version}:${pkg.resolution.integrity}`;
40
- const relPath = calcLeafGlobalVirtualStorePath(fullPkgId, pkgName, pkg.version);
41
+ // The parent's GVS hash must incorporate its optional subdeps; otherwise
42
+ // changing a subdep version while keeping the parent pinned would collide
43
+ // on the same leaf and silently overwrite the previous sibling symlinks.
44
+ const optionalSubdepIds = {};
45
+ for (const subdep of pkg.optionalSubdeps ?? []) {
46
+ optionalSubdepIds[subdep.name] = `${subdep.name}@${subdep.version}:${subdep.resolution.integrity}`;
47
+ }
48
+ const relPath = calcGlobalVirtualStorePathWithSubdeps(fullPkgId, pkgName, pkg.version, optionalSubdepIds);
41
49
  const pkgDirInGlobalVirtualStore = path.join(globalVirtualStoreDir, relPath, 'node_modules', pkgName);
50
+ // The leaf hash captures parent+subdep identities from the lockfile but
51
+ // not the host's `process.arch`/`process.platform` selection. So even if
52
+ // the symlink target is already the expected leaf, the sibling links
53
+ // inside that leaf may target the wrong platform binary if the host's
54
+ // effective arch changed between runs (e.g. Rosetta x64 vs arm64 on
55
+ // macOS). Short-circuit only the parent's re-import/re-symlink in that
56
+ // case; always run installOptionalSubdeps so platform-specific siblings
57
+ // get pruned and relinked.
58
+ const parentSymlinkAlreadyCorrect = existingConfigDeps.includes(pkgName) &&
59
+ await symlinkPointsTo(configDepPath, pkgDirInGlobalVirtualStore);
42
60
  if (!fs.existsSync(path.join(pkgDirInGlobalVirtualStore, 'package.json'))) {
61
+ reportStarted();
43
62
  const { fetching } = await opts.store.fetchPackage({
44
63
  force: true,
45
64
  lockfileDir: opts.rootDir,
@@ -55,6 +74,24 @@ export async function installConfigDeps(configDepsOrLockfile, opts) {
55
74
  filesResponse,
56
75
  });
57
76
  }
77
+ if (pkg.optionalSubdeps?.length) {
78
+ await installOptionalSubdeps({
79
+ parentName: pkgName,
80
+ parentVersion: pkg.version,
81
+ subdeps: pkg.optionalSubdeps,
82
+ // path.dirname would land in the scope subdir for scoped parents; use
83
+ // the leaf's node_modules root so sibling symlinks resolve correctly.
84
+ parentNodeModulesDir: path.join(globalVirtualStoreDir, relPath, 'node_modules'),
85
+ globalVirtualStoreDir,
86
+ rootDir: opts.rootDir,
87
+ store: opts.store,
88
+ reportStarted,
89
+ });
90
+ }
91
+ if (parentSymlinkAlreadyCorrect) {
92
+ return;
93
+ }
94
+ reportStarted();
58
95
  if (existingConfigDeps.includes(pkgName)) {
59
96
  await rimraf(configDepPath);
60
97
  }
@@ -113,14 +150,121 @@ function normalizeFromLockfile(lockfile, registries) {
113
150
  throw new PnpmError('ENV_LOCKFILE_CORRUPTED', `pnpm-lock.yaml is corrupted or incomplete: missing integrity for "${pkgKey}"`);
114
151
  }
115
152
  const registry = pickRegistryForPackage(registries, pkgName);
153
+ const snapshot = lockfile.snapshots[pkgKey];
154
+ const optionalSubdeps = snapshot?.optionalDependencies
155
+ ? readOptionalSubdepsFromLockfile(pkgName, snapshot.optionalDependencies, lockfile, registries)
156
+ : undefined;
116
157
  deps[pkgName] = {
117
158
  version,
118
159
  resolution: {
119
160
  integrity: resolution.integrity,
120
161
  tarball: resolution.tarball ?? getNpmTarballUrl(pkgName, version, { registry }),
121
162
  },
163
+ optionalSubdeps,
122
164
  };
123
165
  }
124
166
  return deps;
125
167
  }
168
+ function readOptionalSubdepsFromLockfile(parentName, optionalDeps, lockfile, registries) {
169
+ const subdeps = [];
170
+ for (const [subdepName, subdepVersion] of Object.entries(optionalDeps)) {
171
+ const subdepKey = `${subdepName}@${subdepVersion}`;
172
+ const subdepInfo = lockfile.packages[subdepKey];
173
+ if (!subdepInfo) {
174
+ throw new PnpmError('ENV_LOCKFILE_CORRUPTED', `pnpm-lock.yaml is corrupted or incomplete: missing packages entry for "${subdepKey}" ` +
175
+ `referenced from optionalDependencies of config dependency "${parentName}"`);
176
+ }
177
+ const subdepResolution = subdepInfo.resolution;
178
+ if (!subdepResolution.integrity) {
179
+ throw new PnpmError('ENV_LOCKFILE_CORRUPTED', `pnpm-lock.yaml is corrupted or incomplete: missing integrity for "${subdepKey}"`);
180
+ }
181
+ const registry = pickRegistryForPackage(registries, subdepName);
182
+ subdeps.push({
183
+ name: subdepName,
184
+ version: subdepVersion,
185
+ resolution: {
186
+ integrity: subdepResolution.integrity,
187
+ tarball: subdepResolution.tarball ?? getNpmTarballUrl(subdepName, subdepVersion, { registry }),
188
+ },
189
+ os: subdepInfo.os,
190
+ cpu: subdepInfo.cpu,
191
+ libc: subdepInfo.libc,
192
+ });
193
+ }
194
+ return subdeps;
195
+ }
196
+ async function installOptionalSubdeps(opts) {
197
+ const parentLogInfo = { id: `${opts.parentName}@${opts.parentVersion}`, name: opts.parentName, version: opts.parentVersion };
198
+ const compatibleSubdeps = opts.subdeps.filter((subdep) => {
199
+ if (!subdep.os && !subdep.cpu && !subdep.libc)
200
+ return true;
201
+ // Use checkPackage rather than packageIsInstallable: the latter emits a
202
+ // user-visible warn for every incompatible variant, which would fire on
203
+ // every install since the env lockfile records all platform variants for
204
+ // portability. We log skipped subdeps at debug instead.
205
+ const error = checkPackage(`${subdep.name}@${subdep.version}`, { os: subdep.os, cpu: subdep.cpu, libc: subdep.libc }, {});
206
+ if (error == null)
207
+ return true;
208
+ skippedOptionalDependencyLogger.debug({
209
+ details: error.toString(),
210
+ package: { id: `${subdep.name}@${subdep.version}`, name: subdep.name, version: subdep.version },
211
+ parents: [parentLogInfo],
212
+ prefix: opts.rootDir,
213
+ reason: error.code === 'ERR_PNPM_UNSUPPORTED_ENGINE' ? 'unsupported_engine' : 'unsupported_platform',
214
+ });
215
+ return false;
216
+ });
217
+ const expectedSiblings = new Set([opts.parentName, ...compatibleSubdeps.map((s) => s.name)]);
218
+ const existingSiblings = await readModulesDir(opts.parentNodeModulesDir) ?? [];
219
+ const orphanSiblings = existingSiblings.filter((name) => !expectedSiblings.has(name));
220
+ if (orphanSiblings.length > 0) {
221
+ opts.reportStarted();
222
+ }
223
+ await Promise.all(orphanSiblings.map((name) => rimraf(path.join(opts.parentNodeModulesDir, name))));
224
+ await Promise.all(compatibleSubdeps.map(async (subdep) => {
225
+ const subdepFullPkgId = `${subdep.name}@${subdep.version}:${subdep.resolution.integrity}`;
226
+ const subdepRelPath = calcLeafGlobalVirtualStorePath(subdepFullPkgId, subdep.name, subdep.version);
227
+ const subdepDirInGlobalVirtualStore = path.join(opts.globalVirtualStoreDir, subdepRelPath, 'node_modules', subdep.name);
228
+ if (!fs.existsSync(path.join(subdepDirInGlobalVirtualStore, 'package.json'))) {
229
+ opts.reportStarted();
230
+ const { fetching } = await opts.store.fetchPackage({
231
+ force: true,
232
+ lockfileDir: opts.rootDir,
233
+ pkg: {
234
+ id: `${subdep.name}@${subdep.version}`,
235
+ resolution: subdep.resolution,
236
+ },
237
+ });
238
+ const { files: filesResponse } = await fetching();
239
+ await opts.store.importPackage(subdepDirInGlobalVirtualStore, {
240
+ force: true,
241
+ requiresBuild: false,
242
+ filesResponse,
243
+ });
244
+ }
245
+ const linkPath = path.join(opts.parentNodeModulesDir, subdep.name);
246
+ if (await symlinkPointsTo(linkPath, subdepDirInGlobalVirtualStore)) {
247
+ return;
248
+ }
249
+ opts.reportStarted();
250
+ await fs.promises.mkdir(path.dirname(linkPath), { recursive: true });
251
+ await symlinkDir(subdepDirInGlobalVirtualStore, linkPath);
252
+ }));
253
+ }
254
+ async function symlinkPointsTo(linkPath, expectedTarget) {
255
+ try {
256
+ // Realpath both sides: the expected target itself may live under a
257
+ // symlinked storeDir, and on case-insensitive filesystems the literal
258
+ // string forms can disagree about casing even when they refer to the
259
+ // same inode.
260
+ const [linkReal, targetReal] = await Promise.all([
261
+ fs.promises.realpath(linkPath),
262
+ fs.promises.realpath(expectedTarget),
263
+ ]);
264
+ return linkReal === targetReal;
265
+ }
266
+ catch {
267
+ return false;
268
+ }
269
+ }
126
270
  //# sourceMappingURL=installConfigDeps.js.map
@@ -4,6 +4,18 @@ export interface NormalizedConfigDep {
4
4
  integrity: string;
5
5
  tarball: string;
6
6
  };
7
+ optionalSubdeps?: NormalizedSubdep[];
8
+ }
9
+ export interface NormalizedSubdep {
10
+ name: string;
11
+ version: string;
12
+ resolution: {
13
+ integrity: string;
14
+ tarball: string;
15
+ };
16
+ os?: string[];
17
+ cpu?: string[];
18
+ libc?: string[];
7
19
  }
8
20
  export declare function parseIntegrity(pkgName: string, pkgSpec: string): {
9
21
  version: string;
@@ -9,6 +9,7 @@ import getNpmTarballUrl from 'get-npm-tarball-url';
9
9
  import { installConfigDeps } from './installConfigDeps.js';
10
10
  import { parseIntegrity } from './parseIntegrity.js';
11
11
  import { pruneEnvLockfile } from './pruneEnvLockfile.js';
12
+ import { resolveOptionalSubdeps } from './resolveOptionalSubdeps.js';
12
13
  /**
13
14
  * Resolves any config dependencies that are missing from the env lockfile,
14
15
  * then installs all config dependencies.
@@ -101,7 +102,13 @@ export async function resolveAndInstallConfigDeps(configDeps, opts) {
101
102
  envLockfile.packages[pkgKey] = {
102
103
  resolution: toLockfileResolution({ name, version }, resolution.resolution, registry),
103
104
  };
104
- envLockfile.snapshots[pkgKey] = {};
105
+ const optionalSubdeps = await resolveOptionalSubdeps(name, resolution.manifest, {
106
+ envLockfile,
107
+ lockfileDir: opts.rootDir,
108
+ registries: opts.registries,
109
+ resolveFromNpm,
110
+ });
111
+ envLockfile.snapshots[pkgKey] = optionalSubdeps ? { optionalDependencies: optionalSubdeps } : {};
105
112
  }));
106
113
  pruneEnvLockfile(envLockfile);
107
114
  await writeEnvLockfile(opts.rootDir, envLockfile);
@@ -8,6 +8,7 @@ import { createFetchFromRegistry } from '@pnpm/network.fetch';
8
8
  import { createNpmResolver } from '@pnpm/resolving.npm-resolver';
9
9
  import { parseWantedDependency } from '@pnpm/resolving.parse-wanted-dependency';
10
10
  import { installConfigDeps } from './installConfigDeps.js';
11
+ import { resolveOptionalSubdeps } from './resolveOptionalSubdeps.js';
11
12
  export async function resolveConfigDeps(configDeps, opts) {
12
13
  if (opts.frozenLockfile) {
13
14
  throw new PnpmError('FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE', 'Cannot resolve configDependencies with "frozen-lockfile" because the lockfile is not up to date');
@@ -45,7 +46,13 @@ export async function resolveConfigDeps(configDeps, opts) {
45
46
  envLockfile.packages[pkgKey] = {
46
47
  resolution: toLockfileResolution({ name: pkgName, version }, resolution.resolution, registry),
47
48
  };
48
- envLockfile.snapshots[pkgKey] = {};
49
+ const optionalSubdeps = await resolveOptionalSubdeps(pkgName, resolution.manifest, {
50
+ envLockfile,
51
+ lockfileDir: opts.rootDir,
52
+ registries: opts.registries,
53
+ resolveFromNpm,
54
+ });
55
+ envLockfile.snapshots[pkgKey] = optionalSubdeps ? { optionalDependencies: optionalSubdeps } : {};
49
56
  }));
50
57
  await Promise.all([
51
58
  writeSettings({
@@ -0,0 +1,13 @@
1
+ import type { EnvLockfile } from '@pnpm/lockfile.fs';
2
+ import type { ResolvedDependencies } from '@pnpm/lockfile.types';
3
+ import type { createNpmResolver } from '@pnpm/resolving.npm-resolver';
4
+ import type { DependencyManifest, Registries } from '@pnpm/types';
5
+ type ResolveFromNpm = ReturnType<typeof createNpmResolver>['resolveFromNpm'];
6
+ export interface ResolveOptionalSubdepsOpts {
7
+ envLockfile: EnvLockfile;
8
+ lockfileDir: string;
9
+ registries: Registries;
10
+ resolveFromNpm: ResolveFromNpm;
11
+ }
12
+ export declare function resolveOptionalSubdeps(parentName: string, parentManifest: DependencyManifest, opts: ResolveOptionalSubdepsOpts): Promise<ResolvedDependencies | undefined>;
13
+ export {};
@@ -0,0 +1,84 @@
1
+ import util from 'node:util';
2
+ import { pickRegistryForPackage } from '@pnpm/config.pick-registry-for-package';
3
+ import { skippedOptionalDependencyLogger } from '@pnpm/core-loggers';
4
+ import { PnpmError } from '@pnpm/error';
5
+ import { toLockfileResolution } from '@pnpm/lockfile.utils';
6
+ import semver from 'semver';
7
+ export async function resolveOptionalSubdeps(parentName, parentManifest, opts) {
8
+ const optionalDeps = parentManifest.optionalDependencies;
9
+ if (!optionalDeps || Object.keys(optionalDeps).length === 0) {
10
+ return undefined;
11
+ }
12
+ const resolved = {};
13
+ await Promise.all(Object.entries(optionalDeps).map(async ([subdepName, subdepSpec]) => {
14
+ if (semver.valid(subdepSpec) == null) {
15
+ // Ranges and tags would let the resolved version drift between machines
16
+ // even with a stable parent integrity, breaking the lockfile's promise
17
+ // of reproducible config-dep installs.
18
+ throw new PnpmError('CONFIG_DEP_OPTIONAL_NOT_EXACT', `Cannot install "${subdepName}@${subdepSpec}" as an optionalDependency of config dependency "${parentName}": only exact versions are supported (got "${subdepSpec}")`);
19
+ }
20
+ let resolution;
21
+ try {
22
+ // `optional: true` opts into full registry metadata so the resolver
23
+ // returns `libc` (and any other fields the abbreviated metadata strips).
24
+ // See pnpm/pnpm#9950.
25
+ resolution = await opts.resolveFromNpm({ alias: subdepName, bareSpecifier: subdepSpec, optional: true }, {
26
+ lockfileDir: opts.lockfileDir,
27
+ preferredVersions: {},
28
+ projectDir: opts.lockfileDir,
29
+ });
30
+ }
31
+ catch (err) {
32
+ // Trust-downgrade is a security signal that must fail the install even
33
+ // for optional deps; everything else mirrors npm's optionalDependencies
34
+ // semantics — log and skip.
35
+ if (util.types.isNativeError(err) && 'code' in err && err.code === 'ERR_PNPM_TRUST_DOWNGRADE') {
36
+ throw err;
37
+ }
38
+ skippedOptionalDependencyLogger.debug({
39
+ details: util.types.isNativeError(err) ? err.toString() : String(err),
40
+ package: {
41
+ name: subdepName,
42
+ // No resolved version yet; surface the requested specifier so log
43
+ // consumers that format `${name}@${version}` don't render `@undefined`.
44
+ version: subdepSpec,
45
+ bareSpecifier: subdepSpec,
46
+ },
47
+ parents: [{ id: `${parentName}@${parentManifest.version}`, name: parentName, version: parentManifest.version }],
48
+ prefix: opts.lockfileDir,
49
+ reason: 'resolution_failure',
50
+ });
51
+ return;
52
+ }
53
+ if (resolution?.resolution == null ||
54
+ !('integrity' in resolution.resolution) ||
55
+ typeof resolution.resolution.integrity !== 'string' ||
56
+ !resolution.resolution.integrity ||
57
+ resolution.manifest == null) {
58
+ throw new PnpmError('BAD_CONFIG_DEP', `Cannot resolve optionalDependency "${subdepName}" of config dependency "${parentName}" because it has no integrity`);
59
+ }
60
+ const subdepVersion = resolution.manifest.version;
61
+ const registry = pickRegistryForPackage(opts.registries, subdepName);
62
+ const subdepKey = `${subdepName}@${subdepVersion}`;
63
+ opts.envLockfile.packages[subdepKey] = {
64
+ resolution: toLockfileResolution({ name: subdepName, version: subdepVersion }, resolution.resolution, registry),
65
+ ...pickPlatformFields(resolution.manifest),
66
+ };
67
+ if (opts.envLockfile.snapshots[subdepKey] == null) {
68
+ opts.envLockfile.snapshots[subdepKey] = { optional: true };
69
+ }
70
+ resolved[subdepName] = subdepVersion;
71
+ }));
72
+ return Object.keys(resolved).length > 0 ? resolved : undefined;
73
+ }
74
+ function pickPlatformFields(manifest) {
75
+ const out = {};
76
+ if (manifest.os?.length)
77
+ out.os = manifest.os;
78
+ if (manifest.cpu?.length)
79
+ out.cpu = manifest.cpu;
80
+ if (manifest.libc?.length)
81
+ out.libc = manifest.libc;
82
+ return out;
83
+ }
84
+ //# sourceMappingURL=resolveOptionalSubdeps.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/installing.env-installer",
3
- "version": "1101.0.10",
3
+ "version": "1101.1.1",
4
4
  "description": "Installer for configurational dependencies",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -27,40 +27,43 @@
27
27
  "dependencies": {
28
28
  "@zkochan/rimraf": "^4.0.0",
29
29
  "get-npm-tarball-url": "^2.1.0",
30
+ "semver": "^7.7.2",
30
31
  "symlink-dir": "^10.0.1",
31
- "@pnpm/config.pick-registry-for-package": "1100.0.3",
32
+ "@pnpm/config.package-is-installable": "1100.0.6",
33
+ "@pnpm/config.writer": "1100.0.9",
34
+ "@pnpm/config.pick-registry-for-package": "1100.0.5",
32
35
  "@pnpm/constants": "1100.0.0",
33
- "@pnpm/core-loggers": "1100.1.0",
34
- "@pnpm/config.writer": "1100.0.8",
35
- "@pnpm/deps.graph-hasher": "1100.2.0",
36
+ "@pnpm/core-loggers": "1100.1.1",
36
37
  "@pnpm/error": "1100.0.0",
37
38
  "@pnpm/fs.read-modules-dir": "1100.0.1",
38
- "@pnpm/installing.deps-resolver": "1100.1.0",
39
- "@pnpm/lockfile.pruner": "1100.0.6",
40
- "@pnpm/lockfile.fs": "1100.1.0",
41
- "@pnpm/lockfile.types": "1100.0.6",
42
- "@pnpm/lockfile.utils": "1100.0.8",
43
- "@pnpm/network.auth-header": "1100.0.2",
44
- "@pnpm/network.fetch": "1100.0.5",
45
- "@pnpm/pkg-manifest.reader": "1100.0.3",
39
+ "@pnpm/deps.graph-hasher": "1100.2.1",
40
+ "@pnpm/installing.deps-resolver": "1100.1.2",
41
+ "@pnpm/lockfile.fs": "1100.1.1",
42
+ "@pnpm/lockfile.pruner": "1100.0.7",
43
+ "@pnpm/lockfile.types": "1100.0.7",
44
+ "@pnpm/network.fetch": "1100.0.6",
45
+ "@pnpm/lockfile.utils": "1100.0.9",
46
+ "@pnpm/network.auth-header": "1100.0.3",
47
+ "@pnpm/pkg-manifest.reader": "1100.0.4",
46
48
  "@pnpm/resolving.parse-wanted-dependency": "1100.0.1",
47
- "@pnpm/resolving.npm-resolver": "1101.2.0",
48
- "@pnpm/store.controller": "1101.0.7",
49
- "@pnpm/store.controller-types": "1100.1.0",
50
- "@pnpm/types": "1101.1.0"
49
+ "@pnpm/store.controller": "1101.0.8",
50
+ "@pnpm/resolving.npm-resolver": "1101.3.1",
51
+ "@pnpm/store.controller-types": "1100.1.1",
52
+ "@pnpm/types": "1101.1.1"
51
53
  },
52
54
  "peerDependencies": {
53
55
  "@pnpm/logger": ">=1001.0.0 <1002.0.0",
54
- "@pnpm/worker": "^1100.1.6"
56
+ "@pnpm/worker": "^1100.1.7"
55
57
  },
56
58
  "devDependencies": {
57
59
  "@jest/globals": "30.3.0",
58
60
  "@pnpm/registry-mock": "6.0.0",
61
+ "@types/semver": "7.7.1",
59
62
  "load-json-file": "^7.0.1",
60
63
  "read-yaml-file": "^3.0.0",
61
- "@pnpm/installing.env-installer": "1101.0.10",
62
- "@pnpm/prepare": "1100.0.9",
63
- "@pnpm/testing.temp-store": "1100.1.0"
64
+ "@pnpm/prepare": "1100.0.10",
65
+ "@pnpm/testing.temp-store": "1100.1.2",
66
+ "@pnpm/installing.env-installer": "1101.1.1"
64
67
  },
65
68
  "engines": {
66
69
  "node": ">=22.13"