@pnpm/engine.pm.commands 1101.1.20 → 1101.1.21
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.
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/self-updater/installPnpm.d.ts +4 -1
- package/lib/self-updater/installPnpm.js +9 -0
- package/lib/self-updater/selfUpdate.js +10 -8
- package/lib/self-updater/verifyPnpmEngineIdentity.d.ts +40 -0
- package/lib/self-updater/verifyPnpmEngineIdentity.js +102 -0
- package/lib/with/with.js +13 -0
- package/package.json +27 -24
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { selfUpdate } from './self-updater/index.js';
|
|
2
2
|
export { exePlatformPkgDirName, exePlatformPkgDirNameNext, installPnpm, installPnpmToStore, linkExePlatformBinary } from './self-updater/installPnpm.js';
|
|
3
|
+
export { verifyPnpmEngineIdentity, type VerifyPnpmEngineIdentityOptions } from './self-updater/verifyPnpmEngineIdentity.js';
|
|
3
4
|
export { setup } from './setup/index.js';
|
|
4
5
|
export { withCmd } from './with/index.js';
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { selfUpdate } from './self-updater/index.js';
|
|
2
2
|
export { exePlatformPkgDirName, exePlatformPkgDirNameNext, installPnpm, installPnpmToStore, linkExePlatformBinary } from './self-updater/installPnpm.js';
|
|
3
|
+
export { verifyPnpmEngineIdentity } from './self-updater/verifyPnpmEngineIdentity.js';
|
|
3
4
|
export { setup } from './setup/index.js';
|
|
4
5
|
export { withCmd } from './with/index.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -2,6 +2,7 @@ import { type GlobalAddOptions } from '@pnpm/global.commands';
|
|
|
2
2
|
import type { EnvLockfile } from '@pnpm/lockfile.types';
|
|
3
3
|
import { type StoreController } from '@pnpm/store.controller';
|
|
4
4
|
import type { Registries } from '@pnpm/types';
|
|
5
|
+
import { type VerifyPnpmEngineIdentityOptions } from './verifyPnpmEngineIdentity.js';
|
|
5
6
|
export interface InstallPnpmResult {
|
|
6
7
|
binDir: string;
|
|
7
8
|
baseDir: string;
|
|
@@ -15,6 +16,8 @@ export interface InstallPnpmOptions extends GlobalAddOptions {
|
|
|
15
16
|
name: string;
|
|
16
17
|
version: string;
|
|
17
18
|
};
|
|
19
|
+
/** See {@link VerifyPnpmEngineIdentityOptions.trustedKeys} — a test seam. */
|
|
20
|
+
trustedKeys?: VerifyPnpmEngineIdentityOptions['trustedKeys'];
|
|
18
21
|
}
|
|
19
22
|
/**
|
|
20
23
|
* Installs pnpm to the global packages directory (for self-update).
|
|
@@ -36,7 +39,7 @@ export declare function installPnpmToStore(pnpmVersion: string, opts: {
|
|
|
36
39
|
name: string;
|
|
37
40
|
version: string;
|
|
38
41
|
};
|
|
39
|
-
}): Promise<{
|
|
42
|
+
} & VerifyPnpmEngineIdentityOptions): Promise<{
|
|
40
43
|
binDir: string;
|
|
41
44
|
}>;
|
|
42
45
|
/**
|
|
@@ -11,6 +11,7 @@ import { headlessInstall } from '@pnpm/installing.deps-restorer';
|
|
|
11
11
|
import { registerProject } from '@pnpm/store.controller';
|
|
12
12
|
import { familySync } from 'detect-libc';
|
|
13
13
|
import { symlinkDir } from 'symlink-dir';
|
|
14
|
+
import { verifyPnpmEngineIdentity } from './verifyPnpmEngineIdentity.js';
|
|
14
15
|
// @pnpm/exe has platform-specific binaries, so its GVS hash must
|
|
15
16
|
// include ENGINE_NAME for correct per-platform resolution.
|
|
16
17
|
const PNPM_ALLOW_BUILDS = { '@pnpm/exe': true };
|
|
@@ -50,6 +51,9 @@ export async function installPnpmToStore(pnpmVersion, opts) {
|
|
|
50
51
|
}
|
|
51
52
|
return { binDir };
|
|
52
53
|
}
|
|
54
|
+
// Reached only on a store cache miss (a genuine download), so verifying the
|
|
55
|
+
// pnpm engine's registry signature here does not slow down repeated commands.
|
|
56
|
+
await verifyPnpmEngineIdentity(opts.envLockfile, pnpmVersion, opts);
|
|
53
57
|
// Install to a temporary directory — headless install with GVS enabled
|
|
54
58
|
// will populate the global virtual store
|
|
55
59
|
const tmpInstallDir = path.join(opts.storeDir, '.tmp', `pnpm-${pnpmVersion}-${Date.now()}`);
|
|
@@ -116,6 +120,11 @@ async function installPnpmToGlobalDir(opts, pkgName, version, wantedLockfile) {
|
|
|
116
120
|
const binDir = path.join(installDir, 'bin');
|
|
117
121
|
try {
|
|
118
122
|
if (wantedLockfile != null && opts.storeController != null && opts.storeDir != null) {
|
|
123
|
+
if (opts.envLockfile != null) {
|
|
124
|
+
// Reached only when actually downloading (no matching global install),
|
|
125
|
+
// so the signature check does not run on every invocation.
|
|
126
|
+
await verifyPnpmEngineIdentity(opts.envLockfile, version, opts);
|
|
127
|
+
}
|
|
119
128
|
await installFromLockfile(installDir, binDir, {
|
|
120
129
|
wantedLockfile,
|
|
121
130
|
allowBuilds: PNPM_ALLOW_BUILDS,
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { linkBins } from '@pnpm/bins.linker';
|
|
4
4
|
import { isExecutedByCorepack, packageManager } from '@pnpm/cli.meta';
|
|
5
5
|
import { docsUrl } from '@pnpm/cli.utils';
|
|
6
|
-
import { parsePackageManager, types as allTypes } from '@pnpm/config.reader';
|
|
6
|
+
import { parsePackageManager, shouldPersistLockfile, types as allTypes } from '@pnpm/config.reader';
|
|
7
7
|
import { getPublishedByPolicy } from '@pnpm/config.version-policy';
|
|
8
8
|
import { PnpmError } from '@pnpm/error';
|
|
9
9
|
import { createResolver, makeResolutionStrict } from '@pnpm/installing.client';
|
|
@@ -156,13 +156,15 @@ export async function handler(opts, params) {
|
|
|
156
156
|
}
|
|
157
157
|
if (manifestChanged)
|
|
158
158
|
await writeProjectManifest(manifest);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
159
|
+
if (shouldPersistLockfile({ ...opts.wantedPackageManager, fromDevEngines: true })) {
|
|
160
|
+
const store = await createStoreController(opts);
|
|
161
|
+
await resolvePackageManagerIntegrities(resolution.manifest.version, {
|
|
162
|
+
registries: opts.registries,
|
|
163
|
+
rootDir: opts.rootProjectManifestDir,
|
|
164
|
+
storeController: store.ctrl,
|
|
165
|
+
storeDir: store.dir,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
166
168
|
}
|
|
167
169
|
else {
|
|
168
170
|
manifest.packageManager = `pnpm@${resolution.manifest.version}`;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type RegistryKey, type VerifySignaturesOptions } from '@pnpm/deps.security.signatures';
|
|
2
|
+
import type { EnvLockfile } from '@pnpm/lockfile.types';
|
|
3
|
+
import type { Registries, RegistryConfig } from '@pnpm/types';
|
|
4
|
+
export type VerifyPnpmEngineIdentityOptions = VerifySignaturesOptions & {
|
|
5
|
+
registries: Registries;
|
|
6
|
+
configByUri?: Record<string, RegistryConfig>;
|
|
7
|
+
/**
|
|
8
|
+
* The npm signing keys to trust. Defaults to {@link getNpmSigningKeys} (npm's
|
|
9
|
+
* embedded public keys). A test seam only — passing an empty array skips
|
|
10
|
+
* verification. Not reachable from project config, so it cannot be used to
|
|
11
|
+
* weaken verification for a real install.
|
|
12
|
+
*/
|
|
13
|
+
trustedKeys?: RegistryKey[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Verifies that the pnpm engine about to be installed (and then executed) for an
|
|
17
|
+
* automatic version switch or self-update is genuinely the published `pnpm` —
|
|
18
|
+
* i.e. the bytes recorded in the env lockfile carry a valid npm registry
|
|
19
|
+
* signature for their exact `name@version`.
|
|
20
|
+
*
|
|
21
|
+
* The wanted pnpm version comes from a repository's `packageManager` /
|
|
22
|
+
* `devEngines.packageManager` field, and the project controls the lockfile
|
|
23
|
+
* integrity and the registry the bytes are fetched from — so without this
|
|
24
|
+
* check, a cloned repository could make pnpm download and run an arbitrary
|
|
25
|
+
* native binary. Signatures are verified against npm's embedded public keys
|
|
26
|
+
* (see `getNpmSigningKeys`), so a project-controlled registry cannot answer with
|
|
27
|
+
* its own key pair; the signed packument is fetched from the configured registry,
|
|
28
|
+
* which an npm mirror proxies transparently.
|
|
29
|
+
*
|
|
30
|
+
* Throws when verification detects tampering (an invalid signature), when a
|
|
31
|
+
* package/version is absent from the registry, or when an engine component
|
|
32
|
+
* present in the lockfile carries no integrity metadata — pnpm can install a
|
|
33
|
+
* tarball without integrity, so a missing integrity must fail closed rather
|
|
34
|
+
* than silently exempt that component from verification. Even an unreachable
|
|
35
|
+
* registry fails closed (with `PNPM_ENGINE_IDENTITY_UNVERIFIABLE`): the
|
|
36
|
+
* lockfile integrity is project-controlled, so it is not a safe fallback.
|
|
37
|
+
* This runs only when the engine is actually being installed (a store cache
|
|
38
|
+
* miss), so it does not add a network round trip to every command.
|
|
39
|
+
*/
|
|
40
|
+
export declare function verifyPnpmEngineIdentity(envLockfile: EnvLockfile, pnpmVersion: string, opts: VerifyPnpmEngineIdentityOptions): Promise<void>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { pickRegistryForPackage } from '@pnpm/config.pick-registry-for-package';
|
|
2
|
+
import { getNpmSigningKeys, verifyInstalledPackageSignatures, } from '@pnpm/deps.security.signatures';
|
|
3
|
+
import { PnpmError } from '@pnpm/error';
|
|
4
|
+
import { createGetAuthHeaderByURI } from '@pnpm/network.auth-header';
|
|
5
|
+
import { familySync } from 'detect-libc';
|
|
6
|
+
import { exePlatformPkgDirName, exePlatformPkgDirNameNext } from './installPnpm.js';
|
|
7
|
+
/**
|
|
8
|
+
* Verifies that the pnpm engine about to be installed (and then executed) for an
|
|
9
|
+
* automatic version switch or self-update is genuinely the published `pnpm` —
|
|
10
|
+
* i.e. the bytes recorded in the env lockfile carry a valid npm registry
|
|
11
|
+
* signature for their exact `name@version`.
|
|
12
|
+
*
|
|
13
|
+
* The wanted pnpm version comes from a repository's `packageManager` /
|
|
14
|
+
* `devEngines.packageManager` field, and the project controls the lockfile
|
|
15
|
+
* integrity and the registry the bytes are fetched from — so without this
|
|
16
|
+
* check, a cloned repository could make pnpm download and run an arbitrary
|
|
17
|
+
* native binary. Signatures are verified against npm's embedded public keys
|
|
18
|
+
* (see `getNpmSigningKeys`), so a project-controlled registry cannot answer with
|
|
19
|
+
* its own key pair; the signed packument is fetched from the configured registry,
|
|
20
|
+
* which an npm mirror proxies transparently.
|
|
21
|
+
*
|
|
22
|
+
* Throws when verification detects tampering (an invalid signature), when a
|
|
23
|
+
* package/version is absent from the registry, or when an engine component
|
|
24
|
+
* present in the lockfile carries no integrity metadata — pnpm can install a
|
|
25
|
+
* tarball without integrity, so a missing integrity must fail closed rather
|
|
26
|
+
* than silently exempt that component from verification. Even an unreachable
|
|
27
|
+
* registry fails closed (with `PNPM_ENGINE_IDENTITY_UNVERIFIABLE`): the
|
|
28
|
+
* lockfile integrity is project-controlled, so it is not a safe fallback.
|
|
29
|
+
* This runs only when the engine is actually being installed (a store cache
|
|
30
|
+
* miss), so it does not add a network round trip to every command.
|
|
31
|
+
*/
|
|
32
|
+
export async function verifyPnpmEngineIdentity(envLockfile, pnpmVersion, opts) {
|
|
33
|
+
const trustedKeys = opts.trustedKeys ?? getNpmSigningKeys();
|
|
34
|
+
if (trustedKeys.length === 0)
|
|
35
|
+
return; // test seam: no trusted keys means skip
|
|
36
|
+
const toVerify = collectEnginePackagesToVerify(envLockfile, opts.registries);
|
|
37
|
+
if (toVerify.length === 0) {
|
|
38
|
+
throw new PnpmError('PNPM_ENGINE_IDENTITY_UNVERIFIABLE', `Cannot verify the identity of pnpm@${pnpmVersion}: its integrity metadata is missing from pnpm-lock.yaml.`);
|
|
39
|
+
}
|
|
40
|
+
const getAuthHeader = createGetAuthHeaderByURI(opts.configByUri ?? {});
|
|
41
|
+
let result;
|
|
42
|
+
try {
|
|
43
|
+
result = await verifyInstalledPackageSignatures(toVerify, trustedKeys, getAuthHeader, opts);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
// Fail closed: we will not run a downloaded pnpm we could not verify, even
|
|
47
|
+
// when the failure is "could not reach the registry". The lockfile integrity
|
|
48
|
+
// is project-controlled, so it is not a safe fallback.
|
|
49
|
+
throw new PnpmError('PNPM_ENGINE_IDENTITY_UNVERIFIABLE', `Refusing to run pnpm@${pnpmVersion}: its npm registry signature could not be verified (${String(err)}).`, { hint: 'The registry signing keys / packument must be reachable to verify the pnpm release. Set `pmOnFail` to `ignore` to skip the version switch.' });
|
|
50
|
+
}
|
|
51
|
+
if (result.verified)
|
|
52
|
+
return;
|
|
53
|
+
const onlyUnreachable = result.failures.every((f) => f.category === 'unreachable');
|
|
54
|
+
throw new PnpmError(onlyUnreachable ? 'PNPM_ENGINE_IDENTITY_UNVERIFIABLE' : 'PNPM_ENGINE_IDENTITY_MISMATCH', `Refusing to run pnpm@${pnpmVersion}: its npm registry signature could not be verified ` +
|
|
55
|
+
`(${describe(result.failures)}). The bytes selected by this project's lockfile/registry do not match a published, signed pnpm release.`, { hint: 'This can indicate a tampered lockfile or a malicious/unreachable registry. Set `pmOnFail` to `ignore` to skip the version switch if this is unexpected.' });
|
|
56
|
+
}
|
|
57
|
+
function collectEnginePackagesToVerify(envLockfile, registries) {
|
|
58
|
+
const pmDeps = envLockfile.importers['.']?.packageManagerDependencies ?? {};
|
|
59
|
+
const toVerify = [];
|
|
60
|
+
for (const name of ['pnpm', '@pnpm/exe']) {
|
|
61
|
+
const version = pmDeps[name]?.version;
|
|
62
|
+
if (version == null)
|
|
63
|
+
continue;
|
|
64
|
+
toVerify.push(engineComponentToVerify(envLockfile, registries, { name, version }));
|
|
65
|
+
}
|
|
66
|
+
// The bytes actually executed are the host's `@pnpm/exe` platform binary,
|
|
67
|
+
// listed as an optional dependency of `@pnpm/exe`.
|
|
68
|
+
const exeVersion = pmDeps['@pnpm/exe']?.version;
|
|
69
|
+
if (exeVersion != null) {
|
|
70
|
+
const optionalDeps = envLockfile.snapshots[`@pnpm/exe@${exeVersion}`]?.optionalDependencies ?? {};
|
|
71
|
+
const libcFamily = familySync();
|
|
72
|
+
const candidateNames = [
|
|
73
|
+
`@pnpm/${exePlatformPkgDirName(process.platform, process.arch, libcFamily)}`,
|
|
74
|
+
`@pnpm/${exePlatformPkgDirNameNext(process.platform, process.arch, libcFamily)}`,
|
|
75
|
+
];
|
|
76
|
+
for (const platformName of candidateNames) {
|
|
77
|
+
const platformVersion = optionalDeps[platformName];
|
|
78
|
+
if (platformVersion == null)
|
|
79
|
+
continue;
|
|
80
|
+
// The first candidate present in the lockfile is the binary the install
|
|
81
|
+
// will link and execute, so it is the one that must be verifiable.
|
|
82
|
+
toVerify.push(engineComponentToVerify(envLockfile, registries, { name: platformName, version: platformVersion }));
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return toVerify;
|
|
87
|
+
}
|
|
88
|
+
function engineComponentToVerify(envLockfile, registries, { name, version }) {
|
|
89
|
+
const integrity = registryIntegrity(envLockfile.packages[`${name}@${version}`]?.resolution);
|
|
90
|
+
if (integrity == null) {
|
|
91
|
+
throw new PnpmError('PNPM_ENGINE_IDENTITY_UNVERIFIABLE', `Cannot verify the identity of ${name}@${version}: its integrity metadata is missing from pnpm-lock.yaml.`);
|
|
92
|
+
}
|
|
93
|
+
return { name, version, registry: pickRegistryForPackage(registries, name), integrity };
|
|
94
|
+
}
|
|
95
|
+
function registryIntegrity(resolution) {
|
|
96
|
+
const integrity = resolution?.integrity;
|
|
97
|
+
return typeof integrity === 'string' && integrity ? integrity : undefined;
|
|
98
|
+
}
|
|
99
|
+
function describe(failures) {
|
|
100
|
+
return failures.map(({ name, version, reason }) => `${name}@${version}: ${reason}`).join('; ');
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=verifyPnpmEngineIdentity.js.map
|
package/lib/with/with.js
CHANGED
|
@@ -64,6 +64,19 @@ export async function handler(opts, params) {
|
|
|
64
64
|
registries: opts.registries,
|
|
65
65
|
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
|
|
66
66
|
packageManager: { name: packageManager.name, version: packageManager.version },
|
|
67
|
+
// Network settings so the engine identity check can reach the canonical
|
|
68
|
+
// npm registry through the user's proxy / TLS configuration.
|
|
69
|
+
ca: opts.ca,
|
|
70
|
+
cert: opts.cert,
|
|
71
|
+
key: opts.key,
|
|
72
|
+
httpProxy: opts.httpProxy,
|
|
73
|
+
httpsProxy: opts.httpsProxy,
|
|
74
|
+
noProxy: opts.noProxy,
|
|
75
|
+
strictSsl: opts.strictSsl,
|
|
76
|
+
localAddress: opts.localAddress,
|
|
77
|
+
maxSockets: opts.maxSockets,
|
|
78
|
+
configByUri: opts.configByUri,
|
|
79
|
+
timeout: opts.fetchTimeout,
|
|
67
80
|
}));
|
|
68
81
|
}
|
|
69
82
|
finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/engine.pm.commands",
|
|
3
|
-
"version": "1101.1.
|
|
3
|
+
"version": "1101.1.21",
|
|
4
4
|
"description": "pnpm commands for self-updating and setting up pnpm",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -35,27 +35,30 @@
|
|
|
35
35
|
"render-help": "^2.0.0",
|
|
36
36
|
"semver": "^7.8.1",
|
|
37
37
|
"symlink-dir": "^10.0.1",
|
|
38
|
-
"@pnpm/bins.linker": "1100.0.
|
|
39
|
-
"@pnpm/
|
|
40
|
-
"@pnpm/cli.
|
|
41
|
-
"@pnpm/
|
|
42
|
-
"@pnpm/
|
|
43
|
-
"@pnpm/deps.graph-hasher": "1100.2.
|
|
38
|
+
"@pnpm/bins.linker": "1100.0.12",
|
|
39
|
+
"@pnpm/cli.meta": "1100.0.7",
|
|
40
|
+
"@pnpm/cli.utils": "1101.0.10",
|
|
41
|
+
"@pnpm/config.pick-registry-for-package": "1100.0.8",
|
|
42
|
+
"@pnpm/building.policy": "1100.0.9",
|
|
43
|
+
"@pnpm/deps.graph-hasher": "1100.2.4",
|
|
44
|
+
"@pnpm/config.version-policy": "1100.1.4",
|
|
45
|
+
"@pnpm/deps.security.signatures": "1101.2.0",
|
|
46
|
+
"@pnpm/config.reader": "1101.7.0",
|
|
44
47
|
"@pnpm/error": "1100.0.0",
|
|
45
|
-
"@pnpm/global.packages": "1100.0.
|
|
46
|
-
"@pnpm/installing.
|
|
47
|
-
"@pnpm/
|
|
48
|
-
"@pnpm/
|
|
49
|
-
"@pnpm/
|
|
50
|
-
"@pnpm/
|
|
51
|
-
"@pnpm/
|
|
52
|
-
"@pnpm/
|
|
53
|
-
"@pnpm/types": "1101.3.0",
|
|
48
|
+
"@pnpm/global.packages": "1100.0.7",
|
|
49
|
+
"@pnpm/installing.env-installer": "1101.1.7",
|
|
50
|
+
"@pnpm/lockfile.fs": "1100.1.4",
|
|
51
|
+
"@pnpm/installing.client": "1100.2.6",
|
|
52
|
+
"@pnpm/lockfile.types": "1100.0.10",
|
|
53
|
+
"@pnpm/network.auth-header": "1101.1.1",
|
|
54
|
+
"@pnpm/store.connection-manager": "1100.2.7",
|
|
55
|
+
"@pnpm/types": "1101.3.1",
|
|
54
56
|
"@pnpm/shell.path": "1100.0.1",
|
|
55
|
-
"@pnpm/
|
|
56
|
-
"@pnpm/
|
|
57
|
-
"@pnpm/
|
|
58
|
-
"@pnpm/
|
|
57
|
+
"@pnpm/global.commands": "1100.0.26",
|
|
58
|
+
"@pnpm/workspace.project-manifest-reader": "1100.0.11",
|
|
59
|
+
"@pnpm/store.controller": "1101.0.12",
|
|
60
|
+
"@pnpm/installing.deps-restorer": "1101.1.10",
|
|
61
|
+
"@pnpm/resolving.npm-resolver": "1101.5.1"
|
|
59
62
|
},
|
|
60
63
|
"peerDependencies": {
|
|
61
64
|
"@pnpm/logger": "^1001.0.1"
|
|
@@ -65,12 +68,12 @@
|
|
|
65
68
|
"@types/cross-spawn": "^6.0.6",
|
|
66
69
|
"@types/ramda": "0.31.1",
|
|
67
70
|
"@types/semver": "7.7.1",
|
|
68
|
-
"@pnpm/
|
|
71
|
+
"@pnpm/engine.pm.commands": "1101.1.21",
|
|
69
72
|
"@pnpm/error": "1100.0.0",
|
|
73
|
+
"@pnpm/testing.mock-agent": "1101.0.1",
|
|
70
74
|
"@pnpm/logger": "1100.0.0",
|
|
71
|
-
"@pnpm/prepare": "1100.0.
|
|
72
|
-
"@pnpm/
|
|
73
|
-
"@pnpm/testing.mock-agent": "1101.0.0"
|
|
75
|
+
"@pnpm/prepare": "1100.0.14",
|
|
76
|
+
"@pnpm/constants": "1100.0.0"
|
|
74
77
|
},
|
|
75
78
|
"engines": {
|
|
76
79
|
"node": ">=22.13"
|