@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 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
- jsonText: string;
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 = pMemoize(fetchMetadataFromFromRegistry.bind(null, fetchOpts), {
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
- pMemoizeClear(fetch);
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
@@ -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(fetchResult.meta, fetchResult.etag, fetchResult.jsonText);
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.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/store.cafs": "1100.1.12",
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/workspace.spec-parser": "1100.0.0"
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/logger": "1100.0.0",
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/network.fetch": "1100.1.4",
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"