@pnpm/resolving.npm-resolver 1102.1.1 → 1102.1.2
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/fetch.d.ts +8 -1
- package/lib/index.js +3 -5
- package/lib/memoizeFetchMetadata.d.ts +24 -0
- package/lib/memoizeFetchMetadata.js +37 -0
- package/lib/pickPackage.js +1 -1
- package/package.json +13 -14
package/lib/fetch.d.ts
CHANGED
|
@@ -3,7 +3,14 @@ import type { FetchFromRegistry, RetryTimeoutOptions } from '@pnpm/fetching.type
|
|
|
3
3
|
import type { PackageMeta } from '@pnpm/resolving.registry.types';
|
|
4
4
|
export interface FetchMetadataResult {
|
|
5
5
|
meta: PackageMeta;
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The raw registry response body, used only to mirror the response to disk
|
|
8
|
+
* without re-serializing `meta`. A fresh fetch always sets it, but it
|
|
9
|
+
* reaches only the caller that initiated the request: the phase-long memo
|
|
10
|
+
* cache holds a body-less clone (see memoizeFetchMetadata.ts), so cache
|
|
11
|
+
* hits see `undefined` and the cache never pins the body.
|
|
12
|
+
*/
|
|
13
|
+
jsonText: string | undefined;
|
|
7
14
|
etag?: string;
|
|
8
15
|
notModified?: false;
|
|
9
16
|
}
|
package/lib/index.js
CHANGED
|
@@ -8,12 +8,12 @@ import { readPkgFromCafs, } from '@pnpm/worker';
|
|
|
8
8
|
import { resolveWorkspaceRange } from '@pnpm/workspace.range-resolver';
|
|
9
9
|
import { LRUCache } from 'lru-cache';
|
|
10
10
|
import normalize from 'normalize-path';
|
|
11
|
-
import pMemoize, { pMemoizeClear } from 'p-memoize';
|
|
12
11
|
import { clone } from 'ramda';
|
|
13
12
|
import semver from 'semver';
|
|
14
13
|
import ssri from 'ssri';
|
|
15
14
|
import versionSelectorType from 'version-selector-type';
|
|
16
15
|
import { fetchMetadataFromFromRegistry, RegistryResponseError } from './fetch.js';
|
|
16
|
+
import { memoizeFetchMetadata } from './memoizeFetchMetadata.js';
|
|
17
17
|
import { normalizeRegistryUrl } from './normalizeRegistryUrl.js';
|
|
18
18
|
import { BUILTIN_NAMED_REGISTRIES, parseBareSpecifier, parseJsrSpecifierToRegistryPackageSpec, parseNamedRegistrySpecifierToRegistryPackageSpec, } from './parseBareSpecifier.js';
|
|
19
19
|
import { pickPackage, } from './pickPackage.js';
|
|
@@ -75,9 +75,7 @@ export function createNpmResolver(fetchFromRegistry, getAuthHeader, opts) {
|
|
|
75
75
|
timeout: opts.timeout ?? 60000,
|
|
76
76
|
fetchWarnTimeoutMs: opts.fetchWarnTimeoutMs ?? 10 * 1000, // 10 sec
|
|
77
77
|
};
|
|
78
|
-
const fetch =
|
|
79
|
-
cacheKey: (...args) => JSON.stringify(args),
|
|
80
|
-
});
|
|
78
|
+
const { fetch, clear: clearFetchCache } = memoizeFetchMetadata(fetchMetadataFromFromRegistry.bind(null, fetchOpts));
|
|
81
79
|
// Track ownership so `clearCache()` below only wipes the in-memory
|
|
82
80
|
// cache when this factory created it. A caller-supplied
|
|
83
81
|
// `opts.metaCache` may be shared with another resolver instance (or
|
|
@@ -149,7 +147,7 @@ export function createNpmResolver(fetchFromRegistry, getAuthHeader, opts) {
|
|
|
149
147
|
if (ownsMetaCache && 'clear' in metaCache && typeof metaCache.clear === 'function') {
|
|
150
148
|
metaCache.clear();
|
|
151
149
|
}
|
|
152
|
-
|
|
150
|
+
clearFetchCache();
|
|
153
151
|
},
|
|
154
152
|
};
|
|
155
153
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FetchMetadataNotModifiedResult, FetchMetadataOptions, FetchMetadataResult } from './fetch.js';
|
|
2
|
+
export type FetchMetadata = (pkgName: string, opts: FetchMetadataOptions) => Promise<FetchMetadataResult | FetchMetadataNotModifiedResult>;
|
|
3
|
+
export interface MemoizedFetchMetadata {
|
|
4
|
+
fetch: FetchMetadata;
|
|
5
|
+
clear: () => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Memoizes metadata fetches for the whole resolution phase (cleared via
|
|
9
|
+
* `clear`, see `clearResolutionCache`), deduplicating concurrent and repeat
|
|
10
|
+
* requests for the same package.
|
|
11
|
+
*
|
|
12
|
+
* Unlike plain memoization, the cache holds a body-less clone of each result:
|
|
13
|
+
* `jsonText` — the raw registry response body, up to tens of MB for a popular
|
|
14
|
+
* package — reaches only the caller that initiated the fetch, which is the
|
|
15
|
+
* caller that writes the disk mirror. A phase-long cache that kept the bodies
|
|
16
|
+
* would pin hundreds of MB on large cold-cache graphs. A cache-hit caller
|
|
17
|
+
* that also writes the mirror falls back to `JSON.stringify(meta)` in
|
|
18
|
+
* `prepareJsonForDisk`, which is equivalent on read: `loadMeta` re-derives
|
|
19
|
+
* `etag` from the headers line.
|
|
20
|
+
*
|
|
21
|
+
* A rejected fetch is evicted so a transient network failure is retried by
|
|
22
|
+
* the next request instead of being cached for the rest of the phase.
|
|
23
|
+
*/
|
|
24
|
+
export declare function memoizeFetchMetadata(fetch: FetchMetadata): MemoizedFetchMetadata;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memoizes metadata fetches for the whole resolution phase (cleared via
|
|
3
|
+
* `clear`, see `clearResolutionCache`), deduplicating concurrent and repeat
|
|
4
|
+
* requests for the same package.
|
|
5
|
+
*
|
|
6
|
+
* Unlike plain memoization, the cache holds a body-less clone of each result:
|
|
7
|
+
* `jsonText` — the raw registry response body, up to tens of MB for a popular
|
|
8
|
+
* package — reaches only the caller that initiated the fetch, which is the
|
|
9
|
+
* caller that writes the disk mirror. A phase-long cache that kept the bodies
|
|
10
|
+
* would pin hundreds of MB on large cold-cache graphs. A cache-hit caller
|
|
11
|
+
* that also writes the mirror falls back to `JSON.stringify(meta)` in
|
|
12
|
+
* `prepareJsonForDisk`, which is equivalent on read: `loadMeta` re-derives
|
|
13
|
+
* `etag` from the headers line.
|
|
14
|
+
*
|
|
15
|
+
* A rejected fetch is evicted so a transient network failure is retried by
|
|
16
|
+
* the next request instead of being cached for the rest of the phase.
|
|
17
|
+
*/
|
|
18
|
+
export function memoizeFetchMetadata(fetch) {
|
|
19
|
+
const cache = new Map();
|
|
20
|
+
return {
|
|
21
|
+
fetch: (pkgName, opts) => {
|
|
22
|
+
const key = JSON.stringify([pkgName, opts]);
|
|
23
|
+
const cached = cache.get(key);
|
|
24
|
+
if (cached != null)
|
|
25
|
+
return cached;
|
|
26
|
+
const pending = fetch(pkgName, opts);
|
|
27
|
+
const bodiless = pending.then((result) => result.notModified ? result : { ...result, jsonText: undefined });
|
|
28
|
+
bodiless.catch(() => cache.delete(key));
|
|
29
|
+
cache.set(key, bodiless);
|
|
30
|
+
return pending;
|
|
31
|
+
},
|
|
32
|
+
clear: () => {
|
|
33
|
+
cache.clear();
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=memoizeFetchMetadata.js.map
|
package/lib/pickPackage.js
CHANGED
|
@@ -344,7 +344,7 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
344
344
|
if (!isModifiedValid || modifiedDate > opts.publishedBy) {
|
|
345
345
|
// Save the abbreviated metadata to the abbreviated cache before re-fetching full.
|
|
346
346
|
if (!opts.dryRun) {
|
|
347
|
-
const abbreviatedJson = prepareJsonForDisk(
|
|
347
|
+
const abbreviatedJson = prepareJsonForDisk(resultToSave.meta, resultToSave.etag, resultToSave.jsonText);
|
|
348
348
|
// Fire-and-forget save to the abbreviated cache path (pkgMirror).
|
|
349
349
|
runLimited(pkgMirror, (limit) => limit(async () => {
|
|
350
350
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/resolving.npm-resolver",
|
|
3
|
-
"version": "1102.1.
|
|
3
|
+
"version": "1102.1.2",
|
|
4
4
|
"description": "Resolver for npm-hosted packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
"lru-cache": "^11.5.0",
|
|
35
35
|
"normalize-path": "^3.0.0",
|
|
36
36
|
"p-limit": "^7.3.0",
|
|
37
|
-
"p-memoize": "8.0.0",
|
|
38
37
|
"parse-npm-tarball-url": "^5.0.0",
|
|
39
38
|
"path-temp": "^3.0.0",
|
|
40
39
|
"ramda": "npm:@pnpm/ramda@0.28.1",
|
|
@@ -45,22 +44,22 @@
|
|
|
45
44
|
"validate-npm-package-name": "7.0.2",
|
|
46
45
|
"version-selector-type": "^3.0.0",
|
|
47
46
|
"@pnpm/config.pick-registry-for-package": "1100.0.9",
|
|
48
|
-
"@pnpm/config.version-policy": "1100.1.6",
|
|
49
|
-
"@pnpm/constants": "1100.0.0",
|
|
50
|
-
"@pnpm/core-loggers": "1100.2.1",
|
|
51
47
|
"@pnpm/crypto.hash": "1100.0.1",
|
|
52
48
|
"@pnpm/error": "1100.0.1",
|
|
49
|
+
"@pnpm/config.version-policy": "1100.1.6",
|
|
53
50
|
"@pnpm/fetching.types": "1100.0.2",
|
|
54
|
-
"@pnpm/fs.graceful-fs": "1100.1.0",
|
|
55
51
|
"@pnpm/resolving.registry.pkg-metadata-filter": "1100.0.9",
|
|
56
|
-
"@pnpm/resolving.jsr-specifier-parser": "1100.0.2",
|
|
57
52
|
"@pnpm/resolving.registry.types": "1100.1.3",
|
|
58
|
-
"@pnpm/
|
|
53
|
+
"@pnpm/resolving.jsr-specifier-parser": "1100.0.2",
|
|
59
54
|
"@pnpm/resolving.resolver-base": "1100.5.1",
|
|
60
|
-
"@pnpm/store.index": "1100.2.1",
|
|
61
55
|
"@pnpm/types": "1101.3.2",
|
|
56
|
+
"@pnpm/fs.graceful-fs": "1100.1.0",
|
|
62
57
|
"@pnpm/workspace.range-resolver": "1100.0.2",
|
|
63
|
-
"@pnpm/
|
|
58
|
+
"@pnpm/store.index": "1100.2.1",
|
|
59
|
+
"@pnpm/workspace.spec-parser": "1100.0.0",
|
|
60
|
+
"@pnpm/store.cafs": "1100.1.12",
|
|
61
|
+
"@pnpm/core-loggers": "1100.2.1",
|
|
62
|
+
"@pnpm/constants": "1100.0.0"
|
|
64
63
|
},
|
|
65
64
|
"peerDependencies": {
|
|
66
65
|
"@pnpm/logger": "^1100.0.0",
|
|
@@ -75,11 +74,11 @@
|
|
|
75
74
|
"@types/validate-npm-package-name": "^4.0.2",
|
|
76
75
|
"load-json-file": "^7.0.1",
|
|
77
76
|
"tempy": "3.0.0",
|
|
78
|
-
"@pnpm/
|
|
79
|
-
"@pnpm/resolving.npm-resolver": "1102.1.1",
|
|
77
|
+
"@pnpm/resolving.npm-resolver": "1102.1.2",
|
|
80
78
|
"@pnpm/test-fixtures": "1100.0.0",
|
|
81
|
-
"@pnpm/
|
|
82
|
-
"@pnpm/testing.mock-agent": "1101.0.4"
|
|
79
|
+
"@pnpm/logger": "1100.0.0",
|
|
80
|
+
"@pnpm/testing.mock-agent": "1101.0.4",
|
|
81
|
+
"@pnpm/network.fetch": "1100.1.4"
|
|
83
82
|
},
|
|
84
83
|
"engines": {
|
|
85
84
|
"node": ">=22.13"
|