@pnpm/deps.graph-hasher 1100.2.17 → 1100.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @pnpm/calc-dep-state
2
2
 
3
+ ## 1100.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added an opt-in proof of concept that lets installs reuse a dependency's build output across machines, by publishing and restoring signed, organization-scoped artifacts through pnpr instead of running the lifecycle scripts locally.
8
+
9
+ Configure it with the new `remoteSideEffectsCache` setting. A workspace names the eligible `organization` and `packages`; everything describing the act of signing — `publish`, `keyId`, `builderId`, `trustedKeys`, `privateKey` and the provenance fields — is refused in `pnpm-workspace.yaml` and read from the global config file or the environment instead.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies:
14
+ - @pnpm/deps.path@1101.0.1
15
+ - @pnpm/engine.runtime.system-version@1100.0.11
16
+ - @pnpm/lockfile.types@1100.1.0
17
+ - @pnpm/lockfile.utils@1102.1.0
18
+ - @pnpm/resolving.resolver-base@1101.2.0
19
+ - @pnpm/types@1102.1.0
20
+
21
+ ## 1100.2.18
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies:
26
+ - @pnpm/deps.path@1101.0.0
27
+ - @pnpm/engine.runtime.system-version@1100.0.10
28
+ - @pnpm/lockfile.types@1100.0.20
29
+ - @pnpm/lockfile.utils@1102.0.0
30
+ - @pnpm/resolving.resolver-base@1101.1.1
31
+ - @pnpm/types@1102.0.0
32
+
3
33
  ## 1100.2.17
4
34
 
5
35
  ### Patch Changes
package/lib/index.d.ts CHANGED
@@ -51,6 +51,31 @@ export interface DepsGraphNode<T extends string> {
51
51
  export interface DepsStateCache {
52
52
  [depPath: string]: string;
53
53
  }
54
+ export declare const DEPENDENCY_SIDE_EFFECTS_INPUT_KEY_PREFIX = "dependency-side-effects:v1:";
55
+ export interface CalcDepStateInputKeyOptions<T extends string> {
56
+ depsGraph: DepsGraph<T>;
57
+ depPath: T;
58
+ patchFileHash?: string;
59
+ supportedArchitectures?: SupportedArchitectures;
60
+ }
61
+ /**
62
+ * Compute the machine-independent lookup key for a remotely shareable
63
+ * dependency build.
64
+ *
65
+ * `depsGraph` must contain `depPath`, and every reachable node must provide
66
+ * either `fullPkgId` or the resolution metadata needed to derive it. The
67
+ * function does not mutate the graph or caller state. Each call uses an
68
+ * isolated cache, so the result is independent of earlier roots and platform
69
+ * selections.
70
+ *
71
+ * The returned key starts with {@link DEPENDENCY_SIDE_EFFECTS_INPUT_KEY_PREFIX},
72
+ * followed by the recursive dependency-graph hash and, when non-empty, the
73
+ * patch-file hash. Host identity is excluded and advertised by the artifact's
74
+ * signed compatibility constraints. When a resolution contains platform
75
+ * variations, `supportedArchitectures` selects the source integrity included
76
+ * in the graph hash.
77
+ */
78
+ export declare function calcDepStateInputKey<T extends string>(opts: CalcDepStateInputKeyOptions<T>): string;
54
79
  export declare function calcDepState<T extends string>(depsGraph: DepsGraph<T>, cache: DepsStateCache, depPath: string, opts: {
55
80
  patchFileHash?: string;
56
81
  includeDepGraphHash: boolean;
package/lib/index.js CHANGED
@@ -71,11 +71,52 @@ export function readSnapshotRuntimePin(children) {
71
71
  const ref = children?.node;
72
72
  return ref != null ? extractRuntimeNodeVersion(ref) : undefined;
73
73
  }
74
+ export const DEPENDENCY_SIDE_EFFECTS_INPUT_KEY_PREFIX = 'dependency-side-effects:v1:';
75
+ /**
76
+ * Compute the machine-independent lookup key for a remotely shareable
77
+ * dependency build.
78
+ *
79
+ * `depsGraph` must contain `depPath`, and every reachable node must provide
80
+ * either `fullPkgId` or the resolution metadata needed to derive it. The
81
+ * function does not mutate the graph or caller state. Each call uses an
82
+ * isolated cache, so the result is independent of earlier roots and platform
83
+ * selections.
84
+ *
85
+ * The returned key starts with {@link DEPENDENCY_SIDE_EFFECTS_INPUT_KEY_PREFIX},
86
+ * followed by the recursive dependency-graph hash and, when non-empty, the
87
+ * patch-file hash. Host identity is excluded and advertised by the artifact's
88
+ * signed compatibility constraints. When a resolution contains platform
89
+ * variations, `supportedArchitectures` selects the source integrity included
90
+ * in the graph hash.
91
+ */
92
+ export function calcDepStateInputKey(opts) {
93
+ if (opts.depsGraph[opts.depPath] == null) {
94
+ throw new Error(`Dependency side-effects input-key root ${opts.depPath} is not present in depsGraph`);
95
+ }
96
+ const depGraphHash = calcDepGraphHash({
97
+ depsGraph: opts.depsGraph,
98
+ cache: {},
99
+ parents: new Set(),
100
+ depPath: opts.depPath,
101
+ context: createDepGraphHashContext(opts.supportedArchitectures),
102
+ });
103
+ let result = `${DEPENDENCY_SIDE_EFFECTS_INPUT_KEY_PREFIX}deps=${depGraphHash}`;
104
+ if (opts.patchFileHash) {
105
+ result += `;patch=${opts.patchFileHash}`;
106
+ }
107
+ return result;
108
+ }
74
109
  export function calcDepState(depsGraph, cache, depPath, opts) {
75
110
  const ownPin = readSnapshotRuntimePin(depsGraph[depPath]?.children);
76
111
  let result = engineName(ownPin ?? opts.nodeVersion);
77
112
  if (opts.includeDepGraphHash) {
78
- const depGraphHash = calcDepGraphHash(depsGraph, cache, new Set(), depPath, opts.supportedArchitectures);
113
+ const depGraphHash = calcDepGraphHash({
114
+ depsGraph,
115
+ cache,
116
+ parents: new Set(),
117
+ depPath: depPath,
118
+ context: createDepGraphHashContext(opts.supportedArchitectures),
119
+ });
79
120
  result += `;deps=${depGraphHash}`;
80
121
  }
81
122
  if (opts.patchFileHash) {
@@ -83,36 +124,47 @@ export function calcDepState(depsGraph, cache, depPath, opts) {
83
124
  }
84
125
  return result;
85
126
  }
86
- function calcDepGraphHash(depsGraph, cache, parents, depPath, supportedArchitectures) {
87
- if (cache[depPath])
88
- return cache[depPath];
127
+ function calcDepGraphHash({ depsGraph, cache, parents, depPath, context, }) {
128
+ const cacheKey = `${context.cacheKeyPrefix}${depPath}`;
129
+ if (cache[cacheKey])
130
+ return cache[cacheKey];
89
131
  const node = depsGraph[depPath];
90
132
  if (!node)
91
133
  return '';
92
- if (!node.fullPkgId) {
93
- if (!node.pkgIdWithPatchHash) {
94
- throw new Error(`pkgIdWithPatchHash is not defined for ${depPath} in depsGraph`);
95
- }
96
- if (!node.resolution) {
97
- throw new Error(`resolution is not defined for ${depPath} in depsGraph`);
98
- }
99
- node.fullPkgId = createFullPkgId(node.pkgIdWithPatchHash, node.resolution, supportedArchitectures);
134
+ let fullPkgId = node.fullPkgId;
135
+ if (node.pkgIdWithPatchHash != null && node.resolution != null) {
136
+ fullPkgId = createFullPkgId(node.pkgIdWithPatchHash, node.resolution, context.supportedArchitectures);
137
+ }
138
+ else if (fullPkgId == null) {
139
+ throw new Error(`fullPkgId or resolution metadata is not defined for ${depPath} in depsGraph`);
100
140
  }
101
141
  const deps = {};
102
- if (Object.keys(node.children).length && !parents.has(node.fullPkgId)) {
103
- const nextParents = new Set([...Array.from(parents), node.fullPkgId]);
142
+ if (Object.keys(node.children).length && !parents.has(fullPkgId)) {
143
+ const nextParents = new Set([...Array.from(parents), fullPkgId]);
104
144
  for (const alias in node.children) {
105
145
  if (Object.hasOwn(node.children, alias)) {
106
146
  const childId = node.children[alias];
107
- deps[alias] = calcDepGraphHash(depsGraph, cache, nextParents, childId, supportedArchitectures);
147
+ deps[alias] = calcDepGraphHash({
148
+ depsGraph,
149
+ cache,
150
+ parents: nextParents,
151
+ depPath: childId,
152
+ context,
153
+ });
108
154
  }
109
155
  }
110
156
  }
111
- cache[depPath] = hashObject({
112
- id: node.fullPkgId,
157
+ cache[cacheKey] = hashObject({
158
+ id: fullPkgId,
113
159
  deps,
114
160
  });
115
- return cache[depPath];
161
+ return cache[cacheKey];
162
+ }
163
+ function createDepGraphHashContext(supportedArchitectures) {
164
+ return {
165
+ supportedArchitectures,
166
+ cacheKeyPrefix: supportedArchitectures == null ? '' : `\0architectures=${hashObject(supportedArchitectures)}\0`,
167
+ };
116
168
  }
117
169
  export function* iterateHashedGraphNodes(graph, pkgMetaIterator, opts = {}) {
118
170
  let builtDepPaths;
@@ -157,7 +209,13 @@ export function calcGraphNodeHash({ graph, cache, builtDepPaths, buildRequiredCa
157
209
  // to the install-wide value.
158
210
  const ownPin = readSnapshotRuntimePin(graph[depPath]?.children);
159
211
  const engine = includeEngine ? engineName(ownPin ?? nodeVersion) : null;
160
- const deps = calcDepGraphHash(graph, cache, new Set(), depPath, supportedArchitectures);
212
+ const deps = calcDepGraphHash({
213
+ depsGraph: graph,
214
+ cache,
215
+ parents: new Set(),
216
+ depPath,
217
+ context: createDepGraphHashContext(supportedArchitectures),
218
+ });
161
219
  const isLocalDirectory = isLocalDirectoryResolution(graph[depPath]?.resolution);
162
220
  // Scoping the slot needs the project's identity; the segment only needs to
163
221
  // know that the package is a local directory, so a caller that leaves
@@ -259,14 +317,16 @@ export function lockfileToDepGraph(lockfile, supportedArchitectures, lockfileDir
259
317
  const linkTargetNodes = new Set();
260
318
  if (lockfile.packages != null) {
261
319
  for (const [depPath, pkgSnapshot] of Object.entries(lockfile.packages)) {
320
+ const pkgIdWithPatchHash = getPkgIdWithPatchHash(depPath);
262
321
  const children = lockfileDepsToGraphChildren({
263
322
  ...pkgSnapshot.dependencies,
264
323
  ...pkgSnapshot.optionalDependencies,
265
324
  }, lockfileDir, linkTargetNodes);
266
325
  graph[depPath] = {
267
326
  children,
327
+ pkgIdWithPatchHash,
268
328
  resolution: pkgSnapshot.resolution,
269
- fullPkgId: createFullPkgId(getPkgIdWithPatchHash(depPath), pkgSnapshot.resolution, supportedArchitectures),
329
+ fullPkgId: createFullPkgId(pkgIdWithPatchHash, pkgSnapshot.resolution, supportedArchitectures),
270
330
  };
271
331
  }
272
332
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/deps.graph-hasher",
3
- "version": "1100.2.17",
3
+ "version": "1100.3.0",
4
4
  "description": "Calculates the state of a dependency",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -28,17 +28,17 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@pnpm/crypto.object-hasher": "1100.0.1",
31
- "@pnpm/deps.path": "1100.1.0",
32
- "@pnpm/engine.runtime.system-version": "1100.0.9",
33
- "@pnpm/lockfile.types": "1100.0.19",
34
- "@pnpm/lockfile.utils": "1101.1.0",
35
- "@pnpm/resolving.resolver-base": "1101.1.0",
36
- "@pnpm/types": "1101.9.0",
31
+ "@pnpm/deps.path": "1101.0.1",
32
+ "@pnpm/engine.runtime.system-version": "1100.0.11",
33
+ "@pnpm/lockfile.types": "1100.1.0",
34
+ "@pnpm/lockfile.utils": "1102.1.0",
35
+ "@pnpm/resolving.resolver-base": "1101.2.0",
36
+ "@pnpm/types": "1102.1.0",
37
37
  "detect-libc": "^2.1.2"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@jest/globals": "30.4.1",
41
- "@pnpm/deps.graph-hasher": "1100.2.17"
41
+ "@pnpm/deps.graph-hasher": "1100.3.0"
42
42
  },
43
43
  "engines": {
44
44
  "node": ">=22.13"