zyrk 0.5.3 → 0.5.5

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.
Files changed (43) hide show
  1. package/README.md +9 -1
  2. package/VERSION +1 -1
  3. package/dist/install/budgets.d.ts +7 -0
  4. package/dist/install/budgets.d.ts.map +1 -0
  5. package/dist/install/budgets.js +25 -0
  6. package/dist/install/import-npm-workspace.d.ts.map +1 -1
  7. package/dist/install/import-npm-workspace.js +1 -3
  8. package/dist/install/import-npm.d.ts.map +1 -1
  9. package/dist/install/import-npm.js +8 -10
  10. package/dist/install/lock.d.ts.map +1 -1
  11. package/dist/install/lock.js +20 -4
  12. package/dist/install/platform.d.ts +5 -0
  13. package/dist/install/platform.d.ts.map +1 -0
  14. package/dist/install/platform.js +97 -0
  15. package/dist/install/run.d.ts +7 -0
  16. package/dist/install/run.d.ts.map +1 -1
  17. package/dist/install/run.js +28 -48
  18. package/dist/install/workspace-graph.d.ts +1 -0
  19. package/dist/install/workspace-graph.d.ts.map +1 -1
  20. package/dist/install/workspace-graph.js +12 -3
  21. package/dist/install/workspace-install.d.ts.map +1 -1
  22. package/dist/install/workspace-install.js +7 -5
  23. package/dist/install/workspace-lock.d.ts +1 -1
  24. package/dist/install/workspace-lock.d.ts.map +1 -1
  25. package/dist/install/workspace-lock.js +11 -1
  26. package/dist/lockfile/fingerprints.js +0 -1
  27. package/dist/lockfile/read.d.ts +1 -1
  28. package/dist/lockfile/read.d.ts.map +1 -1
  29. package/dist/lockfile/read.js +11 -1
  30. package/dist/lockfile/types.d.ts +1 -0
  31. package/dist/lockfile/types.d.ts.map +1 -1
  32. package/dist/lockfile/types.js +1 -0
  33. package/dist/lockfile/write.d.ts.map +1 -1
  34. package/dist/lockfile/write.js +1 -0
  35. package/dist/resolve/resolver.d.ts +1 -21
  36. package/dist/resolve/resolver.d.ts.map +1 -1
  37. package/dist/resolve/resolver.js +13 -1
  38. package/dist/resolve/spec.d.ts.map +1 -1
  39. package/dist/resolve/spec.js +8 -1
  40. package/dist/util/engines.d.ts +2 -0
  41. package/dist/util/engines.d.ts.map +1 -0
  42. package/dist/util/engines.js +23 -0
  43. package/package.json +1 -1
package/README.md CHANGED
@@ -122,7 +122,10 @@ registry; private names never fall back to a public endpoint. Disabled TLS
122
122
  verification is rejected before lookup. Git sources and non-workspace local sources remain unsupported;
123
123
  they fail explicitly. Optional dependencies incompatible with the selected platform
124
124
  are omitted; missing packages, integrity failures, and other optional failures
125
- remain errors. Platform selection is recorded in the lock. macOS, Windows, and
125
+ remain errors. Locks retain exact platform-qualified optional variants; each machine
126
+ selects its compatible local closure without rewriting the lock or recording store
127
+ paths. Older platform-pruned locks need an intentional non-frozen refresh; incomplete
128
+ npm locks must be freshly resolved before portable adoption. macOS, Windows, and
126
129
  broad framework compatibility have not passed their release gates.
127
130
 
128
131
  ### Workspaces
@@ -367,3 +370,8 @@ recorded in the manifest fingerprint; frozen installs reject policy drift.
367
370
  preparation, resolution, verified source counts, realization, approved builds,
368
371
  and project linking on stderr. Interactive terminals show an activity spinner;
369
372
  redirected output contains plain phase messages. `--json` disables progress.
373
+
374
+ Install resolution has a five-minute deadline; each archive extraction has a
375
+ two-minute deadline. Set `ZYRK_RESOLVE_TIMEOUT_MS` or `ZYRK_EXTRACT_TIMEOUT_MS`
376
+ to an integer from `1` to `900000` milliseconds to adjust these timeouts.
377
+ Graph, archive-entry, and byte limits still apply.
package/VERSION CHANGED
@@ -1 +1 @@
1
- v0.5.3
1
+ v0.5.5
@@ -0,0 +1,7 @@
1
+ import type { ResolverLimits } from "../resolve/resolver.ts";
2
+ import type { ExtractionLimits } from "../extract/tarball.ts";
3
+ export declare function installBudgets(env: NodeJS.ProcessEnv): {
4
+ resolveBudget: Partial<ResolverLimits>;
5
+ extractBudget: Partial<ExtractionLimits>;
6
+ };
7
+ //# sourceMappingURL=budgets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budgets.d.ts","sourceRoot":"","sources":["../../src/install/budgets.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAwB9D,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG;IACtD,aAAa,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACvC,aAAa,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC1C,CASA"}
@@ -0,0 +1,25 @@
1
+ /** Public, bounded install timeout configuration. Node and byte limits stay intact. */
2
+ import { ZyrkError } from "../errors.js";
3
+ function timeout(env, key, fallback) {
4
+ const raw = env[key];
5
+ if (raw === undefined)
6
+ return fallback;
7
+ if (!/^[0-9]+$/.test(raw) ||
8
+ !Number.isSafeInteger(Number(raw)) ||
9
+ Number(raw) < 1 ||
10
+ Number(raw) > 900000)
11
+ throw new ZyrkError("ZYRK_POLICY_DENIED", `Cannot configure install: ${key} must be an integer from 1 to 900000.`, {
12
+ remedy: `Set ${key} to a timeout in milliseconds from 1 to 900000, or unset it to use the default.`,
13
+ });
14
+ return Number(raw);
15
+ }
16
+ export function installBudgets(env) {
17
+ return {
18
+ resolveBudget: {
19
+ deadlineMs: timeout(env, "ZYRK_RESOLVE_TIMEOUT_MS", 300000),
20
+ },
21
+ extractBudget: {
22
+ maxDurationMs: timeout(env, "ZYRK_EXTRACT_TIMEOUT_MS", 120000),
23
+ },
24
+ };
25
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"import-npm-workspace.d.ts","sourceRoot":"","sources":["../../src/install/import-npm-workspace.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAQvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,sBAAsB,CAAC;AA0V9B,oEAAoE;AACpE,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,GAC1C,cAAc,CAWhB"}
1
+ {"version":3,"file":"import-npm-workspace.d.ts","sourceRoot":"","sources":["../../src/install/import-npm-workspace.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAOvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,sBAAsB,CAAC;AA6V9B,oEAAoE;AACpE,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,GAC1C,cAAc,CAWhB"}
@@ -8,7 +8,6 @@ import { canonicalJson } from "../util/json.js";
8
8
  import { importNpmLock } from "./import-npm.js";
9
9
  import { lockMismatch, cleanUrl } from "./lock.js";
10
10
  import { ZyrkError } from "../errors.js";
11
- import { assertWorkspacePlatform } from "./workspace-graph.js";
12
11
  function importNpmWorkspaceLockPass(doc, project, origin, overrideState) {
13
12
  if (!doc ||
14
13
  typeof doc !== "object" ||
@@ -173,7 +172,6 @@ function importNpmWorkspaceLockPass(doc, project, origin, overrideState) {
173
172
  peers: {},
174
173
  optionalPeers: [],
175
174
  };
176
- assertWorkspacePlatform({ nodes: [node], importers: {}, omissions: [] });
177
175
  graph.nodes.push(node);
178
176
  locations.set(contextKey, node);
179
177
  contexts.set(node.key, selectedOverride.context);
@@ -230,7 +228,7 @@ function importNpmWorkspaceLockPass(doc, project, origin, overrideState) {
230
228
  continue;
231
229
  }
232
230
  if (requirement.kind === "optional")
233
- continue;
231
+ throw lockMismatch("npm workspace lock lacks an exact optional selection; restore a complete npm lock or intentionally resolve fresh without the pruned lock");
234
232
  throw lockMismatch("npm workspace lock is missing a required selection");
235
233
  }
236
234
  let target;
@@ -1 +1 @@
1
- {"version":3,"file":"import-npm.d.ts","sourceRoot":"","sources":["../../src/install/import-npm.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EACV,cAAc,EAEf,MAAM,wBAAwB,CAAC;AAKhC,wBAAgB,aAAa,CAC3B,GAAG,EAAE,UAAU,EACf,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,EAC3C,SAAS,GAAE,cAAwC,GAClD,cAAc,CAqLhB"}
1
+ {"version":3,"file":"import-npm.d.ts","sourceRoot":"","sources":["../../src/install/import-npm.ts"],"names":[],"mappings":"AACA,qFAAqF;AACrF,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EACV,cAAc,EAEf,MAAM,wBAAwB,CAAC;AAKhC,wBAAgB,aAAa,CAC3B,GAAG,EAAE,UAAU,EACf,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,EAC3C,SAAS,GAAE,cAAwC,GAClD,cAAc,CAkLhB"}
@@ -1,3 +1,4 @@
1
+ import { normalizeEngineConstraints } from "../util/engines.js";
1
2
  /** Adopt npm v2/v3 exact selections without asking the registry to pick versions. */
2
3
  import { createRootOverrides, } from "../resolve/overrides.js";
3
4
  import { validateLockDoc } from "../lockfile/read.js";
@@ -75,14 +76,10 @@ export function importNpmLock(doc, requirements, origin, overrides = createRootO
75
76
  edges: [],
76
77
  peerBindings: [],
77
78
  };
78
- if (entry.engines !== undefined) {
79
- if (typeof entry.engines !== "object" ||
80
- entry.engines === null ||
81
- Array.isArray(entry.engines) ||
82
- Object.values(entry.engines).some((value) => typeof value !== "string"))
79
+ if (entry.engines !== undefined)
80
+ node.engines = normalizeEngineConstraints(entry.engines, () => {
83
81
  throw lockMismatch("invalid npm engine constraint");
84
- node.engines = entry.engines;
85
- }
82
+ });
86
83
  if (typeof entry.bin === "object" &&
87
84
  entry.bin !== null &&
88
85
  !Array.isArray(entry.bin)) {
@@ -125,11 +122,12 @@ export function importNpmLock(doc, requirements, origin, overrides = createRootO
125
122
  const meta = entry.peerDependenciesMeta;
126
123
  const optionalPeer = requirement.kind === "peer" && meta?.[dep]?.optional === true;
127
124
  if (!targetLocation) {
128
- if (requirement.kind === "optional" || optionalPeer) {
129
- if (optionalPeer)
130
- node.peerBindings.push({ name: dep, target: null, optional: true });
125
+ if (optionalPeer) {
126
+ node.peerBindings.push({ name: dep, target: null, optional: true });
131
127
  continue;
132
128
  }
129
+ if (requirement.kind === "optional")
130
+ throw lockMismatch("npm lock lacks an exact optional selection; restore a complete npm lock or intentionally resolve fresh without the pruned lock");
133
131
  throw lockMismatch("npm lock is missing a required dependency");
134
132
  }
135
133
  const target = load(targetLocation, dep, requirement.spec, selected.context);
@@ -1 +1 @@
1
- {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/install/lock.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAKvD,OAAO,KAAK,EACV,cAAc,EAEf,MAAM,wBAAwB,CAAC;AAKhC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMjD;AACD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAa/C;AACD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CASnD;AAID,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,GAAG,cAAc,CA6M3D;AAED,kFAAkF;AAClF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAK,GAAG,IAAI,CAmC3E"}
1
+ {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/install/lock.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAKvD,OAAO,KAAK,EACV,cAAc,EAEf,MAAM,wBAAwB,CAAC;AAKhC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMjD;AACD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAa/C;AACD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CASnD;AAID,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,GAAG,cAAc,CA+M3D;AAYD,kFAAkF;AAClF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAK,GAAG,IAAI,CAM3E"}
@@ -39,6 +39,7 @@ export function lockedGraph(doc) {
39
39
  Object.keys(ext).some((key) => ![
40
40
  "version",
41
41
  "resolver",
42
+ "platformPolicy",
42
43
  "contextCanonicalization",
43
44
  "manifest",
44
45
  "scripts",
@@ -48,6 +49,7 @@ export function lockedGraph(doc) {
48
49
  ].includes(key)) ||
49
50
  ![ext.manifest, ext.scripts, ext.config].every((value) => typeof value === "string" && /^sha512-[A-Za-z0-9+/]{86}==$/.test(value)) ||
50
51
  ext.resolver !== ZYRK_RESOLVER_POLICY ||
52
+ ext.platformPolicy !== "portable-optional/1" ||
51
53
  ext.contextCanonicalization !== CONTEXT_CANONICALIZATION_VERSION ||
52
54
  !record(ext.nodes) ||
53
55
  !record(ext.roots) ||
@@ -180,7 +182,7 @@ export function lockedGraph(doc) {
180
182
  sourceId: raw.source,
181
183
  tarballUrl: raw.tarball,
182
184
  bin,
183
- engines,
185
+ ...(raw.engines === undefined ? {} : { engines }),
184
186
  platform,
185
187
  edges,
186
188
  peerBindings: peers,
@@ -201,10 +203,20 @@ export function lockedGraph(doc) {
201
203
  }
202
204
  return { nodes, roots };
203
205
  }
206
+ // npm publishes this canonical GitHub sponsorship link in funding metadata.
207
+ // Keep source/archive URL validation query-free, and allow no other query here.
208
+ function githubFundingUrl(value) {
209
+ return (/^https:\/\/github\.com\/[A-Za-z0-9_-]+\/[A-Za-z0-9_.-]+\?sponsor=1$/.test(value) && new URL(value).href === value);
210
+ }
204
211
  /** Preserved metadata is subject to the same portability boundary as new data. */
205
212
  export function assertPortableLockMetadata(value, field = "") {
213
+ visitPortableLockMetadata(value, field, field === "funding" ? "funding" : undefined);
214
+ }
215
+ function visitPortableLockMetadata(value, field, funding) {
206
216
  if (typeof value === "string") {
207
- if (/^(?:https?:|file:|git\+|ssh:)/i.test(value) && !cleanUrl(value))
217
+ if (/^(?:https?:|file:|git\+|ssh:)/i.test(value) &&
218
+ !cleanUrl(value) &&
219
+ !(funding && githubFundingUrl(value)))
208
220
  throw lockMismatch("portable metadata contains an unsafe URL");
209
221
  if (value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value))
210
222
  throw lockMismatch("portable metadata contains a local path");
@@ -212,7 +224,7 @@ export function assertPortableLockMetadata(value, field = "") {
212
224
  }
213
225
  if (Array.isArray(value)) {
214
226
  for (const item of value)
215
- assertPortableLockMetadata(item, field);
227
+ visitPortableLockMetadata(item, field, funding === "funding" ? "entry" : undefined);
216
228
  return;
217
229
  }
218
230
  if (value && typeof value === "object")
@@ -234,6 +246,10 @@ export function assertPortableLockMetadata(value, field = "") {
234
246
  ".",
235
247
  ].includes(field))
236
248
  throw lockMismatch("portable metadata contains a credential field");
237
- assertPortableLockMetadata(item, key);
249
+ visitPortableLockMetadata(item, key, key === "funding"
250
+ ? "funding"
251
+ : (funding === "funding" || funding === "entry") && key === "url"
252
+ ? "url"
253
+ : undefined);
238
254
  }
239
255
  }
@@ -0,0 +1,5 @@
1
+ import { type RuntimePlatform, type FinalizedGraph } from "../resolve/resolver.ts";
2
+ import type { WorkspaceGraph } from "./workspace-graph.ts";
3
+ export declare function selectPlatformGraph(graph: FinalizedGraph, optionalRoots: ReadonlySet<string>, platform?: RuntimePlatform): FinalizedGraph;
4
+ export declare function selectWorkspacePlatform(graph: WorkspaceGraph, platform?: RuntimePlatform): WorkspaceGraph;
5
+ //# sourceMappingURL=platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/install/platform.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAuG3D,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,EACrB,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,EAClC,QAAQ,kBAAoB,GAC3B,cAAc,CAqBhB;AACD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,cAAc,EACrB,QAAQ,kBAAoB,GAC3B,cAAc,CAsBhB"}
@@ -0,0 +1,97 @@
1
+ /** Portable selections stay in the lock; only a compatible closure is realized. */
2
+ import { satisfies } from "semver";
3
+ import { ZyrkError } from "../errors.js";
4
+ import { currentPlatform, } from "../resolve/resolver.js";
5
+ import { lockMismatch } from "./lock.js";
6
+ function compatible(node, platform) {
7
+ return ([
8
+ [node.platform?.os, platform.os],
9
+ [node.platform?.cpu, platform.cpu],
10
+ [node.platform?.libc, platform.libc],
11
+ ].every(([list, actual]) => !list?.length ||
12
+ (!!actual &&
13
+ !list.includes(`!${actual}`) &&
14
+ (!list.some((v) => !v.startsWith("!")) ||
15
+ list.includes(actual) ||
16
+ list.includes("any")))) &&
17
+ (!node.engines?.node || satisfies(process.versions.node, node.engines.node)));
18
+ }
19
+ function optional(node, edge) {
20
+ return (edge.kind === "optional" ||
21
+ (edge.kind === "peer" &&
22
+ (node.optionalPeers?.includes(edge.name) === true ||
23
+ node.peerBindings?.some((p) => p.name === edge.name && p.optional) ===
24
+ true)));
25
+ }
26
+ function select(nodes, roots, platform) {
27
+ const byKey = new Map(nodes.map((n) => [n.key, n]));
28
+ const viable = new Set(nodes.filter((n) => compatible(n, platform)).map((n) => n.key));
29
+ // Propagate required incompatibility back to the nearest optional boundary.
30
+ let changed = true;
31
+ while (changed) {
32
+ changed = false;
33
+ for (const node of nodes)
34
+ if (viable.has(node.key) &&
35
+ node.edges.some((e) => !optional(node, e) && !viable.has(e.target))) {
36
+ viable.delete(node.key);
37
+ changed = true;
38
+ }
39
+ }
40
+ const active = new Set();
41
+ const omissions = [];
42
+ const visit = (name, target, isOptional, parent) => {
43
+ if (!byKey.has(target))
44
+ throw lockMismatch("missing platform selection");
45
+ if (!viable.has(target)) {
46
+ if (isOptional) {
47
+ omissions.push({ name, parent, reason: "platform" });
48
+ return;
49
+ }
50
+ throw new ZyrkError("ZYRK_PLATFORM_UNSUPPORTED", `Cannot install required dependency ${name} on the selected platform or Node.js runtime.`, {
51
+ remedy: "Select a compatible package version, platform, or Node.js runtime.",
52
+ });
53
+ }
54
+ if (active.has(target))
55
+ return;
56
+ active.add(target);
57
+ const node = byKey.get(target);
58
+ for (const edge of node.edges)
59
+ visit(edge.name, edge.target, optional(node, edge), node.key);
60
+ };
61
+ for (const root of roots)
62
+ visit(root.name, root.target, root.optional === true, "<root>");
63
+ return { active, viable, omissions };
64
+ }
65
+ export function selectPlatformGraph(graph, optionalRoots, platform = currentPlatform()) {
66
+ const { active, viable, omissions } = select(graph.nodes, graph.roots.map((r) => ({ ...r, optional: optionalRoots.has(r.name) })), platform);
67
+ return {
68
+ roots: graph.roots.filter((r) => active.has(r.target)),
69
+ omissions,
70
+ nodes: graph.nodes
71
+ .filter((n) => active.has(n.key))
72
+ .map((n) => ({
73
+ ...n,
74
+ edges: n.edges.filter((e) => viable.has(e.target)),
75
+ peerBindings: n.peerBindings.map((p) => p.target !== null && !viable.has(p.target)
76
+ ? { ...p, target: null }
77
+ : p),
78
+ })),
79
+ };
80
+ }
81
+ export function selectWorkspacePlatform(graph, platform = currentPlatform()) {
82
+ const { active, viable, omissions } = select(graph.nodes, Object.entries(graph.importers).map(([name, target]) => ({ name, target })), platform);
83
+ return {
84
+ ...graph,
85
+ omissions,
86
+ nodes: graph.nodes
87
+ .filter((n) => active.has(n.key))
88
+ .map((n) => ({
89
+ ...n,
90
+ edges: n.edges.filter((e) => viable.has(e.target)),
91
+ peers: Object.fromEntries(Object.entries(n.peers).map(([name, target]) => [
92
+ name,
93
+ target !== null && !viable.has(target) ? null : target,
94
+ ])),
95
+ })),
96
+ };
97
+ }
@@ -1,5 +1,8 @@
1
+ import type { RuntimePlatform } from "../resolve/resolver.ts";
1
2
  import { type BuildCandidate } from "./private.ts";
2
3
  import { RegistryClient } from "../fetch/registry.ts";
4
+ import type { ResolverLimits } from "../resolve/resolver.ts";
5
+ import type { ExtractionLimits } from "../extract/tarball.ts";
3
6
  import { Store } from "../store/layout.ts";
4
7
  export type InstallProgress = {
5
8
  phase: "planning" | "resolving" | "fetching" | "realizing" | "building" | "linking";
@@ -9,6 +12,8 @@ export type InstallProgress = {
9
12
  export type InstallOptions = {
10
13
  onProgress?: (event: InstallProgress) => void;
11
14
  projectDir: string;
15
+ /** Target selection only; Store.open still checks the actual host. */
16
+ platform?: RuntimePlatform;
12
17
  store: Store;
13
18
  registry: RegistryClient;
14
19
  specs?: readonly string[];
@@ -32,6 +37,8 @@ export type InstallOptions = {
32
37
  approveBuilds?: readonly string[];
33
38
  rebuild?: boolean;
34
39
  repair?: boolean;
40
+ resolveBudget?: Partial<ResolverLimits>;
41
+ extractBudget?: Partial<ExtractionLimits>;
35
42
  fetchBudget?: {
36
43
  maxArchiveBytes?: number;
37
44
  maxExpandedBytes?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/install/run.ts"],"names":[],"mappings":"AAIA,OAAO,EAOL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAYtB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAgC3C,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EACD,UAAU,GACV,WAAW,GACX,UAAU,GACV,WAAW,GACX,UAAU,GACV,SAAS,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AACF,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE;QACZ,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH,CAAC;AACF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EACL,OAAO,GACP,uBAAuB,GACvB,eAAe,GACf,SAAS,GACT,cAAc,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;CAC3B,CAAC;AA4GF,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CA8zBxB"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/install/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAK9D,OAAO,EAOL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAYtB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AA+B3C,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EACD,UAAU,GACV,WAAW,GACX,UAAU,GACV,WAAW,GACX,UAAU,GACV,SAAS,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AACF,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC1C,WAAW,CAAC,EAAE;QACZ,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH,CAAC;AACF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EACL,OAAO,GACP,uBAAuB,GACvB,eAAe,GACf,SAAS,GACT,cAAc,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;CAC3B,CAAC;AA4GF,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAoyBxB"}
@@ -1,3 +1,4 @@
1
+ import { selectPlatformGraph } from "./platform.js";
1
2
  import { findWorkspaceRoot } from "./workspaces.js";
2
3
  import { assertSupportedSourcePayload } from "./source-policy.js";
3
4
  import { prepareRepair } from "./repair.js";
@@ -15,6 +16,7 @@ import npa from "npm-package-arg";
15
16
  import { satisfies } from "semver";
16
17
  import { ZyrkError } from "../errors.js";
17
18
  import { RegistryClient } from "../fetch/registry.js";
19
+ import { installBudgets } from "./budgets.js";
18
20
  import { extractTarball } from "../extract/tarball.js";
19
21
  import { commitExtractedSource } from "../store/source.js";
20
22
  import { Store } from "../store/layout.js";
@@ -22,7 +24,7 @@ import { Transaction } from "../store/txn.js";
22
24
  import { readSourceManifest, sourceManifestPath } from "../store/manifests.js";
23
25
  import { realizeGraph, selectRealizationDirs } from "../store/realize.js";
24
26
  import { createGraphDoc, graphHash } from "../store/graph.js";
25
- import { resolveRequirements, currentPlatform, } from "../resolve/resolver.js";
27
+ import { resolveRequirements, } from "../resolve/resolver.js";
26
28
  import { parseRequirement } from "../resolve/spec.js";
27
29
  import { readLockfile, assertFrozenCompatible } from "../lockfile/read.js";
28
30
  import { writeLockfile } from "../lockfile/write.js";
@@ -94,6 +96,12 @@ function scriptPolicy(manifest) {
94
96
  }
95
97
  export async function installProject(options) {
96
98
  options.onProgress?.({ phase: "planning" });
99
+ const budgets = installBudgets(options.env ?? process.env);
100
+ options = {
101
+ ...options,
102
+ resolveBudget: { ...budgets.resolveBudget, ...options.resolveBudget },
103
+ extractBudget: { ...budgets.extractBudget, ...options.extractBudget },
104
+ };
97
105
  if (await findWorkspaceRoot(options.projectDir)) {
98
106
  const { installWorkspaceProject } = await import("./workspace-install.js");
99
107
  return installWorkspaceProject(options);
@@ -208,7 +216,6 @@ export async function installProject(options) {
208
216
  registry: options.registry.origin,
209
217
  registries: options.registry.scopedRegistries,
210
218
  linker: "shared",
211
- platform: currentPlatform(),
212
219
  ...(object(manifest.zyrk) &&
213
220
  Array.isArray(manifest.zyrk.mutablePackages)
214
221
  ? { mutablePackages: manifest.zyrk.mutablePackages }
@@ -217,7 +224,7 @@ export async function installProject(options) {
217
224
  }),
218
225
  });
219
226
  const lockPath = path.join(project, "package-lock.json");
220
- const existing = (await readLockfile(lockPath))?.doc;
227
+ const existing = (await readLockfile(lockPath, !options.frozen && !options.offline))?.doc;
221
228
  let resolved;
222
229
  if (options.importNpm && (!existing || existing.zyrk))
223
230
  throw lockMismatch("import --from npm requires an existing npm-only lockfile");
@@ -233,6 +240,8 @@ export async function installProject(options) {
233
240
  let compatible = true;
234
241
  try {
235
242
  assertFrozenCompatible(existing, fingerprints());
243
+ if (existing.zyrk?.platformPolicy !== "portable-optional/1")
244
+ throw lockMismatch("lock predates portable platform selections; refresh intentionally");
236
245
  }
237
246
  catch (cause) {
238
247
  if (options.frozen)
@@ -247,11 +256,15 @@ export async function installProject(options) {
247
256
  throw lockMismatch("missing compatible lockfile");
248
257
  if (options.offline)
249
258
  throw error("ZYRK_OFFLINE_MISSING", "Offline installation requires an existing compatible zyrk lockfile.");
250
- const prior = existing?.zyrk ? lockedGraph(existing) : undefined;
259
+ const prior = existing?.zyrk?.platformPolicy === "portable-optional/1"
260
+ ? lockedGraph(existing)
261
+ : undefined;
251
262
  const fresh = new Set(specs.map((spec) => npa(spec).name));
252
263
  options.onProgress?.({ phase: "resolving" });
253
264
  resolved = await resolveRequirements({
254
265
  overrides,
266
+ limits: options.resolveBudget,
267
+ preservePlatformVariants: true,
255
268
  provider: {
256
269
  origin: options.registry.origin,
257
270
  originFor: (name) => options.registry.originFor(name),
@@ -358,49 +371,15 @@ export async function installProject(options) {
358
371
  node.origin !== options.registry.originFor(node.name))
359
372
  throw error("ZYRK_POLICY_DENIED", "Selected source has an unsupported or credential-bearing registry origin or archive URL.");
360
373
  }
361
- const selectedRoots = resolved.roots.filter((r) => !options.omitDev ||
362
- manifest.dependencies?.[r.name] !== undefined ||
363
- manifest.optionalDependencies?.[r.name] !== undefined);
364
- const byKey = new Map(resolved.nodes.map((n) => [n.key, n]));
365
- const activeKeys = new Set();
366
- const visit = (key) => {
367
- if (activeKeys.has(key))
368
- return;
369
- activeKeys.add(key);
370
- const n = byKey.get(key);
371
- if (!n)
372
- throw lockMismatch("missing selected node");
373
- for (const e of n.edges)
374
- visit(e.target);
375
- };
376
- for (const r of selectedRoots)
377
- visit(r.target);
378
- const activeNodes = resolved.nodes.filter((n) => activeKeys.has(n.key));
379
- for (const n of activeNodes) {
380
- for (const [list, current] of [
381
- [n.platform.os, process.platform],
382
- [n.platform.cpu, process.arch],
383
- ]) {
384
- if (list &&
385
- (list.includes(`!${current}`) ||
386
- (list.some((x) => !x.startsWith("!")) &&
387
- !list.includes(current) &&
388
- !list.includes("any"))))
389
- throw new ZyrkError("ZYRK_PLATFORM_UNSUPPORTED", `Cannot install ${n.name}@${n.version} on the selected platform.`, { remedy: "Select a package version supporting this OS and CPU." });
390
- }
391
- if (n.platform.libc?.length) {
392
- const libc = currentPlatform().libc;
393
- if (!libc ||
394
- n.platform.libc.includes(`!${libc}`) ||
395
- (n.platform.libc.some((v) => !v.startsWith("!")) &&
396
- !n.platform.libc.includes(libc)))
397
- throw new ZyrkError("ZYRK_PLATFORM_UNSUPPORTED", `Cannot install ${n.name}@${n.version} with this libc.`, { remedy: "Select a compatible platform or package version." });
398
- }
399
- if (n.engines?.node && !satisfies(process.versions.node, n.engines.node))
400
- throw new ZyrkError("ZYRK_PLATFORM_UNSUPPORTED", `Cannot install ${n.name}@${n.version} on this Node.js version.`, {
401
- remedy: "Use a Node.js version satisfying the package's engines.node range.",
402
- });
403
- }
374
+ const activeResolution = selectPlatformGraph({
375
+ ...resolved,
376
+ roots: resolved.roots.filter((r) => !options.omitDev ||
377
+ manifest.dependencies?.[r.name] !== undefined ||
378
+ manifest.optionalDependencies?.[r.name] !== undefined),
379
+ }, new Set(Object.keys(manifest.optionalDependencies ?? {})), options.platform);
380
+ const selectedRoots = activeResolution.roots;
381
+ const activeNodes = activeResolution.nodes;
382
+ const byKey = new Map(activeNodes.map((n) => [n.key, n]));
404
383
  barrier = await acquireLock(path.join(options.store.paths.locksDir, "liveness.lock"), { name: "store liveness" });
405
384
  txn = await Transaction.begin(options.store, "install");
406
385
  const candidateLock = path.join(txn.dir, "package-lock.json");
@@ -436,7 +415,7 @@ export async function installProject(options) {
436
415
  name: "package.json",
437
416
  contents: `${JSON.stringify(manifest, null, 2)}\n`,
438
417
  });
439
- const warnings = (resolved.omissions ?? []).map((item) => `${item.name}: omitted optional dependency incompatible with the selected platform.`);
418
+ const warnings = (activeResolution.omissions ?? []).map((item) => `${item.name}: omitted optional dependency incompatible with the selected platform.`);
440
419
  for (const stage of ["preinstall", "install", "postinstall"])
441
420
  if (manifest.scripts?.[stage])
442
421
  warnings.push(`Root ${stage} hook was not run; invoke it explicitly after installation.`);
@@ -493,6 +472,7 @@ export async function installProject(options) {
493
472
  label: `${node.name}@${node.version}`,
494
473
  });
495
474
  const extracted = await extractTarball({
475
+ limits: options.extractBudget,
496
476
  signal: operation.operation.signal,
497
477
  onExpandedBytes: operation.operation.consumeExpandedBytes,
498
478
  tarballPath: tarball,
@@ -50,6 +50,7 @@ declare function resolveWorkspaceGraphPass(project: WorkspaceProject, provider:
50
50
  previous?: WorkspaceGraph;
51
51
  fresh?: ReadonlySet<string>;
52
52
  platform?: RuntimePlatform;
53
+ preservePlatformVariants?: boolean;
53
54
  maxNodes?: number;
54
55
  maxDepth?: number;
55
56
  deadlineMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-graph.d.ts","sourceRoot":"","sources":["../../src/install/workspace-graph.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace-graph.d.ts","sourceRoot":"","sources":["../../src/install/workspace-graph.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { normalizeEngineConstraints } from "../util/engines.js";
1
2
  /** Multi-importer resolution. Live nodes are never registry sources or CAS inputs. */
2
3
  import { maxSatisfying, satisfies, valid } from "semver";
3
4
  import { expectedIntegrityFromDist, } from "../fetch/registry.js";
@@ -67,6 +68,11 @@ async function resolveWorkspaceGraphPass(project, provider, options = {}, overri
67
68
  ]).finally(() => clearTimeout(timer));
68
69
  };
69
70
  const compatible = (entry) => {
71
+ const engines = entry.engines === undefined
72
+ ? undefined
73
+ : normalizeEngineConstraints(entry.engines, () => fail("invalid npm engine constraint"));
74
+ if (options.preservePlatformVariants)
75
+ return;
70
76
  for (const [list, actual] of [
71
77
  [entry.os, platform.os],
72
78
  [entry.cpu, platform.cpu],
@@ -79,8 +85,7 @@ async function resolveWorkspaceGraphPass(project, provider, options = {}, overri
79
85
  !list.includes(actual) &&
80
86
  !list.includes("any"))))
81
87
  throw new PlatformMismatch();
82
- if (entry.engines?.node &&
83
- !satisfies(process.versions.node, entry.engines.node))
88
+ if (engines?.node && !satisfies(process.versions.node, engines.node))
84
89
  throw new PlatformMismatch();
85
90
  };
86
91
  const build = async (parent, name, spec, kind, ancestry, explicitLocal) => {
@@ -189,7 +194,11 @@ async function resolveWorkspaceGraphPass(project, provider, options = {}, overri
189
194
  ...(entry.cpu ? { cpu: entry.cpu } : {}),
190
195
  ...(entry.libc ? { libc: entry.libc } : {}),
191
196
  },
192
- ...(entry.engines ? { engines: entry.engines } : {}),
197
+ ...(entry.engines === undefined
198
+ ? {}
199
+ : {
200
+ engines: normalizeEngineConstraints(entry.engines, () => fail("invalid npm engine constraint")),
201
+ }),
193
202
  edges: [],
194
203
  peers: {},
195
204
  optionalPeers: [],
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-install.d.ts","sourceRoot":"","sources":["../../src/install/workspace-install.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAyH9D,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CA6qBxB"}
1
+ {"version":3,"file":"workspace-install.d.ts","sourceRoot":"","sources":["../../src/install/workspace-install.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAuH9D,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAkrBxB"}
@@ -1,9 +1,10 @@
1
+ import { selectWorkspacePlatform } from "./platform.js";
1
2
  /** Multi-importer live workspace installation with one publication journal. */
2
3
  import { chmod, copyFile, lstat, mkdir, readFile, realpath, rename, rm, symlink, } from "node:fs/promises";
3
4
  import { constants } from "node:fs";
4
5
  import * as path from "node:path";
5
6
  import { discoverWorkspaces, findWorkspaceRoot, assertNoLiveSourceBuilds, } from "./workspaces.js";
6
- import { resolveWorkspaceGraph, workspaceRegistryOccurrences, workspaceContextId, assertWorkspacePlatform, } from "./workspace-graph.js";
7
+ import { resolveWorkspaceGraph, workspaceRegistryOccurrences, workspaceContextId, } from "./workspace-graph.js";
7
8
  import { workspaceOverrides } from "./workspace-overrides.js";
8
9
  import { importNpmWorkspaceLock } from "./import-npm-workspace.js";
9
10
  import { readWorkspaceLock, workspaceLockDoc } from "./workspace-lock.js";
@@ -24,7 +25,6 @@ import { randomId } from "../util/ids.js";
24
25
  import { sha512Hex } from "../util/hash.js";
25
26
  import { canonicalJson } from "../util/json.js";
26
27
  import { configFingerprint, scriptPolicyFingerprint, } from "../lockfile/fingerprints.js";
27
- import { currentPlatform } from "../resolve/resolver.js";
28
28
  import { installProjection, installMetadata } from "../project/projection.js";
29
29
  import { prepareWorkspaceMutation } from "./workspace-mutation.js";
30
30
  import { loadProjectPatches, applyProjectPatches } from "../commands/patch.js";
@@ -135,7 +135,6 @@ export async function installWorkspaceProject(options) {
135
135
  registry: options.registry.origin,
136
136
  registries: options.registry.scopedRegistries,
137
137
  linker: "shared",
138
- platform: currentPlatform(),
139
138
  ...(mutable !== undefined
140
139
  ? { mutablePackages: mutable }
141
140
  : {}),
@@ -147,7 +146,7 @@ export async function installWorkspaceProject(options) {
147
146
  : undefined;
148
147
  let existing = options.importNpm
149
148
  ? null
150
- : await readWorkspaceLock(root, project, (name) => options.registry.originFor(name)), graph = imported;
149
+ : await readWorkspaceLock(root, project, (name) => options.registry.originFor(name), !options.frozen && !options.offline), graph = imported;
151
150
  if (existing) {
152
151
  const ext = existing.doc.zyrk;
153
152
  if (!mutation.changed &&
@@ -171,9 +170,10 @@ export async function installWorkspaceProject(options) {
171
170
  }, {
172
171
  ...(existing ? { previous: existing.graph } : {}),
173
172
  fresh: mutation.fresh,
173
+ ...options.resolveBudget,
174
+ preservePlatformVariants: true,
174
175
  });
175
176
  }
176
- assertWorkspacePlatform(graph);
177
177
  mutation.finish(graph);
178
178
  fp.manifest = project.manifestFingerprint;
179
179
  const manifestChanges = project.importers
@@ -189,6 +189,7 @@ export async function installWorkspaceProject(options) {
189
189
  : workspaceLockDoc(project, graph, fp), lockText = options.frozen
190
190
  ? await readFile(path.join(root, "package-lock.json"), "utf8")
191
191
  : JSON.stringify(doc, null, 2) + "\n";
192
+ graph = selectWorkspacePlatform(graph, options.platform);
192
193
  const warnings = graph.omissions.map((o) => `${o.name}: omitted incompatible optional workspace dependency.`);
193
194
  for (const importer of project.importers)
194
195
  for (const stage of ["preinstall", "install", "postinstall"])
@@ -287,6 +288,7 @@ export async function installWorkspaceProject(options) {
287
288
  onBytes: operation.operation.consumeArchiveBytes,
288
289
  });
289
290
  const extraction = await extractTarball({
291
+ limits: options.extractBudget,
290
292
  tarballPath: archive,
291
293
  destDir: path.join(txn.dir, node.sourceId),
292
294
  signal: operation.operation.signal,
@@ -9,7 +9,7 @@ export type WorkspaceFingerprints = {
9
9
  export declare function safeImporter(id: unknown): id is string;
10
10
  export declare function workspaceLockDoc(project: WorkspaceProject, graph: WorkspaceGraph, fp: WorkspaceFingerprints): NpmLockDoc;
11
11
  export declare function validateWorkspaceLockDoc(doc: unknown): WorkspaceGraph;
12
- export declare function readWorkspaceLock(root: string, project?: WorkspaceProject, registryOrigin?: string | ((name: string) => string)): Promise<{
12
+ export declare function readWorkspaceLock(root: string, project?: WorkspaceProject, registryOrigin?: string | ((name: string) => string), allowPlatformRefresh?: boolean): Promise<{
13
13
  doc: NpmLockDoc;
14
14
  graph: WorkspaceGraph;
15
15
  } | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-lock.d.ts","sourceRoot":"","sources":["../../src/install/workspace-lock.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AASvD,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,sBAAsB,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF,wBAAgB,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,MAAM,CAqBtD;AACD,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,cAAc,EACrB,EAAE,EAAE,qBAAqB,GACxB,UAAU,CAyCZ;AACD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAgTrE;AACD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,gBAAgB,EAC1B,cAAc,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,GACnD,OAAO,CAAC;IAAE,GAAG,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,GAAG,IAAI,CAAC,CA2C5D"}
1
+ {"version":3,"file":"workspace-lock.d.ts","sourceRoot":"","sources":["../../src/install/workspace-lock.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AASvD,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,sBAAsB,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF,wBAAgB,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,MAAM,CAqBtD;AACD,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,cAAc,EACrB,EAAE,EAAE,qBAAqB,GACxB,UAAU,CA0CZ;AACD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAmTrE;AACD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,gBAAgB,EAC1B,cAAc,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,EACpD,oBAAoB,UAAQ,GAC3B,OAAO,CAAC;IAAE,GAAG,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,GAAG,IAAI,CAAC,CAmD5D"}
@@ -54,6 +54,7 @@ export function workspaceLockDoc(project, graph, fp) {
54
54
  zyrk: {
55
55
  version: 2,
56
56
  resolver: ZYRK_RESOLVER_POLICY,
57
+ platformPolicy: "portable-optional/1",
57
58
  contextCanonicalization: CONTEXT_CANONICALIZATION_VERSION,
58
59
  ...fp,
59
60
  nodes: {},
@@ -71,6 +72,7 @@ export function validateWorkspaceLockDoc(doc) {
71
72
  if (Object.keys(e).some((k) => ![
72
73
  "version",
73
74
  "resolver",
75
+ "platformPolicy",
74
76
  "contextCanonicalization",
75
77
  "manifest",
76
78
  "scripts",
@@ -85,6 +87,7 @@ export function validateWorkspaceLockDoc(doc) {
85
87
  Object.keys(e.roots).length !== 0 ||
86
88
  e.version !== 2 ||
87
89
  e.resolver !== ZYRK_RESOLVER_POLICY ||
90
+ e.platformPolicy !== "portable-optional/1" ||
88
91
  e.contextCanonicalization !== CONTEXT_CANONICALIZATION_VERSION ||
89
92
  typeof e.manifest !== "string" ||
90
93
  !/^[a-f0-9]{128}$/.test(e.manifest) ||
@@ -249,6 +252,7 @@ export function validateWorkspaceLockDoc(doc) {
249
252
  zyrk: {
250
253
  version: 1,
251
254
  resolver: ZYRK_RESOLVER_POLICY,
255
+ platformPolicy: "portable-optional/1",
252
256
  contextCanonicalization: CONTEXT_CANONICALIZATION_VERSION,
253
257
  manifest: "sha512-" + "A".repeat(86) + "==",
254
258
  scripts: "sha512-" + "A".repeat(86) + "==",
@@ -300,7 +304,7 @@ export function validateWorkspaceLockDoc(doc) {
300
304
  }
301
305
  return g;
302
306
  }
303
- export async function readWorkspaceLock(root, project, registryOrigin) {
307
+ export async function readWorkspaceLock(root, project, registryOrigin, allowPlatformRefresh = false) {
304
308
  let text;
305
309
  try {
306
310
  text = await readFile(path.join(root, "package-lock.json"), "utf8");
@@ -317,6 +321,12 @@ export async function readWorkspaceLock(root, project, registryOrigin) {
317
321
  catch {
318
322
  throw lockMismatch("workspace lock is not valid JSON");
319
323
  }
324
+ if (allowPlatformRefresh &&
325
+ object(doc) &&
326
+ object(doc.zyrk) &&
327
+ doc.zyrk.version === 2 &&
328
+ doc.zyrk.platformPolicy === undefined)
329
+ return null;
320
330
  const graph = validateWorkspaceLockDoc(doc);
321
331
  if (project) {
322
332
  const expected = new Map(project.importers.map((i) => [i.id, i]));
@@ -72,7 +72,6 @@ export function configFingerprint(config) {
72
72
  : {}),
73
73
  linker: config.linker,
74
74
  ...(config.patches === undefined ? {} : { patches: config.patches }),
75
- ...(config.platform === undefined ? {} : { platform: config.platform }),
76
75
  ...(config.mutablePackages === undefined
77
76
  ? {}
78
77
  : { mutablePackages: [...config.mutablePackages].sort() }),
@@ -10,7 +10,7 @@ export type ReadLockResult = {
10
10
  path: string;
11
11
  doc: NpmLockDoc;
12
12
  };
13
- export declare function readLockfile(lockPath: string): Promise<ReadLockResult | null>;
13
+ export declare function readLockfile(lockPath: string, allowPlatformRefresh?: boolean): Promise<ReadLockResult | null>;
14
14
  export declare function validateLockDoc(doc: unknown, label: string): NpmLockDoc;
15
15
  export type FrozenFingerprints = {
16
16
  manifest: string;
@@ -1 +1 @@
1
- {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/lockfile/read.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,EAEL,KAAK,UAAU,EAEhB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,UAAU,CAAC;CACjB,CAAC;AAEF,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAyBhC;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CA2DvE;AA4CD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,kBAAkB,GAC3B,IAAI,CAmCN"}
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/lockfile/read.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,EAEL,KAAK,UAAU,EAEhB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,UAAU,CAAC;CACjB,CAAC;AAEF,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,oBAAoB,UAAQ,GAC3B,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAmChC;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CA2DvE;AA4CD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,kBAAkB,GAC3B,IAAI,CAmCN"}
@@ -11,7 +11,7 @@ import { readFile } from "node:fs/promises";
11
11
  import { ZyrkError } from "../errors.js";
12
12
  import { isErrno, pathExists } from "../util/fsx.js";
13
13
  import { ZYRK_LOCK_EXTENSION_VERSION, } from "./types.js";
14
- export async function readLockfile(lockPath) {
14
+ export async function readLockfile(lockPath, allowPlatformRefresh = false) {
15
15
  if (!(await pathExists(lockPath)))
16
16
  return null;
17
17
  let raw;
@@ -34,6 +34,16 @@ export async function readLockfile(lockPath) {
34
34
  cause: error,
35
35
  });
36
36
  }
37
+ if (allowPlatformRefresh && typeof parsed === "object" && parsed !== null) {
38
+ const extension = parsed.zyrk;
39
+ if (extension?.version === 1 && extension.platformPolicy === undefined) {
40
+ // Validate old metadata without treating a platform-pruned graph as portable.
41
+ const checked = structuredClone(parsed);
42
+ checked.zyrk.platformPolicy = "portable-optional/1";
43
+ validateLockDoc(checked, lockPath);
44
+ return { path: lockPath, doc: parsed };
45
+ }
46
+ }
37
47
  return { path: lockPath, doc: validateLockDoc(parsed, lockPath) };
38
48
  }
39
49
  export function validateLockDoc(doc, label) {
@@ -15,6 +15,7 @@
15
15
  * manifest, policy, or configuration drift.
16
16
  */
17
17
  export declare const ZYRK_LOCK_EXTENSION_VERSION = 1;
18
+ export declare const ZYRK_PLATFORM_POLICY = "portable-optional/1";
18
19
  /** npm package metadata, interpreted during exact-selection import. */
19
20
  export type NpmPackageEntry = Record<string, unknown>;
20
21
  export type NpmLockDoc = {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lockfile/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC,CAAC;IACxD,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACrC,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,uBAAuB,EAAE,MAAM,CAAC;IAChC,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACpC,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,oBAAoB,mBAAmB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lockfile/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAC7C,eAAO,MAAM,oBAAoB,wBAAwB,CAAC;AAE1D,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC,CAAC;IACxD,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACrC,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,uBAAuB,EAAE,MAAM,CAAC;IAChC,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACpC,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,oBAAoB,mBAAmB,CAAC"}
@@ -15,4 +15,5 @@
15
15
  * manifest, policy, or configuration drift.
16
16
  */
17
17
  export const ZYRK_LOCK_EXTENSION_VERSION = 1;
18
+ export const ZYRK_PLATFORM_POLICY = "portable-optional/1";
18
19
  export const ZYRK_RESOLVER_POLICY = "zyrk-resolve/1";
@@ -1 +1 @@
1
- {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/lockfile/write.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjE,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1D,QAAQ,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,mBAAmB,CAAC;IAChC,YAAY,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,eAAe,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;CACzB,CAAC;AAEF,wBAAsB,aAAa,CACjC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,UAAU,CAAC,CAoOrB"}
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/lockfile/write.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjE,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1D,QAAQ,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,mBAAmB,CAAC;IAChC,YAAY,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,eAAe,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;CACzB,CAAC;AAEF,wBAAsB,aAAa,CACjC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,UAAU,CAAC,CAqOrB"}
@@ -172,6 +172,7 @@ export async function writeLockfile(options) {
172
172
  const extension = {
173
173
  version: ZYRK_LOCK_EXTENSION_VERSION,
174
174
  resolver: ZYRK_RESOLVER_POLICY,
175
+ platformPolicy: "portable-optional/1",
175
176
  contextCanonicalization: CONTEXT_CANONICALIZATION_VERSION,
176
177
  manifest: options.fingerprints.manifest,
177
178
  scripts: options.fingerprints.scripts,
@@ -1,24 +1,3 @@
1
- /**
2
- * Registry-backed dependency resolver (SPEC 9).
3
- *
4
- * Turns requirement strings into an immutable resolved graph: exact name,
5
- * version, origin-qualified source identity, dependency edges, and peer
6
- * bindings. No package code runs here; no project files are touched.
7
- *
8
- * Scope of this slice: registry versions, ranges, tags, scoped names, and
9
- * npm aliases; prod and optional groups; required and optional peers with
10
- * explicit-consumer-context bindings; cycles; bundled subtrees (skipped,
11
- * never exposed). Dev groups need full packuments and arrive later, as do
12
- * overrides and workspace relationships. Platform omissions are reported
13
- * explicitly, and resource limits bound graph construction. Remote
14
- * tarballs, Git, and local sources fail explicitly (SPEC 9.2).
15
- *
16
- * Peer contexts (SPEC 9.3-9.4): one verified source may back several
17
- * dependency contexts. Occurrences that declare peers are instantiated per
18
- * requesting parent and keyed by a fingerprint of their resolved peer
19
- * closure, so identical bindings share one occurrence while differing
20
- * bindings stay distinct. Peerless occurrences share by name@version.
21
- */
22
1
  import { type Packument } from "../fetch/registry.ts";
23
2
  import { type OverridePolicy } from "./overrides.ts";
24
3
  export type MetadataProvider = {
@@ -70,6 +49,7 @@ export type ResolveOptions = {
70
49
  overrides?: OverridePolicy;
71
50
  limits?: Partial<ResolverLimits>;
72
51
  platform?: RuntimePlatform;
52
+ preservePlatformVariants?: boolean;
73
53
  };
74
54
  export type RuntimePlatform = {
75
55
  os: string;
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/resolve/resolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/resolve/resolver.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { normalizeEngineConstraints } from "../util/engines.js";
1
2
  /**
2
3
  * Registry-backed dependency resolver (SPEC 9).
3
4
  *
@@ -75,6 +76,7 @@ class ResolverState {
75
76
  occurrences = [];
76
77
  limits;
77
78
  platform;
79
+ preservePlatformVariants;
78
80
  omissions = [];
79
81
  expires;
80
82
  peerExpansions = 0;
@@ -93,6 +95,7 @@ class ResolverState {
93
95
  if (!Number.isSafeInteger(value) || value <= 0)
94
96
  throw budgetError("invalid resolver limits");
95
97
  this.platform = options.platform ?? currentPlatform();
98
+ this.preservePlatformVariants = options.preservePlatformVariants === true;
96
99
  this.expires = Date.now() + this.limits.deadlineMs;
97
100
  }
98
101
  checkBudget(depth = 0, adding = false) {
@@ -104,6 +107,8 @@ class ResolverState {
104
107
  throw budgetError("resolved node limit exceeded");
105
108
  }
106
109
  checkPlatform(name, entry) {
110
+ if (this.preservePlatformVariants)
111
+ return;
107
112
  for (const [constraints, actual] of [
108
113
  [entry.os, this.platform.os],
109
114
  [entry.cpu, this.platform.cpu],
@@ -582,6 +587,13 @@ function assertMergeable(a, b, keys, origin) {
582
587
  }
583
588
  }
584
589
  function toResolved(occ, key, keys, origin) {
590
+ const engines = occ.entry.engines === undefined
591
+ ? undefined
592
+ : normalizeEngineConstraints(occ.entry.engines, () => {
593
+ throw new ZyrkError("ZYRK_RESOLUTION_FAILED", `Cannot resolve ${occ.name}: invalid engine constraints.`, {
594
+ remedy: "Select a package version with valid npm engine metadata.",
595
+ });
596
+ });
585
597
  const edgesByName = new Map();
586
598
  for (const link of occ.children) {
587
599
  const target = keys.get(link.child);
@@ -630,7 +642,7 @@ function toResolved(occ, key, keys, origin) {
630
642
  ...(occ.entry.cpu === undefined ? {} : { cpu: occ.entry.cpu }),
631
643
  ...(occ.entry.libc === undefined ? {} : { libc: occ.entry.libc }),
632
644
  },
633
- ...(occ.entry.engines === undefined ? {} : { engines: occ.entry.engines }),
645
+ ...(engines === undefined ? {} : { engines }),
634
646
  };
635
647
  }
636
648
  function sourceIdForOccurrence(occ, origin) {
@@ -1 +1 @@
1
- {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../src/resolve/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;AAElE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,eAAe,CAAC;IACtB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,UAAU,EAAE,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;CACvC,CAAC;AAcF;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,iBAAiB,CAqFnB"}
1
+ {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../src/resolve/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;AAElE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,eAAe,CAAC;IACtB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,UAAU,EAAE,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;CACvC,CAAC;AAcF;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,iBAAiB,CA6FnB"}
@@ -7,6 +7,7 @@
7
7
  * explicitly — they are never handed to a tool that might run scripts.
8
8
  */
9
9
  import npa from "npm-package-arg";
10
+ import { SemVer } from "semver";
10
11
  import { ZyrkError } from "../errors.js";
11
12
  function unsupported(what, spec) {
12
13
  return new ZyrkError("ZYRK_RESOLUTION_FAILED", `unsupported dependency protocol in "${spec}" (${what})`, {
@@ -46,7 +47,13 @@ export function parseRequirement(edgeName, spec) {
46
47
  edgeName,
47
48
  realName: parsed.name,
48
49
  kind: "exact",
49
- targetSpec: parsed.fetchSpec,
50
+ // npm accepts = and v prefixes for exact versions. SemVer.format()
51
+ // omits build metadata, so append it explicitly for registry lookup.
52
+ targetSpec: (() => {
53
+ const version = new SemVer(parsed.fetchSpec.replace(/^[=v\s]+/, ""));
54
+ return (version.version +
55
+ (version.build.length ? `+${version.build.join(".")}` : ""));
56
+ })(),
50
57
  targetKind: "exact",
51
58
  };
52
59
  }
@@ -0,0 +1,2 @@
1
+ export declare function normalizeEngineConstraints(value: unknown, invalid: () => never): Record<string, string>;
2
+ //# sourceMappingURL=engines.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engines.d.ts","sourceRoot":"","sources":["../../src/util/engines.ts"],"names":[],"mappings":"AAGA,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,KAAK,GACnB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBxB"}
@@ -0,0 +1,23 @@
1
+ /** Normalize npm's historical engine arrays without weakening modern map checks. */
2
+ import { validRange } from "semver";
3
+ export function normalizeEngineConstraints(value, invalid) {
4
+ if (Array.isArray(value)) {
5
+ const engines = {};
6
+ for (const item of value) {
7
+ const match = typeof item === "string"
8
+ ? /^([a-z][a-z0-9._-]*)\s+(.+)$/.exec(item)
9
+ : null;
10
+ if (!match ||
11
+ Object.hasOwn(engines, match[1]) ||
12
+ validRange(match[2]) === null)
13
+ return invalid();
14
+ engines[match[1]] = match[2];
15
+ }
16
+ return engines;
17
+ }
18
+ if (typeof value !== "object" ||
19
+ value === null ||
20
+ Object.values(value).some((item) => typeof item !== "string"))
21
+ return invalid();
22
+ return value;
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zyrk",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Storage-efficient Node package manager: one verified store per user, shared across projects through links.",
5
5
  "license": "MIT",
6
6
  "type": "module",