zyrk 0.5.1 → 0.5.2
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 +7 -1
- package/VERSION +1 -1
- package/dist/install/import-npm-workspace.d.ts +1 -0
- package/dist/install/import-npm-workspace.d.ts.map +1 -1
- package/dist/install/import-npm-workspace.js +33 -11
- package/dist/install/import-npm.d.ts +3 -1
- package/dist/install/import-npm.d.ts.map +1 -1
- package/dist/install/import-npm.js +11 -7
- package/dist/install/run.d.ts.map +1 -1
- package/dist/install/run.js +12 -3
- package/dist/install/workspace-graph.d.ts +7 -1
- package/dist/install/workspace-graph.d.ts.map +1 -1
- package/dist/install/workspace-graph.js +49 -6
- package/dist/install/workspace-install.d.ts.map +1 -1
- package/dist/install/workspace-install.js +4 -3
- package/dist/install/workspace-mutation.d.ts.map +1 -1
- package/dist/install/workspace-mutation.js +4 -1
- package/dist/install/workspace-overrides.d.ts +11 -0
- package/dist/install/workspace-overrides.d.ts.map +1 -0
- package/dist/install/workspace-overrides.js +67 -0
- package/dist/install/workspaces.d.ts.map +1 -1
- package/dist/install/workspaces.js +4 -1
- package/dist/lockfile/fingerprints.d.ts.map +1 -1
- package/dist/lockfile/fingerprints.js +13 -1
- package/dist/resolve/overrides.d.ts +19 -0
- package/dist/resolve/overrides.d.ts.map +1 -0
- package/dist/resolve/overrides.js +142 -0
- package/dist/resolve/resolver.d.ts +2 -0
- package/dist/resolve/resolver.d.ts.map +1 -1
- package/dist/resolve/resolver.js +60 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,7 +119,7 @@ scheme, host, port, and path; redirects cannot expand that scope. Use
|
|
|
119
119
|
Scoped registries route metadata, exact sources, imports and tool commands by
|
|
120
120
|
the real package name, including aliases. Advisory requests are partitioned by
|
|
121
121
|
registry; private names never fall back to a public endpoint. Disabled TLS
|
|
122
|
-
verification is rejected before lookup. Git sources
|
|
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
125
|
remain errors. Platform selection is recorded in the lock. macOS, Windows, and
|
|
@@ -356,3 +356,9 @@ Keep existing data until an explicit migration is supported.
|
|
|
356
356
|
|
|
357
357
|
[MIT](LICENSE). If you want to support development,
|
|
358
358
|
[sponsor the maintainer](https://github.com/sponsors/kingjethro999).
|
|
359
|
+
|
|
360
|
+
Root `package.json` dependency overrides support exact specifications, nested and
|
|
361
|
+
version-qualified selectors, self rules (`.`), and `$direct` references. Direct
|
|
362
|
+
dependency overrides must match the declared specification. Workspace member
|
|
363
|
+
overrides are ignored, matching npm root-only policy. Override rule order is
|
|
364
|
+
recorded in the manifest fingerprint; frozen installs reject policy drift.
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v0.5.
|
|
1
|
+
v0.5.2
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { NpmLockDoc } from "../lockfile/types.ts";
|
|
2
2
|
import type { WorkspaceProject } from "./workspaces.ts";
|
|
3
3
|
import type { WorkspaceGraph } from "./workspace-graph.ts";
|
|
4
|
+
/** Import stays pure while live-node scope refinement converges. */
|
|
4
5
|
export declare function importNpmWorkspaceLock(doc: NpmLockDoc, project: WorkspaceProject, origin: string | ((name: string) => string)): WorkspaceGraph;
|
|
5
6
|
//# sourceMappingURL=import-npm-workspace.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-npm-workspace.d.ts","sourceRoot":"","sources":["../../src/install/import-npm-workspace.ts"],"names":[],"mappings":"
|
|
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,5 +1,6 @@
|
|
|
1
1
|
/** Translate npm's physical virtual tree without selecting new registry versions. */
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { workspaceOverrides, refineWorkspaceOverride, WorkspaceOverrideRestart, } from "./workspace-overrides.js";
|
|
3
4
|
import { satisfies } from "semver";
|
|
4
5
|
import { validateLockDoc } from "../lockfile/read.js";
|
|
5
6
|
import { sha512Hex } from "../util/hash.js";
|
|
@@ -8,7 +9,7 @@ import { importNpmLock } from "./import-npm.js";
|
|
|
8
9
|
import { lockMismatch, cleanUrl } from "./lock.js";
|
|
9
10
|
import { ZyrkError } from "../errors.js";
|
|
10
11
|
import { assertWorkspacePlatform } from "./workspace-graph.js";
|
|
11
|
-
|
|
12
|
+
function importNpmWorkspaceLockPass(doc, project, origin, overrideState) {
|
|
12
13
|
if (!doc ||
|
|
13
14
|
typeof doc !== "object" ||
|
|
14
15
|
Array.isArray(doc) ||
|
|
@@ -65,6 +66,8 @@ export function importNpmWorkspaceLock(doc, project, origin) {
|
|
|
65
66
|
(typeof origin === "string" && !cleanUrl(origin)))
|
|
66
67
|
throw lockMismatch("workspace adoption needs an unannotated npm v2/v3 lock");
|
|
67
68
|
const packages = doc.packages;
|
|
69
|
+
const overrides = overrideState.policy;
|
|
70
|
+
const contexts = new Map(overrideState.contexts);
|
|
68
71
|
const graph = { nodes: [], importers: {}, omissions: [] };
|
|
69
72
|
const locations = new Map();
|
|
70
73
|
const local = new Map(project.importers.map((i) => [i.id === "." ? "" : i.id, i]));
|
|
@@ -131,7 +134,10 @@ export function importNpmWorkspaceLock(doc, project, origin) {
|
|
|
131
134
|
}
|
|
132
135
|
};
|
|
133
136
|
const populated = new Set();
|
|
134
|
-
const load = (location, name, spec, from) => {
|
|
137
|
+
const load = (location, name, spec, from, parentContext) => {
|
|
138
|
+
const selectedOverride = overrides.select(parentContext, name, spec);
|
|
139
|
+
spec = selectedOverride.spec;
|
|
140
|
+
const contextKey = `${location}\0${selectedOverride.context.id}`;
|
|
135
141
|
const entry = packages[location];
|
|
136
142
|
if (entry.link === true) {
|
|
137
143
|
const target = locations.get(entry.resolved);
|
|
@@ -143,6 +149,7 @@ export function importNpmWorkspaceLock(doc, project, origin) {
|
|
|
143
149
|
(!["*", "^", "~"].includes(range) &&
|
|
144
150
|
!satisfies(target.version, range)))
|
|
145
151
|
throw lockMismatch("npm workspace link disagrees with requirement");
|
|
152
|
+
refineWorkspaceOverride(overrideState.contexts, target.key, selectedOverride.context);
|
|
146
153
|
return target;
|
|
147
154
|
}
|
|
148
155
|
// Reuse the strict single-selection adapter's archive, alias, source and
|
|
@@ -155,12 +162,12 @@ export function importNpmWorkspaceLock(doc, project, origin) {
|
|
|
155
162
|
lockfileVersion: 3,
|
|
156
163
|
packages: { "": {}, [`node_modules/${name}`]: shallow },
|
|
157
164
|
}, { [name]: spec }, origin).nodes[0];
|
|
158
|
-
let node = locations.get(
|
|
165
|
+
let node = locations.get(contextKey);
|
|
159
166
|
if (!node) {
|
|
160
167
|
const { peerBindings: _bindings, ...source } = selected;
|
|
161
168
|
node = {
|
|
162
169
|
...source,
|
|
163
|
-
key: `${selected.name}@${selected.version}#${sha512Hex(location).slice(0, 16)}`,
|
|
170
|
+
key: `${selected.name}@${selected.version}#${sha512Hex(selectedOverride.context.id ? contextKey : location).slice(0, 16)}`,
|
|
164
171
|
kind: "registry",
|
|
165
172
|
edges: [],
|
|
166
173
|
peers: {},
|
|
@@ -168,25 +175,26 @@ export function importNpmWorkspaceLock(doc, project, origin) {
|
|
|
168
175
|
};
|
|
169
176
|
assertWorkspacePlatform({ nodes: [node], importers: {}, omissions: [] });
|
|
170
177
|
graph.nodes.push(node);
|
|
171
|
-
locations.set(
|
|
178
|
+
locations.set(contextKey, node);
|
|
179
|
+
contexts.set(node.key, selectedOverride.context);
|
|
172
180
|
}
|
|
173
181
|
try {
|
|
174
182
|
populate(location, node);
|
|
175
183
|
}
|
|
176
184
|
catch (cause) {
|
|
177
|
-
populated.delete(
|
|
178
|
-
locations.delete(
|
|
185
|
+
populated.delete(node.key);
|
|
186
|
+
locations.delete(contextKey);
|
|
179
187
|
graph.nodes = graph.nodes.filter((n) => n !== node);
|
|
180
188
|
throw cause;
|
|
181
189
|
}
|
|
182
190
|
return node;
|
|
183
191
|
};
|
|
184
192
|
const populate = (location, node) => {
|
|
185
|
-
if (populated.has(
|
|
193
|
+
if (populated.has(node.key))
|
|
186
194
|
return;
|
|
187
195
|
if (populated.size >= 10000)
|
|
188
196
|
throw lockMismatch("npm import resource limit exceeded");
|
|
189
|
-
populated.add(
|
|
197
|
+
populated.add(node.key);
|
|
190
198
|
const entry = packages[location];
|
|
191
199
|
const requirements = new Map();
|
|
192
200
|
for (const [field, kind] of [
|
|
@@ -227,7 +235,7 @@ export function importNpmWorkspaceLock(doc, project, origin) {
|
|
|
227
235
|
}
|
|
228
236
|
let target;
|
|
229
237
|
try {
|
|
230
|
-
target = load(targetLocation, name, requirement.spec, location);
|
|
238
|
+
target = load(targetLocation, name, requirement.spec, location, contexts.get(node.key));
|
|
231
239
|
}
|
|
232
240
|
catch (cause) {
|
|
233
241
|
if (requirement.kind === "optional" &&
|
|
@@ -259,7 +267,7 @@ export function importNpmWorkspaceLock(doc, project, origin) {
|
|
|
259
267
|
node.peers[name] = null;
|
|
260
268
|
continue;
|
|
261
269
|
}
|
|
262
|
-
const target = load(targetLocation, name, spec, location);
|
|
270
|
+
const target = load(targetLocation, name, spec, location, contexts.get(node.key));
|
|
263
271
|
node.peers[name] = target.key;
|
|
264
272
|
if (!node.edges.some((edge) => edge.name === name))
|
|
265
273
|
node.edges.push({ name, target: target.key, kind: "peer", spec });
|
|
@@ -281,3 +289,17 @@ export function importNpmWorkspaceLock(doc, project, origin) {
|
|
|
281
289
|
graph.nodes.sort((a, b) => a.key.localeCompare(b.key));
|
|
282
290
|
return graph;
|
|
283
291
|
}
|
|
292
|
+
/** Import stays pure while live-node scope refinement converges. */
|
|
293
|
+
export function importNpmWorkspaceLock(doc, project, origin) {
|
|
294
|
+
const state = workspaceOverrides(project), expires = Date.now() + 30000;
|
|
295
|
+
for (let round = 0; round < 128 && Date.now() < expires; round++) {
|
|
296
|
+
try {
|
|
297
|
+
return importNpmWorkspaceLockPass(doc, project, origin, state);
|
|
298
|
+
}
|
|
299
|
+
catch (cause) {
|
|
300
|
+
if (!(cause instanceof WorkspaceOverrideRestart))
|
|
301
|
+
throw cause;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
throw lockMismatch("npm workspace override import resource limit exceeded");
|
|
305
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
/** Adopt npm v2/v3 exact selections without asking the registry to pick versions. */
|
|
2
|
+
import { type OverridePolicy } from "../resolve/overrides.ts";
|
|
1
3
|
import type { NpmLockDoc } from "../lockfile/types.ts";
|
|
2
4
|
import type { FinalizedGraph } from "../resolve/resolver.ts";
|
|
3
|
-
export declare function importNpmLock(doc: NpmLockDoc, requirements: Record<string, string>, origin: string | ((name: string) => string)): FinalizedGraph;
|
|
5
|
+
export declare function importNpmLock(doc: NpmLockDoc, requirements: Record<string, string>, origin: string | ((name: string) => string), overrides?: OverridePolicy): FinalizedGraph;
|
|
4
6
|
//# sourceMappingURL=import-npm.d.ts.map
|
|
@@ -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":"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,11 +1,12 @@
|
|
|
1
1
|
/** Adopt npm v2/v3 exact selections without asking the registry to pick versions. */
|
|
2
|
+
import { createRootOverrides, } from "../resolve/overrides.js";
|
|
2
3
|
import { validateLockDoc } from "../lockfile/read.js";
|
|
3
4
|
import { canonicalStrongSri } from "../util/integrity.js";
|
|
4
5
|
import { valid, satisfies } from "semver";
|
|
5
6
|
import { parseRequirement } from "../resolve/spec.js";
|
|
6
7
|
import { sourceIdOf, sha512Hex } from "../util/hash.js";
|
|
7
8
|
import { cleanUrl, lockMismatch, packageName } from "./lock.js";
|
|
8
|
-
export function importNpmLock(doc, requirements, origin) {
|
|
9
|
+
export function importNpmLock(doc, requirements, origin, overrides = createRootOverrides({})) {
|
|
9
10
|
validateLockDoc(doc, "package-lock.json");
|
|
10
11
|
const packages = doc.packages;
|
|
11
12
|
if (!packages || !packages[""])
|
|
@@ -26,8 +27,11 @@ export function importNpmLock(doc, requirements, origin) {
|
|
|
26
27
|
: "";
|
|
27
28
|
}
|
|
28
29
|
};
|
|
29
|
-
const load = (location, edgeName, spec) => {
|
|
30
|
-
const
|
|
30
|
+
const load = (location, edgeName, spec, parentContext) => {
|
|
31
|
+
const selected = overrides.select(parentContext, edgeName, spec);
|
|
32
|
+
spec = selected.spec;
|
|
33
|
+
const contextKey = `${location}\0${selected.context.id}`;
|
|
34
|
+
const existing = locations.get(contextKey);
|
|
31
35
|
const entry = packages[location];
|
|
32
36
|
if (!entry || entry.link === true || entry.inBundle === true)
|
|
33
37
|
throw lockMismatch("npm lock contains an unsupported linked or bundled selection");
|
|
@@ -53,7 +57,7 @@ export function importNpmLock(doc, requirements, origin) {
|
|
|
53
57
|
if (existing)
|
|
54
58
|
return existing;
|
|
55
59
|
const node = {
|
|
56
|
-
key: `${name}@${entry.version}#${sha512Hex(location).slice(0, 16)}`,
|
|
60
|
+
key: `${name}@${entry.version}#${sha512Hex(selected.context.id ? contextKey : location).slice(0, 16)}`,
|
|
57
61
|
name,
|
|
58
62
|
version: entry.version,
|
|
59
63
|
origin: selectedOrigin,
|
|
@@ -96,7 +100,7 @@ export function importNpmLock(doc, requirements, origin) {
|
|
|
96
100
|
node.platform[field] = value;
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
|
-
locations.set(
|
|
103
|
+
locations.set(contextKey, node);
|
|
100
104
|
const dependencies = Object.create(null);
|
|
101
105
|
for (const [group, kind] of [
|
|
102
106
|
["dependencies", "prod"],
|
|
@@ -128,7 +132,7 @@ export function importNpmLock(doc, requirements, origin) {
|
|
|
128
132
|
}
|
|
129
133
|
throw lockMismatch("npm lock is missing a required dependency");
|
|
130
134
|
}
|
|
131
|
-
const target = load(targetLocation, dep, requirement.spec);
|
|
135
|
+
const target = load(targetLocation, dep, requirement.spec, selected.context);
|
|
132
136
|
node.edges.push({
|
|
133
137
|
name: dep,
|
|
134
138
|
target: target.key,
|
|
@@ -148,7 +152,7 @@ export function importNpmLock(doc, requirements, origin) {
|
|
|
148
152
|
const location = find("", name);
|
|
149
153
|
if (!location)
|
|
150
154
|
throw lockMismatch("npm lock is missing a direct selection");
|
|
151
|
-
return { name, target: load(location, name, spec).key };
|
|
155
|
+
return { name, target: load(location, name, spec, overrides.root).key };
|
|
152
156
|
});
|
|
153
157
|
return {
|
|
154
158
|
nodes: [...locations.values()].sort((a, b) => a.key.localeCompare(b.key)),
|
|
@@ -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;
|
|
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,cAAc,GAAG;IAC3B,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,CAyyBxB"}
|
package/dist/install/run.js
CHANGED
|
@@ -6,6 +6,7 @@ import { candidates, candidateDigest, approveCandidate, assertApproved, material
|
|
|
6
6
|
import { loadProjectPatches, applyProjectPatches } from "../commands/patch.js";
|
|
7
7
|
import { registerGeneration } from "./metadata.js";
|
|
8
8
|
import { randomId } from "../util/ids.js";
|
|
9
|
+
import { createRootOverrides } from "../resolve/overrides.js";
|
|
9
10
|
import { importNpmLock } from "./import-npm.js";
|
|
10
11
|
/** Native project install: resolve -> verify -> realize -> journaled publication. */
|
|
11
12
|
import { readFile, realpath, lstat, mkdir, rm } from "node:fs/promises";
|
|
@@ -65,10 +66,9 @@ async function readManifest(project) {
|
|
|
65
66
|
throw error("ZYRK_MANIFEST_INVALID", `Project ${group} must contain valid package names and string specifiers.`);
|
|
66
67
|
}
|
|
67
68
|
if (doc.workspaces !== undefined ||
|
|
68
|
-
doc.overrides !== undefined ||
|
|
69
69
|
doc.bundledDependencies !== undefined ||
|
|
70
70
|
doc.bundleDependencies !== undefined)
|
|
71
|
-
throw error("ZYRK_NOT_IMPLEMENTED", "Project workspaces
|
|
71
|
+
throw error("ZYRK_NOT_IMPLEMENTED", "Project workspaces and bundled dependencies are not supported by install yet.");
|
|
72
72
|
if (doc.zyrk !== undefined &&
|
|
73
73
|
(!object(doc.zyrk) ||
|
|
74
74
|
Object.keys(doc.zyrk).some((key) => !["scripts", "audit", "patches", "mutablePackages"].includes(key)) ||
|
|
@@ -98,6 +98,13 @@ export async function installProject(options) {
|
|
|
98
98
|
return installWorkspaceProject(options);
|
|
99
99
|
}
|
|
100
100
|
const project = await realpath(options.projectDir);
|
|
101
|
+
const preliminaryManifest = (await readManifest(project)).doc;
|
|
102
|
+
const preliminaryOverrides = createRootOverrides(preliminaryManifest);
|
|
103
|
+
if (options.importNpm) {
|
|
104
|
+
const preliminaryLock = (await readLockfile(path.join(project, "package-lock.json")))?.doc;
|
|
105
|
+
if (preliminaryLock && !preliminaryLock.zyrk)
|
|
106
|
+
importNpmLock(preliminaryLock, requirements(preliminaryManifest), (name) => options.registry.originFor(name), preliminaryOverrides);
|
|
107
|
+
}
|
|
101
108
|
// A local control-directory link would redirect locks and recovery writes.
|
|
102
109
|
const local = path.join(project, PROJECT_STATE_DIR);
|
|
103
110
|
try {
|
|
@@ -189,6 +196,7 @@ export async function installProject(options) {
|
|
|
189
196
|
if (other !== group && manifest[other])
|
|
190
197
|
delete manifest[other][parsed.name];
|
|
191
198
|
}
|
|
199
|
+
const overrides = createRootOverrides(manifest);
|
|
192
200
|
const allDeps = requirements(manifest);
|
|
193
201
|
for (const [name, spec] of Object.entries(allDeps))
|
|
194
202
|
parseRequirement(name, spec);
|
|
@@ -215,7 +223,7 @@ export async function installProject(options) {
|
|
|
215
223
|
if (existing && !existing.zyrk) {
|
|
216
224
|
if (options.frozen || options.offline || specs.length)
|
|
217
225
|
throw lockMismatch("npm-only lockfile requires a non-frozen adoption install before adding packages");
|
|
218
|
-
resolved = importNpmLock(existing, allDeps, (name) => options.registry.originFor(name));
|
|
226
|
+
resolved = importNpmLock(existing, allDeps, (name) => options.registry.originFor(name), overrides);
|
|
219
227
|
}
|
|
220
228
|
if (existing?.zyrk &&
|
|
221
229
|
specs.length === 0 &&
|
|
@@ -241,6 +249,7 @@ export async function installProject(options) {
|
|
|
241
249
|
const prior = existing?.zyrk ? lockedGraph(existing) : undefined;
|
|
242
250
|
const fresh = new Set(specs.map((spec) => npa(spec).name));
|
|
243
251
|
resolved = await resolveRequirements({
|
|
252
|
+
overrides,
|
|
244
253
|
provider: {
|
|
245
254
|
origin: options.registry.origin,
|
|
246
255
|
originFor: (name) => options.registry.originFor(name),
|
|
@@ -46,17 +46,23 @@ export type WorkspaceGraph = {
|
|
|
46
46
|
reason: "platform";
|
|
47
47
|
}>;
|
|
48
48
|
};
|
|
49
|
-
|
|
49
|
+
declare function resolveWorkspaceGraphPass(project: WorkspaceProject, provider: MetadataProvider, options?: {
|
|
50
50
|
previous?: WorkspaceGraph;
|
|
51
51
|
fresh?: ReadonlySet<string>;
|
|
52
52
|
platform?: RuntimePlatform;
|
|
53
53
|
maxNodes?: number;
|
|
54
54
|
maxDepth?: number;
|
|
55
55
|
deadlineMs?: number;
|
|
56
|
+
}, overrideState?: {
|
|
57
|
+
policy: import("../resolve/overrides.ts").OverridePolicy;
|
|
58
|
+
contexts: Map<string, import("../resolve/overrides.ts").OverrideContext>;
|
|
56
59
|
}): Promise<WorkspaceGraph>;
|
|
60
|
+
/** Retry pure planning when a registry edge narrows a live importer's scope. */
|
|
61
|
+
export declare function resolveWorkspaceGraph(project: WorkspaceProject, provider: MetadataProvider, options?: Parameters<typeof resolveWorkspaceGraphPass>[2]): Promise<WorkspaceGraph>;
|
|
57
62
|
export declare function workspaceRegistryOccurrences(graph: WorkspaceGraph): ResolvedOccurrence[];
|
|
58
63
|
/** Rooted semantic context including live source identity; occurrence keys never split equal peers. */
|
|
59
64
|
export declare function workspaceContextId(graph: WorkspaceGraph, rootKey: string, projectId: string): string;
|
|
60
65
|
/** Recheck locked platform/runtime requirements as strictly as fresh resolution. */
|
|
61
66
|
export declare function assertWorkspacePlatform(graph: WorkspaceGraph): void;
|
|
67
|
+
export {};
|
|
62
68
|
//# sourceMappingURL=workspace-graph.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-graph.d.ts","sourceRoot":"","sources":["../../src/install/workspace-graph.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EAChB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace-graph.d.ts","sourceRoot":"","sources":["../../src/install/workspace-graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { maxSatisfying, satisfies, valid } from "semver";
|
|
3
3
|
import { expectedIntegrityFromDist, } from "../fetch/registry.js";
|
|
4
4
|
import { currentPlatform } from "../resolve/resolver.js";
|
|
5
|
+
import { workspaceOverrides, refineWorkspaceOverride, WorkspaceOverrideRestart, } from "./workspace-overrides.js";
|
|
5
6
|
import { parseRequirement } from "../resolve/spec.js";
|
|
6
7
|
import { sourceIdOf, sha512Hex } from "../util/hash.js";
|
|
7
8
|
import { ZyrkError } from "../errors.js";
|
|
@@ -14,7 +15,9 @@ const fail = (reason) => {
|
|
|
14
15
|
};
|
|
15
16
|
class PlatformMismatch extends Error {
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
+
async function resolveWorkspaceGraphPass(project, provider, options = {}, overrideState = workspaceOverrides(project)) {
|
|
19
|
+
const overrides = overrideState.policy;
|
|
20
|
+
const overrideContexts = new Map(overrideState.contexts);
|
|
18
21
|
const graph = { nodes: [], importers: {}, omissions: [] };
|
|
19
22
|
const byKey = new Map(), manifests = new Map(), chains = new Map();
|
|
20
23
|
const metadata = new Map();
|
|
@@ -83,6 +86,10 @@ export async function resolveWorkspaceGraph(project, provider, options = {}) {
|
|
|
83
86
|
const build = async (parent, name, spec, kind, ancestry, explicitLocal) => {
|
|
84
87
|
check(ancestry.length);
|
|
85
88
|
try {
|
|
89
|
+
const overridden = overrides.select(overrideContexts.get(parent.key), name, spec);
|
|
90
|
+
if (overridden.spec !== spec)
|
|
91
|
+
explicitLocal = undefined;
|
|
92
|
+
spec = overridden.spec;
|
|
86
93
|
let local = explicitLocal === undefined
|
|
87
94
|
? undefined
|
|
88
95
|
: project.importers.find((item) => item.id === explicitLocal);
|
|
@@ -106,8 +113,11 @@ export async function resolveWorkspaceGraph(project, provider, options = {}) {
|
|
|
106
113
|
local = candidate;
|
|
107
114
|
}
|
|
108
115
|
}
|
|
109
|
-
if (local)
|
|
110
|
-
|
|
116
|
+
if (local) {
|
|
117
|
+
const key = graph.importers[local.id];
|
|
118
|
+
refineWorkspaceOverride(overrideState.contexts, key, overridden.context);
|
|
119
|
+
return key;
|
|
120
|
+
}
|
|
111
121
|
if (!requirement)
|
|
112
122
|
throw lockMismatch("missing workspace requirement");
|
|
113
123
|
const doc = await packument(requirement.realName);
|
|
@@ -145,7 +155,8 @@ export async function resolveWorkspaceGraph(project, provider, options = {}) {
|
|
|
145
155
|
.map((key) => byKey.get(key))
|
|
146
156
|
.find((node) => node?.kind === "registry" &&
|
|
147
157
|
node.name === requirement.realName &&
|
|
148
|
-
node.version === version
|
|
158
|
+
node.version === version &&
|
|
159
|
+
overrideContexts.get(node.key)?.id === overridden.context.id);
|
|
149
160
|
if (ancestor)
|
|
150
161
|
return ancestor.key;
|
|
151
162
|
const key = `${requirement.realName}@${version}#${sha512Hex(parent.key + "\0" + name).slice(0, 16)}`;
|
|
@@ -184,6 +195,7 @@ export async function resolveWorkspaceGraph(project, provider, options = {}) {
|
|
|
184
195
|
optionalPeers: [],
|
|
185
196
|
};
|
|
186
197
|
graph.nodes.push(node);
|
|
198
|
+
overrideContexts.set(key, overridden.context);
|
|
187
199
|
byKey.set(key, node);
|
|
188
200
|
manifests.set(key, entry);
|
|
189
201
|
chains.set(key, [...ancestry, parent.key]);
|
|
@@ -235,7 +247,8 @@ export async function resolveWorkspaceGraph(project, provider, options = {}) {
|
|
|
235
247
|
for (const node of graph.nodes) {
|
|
236
248
|
check();
|
|
237
249
|
const manifest = manifests.get(node.key);
|
|
238
|
-
for (const [name,
|
|
250
|
+
for (const [name, rawRange] of Object.entries(manifest.peerDependencies ?? {}).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {
|
|
251
|
+
const range = overrides.select(overrideContexts.get(node.key), name, rawRange).spec;
|
|
239
252
|
const optional = manifest.peerDependenciesMeta?.[name]?.optional === true;
|
|
240
253
|
if (optional && !node.optionalPeers.includes(name))
|
|
241
254
|
node.optionalPeers.push(name);
|
|
@@ -257,7 +270,7 @@ export async function resolveWorkspaceGraph(project, provider, options = {}) {
|
|
|
257
270
|
const selected = byKey.get(target);
|
|
258
271
|
const requested = range.startsWith("workspace:")
|
|
259
272
|
? range.slice(10)
|
|
260
|
-
: range;
|
|
273
|
+
: parseRequirement(name, range).targetSpec;
|
|
261
274
|
if (!["*", "^", "~"].includes(requested) &&
|
|
262
275
|
!satisfies(selected.version, requested))
|
|
263
276
|
throw new ZyrkError("ZYRK_PEER_CONFLICT", `Workspace peer ${name} required by ${node.name} conflicts with its consumer.`, { remedy: "Align the consumer and package peer requirements." });
|
|
@@ -294,6 +307,36 @@ export async function resolveWorkspaceGraph(project, provider, options = {}) {
|
|
|
294
307
|
}
|
|
295
308
|
return fail("peer binding iteration limit exceeded");
|
|
296
309
|
}
|
|
310
|
+
/** Retry pure planning when a registry edge narrows a live importer's scope. */
|
|
311
|
+
export async function resolveWorkspaceGraph(project, provider, options = {}) {
|
|
312
|
+
const state = workspaceOverrides(project), expires = Date.now() + (options.deadlineMs ?? 30000);
|
|
313
|
+
const packuments = new Map();
|
|
314
|
+
const cached = {
|
|
315
|
+
...provider,
|
|
316
|
+
origin: provider.origin,
|
|
317
|
+
...(provider.originFor
|
|
318
|
+
? { originFor: (name) => provider.originFor(name) }
|
|
319
|
+
: {}),
|
|
320
|
+
packument: (name) => {
|
|
321
|
+
let pending = packuments.get(name);
|
|
322
|
+
if (!pending) {
|
|
323
|
+
pending = provider.packument(name);
|
|
324
|
+
packuments.set(name, pending);
|
|
325
|
+
}
|
|
326
|
+
return pending;
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
for (let round = 0; round < 128 && Date.now() < expires; round++) {
|
|
330
|
+
try {
|
|
331
|
+
return await resolveWorkspaceGraphPass(project, cached, { ...options, deadlineMs: Math.max(1, expires - Date.now()) }, state);
|
|
332
|
+
}
|
|
333
|
+
catch (cause) {
|
|
334
|
+
if (!(cause instanceof WorkspaceOverrideRestart))
|
|
335
|
+
throw cause;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
throw lockMismatch("workspace override planning resource limit exceeded");
|
|
339
|
+
}
|
|
297
340
|
export function workspaceRegistryOccurrences(graph) {
|
|
298
341
|
return graph.nodes
|
|
299
342
|
.filter((node) => node.kind === "registry")
|
|
@@ -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;
|
|
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,CA6pBxB"}
|
|
@@ -4,6 +4,7 @@ import { constants } from "node:fs";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { discoverWorkspaces, findWorkspaceRoot, assertNoLiveSourceBuilds, } from "./workspaces.js";
|
|
6
6
|
import { resolveWorkspaceGraph, workspaceRegistryOccurrences, workspaceContextId, assertWorkspacePlatform, } from "./workspace-graph.js";
|
|
7
|
+
import { workspaceOverrides } from "./workspace-overrides.js";
|
|
7
8
|
import { importNpmWorkspaceLock } from "./import-npm-workspace.js";
|
|
8
9
|
import { readWorkspaceLock, workspaceLockDoc } from "./workspace-lock.js";
|
|
9
10
|
import { publishWorkspaceProjection, recoverWorkspaceProjection, workspaceProjectionPaths, } from "./workspace-transaction.js";
|
|
@@ -80,6 +81,7 @@ export async function installWorkspaceProject(options) {
|
|
|
80
81
|
const root = await findWorkspaceRoot(options.projectDir);
|
|
81
82
|
if (!root)
|
|
82
83
|
throw lockMismatch("no authoritative workspace root");
|
|
84
|
+
workspaceOverrides(await discoverWorkspaces(root));
|
|
83
85
|
// Refuse untranslatable locks before creating control directories or
|
|
84
86
|
// recovering a previous generation. Revalidate under the project lock below.
|
|
85
87
|
if (options.importNpm)
|
|
@@ -99,10 +101,9 @@ export async function installWorkspaceProject(options) {
|
|
|
99
101
|
await readFile(path.join(i.directory, "package.json"), "utf8"),
|
|
100
102
|
])));
|
|
101
103
|
const mutation = await prepareWorkspaceMutation(project, options);
|
|
102
|
-
if (manifest.
|
|
103
|
-
manifest.bundledDependencies !== undefined ||
|
|
104
|
+
if (manifest.bundledDependencies !== undefined ||
|
|
104
105
|
manifest.bundleDependencies !== undefined)
|
|
105
|
-
unsupported("Workspace
|
|
106
|
+
unsupported("Workspace bundled root dependencies are unsupported.");
|
|
106
107
|
const policy = object(manifest.zyrk) ? manifest.zyrk.scripts : undefined;
|
|
107
108
|
if (manifest.zyrk !== undefined &&
|
|
108
109
|
(!object(manifest.zyrk) ||
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-mutation.d.ts","sourceRoot":"","sources":["../../src/install/workspace-mutation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAiB3D,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"workspace-mutation.d.ts","sourceRoot":"","sources":["../../src/install/workspace-mutation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAiB3D,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAc3E;AACD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC;IACT,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC,CA8ID"}
|
|
@@ -18,7 +18,10 @@ export function refreshWorkspaceFingerprint(project) {
|
|
|
18
18
|
member.id,
|
|
19
19
|
member.manifest.name ?? null,
|
|
20
20
|
member.manifest.version ?? null,
|
|
21
|
-
manifestFingerprint(
|
|
21
|
+
manifestFingerprint({
|
|
22
|
+
...member.manifest,
|
|
23
|
+
overrides: member.id === "." ? member.manifest.overrides : undefined,
|
|
24
|
+
}),
|
|
22
25
|
])));
|
|
23
26
|
}
|
|
24
27
|
export async function prepareWorkspaceMutation(project, options) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type OverrideContext } from "../resolve/overrides.ts";
|
|
2
|
+
import { type WorkspaceProject } from "./workspaces.ts";
|
|
3
|
+
export declare function workspaceOverrides(project: WorkspaceProject): {
|
|
4
|
+
policy: import("../resolve/overrides.ts").OverridePolicy;
|
|
5
|
+
contexts: Map<string, OverrideContext>;
|
|
6
|
+
};
|
|
7
|
+
/** Internal signal: restart pure planning before any fetch/publication mutation. */
|
|
8
|
+
export declare class WorkspaceOverrideRestart extends Error {
|
|
9
|
+
}
|
|
10
|
+
export declare function refineWorkspaceOverride(contexts: Map<string, OverrideContext>, key: string, incoming: OverrideContext): void;
|
|
11
|
+
//# sourceMappingURL=workspace-overrides.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-overrides.d.ts","sourceRoot":"","sources":["../../src/install/workspace-overrides.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,iBAAiB,CAAC;AAWzB,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB;;;EAuD3D;AACD,oFAAoF;AACpF,qBAAa,wBAAyB,SAAQ,KAAK;CAAG;AACtD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EACtC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,eAAe,GACxB,IAAI,CASN"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Live importers have one dependency graph; compatible scopes may narrow it. */
|
|
2
|
+
import { satisfies } from "semver";
|
|
3
|
+
import { createRootOverrides, } from "../resolve/overrides.js";
|
|
4
|
+
import { parseRequirement } from "../resolve/spec.js";
|
|
5
|
+
import { planWorkspaceDependencies, } from "./workspaces.js";
|
|
6
|
+
import { lockMismatch } from "./lock.js";
|
|
7
|
+
function ancestor(outer, inner) {
|
|
8
|
+
for (let current = inner; current; current = current.parent)
|
|
9
|
+
if (current === outer)
|
|
10
|
+
return true;
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
export function workspaceOverrides(project) {
|
|
14
|
+
const policy = createRootOverrides(project.importers.find((i) => i.id === ".").manifest);
|
|
15
|
+
const contexts = new Map();
|
|
16
|
+
const plans = planWorkspaceDependencies(project);
|
|
17
|
+
for (const importer of project.importers)
|
|
18
|
+
contexts.set(`workspace:${importer.id}`, importer.id === "."
|
|
19
|
+
? policy.root
|
|
20
|
+
: policy.select(policy.root, importer.manifest.name, importer.manifest.version).context);
|
|
21
|
+
for (let round = 0; round <= 128; round++) {
|
|
22
|
+
let changed = false;
|
|
23
|
+
for (const plan of plans) {
|
|
24
|
+
const parent = contexts.get(`workspace:${plan.importer.id}`);
|
|
25
|
+
for (const edge of plan.dependencies) {
|
|
26
|
+
const selected = policy.select(parent, edge.name, edge.spec);
|
|
27
|
+
const localId = edge.selection.kind === "local" ? edge.selection.importer : undefined;
|
|
28
|
+
let local = project.importers.find((i) => i.id === localId);
|
|
29
|
+
if (selected.spec !== edge.spec) {
|
|
30
|
+
const requirement = parseRequirement(edge.name, selected.spec);
|
|
31
|
+
local = project.importers.find((i) => i.id !== "." &&
|
|
32
|
+
i.manifest.name === requirement.realName &&
|
|
33
|
+
requirement.kind !== "alias" &&
|
|
34
|
+
requirement.targetKind !== "tag" &&
|
|
35
|
+
satisfies(i.manifest.version, requirement.targetSpec));
|
|
36
|
+
}
|
|
37
|
+
if (!local)
|
|
38
|
+
continue;
|
|
39
|
+
const key = `workspace:${local.id}`, current = contexts.get(key);
|
|
40
|
+
if (ancestor(current, selected.context)) {
|
|
41
|
+
if (current !== selected.context) {
|
|
42
|
+
contexts.set(key, selected.context);
|
|
43
|
+
changed = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (!ancestor(selected.context, current)) {
|
|
47
|
+
throw lockMismatch("one live workspace cannot represent incompatible scoped override contexts");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!changed)
|
|
52
|
+
return { policy, contexts };
|
|
53
|
+
}
|
|
54
|
+
throw lockMismatch("workspace override propagation resource limit exceeded");
|
|
55
|
+
}
|
|
56
|
+
/** Internal signal: restart pure planning before any fetch/publication mutation. */
|
|
57
|
+
export class WorkspaceOverrideRestart extends Error {
|
|
58
|
+
}
|
|
59
|
+
export function refineWorkspaceOverride(contexts, key, incoming) {
|
|
60
|
+
const current = contexts.get(key);
|
|
61
|
+
if (current === incoming || ancestor(incoming, current))
|
|
62
|
+
return;
|
|
63
|
+
if (!ancestor(current, incoming))
|
|
64
|
+
throw lockMismatch("one live workspace cannot represent incompatible scoped override contexts");
|
|
65
|
+
contexts.set(key, incoming);
|
|
66
|
+
throw new WorkspaceOverrideRestart();
|
|
67
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../src/install/workspaces.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE9E,MAAM,MAAM,iBAAiB,GAAG,mBAAmB,GAAG;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AACF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4EAA4E;IAC5E,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,0EAA0E;IAC1E,MAAM,EAAE;QAAE,IAAI,EAAE,gBAAgB,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9E,CAAC;AACF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAAC;AA2GF,kFAAkF;AAClF,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7D,OAAO,CAAC,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../src/install/workspaces.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE9E,MAAM,MAAM,iBAAiB,GAAG,mBAAmB,GAAG;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AACF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4EAA4E;IAC5E,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,0EAA0E;IAC1E,MAAM,EAAE;QAAE,IAAI,EAAE,gBAAgB,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9E,CAAC;AACF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAAC;AA2GF,kFAAkF;AAClF,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7D,OAAO,CAAC,gBAAgB,CAAC,CAgG3B;AACD,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC;IAC3C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,EACL;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GACnC;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,WAAW,EAAE,iBAAiB,CAAA;KAAE,CAAC;CAC1D,CAAC;AACF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,YAAY,EAAE,mBAAmB,EAAE,CAAC;CACrC,CAAC;AACF,8FAA8F;AAC9F,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,gBAAgB,GACxB,qBAAqB,EAAE,CAmFzB;AACD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,gBAAgB,EACzB,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,iBAAiB,EAAE,CAerB;AACD,0FAA0F;AAC1F,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAChD,MAAM,EAAE,SAAS;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjD,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;IAC9C,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C,GAAG,IAAI,CA+BP;AAED,sFAAsF;AACtF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,SAAS,qBAAqB,EAAE,GACrC,MAAM,EAAE,CAoCV;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiC3E"}
|
|
@@ -164,7 +164,10 @@ export async function discoverWorkspaces(rootDirectory, options = {}) {
|
|
|
164
164
|
member.id,
|
|
165
165
|
member.manifest.name ?? null,
|
|
166
166
|
member.manifest.version ?? null,
|
|
167
|
-
manifestFingerprint(
|
|
167
|
+
manifestFingerprint({
|
|
168
|
+
...member.manifest,
|
|
169
|
+
overrides: member.id === "." ? member.manifest.overrides : undefined,
|
|
170
|
+
}),
|
|
168
171
|
]))),
|
|
169
172
|
};
|
|
170
173
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fingerprints.d.ts","sourceRoot":"","sources":["../../src/lockfile/fingerprints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,MAAM,MAAM,mBAAmB,GAAG;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,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,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;
|
|
1
|
+
{"version":3,"file":"fingerprints.d.ts","sourceRoot":"","sources":["../../src/lockfile/fingerprints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,MAAM,MAAM,mBAAmB,GAAG;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,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,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAcF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CA2BzE;AAED,eAAO,MAAM,qBAAqB;IAAK,OAAO;IAAU,KAAK;IAAM,IAAI;CAAM,CAAC;AAE9E,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAG/D;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACvD,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAalE"}
|
|
@@ -16,6 +16,18 @@ import { canonicalJson } from "../util/json.js";
|
|
|
16
16
|
function fingerprintOf(value) {
|
|
17
17
|
return `sha512-${createHash("sha512").update(canonicalJson(value), "utf8").digest("base64")}`;
|
|
18
18
|
}
|
|
19
|
+
/** npm evaluates sibling override rules in declaration order. Preserve that
|
|
20
|
+
* order inside canonical JSON instead of sorting the policy's object keys. */
|
|
21
|
+
function orderedOverridePolicy(value) {
|
|
22
|
+
if (Array.isArray(value))
|
|
23
|
+
return value.map(orderedOverridePolicy);
|
|
24
|
+
if (value && typeof value === "object")
|
|
25
|
+
return Object.entries(value).map(([key, rule]) => [
|
|
26
|
+
key,
|
|
27
|
+
orderedOverridePolicy(rule),
|
|
28
|
+
]);
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
19
31
|
export function manifestFingerprint(manifest) {
|
|
20
32
|
const normalized = {};
|
|
21
33
|
for (const group of [
|
|
@@ -40,7 +52,7 @@ export function manifestFingerprint(manifest) {
|
|
|
40
52
|
peerDependenciesMeta: manifest.peerDependenciesMeta ?? {},
|
|
41
53
|
...(manifest.overrides === undefined
|
|
42
54
|
? {}
|
|
43
|
-
: { overrides: manifest.overrides }),
|
|
55
|
+
: { overrides: orderedOverridePolicy(manifest.overrides) }),
|
|
44
56
|
...(manifest.workspaces === undefined
|
|
45
57
|
? {}
|
|
46
58
|
: { workspaces: manifest.workspaces }),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ManifestDeclaration } from "../lockfile/fingerprints.ts";
|
|
2
|
+
export type OverrideContext = {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly name?: string;
|
|
5
|
+
readonly range?: string;
|
|
6
|
+
readonly value?: string;
|
|
7
|
+
readonly parent?: OverrideContext;
|
|
8
|
+
readonly children: readonly OverrideContext[];
|
|
9
|
+
};
|
|
10
|
+
export declare class OverridePolicy {
|
|
11
|
+
readonly root: OverrideContext;
|
|
12
|
+
constructor(manifest: ManifestDeclaration);
|
|
13
|
+
select(context: OverrideContext, name: string, spec: string): {
|
|
14
|
+
spec: string;
|
|
15
|
+
context: OverrideContext;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare function createRootOverrides(manifest: ManifestDeclaration): OverridePolicy;
|
|
19
|
+
//# sourceMappingURL=overrides.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../../src/resolve/overrides.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAGvE,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;CAC/C,CAAC;AAaF,qBAAa,cAAc;IACzB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,YAAY,QAAQ,EAAE,mBAAmB,EAsFxC;IACD,MAAM,CACJ,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,eAAe,CAAA;KAAE,CA6C5C;CACF;AACD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,mBAAmB,GAC5B,cAAc,CAEhB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/** Root npm override policy. Parsing never fetches metadata or executes code. */
|
|
2
|
+
import npa from "npm-package-arg";
|
|
3
|
+
import { intersects, validRange } from "semver";
|
|
4
|
+
import { ZyrkError } from "../errors.js";
|
|
5
|
+
import { parseRequirement } from "./spec.js";
|
|
6
|
+
import { packageName } from "../install/lock.js";
|
|
7
|
+
function invalid(reason) {
|
|
8
|
+
throw new ZyrkError("ZYRK_MANIFEST_INVALID", `Cannot apply root dependency overrides (${reason}).`, {
|
|
9
|
+
remedy: "Use root registry override rules, valid nested selectors and references to declared direct dependencies.",
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13
|
+
export class OverridePolicy {
|
|
14
|
+
root;
|
|
15
|
+
constructor(manifest) {
|
|
16
|
+
const raw = manifest.overrides;
|
|
17
|
+
if (raw !== undefined && !object(raw))
|
|
18
|
+
invalid("policy must be an object");
|
|
19
|
+
// npm's $reference lookup checks dev before optional, production and peer.
|
|
20
|
+
const references = {
|
|
21
|
+
...manifest.peerDependencies,
|
|
22
|
+
...manifest.dependencies,
|
|
23
|
+
...manifest.optionalDependencies,
|
|
24
|
+
...manifest.devDependencies,
|
|
25
|
+
};
|
|
26
|
+
let count = 0;
|
|
27
|
+
const build = (value, key, parent, depth = 0) => {
|
|
28
|
+
if (++count > 10000 || depth > 128)
|
|
29
|
+
invalid("policy resource limit exceeded");
|
|
30
|
+
if (typeof value === "string")
|
|
31
|
+
value = { ".": value };
|
|
32
|
+
if (!object(value))
|
|
33
|
+
invalid("each rule must be a string or object");
|
|
34
|
+
let name, range, replacement;
|
|
35
|
+
if (key !== undefined) {
|
|
36
|
+
try {
|
|
37
|
+
const spec = npa(key);
|
|
38
|
+
name = spec.name;
|
|
39
|
+
range = spec.rawSpec || "*";
|
|
40
|
+
if (!name ||
|
|
41
|
+
!packageName(name) ||
|
|
42
|
+
!["version", "range"].includes(spec.type) ||
|
|
43
|
+
validRange(range) === null)
|
|
44
|
+
invalid("selector must be a package name and optional semver range");
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
invalid("invalid registry override selector");
|
|
48
|
+
}
|
|
49
|
+
const self = value["."];
|
|
50
|
+
if (self !== undefined && typeof self !== "string")
|
|
51
|
+
invalid("self replacement must be a string");
|
|
52
|
+
replacement = self === undefined ? range : self || "*";
|
|
53
|
+
if (replacement.startsWith("$")) {
|
|
54
|
+
const reference = replacement.slice(1);
|
|
55
|
+
if (!packageName(reference) || !Object.hasOwn(references, reference))
|
|
56
|
+
invalid("unknown direct dependency reference");
|
|
57
|
+
replacement = references[reference];
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
parseRequirement(name, replacement);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
invalid("replacement must use a supported registry source");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else if (Object.hasOwn(value, "."))
|
|
67
|
+
invalid("root cannot contain a self replacement");
|
|
68
|
+
const children = [];
|
|
69
|
+
const context = {
|
|
70
|
+
id: parent ? `${parent.id}/${key}` : "",
|
|
71
|
+
...(name ? { name } : {}),
|
|
72
|
+
...(range ? { range } : {}),
|
|
73
|
+
...(replacement !== undefined ? { value: replacement } : {}),
|
|
74
|
+
...(parent ? { parent } : {}),
|
|
75
|
+
children,
|
|
76
|
+
};
|
|
77
|
+
for (const [child, spec] of Object.entries(value))
|
|
78
|
+
if (child !== ".")
|
|
79
|
+
children.push(build(spec, child, context, depth + 1));
|
|
80
|
+
return context;
|
|
81
|
+
};
|
|
82
|
+
this.root = build(raw ?? {}, undefined);
|
|
83
|
+
// Legality applies to the edge selected by zyrk's install groups, which
|
|
84
|
+
// prioritize optional > production > development, independently of refs.
|
|
85
|
+
const direct = {
|
|
86
|
+
...manifest.peerDependencies,
|
|
87
|
+
...manifest.devDependencies,
|
|
88
|
+
...manifest.dependencies,
|
|
89
|
+
...manifest.optionalDependencies,
|
|
90
|
+
};
|
|
91
|
+
for (const [name, spec] of Object.entries(direct)) {
|
|
92
|
+
const selected = this.select(this.root, name, spec);
|
|
93
|
+
if (selected.spec !== spec)
|
|
94
|
+
invalid("override conflicts with an exact direct dependency spec");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
select(context, name, spec) {
|
|
98
|
+
const seen = new Set();
|
|
99
|
+
for (let ancestor = context; ancestor; ancestor = ancestor.parent) {
|
|
100
|
+
const rules = [
|
|
101
|
+
...ancestor.children,
|
|
102
|
+
...(ancestor.parent ? [ancestor] : []),
|
|
103
|
+
];
|
|
104
|
+
for (const rule of rules) {
|
|
105
|
+
const key = `${rule.name}@${rule.range}`;
|
|
106
|
+
if (seen.has(key))
|
|
107
|
+
continue;
|
|
108
|
+
seen.add(key);
|
|
109
|
+
if (rule.name !== name)
|
|
110
|
+
continue;
|
|
111
|
+
let matches = rule.range === "*";
|
|
112
|
+
if (!matches) {
|
|
113
|
+
try {
|
|
114
|
+
const workspaceRange = spec.startsWith("workspace:")
|
|
115
|
+
? spec.slice(10)
|
|
116
|
+
: undefined;
|
|
117
|
+
const requested = parseRequirement(name, workspaceRange === undefined
|
|
118
|
+
? spec
|
|
119
|
+
: ["*", "^", "~"].includes(workspaceRange)
|
|
120
|
+
? "*"
|
|
121
|
+
: workspaceRange);
|
|
122
|
+
matches =
|
|
123
|
+
requested.targetKind === "tag" ||
|
|
124
|
+
intersects(requested.targetSpec, rule.range);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
matches = spec.startsWith("file:") || spec.startsWith("link:");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (matches)
|
|
131
|
+
return {
|
|
132
|
+
spec: rule.value === "*" ? spec : rule.value,
|
|
133
|
+
context: rule,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { spec, context };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export function createRootOverrides(manifest) {
|
|
141
|
+
return new OverridePolicy(manifest);
|
|
142
|
+
}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* bindings stay distinct. Peerless occurrences share by name@version.
|
|
21
21
|
*/
|
|
22
22
|
import { type Packument } from "../fetch/registry.ts";
|
|
23
|
+
import { type OverridePolicy } from "./overrides.ts";
|
|
23
24
|
export type MetadataProvider = {
|
|
24
25
|
readonly origin: string;
|
|
25
26
|
originFor?(name: string): string;
|
|
@@ -66,6 +67,7 @@ export type ResolveOptions = {
|
|
|
66
67
|
spec: string;
|
|
67
68
|
optional?: boolean;
|
|
68
69
|
}>;
|
|
70
|
+
overrides?: OverridePolicy;
|
|
69
71
|
limits?: Partial<ResolverLimits>;
|
|
70
72
|
platform?: RuntimePlatform;
|
|
71
73
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/resolve/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,EAEL,KAAK,SAAS,EAEf,MAAM,sBAAsB,CAAC;
|
|
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
|
@@ -24,6 +24,7 @@ import { ZyrkError } from "../errors.js";
|
|
|
24
24
|
import { expectedIntegrityFromDist, } from "../fetch/registry.js";
|
|
25
25
|
import { sha512Hex, sourceIdOf } from "../util/hash.js";
|
|
26
26
|
import { canonicalJson } from "../util/json.js";
|
|
27
|
+
import { createRootOverrides, } from "./overrides.js";
|
|
27
28
|
import { parseRequirement } from "./spec.js";
|
|
28
29
|
export function currentPlatform() {
|
|
29
30
|
const result = { os: process.platform, cpu: process.arch };
|
|
@@ -38,7 +39,11 @@ export function currentPlatform() {
|
|
|
38
39
|
}
|
|
39
40
|
export async function resolveRequirements(options) {
|
|
40
41
|
const state = new ResolverState(options);
|
|
41
|
-
const rootFrame = {
|
|
42
|
+
const rootFrame = {
|
|
43
|
+
label: "<root>",
|
|
44
|
+
children: [],
|
|
45
|
+
overrideContext: state.overrides.root,
|
|
46
|
+
};
|
|
42
47
|
const seenRoots = new Set();
|
|
43
48
|
for (const requirement of [...options.requirements].sort((a, b) => compareStrings(a.name, b.name))) {
|
|
44
49
|
if (seenRoots.has(requirement.name))
|
|
@@ -62,6 +67,7 @@ export async function resolveRequirements(options) {
|
|
|
62
67
|
}
|
|
63
68
|
class ResolverState {
|
|
64
69
|
provider;
|
|
70
|
+
overrides;
|
|
65
71
|
packuments = new Map();
|
|
66
72
|
peerless = new Map();
|
|
67
73
|
peerBearing = new Map();
|
|
@@ -74,6 +80,7 @@ class ResolverState {
|
|
|
74
80
|
peerExpansions = 0;
|
|
75
81
|
constructor(options) {
|
|
76
82
|
this.provider = options.provider;
|
|
83
|
+
this.overrides = options.overrides ?? createRootOverrides({});
|
|
77
84
|
this.limits = {
|
|
78
85
|
maxNodes: 10000,
|
|
79
86
|
maxDepth: 128,
|
|
@@ -153,7 +160,11 @@ class ResolverState {
|
|
|
153
160
|
}
|
|
154
161
|
async buildRequiredChild(parentFrame, chain, edgeName, rawSpec, kind) {
|
|
155
162
|
this.checkBudget();
|
|
156
|
-
const
|
|
163
|
+
const override = this.overrides.select(parentFrame.overrideContext, edgeName, rawSpec);
|
|
164
|
+
// Root override legality was checked against the declared manifest.
|
|
165
|
+
// A caller may pass an existing exact root pin inside that declaration;
|
|
166
|
+
// applying the identical declared range again would discard the pin.
|
|
167
|
+
const req = parseRequirement(edgeName, parentFrame.label === "<root>" ? rawSpec : override.spec);
|
|
157
168
|
const { version, entry } = await selectVersion(this, req);
|
|
158
169
|
this.checkPlatform(req.realName, entry);
|
|
159
170
|
const integrity = expectedIntegrityFromDist(entry.dist);
|
|
@@ -164,9 +175,10 @@ class ResolverState {
|
|
|
164
175
|
});
|
|
165
176
|
}
|
|
166
177
|
const baseKey = `${req.realName}@${version}`;
|
|
178
|
+
const memoKey = `${baseKey}\0${override.context.id}`;
|
|
167
179
|
const hasPeers = peerSpecsOf(entry).length > 0;
|
|
168
180
|
if (!hasPeers) {
|
|
169
|
-
const existing = this.peerless.get(
|
|
181
|
+
const existing = this.peerless.get(memoKey);
|
|
170
182
|
if (existing !== undefined)
|
|
171
183
|
return existing;
|
|
172
184
|
}
|
|
@@ -177,28 +189,37 @@ class ResolverState {
|
|
|
177
189
|
this.peerBearing.set(parentFrame, perParent);
|
|
178
190
|
}
|
|
179
191
|
else {
|
|
180
|
-
const existing = perParent.get(
|
|
192
|
+
const existing = perParent.get(memoKey);
|
|
181
193
|
if (existing !== undefined)
|
|
182
194
|
return existing;
|
|
183
195
|
}
|
|
184
196
|
}
|
|
185
197
|
this.checkBudget(chain.length, true);
|
|
186
198
|
const occ = {
|
|
199
|
+
overrideContext: override.context,
|
|
187
200
|
name: req.realName,
|
|
188
201
|
version,
|
|
189
202
|
entry,
|
|
190
203
|
integrity,
|
|
191
204
|
children: [],
|
|
192
|
-
peerSpecs: peerSpecsOf(entry)
|
|
205
|
+
peerSpecs: peerSpecsOf(entry).map((peer) => ({
|
|
206
|
+
...peer,
|
|
207
|
+
range: this.overrides.select(override.context, peer.name, peer.range)
|
|
208
|
+
.spec,
|
|
209
|
+
})),
|
|
193
210
|
peerBindings: new Map(),
|
|
194
211
|
chain: hasPeers ? [...chain, parentFrame] : [],
|
|
195
212
|
};
|
|
196
213
|
if (!hasPeers)
|
|
197
|
-
this.peerless.set(
|
|
214
|
+
this.peerless.set(memoKey, occ);
|
|
198
215
|
else
|
|
199
|
-
this.peerBearing.get(parentFrame)?.set(
|
|
216
|
+
this.peerBearing.get(parentFrame)?.set(memoKey, occ);
|
|
200
217
|
this.occurrences.push(occ);
|
|
201
|
-
const selfFrame = {
|
|
218
|
+
const selfFrame = {
|
|
219
|
+
label: baseKey,
|
|
220
|
+
children: occ.children,
|
|
221
|
+
overrideContext: override.context,
|
|
222
|
+
};
|
|
202
223
|
const childChain = [...chain, parentFrame];
|
|
203
224
|
for (const dep of regularDepsOf(entry)) {
|
|
204
225
|
const child = await this.buildChild(selfFrame, childChain, dep.name, dep.spec, dep.kind);
|
|
@@ -294,7 +315,8 @@ class ResolverState {
|
|
|
294
315
|
const existing = perOcc.get(spec.name);
|
|
295
316
|
if (existing !== undefined)
|
|
296
317
|
return existing;
|
|
297
|
-
const
|
|
318
|
+
const override = this.overrides.select(occ.overrideContext, spec.name, spec.range);
|
|
319
|
+
const req = parseRequirement(spec.name, override.spec);
|
|
298
320
|
const { version, entry } = await selectVersion(this, {
|
|
299
321
|
realName: req.realName,
|
|
300
322
|
targetSpec: req.targetSpec,
|
|
@@ -310,12 +332,17 @@ class ResolverState {
|
|
|
310
332
|
this.checkPlatform(req.realName, entry);
|
|
311
333
|
this.checkBudget(occ.chain.length + 1, true);
|
|
312
334
|
const fresh = {
|
|
335
|
+
overrideContext: override.context,
|
|
313
336
|
name: req.realName,
|
|
314
337
|
version,
|
|
315
338
|
entry,
|
|
316
339
|
integrity,
|
|
317
340
|
children: [],
|
|
318
|
-
peerSpecs: peerSpecsOf(entry)
|
|
341
|
+
peerSpecs: peerSpecsOf(entry).map((peer) => ({
|
|
342
|
+
...peer,
|
|
343
|
+
range: this.overrides.select(override.context, peer.name, peer.range)
|
|
344
|
+
.spec,
|
|
345
|
+
})),
|
|
319
346
|
peerBindings: new Map(),
|
|
320
347
|
chain: [...occ.chain, this.frameOf(occ)],
|
|
321
348
|
};
|
|
@@ -340,7 +367,11 @@ class ResolverState {
|
|
|
340
367
|
frameOf(occ) {
|
|
341
368
|
let frame = this.frames.get(occ);
|
|
342
369
|
if (frame === undefined) {
|
|
343
|
-
frame = {
|
|
370
|
+
frame = {
|
|
371
|
+
label: `${occ.name}@${occ.version}`,
|
|
372
|
+
children: occ.children,
|
|
373
|
+
overrideContext: occ.overrideContext,
|
|
374
|
+
};
|
|
344
375
|
this.frames.set(occ, frame);
|
|
345
376
|
}
|
|
346
377
|
return frame;
|
|
@@ -458,7 +489,10 @@ function lookupPeer(occ, peerName, range) {
|
|
|
458
489
|
const matches = level.children.filter((link) => link.edgeName === peerName);
|
|
459
490
|
if (matches.length === 0)
|
|
460
491
|
continue;
|
|
461
|
-
const
|
|
492
|
+
const requested = parseRequirement(peerName, range);
|
|
493
|
+
const satisfying = matches.filter((link) => link.child.name === requested.realName &&
|
|
494
|
+
(requested.targetKind === "tag" ||
|
|
495
|
+
satisfies(link.child.version, requested.targetSpec)));
|
|
462
496
|
const first = satisfying[0];
|
|
463
497
|
if (first !== undefined)
|
|
464
498
|
return { status: "bound", target: first.child };
|
|
@@ -490,10 +524,10 @@ function peerConflict(occ, spec, found) {
|
|
|
490
524
|
/** Canonical fingerprint over the transitive peer closure (SPEC 9.4). */
|
|
491
525
|
function occurrenceKey(occ, check) {
|
|
492
526
|
const base = `${occ.name}@${occ.version}`;
|
|
493
|
-
if (occ.peerSpecs.length === 0)
|
|
527
|
+
if (occ.peerSpecs.length === 0 && !occ.overrideContext.id)
|
|
494
528
|
return base;
|
|
495
529
|
const expansion = expandPeerBindings(occ, new Set([occ]), check);
|
|
496
|
-
return `${base}#${sha512Hex(canonicalJson(expansion)).slice(0, 12)}`;
|
|
530
|
+
return `${base}#${sha512Hex(canonicalJson(occ.overrideContext.id ? [occ.overrideContext.id, expansion] : expansion)).slice(0, 12)}`;
|
|
497
531
|
}
|
|
498
532
|
function expandPeerBindings(occ, visiting, check) {
|
|
499
533
|
check(visiting.size);
|
|
@@ -507,14 +541,24 @@ function expandPeerBindings(occ, visiting, check) {
|
|
|
507
541
|
parts.push([spec.name, "absent"]);
|
|
508
542
|
}
|
|
509
543
|
else if (visiting.has(bound)) {
|
|
510
|
-
parts.push([
|
|
544
|
+
parts.push([
|
|
545
|
+
spec.name,
|
|
546
|
+
[
|
|
547
|
+
"ref",
|
|
548
|
+
bound.overrideContext.id
|
|
549
|
+
? [`${bound.name}@${bound.version}`, bound.overrideContext.id]
|
|
550
|
+
: `${bound.name}@${bound.version}`,
|
|
551
|
+
],
|
|
552
|
+
]);
|
|
511
553
|
}
|
|
512
554
|
else {
|
|
513
555
|
parts.push([
|
|
514
556
|
spec.name,
|
|
515
557
|
[
|
|
516
558
|
"node",
|
|
517
|
-
|
|
559
|
+
bound.overrideContext.id
|
|
560
|
+
? [`${bound.name}@${bound.version}`, bound.overrideContext.id]
|
|
561
|
+
: `${bound.name}@${bound.version}`,
|
|
518
562
|
expandPeerBindings(bound, new Set([...visiting, bound]), check),
|
|
519
563
|
],
|
|
520
564
|
]);
|