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.
- package/README.md +9 -1
- package/VERSION +1 -1
- package/dist/install/budgets.d.ts +7 -0
- package/dist/install/budgets.d.ts.map +1 -0
- package/dist/install/budgets.js +25 -0
- package/dist/install/import-npm-workspace.d.ts.map +1 -1
- package/dist/install/import-npm-workspace.js +1 -3
- package/dist/install/import-npm.d.ts.map +1 -1
- package/dist/install/import-npm.js +8 -10
- package/dist/install/lock.d.ts.map +1 -1
- package/dist/install/lock.js +20 -4
- package/dist/install/platform.d.ts +5 -0
- package/dist/install/platform.d.ts.map +1 -0
- package/dist/install/platform.js +97 -0
- package/dist/install/run.d.ts +7 -0
- package/dist/install/run.d.ts.map +1 -1
- package/dist/install/run.js +28 -48
- package/dist/install/workspace-graph.d.ts +1 -0
- package/dist/install/workspace-graph.d.ts.map +1 -1
- package/dist/install/workspace-graph.js +12 -3
- package/dist/install/workspace-install.d.ts.map +1 -1
- package/dist/install/workspace-install.js +7 -5
- package/dist/install/workspace-lock.d.ts +1 -1
- package/dist/install/workspace-lock.d.ts.map +1 -1
- package/dist/install/workspace-lock.js +11 -1
- package/dist/lockfile/fingerprints.js +0 -1
- package/dist/lockfile/read.d.ts +1 -1
- package/dist/lockfile/read.d.ts.map +1 -1
- package/dist/lockfile/read.js +11 -1
- package/dist/lockfile/types.d.ts +1 -0
- package/dist/lockfile/types.d.ts.map +1 -1
- package/dist/lockfile/types.js +1 -0
- package/dist/lockfile/write.d.ts.map +1 -1
- package/dist/lockfile/write.js +1 -0
- package/dist/resolve/resolver.d.ts +1 -21
- package/dist/resolve/resolver.d.ts.map +1 -1
- package/dist/resolve/resolver.js +13 -1
- package/dist/resolve/spec.d.ts.map +1 -1
- package/dist/resolve/spec.js +8 -1
- package/dist/util/engines.d.ts +2 -0
- package/dist/util/engines.d.ts.map +1 -0
- package/dist/util/engines.js +23 -0
- 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.
|
|
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.
|
|
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;
|
|
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
|
-
|
|
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":"
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
129
|
-
|
|
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,
|
|
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"}
|
package/dist/install/lock.js
CHANGED
|
@@ -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) &&
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/dist/install/run.d.ts
CHANGED
|
@@ -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":"
|
|
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"}
|
package/dist/install/run.js
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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 = (
|
|
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":"
|
|
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 (
|
|
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
|
|
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":"
|
|
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,
|
|
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,
|
|
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() }),
|
package/dist/lockfile/read.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/lockfile/read.js
CHANGED
|
@@ -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) {
|
package/dist/lockfile/types.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/lockfile/types.js
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/lockfile/write.js
CHANGED
|
@@ -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":"
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/resolve/resolver.ts"],"names":[],"mappings":"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"}
|
package/dist/resolve/resolver.js
CHANGED
|
@@ -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
|
-
...(
|
|
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;
|
|
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"}
|
package/dist/resolve/spec.js
CHANGED
|
@@ -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
|
-
|
|
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 @@
|
|
|
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
|
+
}
|