@pnpm/resolving.npm-resolver 1101.1.0 → 1101.2.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.
- package/lib/createNpmResolutionVerifier.d.ts +74 -0
- package/lib/createNpmResolutionVerifier.js +473 -0
- package/lib/fetch.js +53 -6
- package/lib/fetchAttestationPublishedAt.d.ts +31 -0
- package/lib/fetchAttestationPublishedAt.js +99 -0
- package/lib/fetchFullMetadataCached.d.ts +33 -0
- package/lib/fetchFullMetadataCached.js +63 -0
- package/lib/index.d.ts +2 -4
- package/lib/index.js +84 -38
- package/lib/pickPackage.d.ts +32 -1
- package/lib/pickPackage.js +130 -25
- package/lib/violationCodes.d.ts +12 -0
- package/lib/violationCodes.js +13 -0
- package/package.json +13 -13
package/lib/pickPackage.js
CHANGED
|
@@ -58,12 +58,13 @@ function pickMax(a, b) {
|
|
|
58
58
|
const pickHighest = pickPackageFromMeta.bind(null, pickVersionByVersionRange);
|
|
59
59
|
const pickLowest = pickPackageFromMeta.bind(null, pickLowestVersionByVersionRange);
|
|
60
60
|
// When minimumReleaseAge is active: try the highest mature version; if none
|
|
61
|
-
//
|
|
62
|
-
//
|
|
61
|
+
// satisfies the range, fall back to the lowest version regardless of maturity
|
|
62
|
+
// so the resolver can report the violation inline and let the install layer
|
|
63
|
+
// (or other caller) decide what to do — never throw at this layer.
|
|
63
64
|
function pickRespectingMinReleaseAge(pickerOpts, spec, meta) {
|
|
64
65
|
return runPicker(pickerOpts, spec, (targetSpec) => {
|
|
65
66
|
const highest = pickHighest(pickerOpts, meta, targetSpec);
|
|
66
|
-
if (highest
|
|
67
|
+
if (highest)
|
|
67
68
|
return highest;
|
|
68
69
|
return pickLowest({
|
|
69
70
|
preferredVersionSelectors: pickerOpts.preferredVersionSelectors,
|
|
@@ -111,7 +112,6 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
111
112
|
publishedByExclude: opts.publishedByExclude,
|
|
112
113
|
pickLowestVersion: opts.pickLowestVersion,
|
|
113
114
|
includeLatestTag: opts.includeLatestTag,
|
|
114
|
-
strictPublishedByCheck: ctx.strictPublishedByCheck,
|
|
115
115
|
ignoreMissingTimeField: ctx.ignoreMissingTimeField,
|
|
116
116
|
};
|
|
117
117
|
validatePackageName(spec.name);
|
|
@@ -123,15 +123,29 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
123
123
|
: ABBREVIATED_META_DIR;
|
|
124
124
|
// Cache key includes fullMetadata to avoid returning abbreviated metadata when full metadata is requested.
|
|
125
125
|
const cacheKey = fullMetadata ? `${spec.name}:full` : spec.name;
|
|
126
|
+
const pkgMirror = getPkgMirrorPath(ctx.cacheDir, metaDir, opts.registry, spec.name);
|
|
126
127
|
const cachedMeta = ctx.metaCache.get(cacheKey);
|
|
127
128
|
if (cachedMeta != null) {
|
|
129
|
+
// The in-memory cache may hold abbreviated metadata from an earlier call
|
|
130
|
+
// that didn't need `time` (no publishedBy then). If this call has
|
|
131
|
+
// publishedBy and the package was modified recently, upgrade to full
|
|
132
|
+
// metadata so the maturity check runs properly.
|
|
133
|
+
const upgrade = await maybeUpgradeAbbreviatedMetaForReleaseAge(ctx, spec, opts, cachedMeta);
|
|
134
|
+
let metaForCache = upgrade.meta;
|
|
135
|
+
if (upgrade.upgradedFrom != null) {
|
|
136
|
+
// Persist the upgraded meta to disk too: the on-disk mirror still holds
|
|
137
|
+
// the abbreviated form, so without this a fresh process would re-trigger
|
|
138
|
+
// the upgrade fetch on its next install.
|
|
139
|
+
metaForCache = opts.dryRun
|
|
140
|
+
? upgrade.meta
|
|
141
|
+
: persistUpgradedMeta(ctx, pkgMirror, upgrade.upgradedFrom);
|
|
142
|
+
ctx.metaCache.set(cacheKey, metaForCache);
|
|
143
|
+
}
|
|
128
144
|
return {
|
|
129
|
-
meta:
|
|
130
|
-
pickedPackage: pickMatchingVersionFinal(pickerOpts, spec,
|
|
145
|
+
meta: metaForCache,
|
|
146
|
+
pickedPackage: pickMatchingVersionFinal(pickerOpts, spec, metaForCache),
|
|
131
147
|
};
|
|
132
148
|
}
|
|
133
|
-
const registryName = getRegistryName(opts.registry);
|
|
134
|
-
const pkgMirror = path.join(ctx.cacheDir, metaDir, registryName, `${encodePkgName(spec.name)}.jsonl`);
|
|
135
149
|
return runLimited(pkgMirror, async (limit) => {
|
|
136
150
|
let metaCachedInStore;
|
|
137
151
|
if (ctx.offline === true || ctx.preferOffline === true || opts.pickLowestVersion) {
|
|
@@ -145,6 +159,17 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
145
159
|
throw new PnpmError('NO_OFFLINE_META', `Failed to resolve ${toRaw(spec)} in package mirror ${pkgMirror}`);
|
|
146
160
|
}
|
|
147
161
|
if (metaCachedInStore != null) {
|
|
162
|
+
// Disk-cached meta may be abbreviated; upgrade for the maturity check
|
|
163
|
+
// before letting pickMatchingVersionFinal warn-and-skip on missing time.
|
|
164
|
+
const upgrade = await maybeUpgradeAbbreviatedMetaForReleaseAge(ctx, spec, opts, metaCachedInStore);
|
|
165
|
+
metaCachedInStore = upgrade.meta;
|
|
166
|
+
if (upgrade.upgradedFrom != null) {
|
|
167
|
+
// Persist so the next install skips this upgrade fetch entirely.
|
|
168
|
+
if (!opts.dryRun) {
|
|
169
|
+
metaCachedInStore = persistUpgradedMeta(ctx, pkgMirror, upgrade.upgradedFrom);
|
|
170
|
+
}
|
|
171
|
+
ctx.metaCache.set(cacheKey, metaCachedInStore);
|
|
172
|
+
}
|
|
148
173
|
const pickedPackage = pickMatchingVersionFinal(pickerOpts, spec, metaCachedInStore);
|
|
149
174
|
if (pickedPackage) {
|
|
150
175
|
return {
|
|
@@ -168,10 +193,11 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
168
193
|
};
|
|
169
194
|
}
|
|
170
195
|
}
|
|
171
|
-
catch
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
196
|
+
catch {
|
|
197
|
+
// Swallow fast-path errors (e.g. ERR_PNPM_MISSING_TIME from
|
|
198
|
+
// abbreviated meta) and fall through to the network fetch, which
|
|
199
|
+
// can upgrade to full metadata and run the maturity check on
|
|
200
|
+
// real `time` data.
|
|
175
201
|
}
|
|
176
202
|
}
|
|
177
203
|
}
|
|
@@ -189,13 +215,8 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
189
215
|
};
|
|
190
216
|
}
|
|
191
217
|
}
|
|
192
|
-
catch
|
|
193
|
-
//
|
|
194
|
-
// let the code fall through to the network fetch path which will get full metadata.
|
|
195
|
-
if (ctx.strictPublishedByCheck &&
|
|
196
|
-
!(isMissingTimeError(err))) {
|
|
197
|
-
throw err;
|
|
198
|
-
}
|
|
218
|
+
catch {
|
|
219
|
+
// Same as above — fall through to the network fetch.
|
|
199
220
|
}
|
|
200
221
|
}
|
|
201
222
|
}
|
|
@@ -219,6 +240,19 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
219
240
|
if (fetchResult.notModified) {
|
|
220
241
|
metaCachedInStore = metaCachedInStore ?? await limit(async () => loadMeta(pkgMirror));
|
|
221
242
|
if (metaCachedInStore != null) {
|
|
243
|
+
// The cached metadata may be abbreviated (no per-version `time`).
|
|
244
|
+
// When minimumReleaseAge is active we need `time` for the maturity check,
|
|
245
|
+
// so upgrade to full metadata via a follow-up fetch when warranted.
|
|
246
|
+
// Without this, repeat installs of recently-modified packages would
|
|
247
|
+
// silently bypass the maturity check via the warn-and-skip fallback.
|
|
248
|
+
const upgrade = await maybeUpgradeAbbreviatedMetaForReleaseAge(ctx, spec, opts, metaCachedInStore);
|
|
249
|
+
metaCachedInStore = upgrade.meta;
|
|
250
|
+
if (upgrade.upgradedFrom != null && !opts.dryRun) {
|
|
251
|
+
// Persist the upgraded full metadata to disk so subsequent installs
|
|
252
|
+
// skip this upgrade fetch entirely (the cached meta will then have
|
|
253
|
+
// `time` populated, so the upgrade condition won't trigger).
|
|
254
|
+
metaCachedInStore = persistUpgradedMeta(ctx, pkgMirror, upgrade.upgradedFrom);
|
|
255
|
+
}
|
|
222
256
|
ctx.metaCache.set(cacheKey, metaCachedInStore);
|
|
223
257
|
return {
|
|
224
258
|
meta: metaCachedInStore,
|
|
@@ -307,6 +341,70 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
307
341
|
}
|
|
308
342
|
});
|
|
309
343
|
}
|
|
344
|
+
// When `minimumReleaseAge` is active and we have abbreviated metadata (which
|
|
345
|
+
// the npm registry serves by default and which omits per-version `time`),
|
|
346
|
+
// the maturity check can't run on the data we have. If the package has been
|
|
347
|
+
// modified since the maturity cutoff, re-fetch with `fullMetadata: true` so
|
|
348
|
+
// `time` is populated and the check can proceed properly. Without this,
|
|
349
|
+
// `pickMatchingVersionFinal` would fall back to its warn-and-skip path,
|
|
350
|
+
// silently bypassing the minimumReleaseAge guarantee for affected packages.
|
|
351
|
+
//
|
|
352
|
+
// Returns the original meta when no upgrade is needed. When an upgrade
|
|
353
|
+
// happens, returns both the upgraded meta and the underlying fetch result
|
|
354
|
+
// so callers can persist it to disk and avoid re-fetching on next install.
|
|
355
|
+
async function maybeUpgradeAbbreviatedMetaForReleaseAge(ctx, spec, opts, meta) {
|
|
356
|
+
if (ctx.offline === true ||
|
|
357
|
+
!opts.publishedBy ||
|
|
358
|
+
meta.time != null ||
|
|
359
|
+
opts.publishedByExclude?.(spec.name) === true) {
|
|
360
|
+
return { meta };
|
|
361
|
+
}
|
|
362
|
+
const modifiedDate = meta.modified ? new Date(meta.modified) : null;
|
|
363
|
+
const isModifiedValid = modifiedDate != null && !Number.isNaN(modifiedDate.getTime());
|
|
364
|
+
if (isModifiedValid && modifiedDate < opts.publishedBy) {
|
|
365
|
+
// The package was last modified before the maturity cutoff. No individual
|
|
366
|
+
// version can be newer than the cutoff, so the abbreviated form is fine.
|
|
367
|
+
return { meta };
|
|
368
|
+
}
|
|
369
|
+
// When `modified` is missing or malformed we fall through to the upgrade
|
|
370
|
+
// fetch: prefer correctness (run the maturity check on real `time` data)
|
|
371
|
+
// over saving a network call when our cached freshness signal is unusable.
|
|
372
|
+
// Forward etag/modified so the registry can answer 304 if the upgraded
|
|
373
|
+
// representation hasn't actually changed (rare on the npm registry where
|
|
374
|
+
// full and abbreviated have distinct etags, but cheap to support).
|
|
375
|
+
const fullFetchResult = await ctx.fetch(spec.name, {
|
|
376
|
+
authHeaderValue: opts.authHeaderValue,
|
|
377
|
+
fullMetadata: true,
|
|
378
|
+
etag: meta.etag,
|
|
379
|
+
modified: meta.modified,
|
|
380
|
+
registry: opts.registry,
|
|
381
|
+
});
|
|
382
|
+
if (fullFetchResult.notModified) {
|
|
383
|
+
// Upgrade fetch came back 304: keep the abbreviated meta. The downstream
|
|
384
|
+
// `pickMatchingVersionFinal` will fall through to its warn-and-skip path.
|
|
385
|
+
return { meta };
|
|
386
|
+
}
|
|
387
|
+
return { meta: fullFetchResult.meta, upgradedFrom: fullFetchResult };
|
|
388
|
+
}
|
|
389
|
+
// Persists upgraded full metadata to the on-disk cache mirror and returns
|
|
390
|
+
// the meta to store in the in-memory cache. When `filterMetadata` is on, the
|
|
391
|
+
// in-memory and on-disk forms are both stripped via `clearMeta`; otherwise
|
|
392
|
+
// the original raw response body is written and the unstripped meta is kept.
|
|
393
|
+
function persistUpgradedMeta(ctx, pkgMirror, upgradedFrom) {
|
|
394
|
+
const metaForCache = ctx.filterMetadata ? clearMeta(upgradedFrom.meta) : upgradedFrom.meta;
|
|
395
|
+
const jsonForDisk = ctx.filterMetadata
|
|
396
|
+
? prepareJsonForDisk(metaForCache, upgradedFrom.etag)
|
|
397
|
+
: prepareJsonForDisk(upgradedFrom.meta, upgradedFrom.etag, upgradedFrom.jsonText);
|
|
398
|
+
runLimited(pkgMirror, (l) => l(async () => {
|
|
399
|
+
try {
|
|
400
|
+
await saveMeta(pkgMirror, jsonForDisk);
|
|
401
|
+
}
|
|
402
|
+
catch (err) { // eslint-disable-line
|
|
403
|
+
// We don't care if this file was not written to the cache
|
|
404
|
+
}
|
|
405
|
+
}));
|
|
406
|
+
return metaForCache;
|
|
407
|
+
}
|
|
310
408
|
function clearMeta(pkg) {
|
|
311
409
|
const versions = {};
|
|
312
410
|
for (const [version, info] of Object.entries(pkg.versions)) {
|
|
@@ -342,18 +440,25 @@ function clearMeta(pkg) {
|
|
|
342
440
|
modified: pkg.modified,
|
|
343
441
|
};
|
|
344
442
|
}
|
|
345
|
-
function encodePkgName(pkgName) {
|
|
443
|
+
export function encodePkgName(pkgName) {
|
|
346
444
|
if (pkgName !== pkgName.toLowerCase()) {
|
|
347
445
|
return `${pkgName}_${createHexHash(pkgName)}`;
|
|
348
446
|
}
|
|
349
447
|
return pkgName;
|
|
350
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* Path of the on-disk JSONL document where pnpm mirrors a package's registry
|
|
451
|
+
* metadata. `metaDir` selects between abbreviated and full caches.
|
|
452
|
+
*/
|
|
453
|
+
export function getPkgMirrorPath(cacheDir, metaDir, registry, pkgName) {
|
|
454
|
+
return path.join(cacheDir, metaDir, getRegistryName(registry), `${encodePkgName(pkgName)}.jsonl`);
|
|
455
|
+
}
|
|
351
456
|
/**
|
|
352
457
|
* Formats metadata for disk storage as two-line NDJSON:
|
|
353
458
|
* Line 1: cache headers (etag, modified) — small, fast to read
|
|
354
459
|
* Line 2: the full registry metadata JSON — unchanged from the registry response
|
|
355
460
|
*/
|
|
356
|
-
function prepareJsonForDisk(meta, etag, jsonText) {
|
|
461
|
+
export function prepareJsonForDisk(meta, etag, jsonText) {
|
|
357
462
|
const modified = meta.modified ?? meta.time?.modified;
|
|
358
463
|
const headers = JSON.stringify({ etag, modified });
|
|
359
464
|
const body = jsonText ?? JSON.stringify(meta);
|
|
@@ -369,7 +474,7 @@ function isMissingTimeError(err) {
|
|
|
369
474
|
// memory via this Set as they resolve ever more distinct packages.
|
|
370
475
|
const MAX_WARNED_MISSING_TIME = 1024;
|
|
371
476
|
const warnedMissingTimeFor = new Set();
|
|
372
|
-
function warnMissingTimeFieldOnce(pkgName) {
|
|
477
|
+
export function warnMissingTimeFieldOnce(pkgName) {
|
|
373
478
|
if (warnedMissingTimeFor.has(pkgName))
|
|
374
479
|
return;
|
|
375
480
|
if (warnedMissingTimeFor.size >= MAX_WARNED_MISSING_TIME) {
|
|
@@ -396,7 +501,7 @@ async function getFileMtime(filePath) {
|
|
|
396
501
|
* parsing the full metadata (which can be megabytes for popular packages)
|
|
397
502
|
* when we only need conditional-request headers.
|
|
398
503
|
*/
|
|
399
|
-
async function loadMetaHeaders(pkgMirror) {
|
|
504
|
+
export async function loadMetaHeaders(pkgMirror) {
|
|
400
505
|
let fh;
|
|
401
506
|
try {
|
|
402
507
|
fh = await fs.open(pkgMirror, 'r');
|
|
@@ -423,7 +528,7 @@ async function loadMetaHeaders(pkgMirror) {
|
|
|
423
528
|
* Line 1: cache headers (etag, modified)
|
|
424
529
|
* Line 2: registry metadata JSON
|
|
425
530
|
*/
|
|
426
|
-
async function loadMeta(pkgMirror) {
|
|
531
|
+
export async function loadMeta(pkgMirror) {
|
|
427
532
|
try {
|
|
428
533
|
const data = await gfs.readFile(pkgMirror, 'utf8');
|
|
429
534
|
const newlineIdx = data.indexOf('\n');
|
|
@@ -439,7 +544,7 @@ async function loadMeta(pkgMirror) {
|
|
|
439
544
|
}
|
|
440
545
|
}
|
|
441
546
|
const createdDirs = new Set();
|
|
442
|
-
async function saveMeta(pkgMirror, json) {
|
|
547
|
+
export async function saveMeta(pkgMirror, json) {
|
|
443
548
|
const dir = path.dirname(pkgMirror);
|
|
444
549
|
if (!createdDirs.has(dir)) {
|
|
445
550
|
await fs.mkdir(dir, { recursive: true });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Violation codes the npm resolver attaches to
|
|
3
|
+
* `ResolutionPolicyViolation.code` when an inline policy check rejects
|
|
4
|
+
* a pick. Exported so downstream code (the install command, the strict
|
|
5
|
+
* resolver wrapper, tests) references one source of truth instead of
|
|
6
|
+
* re-typing the string.
|
|
7
|
+
*
|
|
8
|
+
* Lives in its own module — both `index.ts` and `createNpmResolutionVerifier.ts`
|
|
9
|
+
* import it, so keeping the constants here avoids a cycle.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MINIMUM_RELEASE_AGE_VIOLATION_CODE = "MINIMUM_RELEASE_AGE_VIOLATION";
|
|
12
|
+
export declare const TRUST_DOWNGRADE_VIOLATION_CODE = "TRUST_DOWNGRADE";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Violation codes the npm resolver attaches to
|
|
3
|
+
* `ResolutionPolicyViolation.code` when an inline policy check rejects
|
|
4
|
+
* a pick. Exported so downstream code (the install command, the strict
|
|
5
|
+
* resolver wrapper, tests) references one source of truth instead of
|
|
6
|
+
* re-typing the string.
|
|
7
|
+
*
|
|
8
|
+
* Lives in its own module — both `index.ts` and `createNpmResolutionVerifier.ts`
|
|
9
|
+
* import it, so keeping the constants here avoids a cycle.
|
|
10
|
+
*/
|
|
11
|
+
export const MINIMUM_RELEASE_AGE_VIOLATION_CODE = 'MINIMUM_RELEASE_AGE_VIOLATION';
|
|
12
|
+
export const TRUST_DOWNGRADE_VIOLATION_CODE = 'TRUST_DOWNGRADE';
|
|
13
|
+
//# sourceMappingURL=violationCodes.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/resolving.npm-resolver",
|
|
3
|
-
"version": "1101.
|
|
3
|
+
"version": "1101.2.0",
|
|
4
4
|
"description": "Resolver for npm-hosted packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -41,25 +41,26 @@
|
|
|
41
41
|
"ssri": "13.0.1",
|
|
42
42
|
"version-selector-type": "^3.0.0",
|
|
43
43
|
"@pnpm/config.pick-registry-for-package": "1100.0.3",
|
|
44
|
+
"@pnpm/config.version-policy": "1100.1.0",
|
|
45
|
+
"@pnpm/constants": "1100.0.0",
|
|
46
|
+
"@pnpm/core-loggers": "1100.1.0",
|
|
44
47
|
"@pnpm/crypto.hash": "1100.0.1",
|
|
45
48
|
"@pnpm/error": "1100.0.0",
|
|
46
49
|
"@pnpm/fetching.types": "1100.0.1",
|
|
47
|
-
"@pnpm/resolving.jsr-specifier-parser": "1100.0.0",
|
|
48
50
|
"@pnpm/fs.graceful-fs": "1100.1.0",
|
|
51
|
+
"@pnpm/resolving.jsr-specifier-parser": "1100.0.0",
|
|
49
52
|
"@pnpm/resolving.registry.pkg-metadata-filter": "1100.0.3",
|
|
50
|
-
"@pnpm/store.cafs": "1100.1.3",
|
|
51
|
-
"@pnpm/store.index": "1100.1.0",
|
|
52
53
|
"@pnpm/resolving.registry.types": "1100.0.3",
|
|
54
|
+
"@pnpm/resolving.resolver-base": "1100.2.0",
|
|
55
|
+
"@pnpm/store.cafs": "1100.1.5",
|
|
56
|
+
"@pnpm/store.index": "1100.1.0",
|
|
53
57
|
"@pnpm/types": "1101.1.0",
|
|
54
|
-
"@pnpm/resolving.resolver-base": "1100.1.3",
|
|
55
|
-
"@pnpm/constants": "1100.0.0",
|
|
56
58
|
"@pnpm/workspace.range-resolver": "1100.0.1",
|
|
57
|
-
"@pnpm/workspace.spec-parser": "1100.0.0"
|
|
58
|
-
"@pnpm/core-loggers": "1100.0.2"
|
|
59
|
+
"@pnpm/workspace.spec-parser": "1100.0.0"
|
|
59
60
|
},
|
|
60
61
|
"peerDependencies": {
|
|
61
62
|
"@pnpm/logger": ">=1001.0.0 <1002.0.0",
|
|
62
|
-
"@pnpm/worker": "^1100.1.
|
|
63
|
+
"@pnpm/worker": "^1100.1.6"
|
|
63
64
|
},
|
|
64
65
|
"devDependencies": {
|
|
65
66
|
"@jest/globals": "30.3.0",
|
|
@@ -69,12 +70,11 @@
|
|
|
69
70
|
"@types/ssri": "^7.1.5",
|
|
70
71
|
"load-json-file": "^7.0.1",
|
|
71
72
|
"tempy": "3.0.0",
|
|
72
|
-
"@pnpm/config.version-policy": "1100.0.3",
|
|
73
73
|
"@pnpm/logger": "1100.0.0",
|
|
74
|
-
"@pnpm/resolving.npm-resolver": "1101.
|
|
75
|
-
"@pnpm/network.fetch": "1100.0.
|
|
74
|
+
"@pnpm/resolving.npm-resolver": "1101.2.0",
|
|
75
|
+
"@pnpm/network.fetch": "1100.0.5",
|
|
76
76
|
"@pnpm/test-fixtures": "1100.0.0",
|
|
77
|
-
"@pnpm/testing.mock-agent": "1100.0.
|
|
77
|
+
"@pnpm/testing.mock-agent": "1100.0.5"
|
|
78
78
|
},
|
|
79
79
|
"engines": {
|
|
80
80
|
"node": ">=22.13"
|