@pnpm/engine.runtime.node-resolver 1101.1.22 → 1101.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @pnpm/node.resolver
2
2
 
3
+ ## 1101.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies:
8
+ - @pnpm/config.reader@1102.0.0
9
+ - @pnpm/crypto.shasums-file@1100.2.1
10
+ - @pnpm/error@1100.1.3
11
+ - @pnpm/resolving.resolver-base@1101.1.1
12
+ - @pnpm/types@1102.0.0
13
+
14
+ ## 1101.2.0
15
+
16
+ ### Minor Changes
17
+
18
+ - Resolving a Node.js runtime version (`devEngines.runtime` / `runtime:` specifiers) is now much faster: the per-version release metadata is cached in the pnpm cache directory after its signature is verified, and an exact stable version such as `runtime:22.23.2` no longer downloads the Node.js release index. A pinned runtime whose metadata was fetched once resolves without any network access, which removes the noticeable delay on the first `node` invocation in a project pinning an already-downloaded runtime [#13899](https://github.com/pnpm/pnpm/issues/13899).
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies:
23
+ - @pnpm/config.reader@1101.17.0
24
+ - @pnpm/crypto.shasums-file@1100.2.0
25
+ - @pnpm/error@1100.1.2
26
+
3
27
  ## 1101.1.22
4
28
 
5
29
  ### Patch Changes
package/lib/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export declare function resolveNodeRuntime(ctx: {
15
15
  fetchFromRegistry: FetchFromRegistry;
16
16
  nodeDownloadMirrors?: Record<string, string>;
17
17
  offline?: boolean;
18
+ cacheDir?: string;
18
19
  }, wantedDependency: WantedDependency, opts?: Partial<ResolveOptions>): Promise<NodeRuntimeResolveResult | null>;
19
20
  export declare function resolveLatestNodeRuntime(ctx: {
20
21
  fetchFromRegistry: FetchFromRegistry;
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { fetchShasumsFile, fetchVerifiedNodeShasumsFile } from '@pnpm/crypto.shasums-file';
1
+ import { fetchShasumsFileCached, fetchVerifiedNodeShasumsFileCached } from '@pnpm/crypto.shasums-file';
2
2
  import { PnpmError } from '@pnpm/error';
3
3
  import semver from 'semver';
4
4
  import versionSelectorType from 'version-selector-type';
@@ -28,11 +28,32 @@ export async function resolveNodeRuntime(ctx, wantedDependency, opts) {
28
28
  const versionSpec = normalizeRuntimeSpec(wantedDependency.bareSpecifier.substring('runtime:'.length));
29
29
  const { releaseChannel, versionSpecifier } = parseNodeSpecifier(versionSpec);
30
30
  const nodeMirrorBaseUrl = getNodeMirror(ctx.nodeDownloadMirrors, releaseChannel);
31
- const version = await resolveNodeVersion(ctx.fetchFromRegistry, versionSpecifier, nodeMirrorBaseUrl);
32
- if (!version) {
33
- throw new PnpmError('NODEJS_VERSION_NOT_FOUND', `Could not find a Node.js version that satisfies ${versionSpec}`);
31
+ // An exact stable-release specifier is its own resolution, so the
32
+ // release-index fetch is skipped for it and existence is proven by the
33
+ // asset-list fetch below.
34
+ const exactVersion = exactReleaseVersion(releaseChannel, versionSpecifier);
35
+ let version = exactVersion;
36
+ if (version == null) {
37
+ version = await resolveNodeVersion(ctx.fetchFromRegistry, versionSpecifier, nodeMirrorBaseUrl) ?? undefined;
38
+ if (!version) {
39
+ throw new PnpmError('NODEJS_VERSION_NOT_FOUND', `Could not find a Node.js version that satisfies ${versionSpec}`);
40
+ }
41
+ }
42
+ let variants;
43
+ try {
44
+ variants = await readNodeAssets(ctx.fetchFromRegistry, { nodeMirrorBaseUrl, version, releaseChannel, cacheDir: ctx.cacheDir });
45
+ }
46
+ catch (err) {
47
+ // The exact-specifier pick skipped the release index, so a failed asset
48
+ // read is ambiguous: the version may simply not exist. Consult the index
49
+ // now, purely to raise the same NODEJS_VERSION_NOT_FOUND the index-first
50
+ // path raises for a nonexistent version; any other outcome re-raises the
51
+ // asset error unchanged.
52
+ if (exactVersion != null && await versionMissingFromIndex(ctx.fetchFromRegistry, exactVersion, nodeMirrorBaseUrl)) {
53
+ throw new PnpmError('NODEJS_VERSION_NOT_FOUND', `Could not find a Node.js version that satisfies ${versionSpec}`);
54
+ }
55
+ throw err;
34
56
  }
35
- const variants = await readNodeAssets(ctx.fetchFromRegistry, nodeMirrorBaseUrl, version, releaseChannel);
36
57
  const range = createNodeRuntimeVersionSpec(versionSpec, version, wantedDependency);
37
58
  return {
38
59
  id: `node@runtime:${version}`,
@@ -75,20 +96,44 @@ function createNodeRuntimeVersionSpec(versionSpec, resolvedVersion, wantedDepend
75
96
  return `~${resolvedVersion}`;
76
97
  return resolvedVersion;
77
98
  }
78
- async function readNodeAssets(fetch, nodeMirrorBaseUrl, version, releaseChannel) {
99
+ /**
100
+ * The concrete version an exact stable-release specifier names, when the
101
+ * specifier is already in canonical `X.Y.Z` form. Such a specifier needs no
102
+ * release-index lookup: the index would resolve it to itself. Prereleases are
103
+ * excluded — on the `release` channel they never exist, so routing them
104
+ * through the index keeps the canonical not-found error path.
105
+ */
106
+ function exactReleaseVersion(releaseChannel, versionSpecifier) {
107
+ if (releaseChannel !== 'release')
108
+ return undefined;
109
+ const parsed = semver.parse(versionSpecifier);
110
+ if (parsed == null || parsed.prerelease.length > 0 || parsed.build.length > 0 || parsed.version !== versionSpecifier)
111
+ return undefined;
112
+ return parsed.version;
113
+ }
114
+ async function versionMissingFromIndex(fetch, version, nodeMirrorBaseUrl) {
115
+ try {
116
+ return (await resolveNodeVersion(fetch, version, nodeMirrorBaseUrl)) == null;
117
+ }
118
+ catch {
119
+ return false;
120
+ }
121
+ }
122
+ async function readNodeAssets(fetch, opts) {
123
+ const { nodeMirrorBaseUrl, version, releaseChannel, cacheDir } = opts;
79
124
  // The mirror is repository-configurable, so the SHASUMS file's hashes are only
80
125
  // trustworthy once its OpenPGP signature is verified against the Node.js
81
126
  // release keys embedded in pnpm. Only the `release` channel publishes a signed
82
127
  // SHASUMS256.txt; pre-release channels (rc, nightly, …) are unsigned by Node,
83
128
  // so they cannot be verified this way.
84
- const assets = await readNodeAssetsFromMirror(fetch, { nodeMirrorBaseUrl, version, muslOnly: false, verifySignature: releaseChannel === 'release' });
129
+ const assets = await readNodeAssetsFromMirror(fetch, { nodeMirrorBaseUrl, version, muslOnly: false, verifySignature: releaseChannel === 'release', cacheDir });
85
130
  // When using the default mirror, also fetch musl variants from unofficial-builds.nodejs.org,
86
131
  // since musl builds are not available on the official mirror. That URL is hardcoded (not
87
132
  // repository-configurable) and signed by a different (unofficial-builds) key, so it is trusted
88
133
  // over TLS rather than verified against the official release keys.
89
134
  if (nodeMirrorBaseUrl === DEFAULT_NODE_MIRROR_BASE_URL) {
90
135
  try {
91
- const muslAssets = await readNodeAssetsFromMirror(fetch, { nodeMirrorBaseUrl: UNOFFICIAL_NODE_MIRROR_BASE_URL, version, muslOnly: true, verifySignature: false });
136
+ const muslAssets = await readNodeAssetsFromMirror(fetch, { nodeMirrorBaseUrl: UNOFFICIAL_NODE_MIRROR_BASE_URL, version, muslOnly: true, verifySignature: false, cacheDir });
92
137
  assets.push(...muslAssets);
93
138
  }
94
139
  catch {
@@ -98,11 +143,13 @@ async function readNodeAssets(fetch, nodeMirrorBaseUrl, version, releaseChannel)
98
143
  return assets;
99
144
  }
100
145
  async function readNodeAssetsFromMirror(fetch, opts) {
101
- const { nodeMirrorBaseUrl, version, muslOnly, verifySignature } = opts;
146
+ const { nodeMirrorBaseUrl, version, muslOnly, verifySignature, cacheDir } = opts;
147
+ // The URL is pinned to one released version, which is what makes it
148
+ // eligible for the SHASUMS disk cache.
102
149
  const integritiesFileUrl = `${nodeMirrorBaseUrl}v${version}/SHASUMS256.txt`;
103
150
  const shasumsFileItems = verifySignature
104
- ? await fetchVerifiedNodeShasumsFile(fetch, integritiesFileUrl)
105
- : await fetchShasumsFile(fetch, integritiesFileUrl);
151
+ ? await fetchVerifiedNodeShasumsFileCached(fetch, integritiesFileUrl, { cacheDir })
152
+ : await fetchShasumsFileCached(fetch, integritiesFileUrl, { cacheDir });
106
153
  const escaped = version.replace(/\\/g, '\\\\').replace(/\./g, '\\.');
107
154
  // The second capture group uses [^.-]+ to stop at a dash, so that the optional
108
155
  // third group can capture the '-musl' suffix separately (e.g. 'x64' + '-musl').
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/engine.runtime.node-resolver",
3
- "version": "1101.1.22",
3
+ "version": "1101.2.1",
4
4
  "description": "Resolves a Node.js version specifier to an exact Node.js version",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -29,19 +29,19 @@
29
29
  "!*.map"
30
30
  ],
31
31
  "dependencies": {
32
- "@pnpm/config.reader": "1101.16.1",
33
- "@pnpm/crypto.shasums-file": "1100.1.5",
34
- "@pnpm/error": "1100.1.1",
32
+ "@pnpm/config.reader": "1102.0.0",
33
+ "@pnpm/crypto.shasums-file": "1100.2.1",
34
+ "@pnpm/error": "1100.1.3",
35
35
  "@pnpm/fetching.types": "1100.0.3",
36
- "@pnpm/resolving.resolver-base": "1101.1.0",
37
- "@pnpm/types": "1101.9.0",
36
+ "@pnpm/resolving.resolver-base": "1101.1.1",
37
+ "@pnpm/types": "1102.0.0",
38
38
  "semver": "^7.8.5",
39
39
  "version-selector-type": "^3.0.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@jest/globals": "30.4.1",
43
- "@pnpm/engine.runtime.node-resolver": "1101.1.22",
44
- "@pnpm/network.fetch": "1100.1.11",
43
+ "@pnpm/engine.runtime.node-resolver": "1101.2.1",
44
+ "@pnpm/network.fetch": "1100.1.13",
45
45
  "@types/semver": "7.8.0"
46
46
  },
47
47
  "engines": {