@pnpm/deps.graph-hasher 1100.1.4 → 1100.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/index.d.ts CHANGED
@@ -1,5 +1,44 @@
1
1
  import type { LockfileObject, LockfileResolution, PackageSnapshot } from '@pnpm/lockfile.types';
2
2
  import type { AllowBuild, DepPath, PkgIdWithPatchHash, SupportedArchitectures } from '@pnpm/types';
3
+ /**
4
+ * Scan an iterable of lockfile snapshot keys for the resolved
5
+ * `engines.runtime` / `devEngines.runtime` Node version and return
6
+ * its bare version string (e.g. `"22.11.0"`), or `undefined` when
7
+ * no snapshot pins a runtime.
8
+ *
9
+ * Pnpm's runtime resolver writes the pinned Node into the lockfile as
10
+ * a snapshot with key `node@runtime:<version>[(<peers>)]`
11
+ * (see [`engine/runtime/node-resolver/src/index.ts`](https://github.com/pnpm/pnpm/blob/29a42efc3b/engine/runtime/node-resolver/src/index.ts)).
12
+ * The first such key found is treated as authoritative. This is fine
13
+ * as an install-wide fallback (project-pin in the typical case), but
14
+ * snapshots that pin their own Node still need
15
+ * {@link readSnapshotRuntimePin} to get a per-snapshot result.
16
+ *
17
+ * Callers typically pass `Object.keys(lockfile.packages ?? {})` — the
18
+ * in-memory `LockfileObject` merges the on-disk `packages:` and
19
+ * `snapshots:` sections under a single `packages` field, so its keys
20
+ * include every snapshot key the install will hash.
21
+ */
22
+ export declare function findRuntimeNodeVersion(snapshotKeys: Iterable<string>): string | undefined;
23
+ /**
24
+ * Read a single graph node's own `engines.runtime` Node pin from its
25
+ * `children` map. The resolver desugars `engines.runtime` declared on
26
+ * a dependency's manifest into `dependencies.node: 'runtime:<version>'`
27
+ * (see [`installing/deps-resolver/src/resolveDependencies.ts`](https://github.com/pnpm/pnpm/blob/29a42efc3b/installing/deps-resolver/src/resolveDependencies.ts)),
28
+ * which then becomes a `children.node` entry pointing at the
29
+ * `node@runtime:<version>[(peers)]` snapshot key.
30
+ *
31
+ * Returns the bare version (e.g. `"22.11.0"`) when this snapshot pins
32
+ * its own Node — or `undefined` when it doesn't and the caller should
33
+ * fall back to the install-wide pin / host probe.
34
+ *
35
+ * Per-snapshot resolution matters because the bin linker routes
36
+ * lifecycle-script spawns for a pinning package through *that
37
+ * package's* downloaded Node — anchoring the snapshot's GVS engine
38
+ * hash to an install-wide value would produce the wrong
39
+ * side-effects-cache key for cross-pinning installs.
40
+ */
41
+ export declare function readSnapshotRuntimePin(children: Record<string, string> | undefined): string | undefined;
3
42
  export type DepsGraph<T extends string> = Record<T, DepsGraphNode<T>>;
4
43
  export interface DepsGraphNode<T extends string> {
5
44
  children: {
@@ -16,6 +55,17 @@ export declare function calcDepState<T extends string>(depsGraph: DepsGraph<T>,
16
55
  patchFileHash?: string;
17
56
  includeDepGraphHash: boolean;
18
57
  supportedArchitectures?: SupportedArchitectures;
58
+ /**
59
+ * Install-wide fallback `engines.runtime` / `devEngines.runtime`
60
+ * Node version (e.g. `"22.11.0"`). Used only when the snapshot at
61
+ * `depPath` doesn't itself pin a Node: per-snapshot pins take
62
+ * precedence so the side-effects-cache key reflects the actual
63
+ * script-runner Node the bin linker would spawn for the package
64
+ * (see {@link readSnapshotRuntimePin}). Typically computed once
65
+ * per install via {@link findRuntimeNodeVersion} over the
66
+ * lockfile's snapshot keys.
67
+ */
68
+ nodeVersion?: string;
19
69
  }): string;
20
70
  export interface PkgMeta {
21
71
  depPath: DepPath;
@@ -27,13 +77,28 @@ export interface HashedDepPath<T extends PkgMeta> {
27
77
  pkgMeta: T;
28
78
  hash: string;
29
79
  }
30
- export declare function iterateHashedGraphNodes<T extends PkgMeta>(graph: DepsGraph<DepPath>, pkgMetaIterator: PkgMetaIterator<T>, allowBuild?: AllowBuild, supportedArchitectures?: SupportedArchitectures): IterableIterator<HashedDepPath<T>>;
31
- export declare function calcGraphNodeHash<T extends PkgMeta>({ graph, cache, builtDepPaths, buildRequiredCache, supportedArchitectures }: {
80
+ export declare function iterateHashedGraphNodes<T extends PkgMeta>(graph: DepsGraph<DepPath>, pkgMetaIterator: PkgMetaIterator<T>, allowBuild?: AllowBuild, supportedArchitectures?: SupportedArchitectures,
81
+ /**
82
+ * Install-wide fallback `engines.runtime` / `devEngines.runtime`
83
+ * Node version. Used only for snapshots that don't pin their own
84
+ * Node; pinning snapshots get resolved per-snapshot via
85
+ * {@link readSnapshotRuntimePin} so the GVS engine hash matches
86
+ * the Node the bin linker would actually spawn for each package
87
+ * (see [`bins/linker/src/index.ts`](https://github.com/pnpm/pnpm/blob/29a42efc3b/bins/linker/src/index.ts)).
88
+ * Typically obtained via {@link findRuntimeNodeVersion} over the
89
+ * lockfile's snapshot keys. `undefined` falls back to
90
+ * {@link engineName}'s default (system `node --version`, with
91
+ * `process.version` as a last resort).
92
+ */
93
+ nodeVersion?: string): IterableIterator<HashedDepPath<T>>;
94
+ export declare function calcGraphNodeHash<T extends PkgMeta>({ graph, cache, builtDepPaths, buildRequiredCache, supportedArchitectures, nodeVersion }: {
32
95
  graph: DepsGraph<DepPath>;
33
96
  cache: DepsStateCache;
34
97
  builtDepPaths?: Set<DepPath>;
35
98
  buildRequiredCache?: Record<string, boolean>;
36
99
  supportedArchitectures?: SupportedArchitectures;
100
+ /** See [`iterateHashedGraphNodes`]'s `nodeVersion` parameter. */
101
+ nodeVersion?: string;
37
102
  }, pkgMeta: T): string;
38
103
  export declare function calcLeafGlobalVirtualStorePath(fullPkgId: string, name: string, version: string): string;
39
104
  export interface PkgMetaAndSnapshot extends PkgMeta {
package/lib/index.js CHANGED
@@ -1,11 +1,78 @@
1
- import { ENGINE_NAME } from '@pnpm/constants';
2
1
  import { hashObject, hashObjectWithoutSorting } from '@pnpm/crypto.object-hasher';
3
2
  import { getPkgIdWithPatchHash, refToRelative } from '@pnpm/deps.path';
3
+ import { engineName } from '@pnpm/engine.runtime.system-node-version';
4
4
  import { nameVerFromPkgSnapshot } from '@pnpm/lockfile.utils';
5
5
  import { resolvePlatformSelector, selectPlatformVariant } from '@pnpm/resolving.resolver-base';
6
6
  import { familySync } from 'detect-libc';
7
+ /**
8
+ * Strip the `node@runtime:` prefix and any peer-context suffix `(...)`
9
+ * from a single snapshot key, returning the bare Node version (e.g.
10
+ * `"22.11.0"`) — or `undefined` if the key isn't a Node runtime pin.
11
+ *
12
+ * Peer-suffixed (`node@runtime:22.11.0(node@22.11.0)`) and bare
13
+ * (`node@runtime:22.11.0`) forms must reduce to the same answer; the
14
+ * pacquet side relies on the same rule for GVS-hash parity.
15
+ */
16
+ function extractRuntimeNodeVersion(snapshotKey) {
17
+ const prefix = 'node@runtime:';
18
+ if (!snapshotKey.startsWith(prefix))
19
+ return undefined;
20
+ const versionWithPeers = snapshotKey.slice(prefix.length);
21
+ const parenAt = versionWithPeers.indexOf('(');
22
+ return parenAt === -1 ? versionWithPeers : versionWithPeers.slice(0, parenAt);
23
+ }
24
+ /**
25
+ * Scan an iterable of lockfile snapshot keys for the resolved
26
+ * `engines.runtime` / `devEngines.runtime` Node version and return
27
+ * its bare version string (e.g. `"22.11.0"`), or `undefined` when
28
+ * no snapshot pins a runtime.
29
+ *
30
+ * Pnpm's runtime resolver writes the pinned Node into the lockfile as
31
+ * a snapshot with key `node@runtime:<version>[(<peers>)]`
32
+ * (see [`engine/runtime/node-resolver/src/index.ts`](https://github.com/pnpm/pnpm/blob/29a42efc3b/engine/runtime/node-resolver/src/index.ts)).
33
+ * The first such key found is treated as authoritative. This is fine
34
+ * as an install-wide fallback (project-pin in the typical case), but
35
+ * snapshots that pin their own Node still need
36
+ * {@link readSnapshotRuntimePin} to get a per-snapshot result.
37
+ *
38
+ * Callers typically pass `Object.keys(lockfile.packages ?? {})` — the
39
+ * in-memory `LockfileObject` merges the on-disk `packages:` and
40
+ * `snapshots:` sections under a single `packages` field, so its keys
41
+ * include every snapshot key the install will hash.
42
+ */
43
+ export function findRuntimeNodeVersion(snapshotKeys) {
44
+ for (const key of snapshotKeys) {
45
+ const version = extractRuntimeNodeVersion(key);
46
+ if (version != null)
47
+ return version;
48
+ }
49
+ return undefined;
50
+ }
51
+ /**
52
+ * Read a single graph node's own `engines.runtime` Node pin from its
53
+ * `children` map. The resolver desugars `engines.runtime` declared on
54
+ * a dependency's manifest into `dependencies.node: 'runtime:<version>'`
55
+ * (see [`installing/deps-resolver/src/resolveDependencies.ts`](https://github.com/pnpm/pnpm/blob/29a42efc3b/installing/deps-resolver/src/resolveDependencies.ts)),
56
+ * which then becomes a `children.node` entry pointing at the
57
+ * `node@runtime:<version>[(peers)]` snapshot key.
58
+ *
59
+ * Returns the bare version (e.g. `"22.11.0"`) when this snapshot pins
60
+ * its own Node — or `undefined` when it doesn't and the caller should
61
+ * fall back to the install-wide pin / host probe.
62
+ *
63
+ * Per-snapshot resolution matters because the bin linker routes
64
+ * lifecycle-script spawns for a pinning package through *that
65
+ * package's* downloaded Node — anchoring the snapshot's GVS engine
66
+ * hash to an install-wide value would produce the wrong
67
+ * side-effects-cache key for cross-pinning installs.
68
+ */
69
+ export function readSnapshotRuntimePin(children) {
70
+ const ref = children?.node;
71
+ return ref != null ? extractRuntimeNodeVersion(ref) : undefined;
72
+ }
7
73
  export function calcDepState(depsGraph, cache, depPath, opts) {
8
- let result = ENGINE_NAME;
74
+ const ownPin = readSnapshotRuntimePin(depsGraph[depPath]?.children);
75
+ let result = engineName(ownPin ?? opts.nodeVersion);
9
76
  if (opts.includeDepGraphHash) {
10
77
  const depGraphHash = calcDepGraphHash(depsGraph, cache, new Set(), depPath, opts.supportedArchitectures);
11
78
  result += `;deps=${depGraphHash}`;
@@ -46,7 +113,20 @@ function calcDepGraphHash(depsGraph, cache, parents, depPath, supportedArchitect
46
113
  });
47
114
  return cache[depPath];
48
115
  }
49
- export function* iterateHashedGraphNodes(graph, pkgMetaIterator, allowBuild, supportedArchitectures) {
116
+ export function* iterateHashedGraphNodes(graph, pkgMetaIterator, allowBuild, supportedArchitectures,
117
+ /**
118
+ * Install-wide fallback `engines.runtime` / `devEngines.runtime`
119
+ * Node version. Used only for snapshots that don't pin their own
120
+ * Node; pinning snapshots get resolved per-snapshot via
121
+ * {@link readSnapshotRuntimePin} so the GVS engine hash matches
122
+ * the Node the bin linker would actually spawn for each package
123
+ * (see [`bins/linker/src/index.ts`](https://github.com/pnpm/pnpm/blob/29a42efc3b/bins/linker/src/index.ts)).
124
+ * Typically obtained via {@link findRuntimeNodeVersion} over the
125
+ * lockfile's snapshot keys. `undefined` falls back to
126
+ * {@link engineName}'s default (system `node --version`, with
127
+ * `process.version` as a last resort).
128
+ */
129
+ nodeVersion) {
50
130
  let builtDepPaths;
51
131
  let entries;
52
132
  if (allowBuild != null) {
@@ -63,6 +143,7 @@ export function* iterateHashedGraphNodes(graph, pkgMetaIterator, allowBuild, sup
63
143
  builtDepPaths,
64
144
  buildRequiredCache: builtDepPaths !== undefined ? {} : undefined,
65
145
  supportedArchitectures,
146
+ nodeVersion,
66
147
  };
67
148
  for (const pkgMeta of entries) {
68
149
  yield {
@@ -71,7 +152,7 @@ export function* iterateHashedGraphNodes(graph, pkgMetaIterator, allowBuild, sup
71
152
  };
72
153
  }
73
154
  }
74
- export function calcGraphNodeHash({ graph, cache, builtDepPaths, buildRequiredCache, supportedArchitectures }, pkgMeta) {
155
+ export function calcGraphNodeHash({ graph, cache, builtDepPaths, buildRequiredCache, supportedArchitectures, nodeVersion }, pkgMeta) {
75
156
  const { name, version, depPath } = pkgMeta;
76
157
  // When builtDepPaths is provided (derived from the allowBuilds config),
77
158
  // we only include the engine name for packages that are allowed to build
@@ -80,7 +161,13 @@ export function calcGraphNodeHash({ graph, cache, builtDepPaths, buildRequiredCa
80
161
  // so they survive Node.js upgrades and architecture changes.
81
162
  const includeEngine = builtDepPaths === undefined ||
82
163
  transitivelyRequiresBuild(graph, builtDepPaths, buildRequiredCache ??= {}, depPath, new Set());
83
- const engine = includeEngine ? ENGINE_NAME : null;
164
+ // A snapshot that declares `engines.runtime` carries the desugared
165
+ // `node@runtime:<version>` pin as a child; that's the Node the bin
166
+ // linker spawns for its lifecycle scripts, so it has to drive the
167
+ // engine portion of the hash too. Non-pinning siblings fall through
168
+ // to the install-wide value.
169
+ const ownPin = readSnapshotRuntimePin(graph[depPath]?.children);
170
+ const engine = includeEngine ? engineName(ownPin ?? nodeVersion) : null;
84
171
  const deps = calcDepGraphHash(graph, cache, new Set(), depPath, supportedArchitectures);
85
172
  const hexDigest = hashObjectWithoutSorting({ engine, deps }, { encoding: 'hex' });
86
173
  return formatGlobalVirtualStorePath(name, version, hexDigest);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/deps.graph-hasher",
3
- "version": "1100.1.4",
3
+ "version": "1100.2.0",
4
4
  "description": "Calculates the state of a dependency",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -24,18 +24,18 @@
24
24
  "!*.map"
25
25
  ],
26
26
  "dependencies": {
27
- "detect-libc": "^2.1.2",
28
- "@pnpm/constants": "1100.0.0",
29
- "@pnpm/deps.path": "1100.0.2",
30
- "@pnpm/lockfile.types": "1100.0.4",
31
- "@pnpm/resolving.resolver-base": "1100.1.2",
27
+ "detect-libc": "^2.0.3",
32
28
  "@pnpm/crypto.object-hasher": "1100.0.0",
33
- "@pnpm/lockfile.utils": "1100.0.6",
34
- "@pnpm/types": "1101.0.0"
29
+ "@pnpm/deps.path": "1100.0.3",
30
+ "@pnpm/lockfile.types": "1100.0.6",
31
+ "@pnpm/engine.runtime.system-node-version": "1100.1.0",
32
+ "@pnpm/resolving.resolver-base": "1100.2.0",
33
+ "@pnpm/types": "1101.1.0",
34
+ "@pnpm/lockfile.utils": "1100.0.8"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@jest/globals": "30.3.0",
38
- "@pnpm/deps.graph-hasher": "1100.1.4"
38
+ "@pnpm/deps.graph-hasher": "1100.2.0"
39
39
  },
40
40
  "engines": {
41
41
  "node": ">=22.13"