@stll/oxlint-config 0.12.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +261 -1
  2. package/dist/cloud-setup-cli.cjs +6 -4
  3. package/dist/cloud-setup-cli.cjs.map +1 -1
  4. package/dist/cloud-setup-cli.mjs +3 -1
  5. package/dist/cloud-setup-cli.mjs.map +1 -1
  6. package/dist/compiler-packages.cjs +28 -0
  7. package/dist/compiler-packages.cjs.map +1 -0
  8. package/dist/compiler-packages.mjs +17 -0
  9. package/dist/compiler-packages.mjs.map +1 -0
  10. package/dist/consumer-compat-cli.cjs +1091 -0
  11. package/dist/consumer-compat-cli.cjs.map +1 -0
  12. package/dist/consumer-compat-cli.d.cts +1 -0
  13. package/dist/consumer-compat-cli.d.mts +1 -0
  14. package/dist/consumer-compat-cli.mjs +1089 -0
  15. package/dist/consumer-compat-cli.mjs.map +1 -0
  16. package/dist/index.cjs +50 -40
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +46 -41
  19. package/dist/index.d.mts +46 -41
  20. package/dist/index.mjs +50 -41
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/publish-build-target-nuxt-helper.cjs +71 -0
  23. package/dist/publish-build-target-nuxt-helper.cjs.map +1 -0
  24. package/dist/publish-build-target-nuxt-helper.d.cts +10 -0
  25. package/dist/publish-build-target-nuxt-helper.d.mts +10 -0
  26. package/dist/publish-build-target-nuxt-helper.mjs +68 -0
  27. package/dist/publish-build-target-nuxt-helper.mjs.map +1 -0
  28. package/dist/publish-build-target-vite-dts-helper.cjs +3 -0
  29. package/dist/publish-build-target-vite-dts-helper.d.cts +24 -0
  30. package/dist/publish-build-target-vite-dts-helper.d.mts +24 -0
  31. package/dist/publish-build-target-vite-dts-helper.mjs +2 -0
  32. package/dist/publish-build-target-vite-dts-helper2.cjs +372 -0
  33. package/dist/publish-build-target-vite-dts-helper2.cjs.map +1 -0
  34. package/dist/publish-build-target-vite-dts-helper2.mjs +359 -0
  35. package/dist/publish-build-target-vite-dts-helper2.mjs.map +1 -0
  36. package/dist/publish-contract-cli.cjs +983 -0
  37. package/dist/publish-contract-cli.cjs.map +1 -0
  38. package/dist/publish-contract-cli.d.cts +1 -0
  39. package/dist/publish-contract-cli.d.mts +1 -0
  40. package/dist/publish-contract-cli.mjs +981 -0
  41. package/dist/publish-contract-cli.mjs.map +1 -0
  42. package/dist/publish-contract.cjs +431 -0
  43. package/dist/publish-contract.cjs.map +1 -0
  44. package/dist/publish-contract.mjs +396 -0
  45. package/dist/publish-contract.mjs.map +1 -0
  46. package/dist/rolldown-runtime.cjs +28 -0
  47. package/dist/toolchain-changed-cli.cjs +34 -0
  48. package/dist/toolchain-changed-cli.cjs.map +1 -0
  49. package/dist/toolchain-changed-cli.d.cts +1 -0
  50. package/dist/toolchain-changed-cli.d.mts +1 -0
  51. package/dist/toolchain-changed-cli.mjs +34 -0
  52. package/dist/toolchain-changed-cli.mjs.map +1 -0
  53. package/dist/toolchain-changed.cjs +1470 -0
  54. package/dist/toolchain-changed.cjs.map +1 -0
  55. package/dist/toolchain-changed.mjs +1463 -0
  56. package/dist/toolchain-changed.mjs.map +1 -0
  57. package/dist/toolchain-check-cli.cjs +2 -1
  58. package/dist/toolchain-check-cli.cjs.map +1 -1
  59. package/dist/toolchain-check-cli.mjs +2 -1
  60. package/dist/toolchain-check-cli.mjs.map +1 -1
  61. package/dist/toolchain-guard.cjs +360 -1161
  62. package/dist/toolchain-guard.cjs.map +1 -1
  63. package/dist/toolchain-guard.mjs +338 -1100
  64. package/dist/toolchain-guard.mjs.map +1 -1
  65. package/dist/toolchain-inputs.cjs +926 -0
  66. package/dist/toolchain-inputs.cjs.map +1 -0
  67. package/dist/toolchain-inputs.mjs +816 -0
  68. package/dist/toolchain-inputs.mjs.map +1 -0
  69. package/dist/toolchain-schema.cjs +702 -0
  70. package/dist/toolchain-schema.cjs.map +1 -0
  71. package/dist/toolchain-schema.mjs +580 -0
  72. package/dist/toolchain-schema.mjs.map +1 -0
  73. package/dist/typecheck-parity-cli.cjs +38 -898
  74. package/dist/typecheck-parity-cli.cjs.map +1 -1
  75. package/dist/typecheck-parity-cli.mjs +39 -899
  76. package/dist/typecheck-parity-cli.mjs.map +1 -1
  77. package/dist/typecheck-parity.cjs +905 -0
  78. package/dist/typecheck-parity.cjs.map +1 -0
  79. package/dist/typecheck-parity.mjs +894 -0
  80. package/dist/typecheck-parity.mjs.map +1 -0
  81. package/dist/typecheck-probe-cli.cjs +176 -0
  82. package/dist/typecheck-probe-cli.cjs.map +1 -0
  83. package/dist/typecheck-probe-cli.d.cts +1 -0
  84. package/dist/typecheck-probe-cli.d.mts +1 -0
  85. package/dist/typecheck-probe-cli.mjs +174 -0
  86. package/dist/typecheck-probe-cli.mjs.map +1 -0
  87. package/package.json +43 -5
  88. package/toolchain.json +9 -1
@@ -0,0 +1,1470 @@
1
+ const require_rolldown_runtime = require("./rolldown-runtime.cjs");
2
+ const require_toolchain_inputs = require("./toolchain-inputs.cjs");
3
+ const require_compiler_packages = require("./compiler-packages.cjs");
4
+ let node_child_process = require("node:child_process");
5
+ let node_path = require("node:path");
6
+ node_path = require_rolldown_runtime.__toESM(node_path, 1);
7
+ let semver = require("semver");
8
+ let yaml = require("yaml");
9
+ let node_util = require("node:util");
10
+ let smol_toml = require("smol-toml");
11
+ //#region src/toolchain-changed-locks.ts
12
+ const toolchainChangedTools = [
13
+ "bun",
14
+ "node",
15
+ "typescript",
16
+ "oxlint",
17
+ "oxlint-tsgolint",
18
+ "oxfmt",
19
+ "shared"
20
+ ];
21
+ const changedRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
22
+ const changedPackageTool = (name) => {
23
+ if (require_compiler_packages.isCompilerPackage(name)) return "typescript";
24
+ if (name === "oxlint" || name === "oxfmt" || name === "oxlint-tsgolint") return name;
25
+ if (name.startsWith("@stll/") || name === "@oxlint/plugins" || name === "lefthook") return "shared";
26
+ };
27
+ /** Compiler patch declarations retain hashes as well as referenced source paths. */
28
+ const changedCompilerPatches = ({ value, stringsArePaths, compilerNames }) => {
29
+ const patches = [];
30
+ if (!changedRecord(value)) return patches;
31
+ const declarations = value["patchedDependencies"];
32
+ if (declarations === void 0) return patches;
33
+ if (!changedRecord(declarations)) throw new Error("Unclassifiable patchedDependencies declaration");
34
+ for (const [descriptor, metadata] of Object.entries(declarations)) {
35
+ const name = /^(@[^/]+\/[^@]+|[^@]+)(?:@.*)?$/.exec(descriptor)?.[1];
36
+ if (!name || changedPackageTool(name) !== "typescript" && !compilerNames.has(name)) continue;
37
+ let reference;
38
+ if (typeof metadata === "string") {
39
+ if (metadata.trim() === "") throw new Error(`Empty compiler patch metadata: ${descriptor}`);
40
+ if (stringsArePaths) reference = metadata;
41
+ } else if (changedRecord(metadata)) {
42
+ const declaredPath = metadata["path"];
43
+ if (declaredPath !== void 0) {
44
+ if (typeof declaredPath !== "string") throw new Error(`Invalid compiler patch path: ${descriptor}`);
45
+ reference = declaredPath;
46
+ }
47
+ } else throw new Error(`Invalid compiler patch metadata: ${descriptor}`);
48
+ if (stringsArePaths && reference === void 0) throw new Error(`Missing compiler patch path: ${descriptor}`);
49
+ if (reference === void 0) patches.push({
50
+ name: descriptor,
51
+ metadata
52
+ });
53
+ else patches.push({
54
+ name: descriptor,
55
+ metadata,
56
+ reference
57
+ });
58
+ }
59
+ return patches;
60
+ };
61
+ /** JSONC permits comments and trailing commas, never alterations inside strings. */
62
+ const parseChangedJson = (text) => {
63
+ let clean = "";
64
+ let quote = false;
65
+ for (let index = 0; index < text.length; index++) {
66
+ const char = text[index];
67
+ if (quote) {
68
+ clean += char;
69
+ if (char === "\\") {
70
+ index++;
71
+ clean += text[index] ?? "";
72
+ } else if (char === "\"") quote = false;
73
+ continue;
74
+ }
75
+ if (char === "\"") {
76
+ quote = true;
77
+ clean += char;
78
+ continue;
79
+ }
80
+ if (char === "/" && text[index + 1] === "/") {
81
+ while (index < text.length && text[index] !== "\n") index++;
82
+ clean += "\n";
83
+ continue;
84
+ }
85
+ if (char === "/" && text[index + 1] === "*") {
86
+ index += 2;
87
+ while (index < text.length && !(text[index] === "*" && text[index + 1] === "/")) index++;
88
+ if (index >= text.length) throw new Error("Unterminated JSONC comment");
89
+ index++;
90
+ clean += " ";
91
+ continue;
92
+ }
93
+ clean += char;
94
+ }
95
+ let result = "";
96
+ quote = false;
97
+ for (let index = 0; index < clean.length; index++) {
98
+ const char = clean[index];
99
+ if (quote) {
100
+ result += char;
101
+ if (char === "\\") {
102
+ index++;
103
+ result += clean[index] ?? "";
104
+ } else if (char === "\"") quote = false;
105
+ continue;
106
+ }
107
+ if (char === "\"") quote = true;
108
+ if (char === ",") {
109
+ let next = index + 1;
110
+ while (/\s/.test(clean[next] ?? "") && next < clean.length) next++;
111
+ if (clean[next] === "}" || clean[next] === "]") continue;
112
+ }
113
+ result += char;
114
+ }
115
+ return JSON.parse(result);
116
+ };
117
+ const changedYarnSelectors = (location) => location.replace(/"/g, "").split(/,\s*/).map((selector) => selector.trim());
118
+ const packageDescriptor = (descriptor) => {
119
+ const match = /^(@[^/]+\/[^@]+|[^@]+)@(.+)$/.exec(descriptor.replace(/^\//, ""));
120
+ const name = match?.[1];
121
+ const version = match?.[2];
122
+ if (!name || !version) return void 0;
123
+ return {
124
+ name,
125
+ version
126
+ };
127
+ };
128
+ const dependencyName = (location) => {
129
+ const parts = location.split("/");
130
+ const last = parts.at(-1);
131
+ const scope = parts.at(-2);
132
+ return scope?.startsWith("@") ? `${scope}/${last}` : last ?? location;
133
+ };
134
+ const exactVersion = (value) => {
135
+ const version = value.split("(")[0];
136
+ if (version && (0, semver.valid)(version) === version) return version;
137
+ throw new Error(`Unresolved toolchain lock version: ${value}`);
138
+ };
139
+ const stableIdentity = (value) => {
140
+ if (Array.isArray(value)) return JSON.stringify(value.map(stableIdentity));
141
+ if (changedRecord(value)) return JSON.stringify(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => [key, stableIdentity(entry)]));
142
+ if (value === void 0) return "undefined";
143
+ return JSON.stringify(value);
144
+ };
145
+ const validIntegrity = (value) => value.split(/\s+/).some((item) => {
146
+ const match = /^(sha1|sha256|sha384|sha512)-([A-Za-z0-9+/]+={0,2})$/.exec(item);
147
+ const algorithm = match?.[1];
148
+ const encoded = match?.[2];
149
+ if (!algorithm || !encoded) return false;
150
+ const bytes = Buffer.from(encoded, "base64");
151
+ if (bytes.toString("base64") !== encoded) return false;
152
+ switch (algorithm) {
153
+ case "sha1": return bytes.length === 20;
154
+ case "sha256": return bytes.length === 32;
155
+ case "sha384": return bytes.length === 48;
156
+ case "sha512": return bytes.length === 64;
157
+ default: return false;
158
+ }
159
+ });
160
+ const lockSourceProof = (raw) => {
161
+ const sources = [];
162
+ const evidence = [];
163
+ const commits = [];
164
+ const source = (value) => {
165
+ if (typeof value !== "string") return;
166
+ sources.push(value);
167
+ const commit = /#(?:commit=)?([a-f0-9]{40}|[a-f0-9]{64})(?:[&#]|$)/i.exec(value)?.[1];
168
+ if (commit) commits.push(`commit:${commit.toLowerCase()}`);
169
+ };
170
+ const digest = (key, value) => {
171
+ if (typeof value !== "string") return;
172
+ if (key === "integrity" && validIntegrity(value) || key === "checksum" && /^(?:[a-f0-9]+\/)?[a-f0-9]{128}$/i.test(value) || key === "shasum" && /^[a-f0-9]{40}$/i.test(value)) evidence.push(`${key}:${value}`);
173
+ if (key === "commit" && /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/i.test(value)) commits.push(`commit:${value.toLowerCase()}`);
174
+ };
175
+ const metadata = (value) => {
176
+ if (!changedRecord(value)) return;
177
+ for (const key of [
178
+ "resolved",
179
+ "resolution",
180
+ "tarball",
181
+ "repo",
182
+ "version"
183
+ ]) source(value[key]);
184
+ for (const key of [
185
+ "integrity",
186
+ "checksum",
187
+ "shasum",
188
+ "commit"
189
+ ]) digest(key, value[key]);
190
+ if (changedRecord(value["resolution"])) {
191
+ const resolution = value["resolution"];
192
+ for (const key of [
193
+ "tarball",
194
+ "repo",
195
+ "directory"
196
+ ]) source(resolution[key]);
197
+ for (const key of ["integrity", "commit"]) digest(key, resolution[key]);
198
+ if (resolution["type"] === "directory" || typeof resolution["directory"] === "string") sources.push("file:");
199
+ if (resolution["type"] === "git") sources.push("git:");
200
+ }
201
+ if (value["link"] === true) sources.push("link:");
202
+ };
203
+ if (Array.isArray(raw)) {
204
+ source(raw[0]);
205
+ source(raw[1]);
206
+ digest("integrity", raw[3]);
207
+ } else if (changedRecord(raw)) if (changedRecord(raw["metadata"])) {
208
+ metadata(raw["metadata"]);
209
+ const descriptor = raw["descriptor"];
210
+ source(typeof descriptor === "string" ? descriptor.replace(/^\//, "") : descriptor);
211
+ } else metadata(raw);
212
+ if (sources.some((value) => {
213
+ if (/(?:^|@)(?:link:|portal:|workspace:)/i.test(value)) return true;
214
+ const local = /(?:^|@)file:(.*)$/i.exec(value)?.[1];
215
+ if (local !== void 0) return !/\.(?:tgz|tar\.gz)(?:[?#].*)?$/i.test(local);
216
+ return /^(?:\.{1,2}\/|\/)/.test(value) && !/\.(?:tgz|tar\.gz)(?:[?#].*)?$/i.test(value);
217
+ })) return { type: "unidentified" };
218
+ if (sources.some((value) => /(?:^|@)(?:git(?:\+[^:]+)?:|github:|gitlab:|bitbucket:|ssh:)|(?:github\.com|gitlab\.com)[:/]|(?:^|@)git@|^[\w.-]+@(?!(?:npm|patch|file|link|portal|workspace|https?|git(?:\+[^:]+)?|github|gitlab|bitbucket|ssh):)[^/:]+:|\.git(?:#|$)/i.test(value))) {
219
+ const proof = commits;
220
+ return proof.length > 0 ? {
221
+ type: "immutable",
222
+ identity: stableIdentity(proof.sort())
223
+ } : { type: "unidentified" };
224
+ }
225
+ if (sources.some((value) => /(?:^|@)file:/i.test(value) || /(?:^|@)https?:/i.test(value) && !/(?:^|@)https:\/\/(?:registry\.npmjs\.org|registry\.yarnpkg\.com)\//i.test(value)) && evidence.length === 0) return { type: "unidentified" };
226
+ if (evidence.length > 0) return {
227
+ type: "immutable",
228
+ identity: stableIdentity(evidence.sort())
229
+ };
230
+ return { type: "registry" };
231
+ };
232
+ const addResolution = ({ raw, resolutions, name, version, dependency, location }) => {
233
+ const tool = changedPackageTool(name) ?? changedPackageTool(dependency);
234
+ if (tool === void 0) return;
235
+ const sourceProof = lockSourceProof(raw);
236
+ if (tool === "typescript" && sourceProof.type === "unidentified") throw new Error(`Compiler source has no immutable identity: ${name}`);
237
+ const identity = stableIdentity(raw);
238
+ if (version.startsWith("workspace:") || version.startsWith("link:")) {
239
+ if (tool !== "shared") throw new Error(`Unresolved compiler workspace lock version: ${name}`);
240
+ resolutions.push({
241
+ tool,
242
+ name,
243
+ version,
244
+ dependency,
245
+ location,
246
+ identity,
247
+ sourceProof
248
+ });
249
+ return;
250
+ }
251
+ resolutions.push({
252
+ tool,
253
+ name,
254
+ version: exactVersion(version),
255
+ identity,
256
+ sourceProof,
257
+ dependency,
258
+ location
259
+ });
260
+ };
261
+ const dependencyFields = [
262
+ "dependencies",
263
+ "devDependencies",
264
+ "optionalDependencies"
265
+ ];
266
+ const importerDependencies = (value) => {
267
+ if (!changedRecord(value)) throw new Error("Invalid lockfile importer");
268
+ const result = {};
269
+ for (const field of dependencyFields) {
270
+ const dependencies = value[field];
271
+ if (dependencies === void 0) continue;
272
+ if (!changedRecord(dependencies)) throw new Error("Invalid lockfile dependency table");
273
+ for (const [name, metadata] of Object.entries(dependencies)) if (typeof metadata === "string") result[name] = metadata;
274
+ else if (changedRecord(metadata) && typeof metadata["version"] === "string") result[name] = metadata["version"];
275
+ else if (changedPackageTool(name) !== void 0) throw new Error(`Unresolved lockfile importer dependency: ${name}`);
276
+ }
277
+ return result;
278
+ };
279
+ const parseChangedLock = ({ file, text }) => {
280
+ const resolutions = [];
281
+ const importers = {};
282
+ if (file.endsWith("bun.lock")) {
283
+ const parsed = parseChangedJson(text);
284
+ if (!changedRecord(parsed) || ![0, 1].includes(Number(parsed["lockfileVersion"])) || !changedRecord(parsed["packages"]) || !changedRecord(parsed["workspaces"])) throw new Error("Unsupported Bun lockfile shape");
285
+ for (const [directory, workspace] of Object.entries(parsed["workspaces"])) importers[directory || "."] = importerDependencies(workspace);
286
+ for (const [location, value] of Object.entries(parsed["packages"])) {
287
+ const dependency = dependencyName(location);
288
+ const first = Array.isArray(value) ? value.at(0) : void 0;
289
+ const descriptor = typeof first === "string" ? packageDescriptor(first) : void 0;
290
+ if (descriptor !== void 0) addResolution({
291
+ resolutions,
292
+ ...descriptor,
293
+ dependency,
294
+ location,
295
+ raw: value
296
+ });
297
+ else if (changedPackageTool(dependency) !== void 0) throw new Error(`Invalid Bun toolchain lock entry: ${location}`);
298
+ }
299
+ } else if (file.endsWith("pnpm-lock.yaml")) {
300
+ const document = (0, yaml.parseDocument)(text);
301
+ if (document.errors.length > 0) throw new Error("Invalid pnpm lockfile YAML");
302
+ const parsed = document.toJS({ maxAliasCount: 100 });
303
+ if (!changedRecord(parsed) || ![
304
+ 5.4,
305
+ 6,
306
+ 9
307
+ ].includes(Number(parsed["lockfileVersion"]))) throw new Error("Unsupported pnpm lockfile shape");
308
+ const sourceImporters = changedRecord(parsed["importers"]) ? parsed["importers"] : { ".": parsed };
309
+ for (const [directory, importer] of Object.entries(sourceImporters)) importers[directory] = importerDependencies(importer);
310
+ const packages = parsed["packages"] ?? {};
311
+ if (!changedRecord(packages)) throw new Error("Invalid pnpm lockfile packages");
312
+ for (const [location, metadata] of Object.entries(packages)) {
313
+ let descriptor = packageDescriptor(location);
314
+ if (descriptor === void 0) {
315
+ const legacy = /^(.*)\/([^/]+)$/.exec(location.replace(/^\//, ""));
316
+ if (legacy?.[1] && legacy[2]) descriptor = {
317
+ name: legacy[1],
318
+ version: legacy[2]
319
+ };
320
+ }
321
+ if (!descriptor) continue;
322
+ const version = changedRecord(metadata) && typeof metadata["version"] === "string" ? metadata["version"] : descriptor.version;
323
+ const baseDescriptor = location.split("(").at(0);
324
+ const snapshots = changedRecord(parsed["snapshots"]) ? Object.fromEntries(Object.entries(parsed["snapshots"]).filter(([key]) => key.split("(").at(0) === baseDescriptor)) : {};
325
+ addResolution({
326
+ resolutions,
327
+ name: descriptor.name,
328
+ raw: {
329
+ descriptor: location,
330
+ metadata,
331
+ snapshots
332
+ },
333
+ version,
334
+ dependency: descriptor.name,
335
+ location
336
+ });
337
+ }
338
+ } else if (file.endsWith("yarn.lock")) {
339
+ const classic = /^# yarn lockfile v1\s*$/m.test(text);
340
+ let yaml$1 = text;
341
+ if (classic) yaml$1 = text.split(/\r?\n/).map((line) => {
342
+ if (line === "" || /^\s*#/.test(line)) return line;
343
+ if (!/^\s/.test(line) && line.endsWith(":")) return `${JSON.stringify(line.slice(0, -1))}:`;
344
+ const field = /^(\s+)([A-Za-z][\w-]*|"(?:[^"\\]|\\.)*") (.+)$/.exec(line);
345
+ if (field) return `${field[1]}${field[2]}: ${field[3]}`;
346
+ return line;
347
+ }).join("\n");
348
+ const document = (0, yaml.parseDocument)(yaml$1);
349
+ if (document.errors.length > 0) throw new Error("Invalid Yarn lockfile");
350
+ const parsed = document.toJS({ maxAliasCount: 100 });
351
+ if (!changedRecord(parsed) || !classic && !changedRecord(parsed["__metadata"])) throw new Error("Unsupported Yarn lockfile shape");
352
+ for (const [location, metadata] of Object.entries(parsed)) {
353
+ if (location === "__metadata") continue;
354
+ const selectors = changedYarnSelectors(location);
355
+ const descriptors = selectors.map(packageDescriptor).filter((item) => item !== void 0);
356
+ for (const descriptor of descriptors) {
357
+ const alias = descriptor.version.startsWith("npm:") ? packageDescriptor(descriptor.version.slice(4)) : void 0;
358
+ const dependency = descriptor.name;
359
+ const resolution = changedRecord(metadata) && typeof metadata["resolution"] === "string" ? packageDescriptor(metadata["resolution"]) : void 0;
360
+ let name = (resolution?.version.startsWith("npm:") ? packageDescriptor(resolution.version.slice(4)) : void 0)?.name ?? resolution?.name ?? dependency;
361
+ if (alias && !require_compiler_packages.isCompilerPackage(name)) name = alias.name;
362
+ const patchDescriptor = descriptor.version.startsWith("patch:") ? packageDescriptor(descriptor.version.slice(6)) : void 0;
363
+ if ((require_compiler_packages.isCompilerPackage(name) || require_compiler_packages.isCompilerPackage(dependency) || patchDescriptor !== void 0 && require_compiler_packages.isCompilerPackage(patchDescriptor.name)) && (descriptor.version.startsWith("patch:") || resolution?.version.startsWith("patch:"))) throw new Error(`Yarn compiler patch identity requires tracked patch bytes: ${location}`);
364
+ if (changedPackageTool(name) === void 0 && changedPackageTool(dependency) === void 0) continue;
365
+ if (!changedRecord(metadata) || typeof metadata["version"] !== "string") throw new Error(`Missing Yarn toolchain version: ${location}`);
366
+ if (!classic && typeof metadata["resolution"] !== "string") throw new Error(`Missing Yarn compiler resolution: ${location}`);
367
+ addResolution({
368
+ resolutions,
369
+ name,
370
+ version: metadata["version"],
371
+ dependency,
372
+ location: selectors.join(", "),
373
+ raw: {
374
+ selectors,
375
+ metadata
376
+ }
377
+ });
378
+ }
379
+ }
380
+ } else {
381
+ const parsed = parseChangedJson(text);
382
+ if (!changedRecord(parsed) || ![
383
+ 1,
384
+ 2,
385
+ 3
386
+ ].includes(Number(parsed["lockfileVersion"]))) throw new Error("Unsupported npm lockfile shape");
387
+ if (changedRecord(parsed["packages"])) for (const [location, metadata] of Object.entries(parsed["packages"])) {
388
+ if (!changedRecord(metadata)) throw new Error("Invalid npm lockfile package");
389
+ if (!location.includes("node_modules/")) {
390
+ const ownName = metadata["name"];
391
+ if (typeof ownName === "string" && require_compiler_packages.isCompilerPackage(ownName)) throw new Error(`Compiler directory lock entry has no tracked byte identity: ${location || "."}`);
392
+ importers[location || "."] = importerDependencies(metadata);
393
+ continue;
394
+ }
395
+ const dependency = dependencyName(location);
396
+ const name = typeof metadata["name"] === "string" ? metadata["name"] : dependency;
397
+ if (metadata["link"] === true) {
398
+ addResolution({
399
+ resolutions,
400
+ name,
401
+ raw: metadata,
402
+ version: `link:${String(metadata["resolved"])}`,
403
+ dependency,
404
+ location
405
+ });
406
+ continue;
407
+ }
408
+ if (typeof metadata["version"] === "string") addResolution({
409
+ resolutions,
410
+ name,
411
+ raw: metadata,
412
+ version: metadata["version"],
413
+ dependency,
414
+ location
415
+ });
416
+ else if (changedPackageTool(name) !== void 0 || changedPackageTool(dependency) !== void 0) throw new Error(`Missing npm toolchain version: ${name}`);
417
+ }
418
+ else if (changedRecord(parsed["dependencies"])) {
419
+ const visit = ({ dependencies, parent }) => {
420
+ for (const [dependency, metadata] of Object.entries(dependencies)) {
421
+ if (!changedRecord(metadata)) throw new Error("Invalid npm lockfile dependency");
422
+ const location = `${parent}node_modules/${dependency}`;
423
+ const version = metadata["version"];
424
+ const alias = typeof version === "string" && version.startsWith("npm:") ? packageDescriptor(version.slice(4)) : void 0;
425
+ if (typeof version === "string") addResolution({
426
+ resolutions,
427
+ name: alias?.name ?? dependency,
428
+ raw: metadata,
429
+ version: alias?.version ?? version,
430
+ dependency,
431
+ location
432
+ });
433
+ else if (changedPackageTool(dependency) !== void 0) throw new Error(`Missing npm toolchain version: ${dependency}`);
434
+ if (changedRecord(metadata["dependencies"])) visit({
435
+ dependencies: metadata["dependencies"],
436
+ parent: `${location}/`
437
+ });
438
+ }
439
+ };
440
+ visit({
441
+ dependencies: parsed["dependencies"],
442
+ parent: ""
443
+ });
444
+ } else throw new Error("Missing npm lockfile dependency resolutions");
445
+ }
446
+ return {
447
+ resolutions,
448
+ importers
449
+ };
450
+ };
451
+ //#endregion
452
+ //#region src/toolchain-changed.ts
453
+ const git = (0, node_util.promisify)(node_child_process.execFile);
454
+ const otherRuntimeFiles = /* @__PURE__ */ new Set([
455
+ ".python-version",
456
+ "rust-toolchain",
457
+ "rust-toolchain.toml",
458
+ ".go-version",
459
+ "go.mod",
460
+ "go.work",
461
+ ".java-version",
462
+ ".ruby-version",
463
+ ".sdkmanrc",
464
+ ".dotnet-version",
465
+ "global.json",
466
+ ".terraform-version",
467
+ ".deno-version"
468
+ ]);
469
+ const containerDefinition = (file) => require_toolchain_inputs.isDockerDefinitionPath(file) || require_toolchain_inputs.isComposeDefinitionPath(file) || require_toolchain_inputs.isKubernetesDefinitionPath(file);
470
+ const compilerPatchPackageFile = (file, compilerNames) => {
471
+ if (!file.endsWith(".patch")) return false;
472
+ const target = node_path.default.posix.basename(file).split("++").at(-1);
473
+ if (target === void 0) return false;
474
+ const encoded = /^(@[^+]+\+[^+]+|[^+]+)\+/.exec(target)?.[1];
475
+ return encoded !== void 0 && (changedPackageTool(encoded.replace("+", "/")) === "typescript" || compilerNames.has(encoded.replace("+", "/")));
476
+ };
477
+ const trackedInput = (file) => {
478
+ if (file.split("/").some((part) => part === "node_modules" || part === "vendor")) return false;
479
+ const name = node_path.default.posix.basename(file);
480
+ return [
481
+ "package.json",
482
+ "bun.lock",
483
+ "bun.lockb",
484
+ "pnpm-lock.yaml",
485
+ "package-lock.json",
486
+ "npm-shrinkwrap.json",
487
+ "yarn.lock",
488
+ "pnpm-workspace.yaml",
489
+ ".node-version",
490
+ ".nvmrc",
491
+ ".bun-version",
492
+ ".tool-versions",
493
+ "toolchain.json",
494
+ ".npmrc"
495
+ ].includes(name) || file.endsWith(".patch") || otherRuntimeFiles.has(name) || require_toolchain_inputs.isMiseConfigPath(file) || require_toolchain_inputs.githubAutomationFileKind(file) !== void 0 || containerDefinition(file);
496
+ };
497
+ const readTree = async ({ repo, ref }) => {
498
+ const { stdout } = await git("git", [
499
+ "ls-tree",
500
+ "-rz",
501
+ "--full-tree",
502
+ ref
503
+ ], {
504
+ cwd: repo,
505
+ encoding: "utf8",
506
+ maxBuffer: 32 * 1024 * 1024
507
+ });
508
+ const entries = /* @__PURE__ */ new Map();
509
+ for (const entry of stdout.split("\0")) {
510
+ if (entry === "") continue;
511
+ const tab = entry.indexOf(" ");
512
+ if (tab < 0) throw new Error("Invalid Git tree response");
513
+ const file = entry.slice(tab + 1);
514
+ const [mode, type, oid] = entry.slice(0, tab).split(" ");
515
+ if (type !== "blob" && !trackedInput(file)) continue;
516
+ if (type !== "blob" || !mode || !oid || !/^[a-f0-9]{40,64}$/.test(oid)) throw new Error("Unreadable tracked toolchain input");
517
+ entries.set(file, {
518
+ mode,
519
+ oid
520
+ });
521
+ }
522
+ return entries;
523
+ };
524
+ const readBlobs = async ({ repo, oids }) => {
525
+ if (oids.length === 0) return /* @__PURE__ */ new Map();
526
+ const data = await new Promise((resolve, reject) => {
527
+ const child = (0, node_child_process.spawn)("git", ["cat-file", "--batch"], {
528
+ cwd: repo,
529
+ stdio: [
530
+ "pipe",
531
+ "pipe",
532
+ "pipe"
533
+ ]
534
+ });
535
+ const chunks = [];
536
+ let size = 0;
537
+ child.stdout.on("data", (chunk) => {
538
+ size += chunk.length;
539
+ if (size > 128 * 1024 * 1024) {
540
+ child.kill();
541
+ reject(/* @__PURE__ */ new Error("Toolchain Git snapshot exceeds the read limit"));
542
+ return;
543
+ }
544
+ chunks.push(chunk);
545
+ });
546
+ child.stderr.resume();
547
+ child.on("error", reject);
548
+ child.on("close", (code) => code === 0 ? resolve(Buffer.concat(chunks)) : reject(/* @__PURE__ */ new Error("Unable to read Git snapshot blobs")));
549
+ child.stdin.on("error", reject);
550
+ child.stdin.end(`${oids.join("\n")}\n`);
551
+ });
552
+ const blobs = /* @__PURE__ */ new Map();
553
+ let offset = 0;
554
+ for (const oid of oids) {
555
+ const end = data.indexOf(10, offset);
556
+ if (end < 0) throw new Error("Invalid Git blob response");
557
+ const [actual, type, length] = data.subarray(offset, end).toString("utf8").split(" ");
558
+ const size = Number(length);
559
+ if (actual !== oid || type !== "blob" || !Number.isSafeInteger(size) || size < 0 || end + size + 1 >= data.length) throw new Error("Unreadable Git blob response");
560
+ offset = end + 1;
561
+ blobs.set(oid, data.subarray(offset, offset + size).toString("utf8"));
562
+ offset += size + 1;
563
+ }
564
+ if (offset !== data.length) throw new Error("Unexpected Git blob response");
565
+ return blobs;
566
+ };
567
+ const snapshotText = ({ snapshot, file, visited = /* @__PURE__ */ new Set() }) => {
568
+ if (visited.has(file)) throw new Error("Cyclic tracked toolchain file reference");
569
+ visited.add(file);
570
+ const entry = snapshot.entries.get(file);
571
+ const text = entry && snapshot.blobs.get(entry.oid);
572
+ if (!entry || text === void 0) throw new Error(`Missing tracked toolchain input: ${file}`);
573
+ if (entry.mode !== "120000") return text;
574
+ const target = node_path.default.posix.normalize(node_path.default.posix.join(node_path.default.posix.dirname(file), text));
575
+ if (node_path.default.posix.isAbsolute(text) || target === ".." || target.startsWith("../")) throw new Error("Toolchain file reference leaves the tracked snapshot");
576
+ return snapshotText({
577
+ snapshot,
578
+ file: target,
579
+ visited
580
+ });
581
+ };
582
+ /** Read declared selector files too, without executing workflows or inspecting installed tools. */
583
+ const selectorFiles = (snapshot) => {
584
+ const files = /* @__PURE__ */ new Map();
585
+ const visit = (value) => {
586
+ if (Array.isArray(value)) {
587
+ for (const entry of value) visit(entry);
588
+ return;
589
+ }
590
+ if (!changedRecord(value)) return;
591
+ for (const [key, entry] of Object.entries(value)) {
592
+ const input = key.toLowerCase();
593
+ if (!/(?:^|-)version-file$/.test(input) && ![
594
+ "toolchain-file",
595
+ "rust-toolchain-file",
596
+ "global-json-file"
597
+ ].includes(input)) {
598
+ visit(entry);
599
+ continue;
600
+ }
601
+ if (typeof entry !== "string" || entry.trim() === "" || entry.includes("${{") || node_path.default.posix.isAbsolute(entry) || entry.split("/").includes("..")) throw new Error("Unclassifiable setup version-file declaration");
602
+ const file = node_path.default.posix.normalize(entry);
603
+ if (file === "." || file.split("/").some((part) => part === "vendor" || part === "node_modules")) throw new Error("Setup version-file leaves the tracked toolchain scope");
604
+ let tool = "shared";
605
+ if (input === "node-version-file") tool = "node";
606
+ else if (input === "bun-version-file") tool = "bun";
607
+ const selected = files.get(file) ?? /* @__PURE__ */ new Set();
608
+ selected.add(tool);
609
+ files.set(file, selected);
610
+ }
611
+ };
612
+ for (const file of snapshot.entries.keys()) {
613
+ if (require_toolchain_inputs.githubAutomationFileKind(file) === void 0) continue;
614
+ const document = (0, yaml.parseDocument)(snapshotText({
615
+ snapshot,
616
+ file
617
+ }));
618
+ if (document.errors.length > 0) throw new Error("Unreadable workflow declaration");
619
+ visit(document.toJS({ maxAliasCount: 100 }));
620
+ }
621
+ return files;
622
+ };
623
+ const compilerInstallationNames = (snapshot) => {
624
+ const names = /* @__PURE__ */ new Set();
625
+ const collectCatalogs = (value) => {
626
+ if (!changedRecord(value)) return;
627
+ const visit = (entry) => {
628
+ if (!changedRecord(entry)) return;
629
+ for (const [name, specifier] of Object.entries(entry)) if (typeof specifier === "string" && changedPackageTool(packageTarget({
630
+ name,
631
+ specifier
632
+ })) === "typescript") names.add(name);
633
+ else visit(specifier);
634
+ };
635
+ visit(value["catalog"]);
636
+ visit(value["catalogs"]);
637
+ };
638
+ for (const file of snapshot.entries.keys()) {
639
+ const basename = node_path.default.posix.basename(file);
640
+ if (basename === "package.json") {
641
+ const manifest = parseChangedJson(snapshotText({
642
+ snapshot,
643
+ file
644
+ }));
645
+ if (!changedRecord(manifest)) throw new Error(`Invalid tracked manifest: ${file}`);
646
+ if (typeof manifest["name"] === "string" && changedPackageTool(manifest["name"]) === "typescript") throw new Error(`Tracked compiler source has no immutable artifact identity: ${file}`);
647
+ collectCatalogs(manifest);
648
+ collectCatalogs(manifest["workspaces"]);
649
+ for (const field of installedFields) {
650
+ const dependencies = manifest[field];
651
+ if (!changedRecord(dependencies)) continue;
652
+ for (const [name, specifier] of Object.entries(dependencies)) if (typeof specifier === "string" && changedPackageTool(packageTarget({
653
+ name,
654
+ specifier
655
+ })) === "typescript") names.add(name);
656
+ }
657
+ } else if (basename === "pnpm-workspace.yaml") {
658
+ const document = (0, yaml.parseDocument)(snapshotText({
659
+ snapshot,
660
+ file
661
+ }));
662
+ if (document.errors.length > 0) throw new Error(`Invalid catalog YAML: ${file}`);
663
+ collectCatalogs(document.toJS({ maxAliasCount: 100 }));
664
+ } else if ([
665
+ "bun.lock",
666
+ "pnpm-lock.yaml",
667
+ "package-lock.json",
668
+ "npm-shrinkwrap.json",
669
+ "yarn.lock"
670
+ ].includes(basename)) {
671
+ const lock = parseChangedLock({
672
+ file,
673
+ text: snapshotText({
674
+ snapshot,
675
+ file
676
+ })
677
+ });
678
+ for (const resolution of lock.resolutions) if (resolution.tool === "typescript") names.add(resolution.dependency);
679
+ for (const dependencies of Object.values(lock.importers)) for (const [name, version] of Object.entries(dependencies)) if (changedPackageTool(importerRegistryName({
680
+ dependency: name,
681
+ version
682
+ }) ?? name) === "typescript") names.add(name);
683
+ }
684
+ }
685
+ return names;
686
+ };
687
+ const effectiveCatalogSpecifier = ({ snapshot, file, dependency, specifier }) => {
688
+ if (!specifier.startsWith("catalog:")) return specifier;
689
+ const catalog = specifier.slice(8);
690
+ const owners = [...snapshot.entries.keys()].filter((candidate) => ["package.json", "pnpm-workspace.yaml"].includes(node_path.default.posix.basename(candidate)) && isAncestor(node_path.default.posix.dirname(candidate), node_path.default.posix.dirname(file))).sort((a, b) => directoryDepth(node_path.default.posix.dirname(b)) - directoryDepth(node_path.default.posix.dirname(a)) || Number(b.endsWith("pnpm-workspace.yaml")) - Number(a.endsWith("pnpm-workspace.yaml")));
691
+ for (const owner of owners) {
692
+ const text = snapshotText({
693
+ snapshot,
694
+ file: owner
695
+ });
696
+ let value;
697
+ if (owner.endsWith(".yaml")) {
698
+ const document = (0, yaml.parseDocument)(text);
699
+ if (document.errors.length > 0) throw new Error(`Invalid catalog YAML: ${owner}`);
700
+ value = document.toJS({ maxAliasCount: 100 });
701
+ } else value = parseChangedJson(text);
702
+ if (!changedRecord(value)) continue;
703
+ const sources = [value];
704
+ if (changedRecord(value["workspaces"])) sources.push(value["workspaces"]);
705
+ for (const source of sources) {
706
+ let declarations;
707
+ if (catalog === "") declarations = source["catalog"];
708
+ else if (changedRecord(source["catalogs"])) declarations = source["catalogs"][catalog];
709
+ if (declarations === void 0) continue;
710
+ if (!changedRecord(declarations) || typeof declarations[dependency] !== "string") throw new Error(`Missing effective catalog dependency: ${file}:${dependency}`);
711
+ const effective = declarations[dependency];
712
+ if (effective.startsWith("catalog:")) throw new Error(`Recursive compiler catalog: ${file}:${dependency}`);
713
+ return effective;
714
+ }
715
+ }
716
+ throw new Error(`Missing effective catalog: ${file}:${dependency}`);
717
+ };
718
+ const compilerPatches = (snapshot) => {
719
+ const compilerNames = compilerInstallationNames(snapshot);
720
+ const result = [];
721
+ for (const file of snapshot.entries.keys()) {
722
+ const name = node_path.default.posix.basename(file);
723
+ if (![
724
+ "package.json",
725
+ "pnpm-workspace.yaml",
726
+ "pnpm-lock.yaml",
727
+ "bun.lock"
728
+ ].includes(name)) continue;
729
+ const text = snapshotText({
730
+ snapshot,
731
+ file
732
+ });
733
+ let value;
734
+ if (name.endsWith(".yaml")) {
735
+ const document = (0, yaml.parseDocument)(text);
736
+ if (document.errors.length > 0) throw new Error(`Invalid compiler patch YAML: ${file}`);
737
+ value = document.toJS({ maxAliasCount: 100 });
738
+ } else value = parseChangedJson(text);
739
+ const sources = [value];
740
+ if (name === "package.json" && changedRecord(value)) sources.push(value["pnpm"]);
741
+ for (const source of sources) for (const patch of changedCompilerPatches({
742
+ value: source,
743
+ compilerNames,
744
+ stringsArePaths: name === "package.json" || name === "pnpm-workspace.yaml"
745
+ })) {
746
+ if (patch.reference === void 0) {
747
+ result.push({
748
+ file,
749
+ name: patch.name,
750
+ metadata: patch.metadata
751
+ });
752
+ continue;
753
+ }
754
+ const reference = node_path.default.posix.normalize(node_path.default.posix.join(node_path.default.posix.dirname(file), patch.reference));
755
+ if (patch.reference.trim() === "" || node_path.default.posix.isAbsolute(patch.reference) || patch.reference.includes("\\") || patch.reference.split("").some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127) || reference === ".." || reference.startsWith("../")) throw new Error(`Compiler patch reference leaves the tracked snapshot: ${file}:${patch.name}`);
756
+ result.push({
757
+ file,
758
+ name: patch.name,
759
+ metadata: patch.metadata,
760
+ reference
761
+ });
762
+ }
763
+ if (name === "pnpm-lock.yaml" && changedRecord(value) && changedRecord(value["snapshots"])) for (const descriptor of Object.keys(value["snapshots"])) {
764
+ const packageName = /^(@[^/]+\/[^@]+|[^@]+)@/.exec(descriptor)?.[1];
765
+ if (packageName && (changedPackageTool(packageName) === "typescript" || compilerNames.has(packageName)) && descriptor.includes("patch_hash=")) result.push({
766
+ file,
767
+ name: descriptor,
768
+ metadata: descriptor
769
+ });
770
+ }
771
+ }
772
+ return result;
773
+ };
774
+ const readSnapshots = async ({ repo, trees }) => {
775
+ const selected = trees.map((tree) => new Map([...tree].filter(([file]) => trackedInput(file))));
776
+ const blobs = /* @__PURE__ */ new Map();
777
+ for (;;) {
778
+ const pending = [...new Set(selected.flatMap((entries) => [...entries.values()].map((entry) => entry.oid)))].filter((oid) => !blobs.has(oid));
779
+ for (const [oid, text] of await readBlobs({
780
+ repo,
781
+ oids: pending
782
+ })) blobs.set(oid, text);
783
+ let expanded = false;
784
+ for (let index = 0; index < selected.length; index += 1) {
785
+ const entries = selected.at(index);
786
+ const tree = trees.at(index);
787
+ if (!entries || !tree) throw new Error("Missing Git tree");
788
+ const required = /* @__PURE__ */ new Set();
789
+ const requiredPatches = /* @__PURE__ */ new Set();
790
+ for (const [file, entry] of entries) {
791
+ if (entry.mode !== "120000") continue;
792
+ const text = blobs.get(entry.oid);
793
+ if (text === void 0) throw new Error("Missing tracked symlink");
794
+ const target = node_path.default.posix.normalize(node_path.default.posix.join(node_path.default.posix.dirname(file), text));
795
+ if (node_path.default.posix.isAbsolute(text) || target === ".." || target.startsWith("../")) throw new Error("Toolchain symlink leaves the tracked tree");
796
+ required.add(target);
797
+ }
798
+ if (![...required].some((file) => !entries.has(file))) {
799
+ const snapshot = {
800
+ entries,
801
+ blobs
802
+ };
803
+ for (const file of selectorFiles(snapshot).keys()) required.add(file);
804
+ for (const patch of compilerPatches(snapshot)) if (patch.reference !== void 0) {
805
+ required.add(patch.reference);
806
+ requiredPatches.add(patch.reference);
807
+ }
808
+ }
809
+ for (const file of required) {
810
+ if (entries.has(file)) continue;
811
+ const entry = tree.get(file);
812
+ if (!entry) throw new Error(requiredPatches.has(file) ? `Missing tracked compiler patch: ${file}` : `Missing tracked toolchain selector file: ${file}`);
813
+ entries.set(file, entry);
814
+ expanded = true;
815
+ }
816
+ }
817
+ if (!expanded) return selected.map((entries) => ({
818
+ entries,
819
+ blobs
820
+ }));
821
+ }
822
+ };
823
+ const stableJson = (value) => {
824
+ if (Array.isArray(value)) return JSON.stringify(value.map(stableJson));
825
+ if (changedRecord(value)) return JSON.stringify(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, stableJson(item)]));
826
+ if (value === void 0) return "undefined";
827
+ return JSON.stringify(value);
828
+ };
829
+ const inventory = () => ({
830
+ bun: /* @__PURE__ */ new Set(),
831
+ node: /* @__PURE__ */ new Set(),
832
+ typescript: /* @__PURE__ */ new Set(),
833
+ oxlint: /* @__PURE__ */ new Set(),
834
+ "oxlint-tsgolint": /* @__PURE__ */ new Set(),
835
+ oxfmt: /* @__PURE__ */ new Set(),
836
+ shared: /* @__PURE__ */ new Set()
837
+ });
838
+ const installedFields = [
839
+ "dependencies",
840
+ "devDependencies",
841
+ "optionalDependencies"
842
+ ];
843
+ const exactBun = (value) => stableVersion(value);
844
+ const nonRegistryResolution = (specifier) => /^(?:link:|workspace:|file:|portal:|catalog:|git:|github:|gitlab:|bitbucket:|git\+|https?:|ssh:|git@)/i.test(specifier);
845
+ const packageTarget = ({ name, specifier }) => {
846
+ if (!specifier.startsWith("npm:")) return name;
847
+ const target = /^npm:((?:@[^/@\s]+\/)?[^/@\s]+)(?:@.*)?$/.exec(specifier)?.[1];
848
+ if (target === void 0) throw new Error("Unclassifiable npm alias declaration");
849
+ return target;
850
+ };
851
+ /** Importers contain resolved descriptors, not declarations; classify protocols first. */
852
+ const importerRegistryName = ({ dependency, version }) => {
853
+ if (nonRegistryResolution(version)) return dependency;
854
+ if (version.startsWith("npm:")) return packageTarget({
855
+ name: dependency,
856
+ specifier: version
857
+ });
858
+ const descriptor = version.split("(").at(0) ?? "";
859
+ return /^((?:@[^/@\s]+\/)?[^/@\s]+)@/.exec(descriptor)?.[1];
860
+ };
861
+ const directoryDepth = (directory) => directory.split("/").filter((part) => part !== "." && part !== "").length;
862
+ const isAncestor = (directory, child) => {
863
+ const relative = node_path.default.posix.relative(directory, child);
864
+ return relative !== ".." && !relative.startsWith("../") && !node_path.default.posix.isAbsolute(relative);
865
+ };
866
+ const boundResolution = ({ lockFile, lock, file, manifest, dependency, name }) => {
867
+ const directory = node_path.default.posix.relative(node_path.default.posix.dirname(lockFile), node_path.default.posix.dirname(file)) || ".";
868
+ const importerVersion = lock.importers[directory]?.[dependency];
869
+ const importerName = node_path.default.posix.basename(lockFile) === "pnpm-lock.yaml" && importerVersion !== void 0 ? importerRegistryName({
870
+ dependency,
871
+ version: importerVersion
872
+ }) : void 0;
873
+ const candidates = lock.resolutions.filter((item) => item.name === (importerName ?? name) || item.dependency === dependency);
874
+ if (node_path.default.posix.basename(lockFile) === "pnpm-lock.yaml") {
875
+ const value = lock.importers[directory]?.[dependency];
876
+ if (value === void 0) throw new Error(`Missing resolved importer: ${file}:${dependency}`);
877
+ const descriptor = value.replace(/^npm:/, "");
878
+ const resolvedVersion = (descriptor.startsWith(`${importerName ?? name}@`) ? descriptor.slice((importerName ?? name).length + 1) : descriptor).split("(").at(0);
879
+ const matched = candidates.filter((item) => item.version === resolvedVersion);
880
+ if (new Set(matched.map((item) => `${item.name}@${item.version}`)).size === 1) return matched;
881
+ throw new Error(`Ambiguous resolved importer: ${file}:${dependency}`);
882
+ }
883
+ if (node_path.default.posix.basename(lockFile) === "yarn.lock") {
884
+ const declarations = installedFields.flatMap((field) => {
885
+ const source = manifest[field];
886
+ return changedRecord(source) && typeof source[dependency] === "string" ? [source[dependency]] : [];
887
+ });
888
+ const declaredSelectors = new Set(declarations.flatMap((specifier) => [`${dependency}@${specifier}`, `${dependency}@npm:${specifier}`].flatMap(changedYarnSelectors)));
889
+ const matched = candidates.filter((item) => changedYarnSelectors(item.location).some((selector) => declaredSelectors.has(selector)));
890
+ if (matched.length > 0) return matched;
891
+ throw new Error(`Missing resolved Yarn compiler declaration: ${file}:${dependency}`);
892
+ }
893
+ const locations = [];
894
+ if (node_path.default.posix.basename(lockFile) === "bun.lock") {
895
+ if (directory !== "." && typeof manifest["name"] !== "string" && candidates.length > 1) throw new Error(`Ambiguous Bun workspace resolution: ${file}:${dependency}`);
896
+ if (directory !== "." && typeof manifest["name"] === "string") locations.push(`${manifest["name"]}/${dependency}`);
897
+ locations.push(dependency);
898
+ } else {
899
+ let current = directory;
900
+ for (;;) {
901
+ locations.push(node_path.default.posix.join(current, "node_modules", dependency));
902
+ if (current === ".") break;
903
+ current = node_path.default.posix.dirname(current);
904
+ }
905
+ }
906
+ for (const location of locations) {
907
+ const matched = candidates.filter((item) => item.location === location);
908
+ if (matched.length === 1) return matched;
909
+ if (matched.length > 1) throw new Error(`Ambiguous lock location: ${file}:${dependency}`);
910
+ }
911
+ throw new Error(`Missing resolved toolchain dependency: ${file}:${dependency}`);
912
+ };
913
+ const stableVersion = (value) => {
914
+ if (typeof value !== "string") throw new Error("Runtime selector is not an exact stable version");
915
+ const version = value.trim().replace(/^v/, "");
916
+ if (!/^\d+\.\d+\.\d+$/.test(version) || (0, semver.valid)(version) !== version) throw new Error("Runtime selector is not an exact stable version");
917
+ return version;
918
+ };
919
+ const setupRuntimeVersion = ({ value, runtime }) => {
920
+ if (typeof value !== "string" && typeof value !== "number") throw new Error("Unclassifiable setup runtime selector");
921
+ const selector = String(value).trim();
922
+ if (runtime === "rust" && /^nightly-\d{4}-\d{2}-\d{2}$/.test(selector)) {
923
+ const date = selector.slice(8);
924
+ if (new Date(date).toISOString().slice(0, 10) !== date) throw new Error("Invalid dated Rust toolchain");
925
+ return selector;
926
+ }
927
+ return stableVersion(selector);
928
+ };
929
+ const setupRuntimeFileVersion = ({ snapshot, file, runtime }) => {
930
+ const text = snapshotText({
931
+ snapshot,
932
+ file
933
+ }).trim();
934
+ if (runtime === "bun" && file.endsWith(".json")) {
935
+ const manifest = parseChangedJson(text);
936
+ const manager = changedRecord(manifest) ? manifest["packageManager"] : void 0;
937
+ if (typeof manager !== "string" || !manager.startsWith("bun@")) throw new Error("Bun selector manifest lacks an exact Bun packageManager");
938
+ return stableVersion(manager.slice(4));
939
+ }
940
+ if (runtime === "go" && node_path.default.posix.basename(file) === "go.mod") {
941
+ const goLines = text.split(/\r?\n/).filter((line) => /^\s*go\b/.test(line));
942
+ const toolchainLines = text.split(/\r?\n/).filter((line) => /^\s*toolchain\b/.test(line));
943
+ if (goLines.length !== 1 || toolchainLines.length > 1) throw new Error("Unclassifiable Go runtime directives");
944
+ const version = /^\s*go\s+(\S+)\s*(?:\/\/.*)?$/.exec(goLines.at(0) ?? "")?.[1];
945
+ const go = stableVersion(version);
946
+ if (toolchainLines.length === 0) return go;
947
+ const toolchain = /^\s*toolchain\s+(\S+)\s*(?:\/\/.*)?$/.exec(toolchainLines.at(0) ?? "")?.[1];
948
+ if (toolchain === void 0) throw new Error("Unclassifiable Go toolchain directive");
949
+ if (!toolchain.startsWith("go")) throw new Error("Unclassifiable Go toolchain directive");
950
+ return stableVersion(toolchain.slice(2));
951
+ }
952
+ if (file.endsWith(".toml")) {
953
+ const value = (0, smol_toml.parse)(text);
954
+ if (!changedRecord(value)) throw new Error("Unclassifiable setup runtime TOML");
955
+ if (runtime === "rust" && changedRecord(value["toolchain"])) return setupRuntimeVersion({
956
+ value: value["toolchain"]["channel"],
957
+ runtime
958
+ });
959
+ if (runtime === "python") return setupRuntimeVersion({
960
+ value: value["python-version"],
961
+ runtime
962
+ });
963
+ throw new Error("Unclassifiable setup runtime TOML");
964
+ }
965
+ return setupRuntimeVersion({
966
+ value: text,
967
+ runtime
968
+ });
969
+ };
970
+ const setupRuntimeVersions = (snapshot) => {
971
+ const versions = [];
972
+ const step = (value, file) => {
973
+ if (!changedRecord(value) || typeof value["uses"] !== "string") return;
974
+ const action = value["uses"].split("@").at(0)?.toLowerCase();
975
+ let runtime = /(?:^|\/)setup-([^/]+)$/.exec(action ?? "")?.[1];
976
+ if (action === "dtolnay/rust-toolchain") runtime = "rust";
977
+ if (runtime === void 0) return;
978
+ const raw = value["with"];
979
+ if (!changedRecord(raw)) throw new Error(`Missing setup runtime selector: ${file}:${runtime}`);
980
+ const inputs = /* @__PURE__ */ new Map();
981
+ for (const [key, input] of Object.entries(raw)) {
982
+ const folded = key.toLowerCase();
983
+ if (inputs.has(folded)) throw new Error(`Duplicate setup input: ${file}:${folded}`);
984
+ inputs.set(folded, input);
985
+ }
986
+ let selected = false;
987
+ for (const [key, input] of inputs) {
988
+ if (!/(?:^|-)version(?:-file)?$/.test(key) && ![
989
+ "toolchain",
990
+ "toolchain-file",
991
+ "rust-toolchain-file"
992
+ ].includes(key)) continue;
993
+ if ([
994
+ "bun",
995
+ "node",
996
+ "python",
997
+ "go",
998
+ "java"
999
+ ].includes(runtime)) {
1000
+ if (key === `${runtime}-version` || key === `${runtime}-version-file`) selected = true;
1001
+ } else if (runtime === "rust") {
1002
+ if ([
1003
+ "toolchain",
1004
+ "toolchain-file",
1005
+ "rust-toolchain-file",
1006
+ "rust-version",
1007
+ "rust-version-file"
1008
+ ].includes(key)) selected = true;
1009
+ } else if (key === "version" || key === "version-file" || key === `${runtime}-version` || key === `${runtime}-version-file`) selected = true;
1010
+ const inputRuntime = [
1011
+ "bun",
1012
+ "node",
1013
+ "python",
1014
+ "go",
1015
+ "java",
1016
+ "rust"
1017
+ ].find((candidate) => key.startsWith(`${candidate}-`)) ?? runtime;
1018
+ let version;
1019
+ if (key.endsWith("-file")) {
1020
+ if (typeof input !== "string" || input.trim() === "" || input.includes("${{")) throw new Error("Unclassifiable setup version-file declaration");
1021
+ version = setupRuntimeFileVersion({
1022
+ snapshot,
1023
+ file: node_path.default.posix.normalize(input),
1024
+ runtime: inputRuntime
1025
+ });
1026
+ } else version = setupRuntimeVersion({
1027
+ value: input,
1028
+ runtime: inputRuntime
1029
+ });
1030
+ versions.push({
1031
+ file,
1032
+ runtime: inputRuntime,
1033
+ version
1034
+ });
1035
+ }
1036
+ if (!selected) throw new Error(`Missing setup runtime selector: ${file}:${runtime}`);
1037
+ };
1038
+ const visit = (value, file) => {
1039
+ if (Array.isArray(value)) {
1040
+ for (const entry of value) visit(entry, file);
1041
+ return;
1042
+ }
1043
+ if (!changedRecord(value)) return;
1044
+ for (const [key, entry] of Object.entries(value)) if (key === "steps" && Array.isArray(entry)) for (const child of entry) step(child, file);
1045
+ else visit(entry, file);
1046
+ };
1047
+ for (const file of snapshot.entries.keys()) {
1048
+ if (require_toolchain_inputs.githubAutomationFileKind(file) === void 0) continue;
1049
+ const document = (0, yaml.parseDocument)(snapshotText({
1050
+ snapshot,
1051
+ file
1052
+ }));
1053
+ if (document.errors.length > 0) throw new Error("Unreadable workflow declaration");
1054
+ visit(document.toJS({ maxAliasCount: 100 }), file);
1055
+ }
1056
+ return versions;
1057
+ };
1058
+ const stableBunImageVersion = (image) => {
1059
+ const match = /^oven\/bun(?::([^@]+))?(?:@[^\s]+)?$/.exec(image);
1060
+ if (match === null) return void 0;
1061
+ const version = match[1]?.replace(/-(?:alpine|slim|debian|distroless)$/, "");
1062
+ return stableVersion(version);
1063
+ };
1064
+ const assertLiteralDockerImages = (text) => {
1065
+ const defaults = /* @__PURE__ */ new Map();
1066
+ for (const line of text.replace(/\\\r?\n/g, " ").split(/\r?\n/)) {
1067
+ const arg = /^\s*ARG\s+([A-Za-z_]\w*)(?:=(\S+))?\s*(?:#.*)?$/i.exec(line);
1068
+ if (arg?.[1]) {
1069
+ const value = arg[2];
1070
+ if (value !== void 0 && /^[A-Za-z0-9_.:/@+-]+$/.test(value)) defaults.set(arg[1], value);
1071
+ else defaults.delete(arg[1]);
1072
+ }
1073
+ const image = /^\s*FROM\s+(?:--[^\s]+\s+)*(\S+)/i.exec(line)?.[1];
1074
+ if (image === void 0) continue;
1075
+ const expanded = image.replace(/\$\{([A-Za-z_]\w*)\}|\$([A-Za-z_]\w*)/g, (original, braced, plain) => defaults.get(braced ?? plain ?? "") ?? original);
1076
+ if (expanded.includes("$")) throw new Error("Unclassifiable Docker image selector");
1077
+ stableBunImageVersion(expanded);
1078
+ }
1079
+ };
1080
+ const parseSnapshot = (snapshot) => {
1081
+ const tools = inventory();
1082
+ const bunVersions = /* @__PURE__ */ new Set();
1083
+ const bunRanges = [];
1084
+ const manifests = /* @__PURE__ */ new Map();
1085
+ const locks = /* @__PURE__ */ new Map();
1086
+ const workspacePatterns = /* @__PURE__ */ new Map();
1087
+ const typescript = /* @__PURE__ */ new Set();
1088
+ const selectedFiles = selectorFiles(snapshot);
1089
+ for (const { file, runtime, version } of setupRuntimeVersions(snapshot)) {
1090
+ let tool = "shared";
1091
+ if (runtime === "bun") tool = "bun";
1092
+ else if (runtime === "node") tool = "node";
1093
+ tools[tool].add(`setup-runtime:${file}:${runtime}:${version}`);
1094
+ }
1095
+ const compilerNames = compilerInstallationNames(snapshot);
1096
+ for (const patch of compilerPatches(snapshot)) {
1097
+ tools.typescript.add(`compiler-patch:${patch.file}:${patch.name}:${stableJson(patch.metadata)}`);
1098
+ if (patch.reference !== void 0) tools.typescript.add(`compiler-patch-source:${patch.reference}:${snapshotText({
1099
+ snapshot,
1100
+ file: patch.reference
1101
+ })}`);
1102
+ }
1103
+ const addBun = (value) => {
1104
+ const version = exactBun(value);
1105
+ bunVersions.add(version);
1106
+ tools.bun.add(version);
1107
+ };
1108
+ const catalogs = (file, value) => {
1109
+ for (const field of ["catalog", "catalogs"]) {
1110
+ if (value[field] === void 0) continue;
1111
+ tools.shared.add(`${file}:${field}:${stableJson(value[field])}`);
1112
+ const visit = (entry, at) => {
1113
+ if (!changedRecord(entry)) return;
1114
+ for (const [name, specifier] of Object.entries(entry)) {
1115
+ if (changedPackageTool(typeof specifier === "string" ? packageTarget({
1116
+ name,
1117
+ specifier
1118
+ }) : name) === "typescript") tools.typescript.add(`${file}:${at}:${name}:${stableJson(specifier)}`);
1119
+ visit(specifier, `${at}:${name}`);
1120
+ }
1121
+ };
1122
+ visit(value[field], field);
1123
+ }
1124
+ };
1125
+ for (const file of snapshot.entries.keys()) {
1126
+ const text = snapshotText({
1127
+ snapshot,
1128
+ file
1129
+ });
1130
+ const name = node_path.default.posix.basename(file);
1131
+ if (compilerPatchPackageFile(file, compilerNames)) tools.typescript.add(`patch-package:${file}:${text}`);
1132
+ for (const tool of selectedFiles.get(file) ?? []) tools[tool].add(`selector-file:${file}:${text}`);
1133
+ if (name === "package.json") {
1134
+ const manifest = parseChangedJson(text);
1135
+ if (!changedRecord(manifest)) throw new Error(`Invalid tracked manifest: ${file}`);
1136
+ if (typeof manifest["name"] === "string" && changedPackageTool(manifest["name"]) === "typescript") throw new Error(`Tracked compiler source has no immutable artifact identity: ${file}`);
1137
+ manifests.set(file, manifest);
1138
+ catalogs(file, manifest);
1139
+ if (changedRecord(manifest["workspaces"])) catalogs(`${file}:workspaces`, manifest["workspaces"]);
1140
+ } else if (name === ".npmrc") for (const line of text.split(/\r?\n/)) {
1141
+ if (!/^\s*(?:use-node-version|node-version)\s*=/i.test(line)) continue;
1142
+ if (/\$\{/.test(line)) throw new Error("Unclassifiable npm Node selector");
1143
+ const selector = /^\s*(?:use-node-version|node-version)\s*=\s*(.*?)\s*$/i.exec(line)?.[1];
1144
+ tools.node.add(`${file}:${stableVersion(selector)}`);
1145
+ }
1146
+ else if (name === "pnpm-workspace.yaml") {
1147
+ const document = (0, yaml.parseDocument)(text);
1148
+ if (document.errors.length > 0) throw new Error("Invalid pnpm workspace YAML");
1149
+ const workspace = document.toJS({ maxAliasCount: 100 });
1150
+ if (!changedRecord(workspace)) throw new Error("Invalid pnpm workspace configuration");
1151
+ workspacePatterns.set(node_path.default.posix.dirname(file), workspace["packages"]);
1152
+ catalogs(file, workspace);
1153
+ for (const key of ["useNodeVersion", "nodeVersion"]) if (workspace[key] !== void 0) tools.node.add(`${file}:${key}:${stableVersion(workspace[key])}`);
1154
+ } else if ([
1155
+ "bun.lock",
1156
+ "pnpm-lock.yaml",
1157
+ "package-lock.json",
1158
+ "npm-shrinkwrap.json",
1159
+ "yarn.lock"
1160
+ ].includes(name)) locks.set(file, parseChangedLock({
1161
+ file,
1162
+ text
1163
+ }));
1164
+ else if (name === "bun.lockb") {
1165
+ if (!snapshot.entries.has(node_path.default.posix.join(node_path.default.posix.dirname(file), "bun.lock"))) throw new Error("Binary Bun lockfile cannot be resolved without executing Bun");
1166
+ } else if (name === ".bun-version") addBun(text.trim());
1167
+ else if (otherRuntimeFiles.has(name)) tools.shared.add(`${file}:${text}`);
1168
+ else if (name === ".node-version" || name === ".nvmrc") tools.node.add(`${file}:${stableVersion(text)}`);
1169
+ else if (name === "toolchain.json") {
1170
+ const policy = parseChangedJson(text);
1171
+ if (!changedRecord(policy)) throw new Error("Invalid shared toolchain policy");
1172
+ tools.shared.add(`${file}:${stableJson(policy)}`);
1173
+ if (typeof policy["bun"] === "string") addBun(policy["bun"]);
1174
+ } else if (name === ".tool-versions") {
1175
+ tools.bun.add(`${file}:${text}`);
1176
+ tools.typescript.add(`${file}:${text}`);
1177
+ for (const line of text.split(/\r?\n/)) {
1178
+ const [tool, ...versions] = line.trim().split(/\s+/);
1179
+ const value = versions.join(" ").split("#")[0]?.trim();
1180
+ if (tool === "bun" && value) addBun(value);
1181
+ if (tool === "node" || tool === "nodejs") tools.node.add(`${file}:${stableVersion(value)}`);
1182
+ }
1183
+ } else if (require_toolchain_inputs.isMiseConfigPath(file)) {
1184
+ tools.bun.add(`${file}:${text}`);
1185
+ tools.typescript.add(`${file}:${text}`);
1186
+ const parsed = (0, smol_toml.parse)(text);
1187
+ if (changedRecord(parsed) && changedRecord(parsed["tools"])) for (const [tool, selector] of Object.entries(parsed["tools"])) {
1188
+ const value = changedRecord(selector) ? selector["version"] : selector;
1189
+ const values = Array.isArray(value) ? value : [value];
1190
+ if (tool === "bun" || tool === "core:bun") for (const version of values) {
1191
+ if (typeof version !== "string") throw new Error("Unresolved mise Bun selector");
1192
+ addBun(version);
1193
+ }
1194
+ if ([
1195
+ "node",
1196
+ "nodejs",
1197
+ "core:node"
1198
+ ].includes(tool)) {
1199
+ if (values.length === 0) throw new Error("Unresolved mise Node selector");
1200
+ for (const version of values) tools.node.add(`${file}:${stableVersion(version)}`);
1201
+ }
1202
+ }
1203
+ } else if (require_toolchain_inputs.githubAutomationFileKind(file) !== void 0) {
1204
+ tools.shared.add(`${file}:${text}`);
1205
+ tools.bun.add(`${file}:${text}`);
1206
+ tools.typescript.add(`${file}:${text}`);
1207
+ } else if (require_toolchain_inputs.isDockerDefinitionPath(file)) {
1208
+ assertLiteralDockerImages(text);
1209
+ tools.shared.add(`${file}:${text}`);
1210
+ tools.node.add(`${file}:${text}`);
1211
+ tools.bun.add(`${file}:${text}`);
1212
+ tools.typescript.add(`${file}:${text}`);
1213
+ for (const match of text.matchAll(/^\s*FROM\s+(?:--[^\s]+\s+)*oven\/bun:([\w.+-]+)/gim)) {
1214
+ const tag = match[1];
1215
+ if (tag !== void 0) {
1216
+ const version = stableBunImageVersion(`oven/bun:${tag}`);
1217
+ if (version !== void 0) addBun(version);
1218
+ }
1219
+ }
1220
+ } else if (containerDefinition(file)) for (const document of (0, yaml.parseAllDocuments)(text)) {
1221
+ if (document.errors.length > 0) {
1222
+ if (require_toolchain_inputs.isComposeDefinitionPath(file) || /(?:^|\n)\s*(?:apiVersion|kind)\s*:/.test(text)) throw new Error(`Unreadable container declaration: ${file}`);
1223
+ continue;
1224
+ }
1225
+ const container = require_toolchain_inputs.containerDocumentImages(document.toJS({ maxAliasCount: 100 }), file);
1226
+ if (container === void 0) {
1227
+ if (require_toolchain_inputs.isComposeDefinitionPath(file)) throw new Error(`Unclassifiable Compose declaration: ${file}`);
1228
+ continue;
1229
+ }
1230
+ for (const { image } of container.images) {
1231
+ if (typeof image !== "string" || image.includes("$")) throw new Error(`Unclassifiable container image selector: ${file}`);
1232
+ stableBunImageVersion(image);
1233
+ }
1234
+ for (const tool of [
1235
+ "bun",
1236
+ "node",
1237
+ "typescript",
1238
+ "shared"
1239
+ ]) tools[tool].add(`${file}:${text}`);
1240
+ }
1241
+ }
1242
+ const activeManifest = (file) => {
1243
+ if (file === "package.json") return true;
1244
+ const directory = node_path.default.posix.dirname(file);
1245
+ if ([
1246
+ "bun.lock",
1247
+ "pnpm-lock.yaml",
1248
+ "package-lock.json",
1249
+ "npm-shrinkwrap.json",
1250
+ "yarn.lock"
1251
+ ].some((name) => locks.has(node_path.default.posix.join(directory, name)))) return true;
1252
+ const owners = [...manifests].filter(([ownerFile, owner]) => owner["workspaces"] !== void 0 && isAncestor(node_path.default.posix.dirname(ownerFile), directory)).map(([ownerFile, owner]) => ({
1253
+ directory: node_path.default.posix.dirname(ownerFile),
1254
+ patterns: changedRecord(owner["workspaces"]) ? owner["workspaces"]["packages"] : owner["workspaces"]
1255
+ }));
1256
+ const pnpmOwner = [...workspacePatterns].filter(([ownerDirectory]) => isAncestor(ownerDirectory, directory)).sort(([a], [b]) => directoryDepth(b) - directoryDepth(a)).at(0);
1257
+ if (pnpmOwner) return require_toolchain_inputs.workspaceContains({
1258
+ directory: pnpmOwner[0],
1259
+ file,
1260
+ patterns: pnpmOwner[1],
1261
+ rootMembership: "implicit"
1262
+ });
1263
+ const owner = owners.sort((a, b) => directoryDepth(b.directory) - directoryDepth(a.directory)).at(0);
1264
+ if (owner) return require_toolchain_inputs.workspaceContains({
1265
+ directory: owner.directory,
1266
+ file,
1267
+ patterns: owner.patterns,
1268
+ rootMembership: "implicit"
1269
+ });
1270
+ for (const [lockFile, lock] of locks) {
1271
+ const relative = node_path.default.posix.relative(node_path.default.posix.dirname(lockFile), directory) || ".";
1272
+ if (Object.hasOwn(lock.importers, relative)) return true;
1273
+ }
1274
+ return false;
1275
+ };
1276
+ for (const [file, manifest] of manifests) {
1277
+ const active = activeManifest(file);
1278
+ const manager = manifest["packageManager"];
1279
+ if (manager !== void 0) {
1280
+ if (typeof manager !== "string") throw new Error(`Unclassifiable package manager: ${file}`);
1281
+ if (manager.startsWith("bun@")) addBun(manager.slice(4));
1282
+ else tools.shared.add(`${file}:packageManager:${manager}`);
1283
+ }
1284
+ for (const field of ["volta", "devEngines"]) {
1285
+ if (manifest[field] === void 0) continue;
1286
+ for (const tool of [
1287
+ "bun",
1288
+ "node",
1289
+ "typescript",
1290
+ "shared"
1291
+ ]) tools[tool].add(`${file}:${field}:${stableJson(manifest[field])}`);
1292
+ }
1293
+ const engines = manifest["engines"];
1294
+ if (changedRecord(engines)) {
1295
+ if (typeof engines["node"] === "string") tools.node.add(`${file}:${engines["node"]}`);
1296
+ if (typeof engines["bun"] === "string") {
1297
+ if (!(0, semver.validRange)(engines["bun"])) throw new Error("Invalid Bun engine selector");
1298
+ bunRanges.push(engines["bun"]);
1299
+ tools.bun.add(`${file}:engines.bun:${engines["bun"]}`);
1300
+ if ((0, semver.valid)(engines["bun"]) !== null) addBun(engines["bun"]);
1301
+ }
1302
+ }
1303
+ for (const field of active ? ["peerDependencies"] : [...installedFields, "peerDependencies"]) {
1304
+ const declarations = manifest[field];
1305
+ if (declarations === void 0) continue;
1306
+ if (!changedRecord(declarations)) throw new Error(`Invalid toolchain declarations: ${file}:${field}`);
1307
+ for (const [dependency, specifier] of Object.entries(declarations)) {
1308
+ if (typeof specifier !== "string") throw new Error(`Unclassifiable toolchain declaration: ${file}:${dependency}`);
1309
+ const tool = changedPackageTool(packageTarget({
1310
+ name: dependency,
1311
+ specifier
1312
+ })) ?? changedPackageTool(dependency) ?? (compilerNames.has(dependency) ? "typescript" : void 0);
1313
+ if (tool !== void 0) tools[tool].add(`${file}:${field}:${dependency}:${specifier}`);
1314
+ }
1315
+ }
1316
+ if (!active) continue;
1317
+ const ownName = manifest["name"];
1318
+ const ownVersion = manifest["version"];
1319
+ if (typeof ownName === "string" && changedPackageTool(ownName) === "shared" && typeof ownVersion === "string") tools.shared.add(`${ownName}@${ownVersion}`);
1320
+ for (const field of installedFields) {
1321
+ const dependencies = manifest[field];
1322
+ if (dependencies === void 0) continue;
1323
+ if (!changedRecord(dependencies)) throw new Error(`Invalid manifest dependency table: ${file}`);
1324
+ for (const [dependency, specifier] of Object.entries(dependencies)) {
1325
+ if (typeof specifier !== "string") throw new Error(`Invalid dependency specifier: ${file}`);
1326
+ if (changedPackageTool(packageTarget({
1327
+ name: dependency,
1328
+ specifier
1329
+ })) === void 0 && changedPackageTool(dependency) === void 0 && !compilerNames.has(dependency)) continue;
1330
+ const effectiveSpecifier = effectiveCatalogSpecifier({
1331
+ snapshot,
1332
+ file,
1333
+ dependency,
1334
+ specifier
1335
+ });
1336
+ const name = packageTarget({
1337
+ name: dependency,
1338
+ specifier: effectiveSpecifier
1339
+ });
1340
+ const tool = changedPackageTool(name) ?? changedPackageTool(dependency) ?? (compilerNames.has(dependency) ? "typescript" : void 0);
1341
+ if (tool === void 0) continue;
1342
+ const sourceSpecifier = effectiveSpecifier.startsWith("npm:") ? effectiveSpecifier.slice(4 + name.length + 1) : effectiveSpecifier;
1343
+ if (tool === "typescript" && (/^(?:link:|portal:|workspace:)/i.test(sourceSpecifier) || /^(?:file:|\.{1,2}\/|\/)/i.test(sourceSpecifier) && !/\.(?:tgz|tar\.gz)(?:[?#].*)?$/i.test(sourceSpecifier))) throw new Error(`Compiler source has no immutable identity: ${file}:${dependency}`);
1344
+ if (tool === "typescript") tools.typescript.add(`${file}:${field}:${dependency}:specifier:${specifier}`);
1345
+ const candidates = [...locks].filter(([lockFile]) => {
1346
+ const relative = node_path.default.posix.relative(node_path.default.posix.dirname(lockFile), node_path.default.posix.dirname(file));
1347
+ return relative !== ".." && !relative.startsWith("../");
1348
+ }).sort(([a], [b]) => directoryDepth(node_path.default.posix.dirname(b)) - directoryDepth(node_path.default.posix.dirname(a)));
1349
+ const nearest = candidates.at(0);
1350
+ if (!nearest) throw new Error(`Missing toolchain lockfile: ${file}:${dependency}`);
1351
+ const lockDirectory = node_path.default.posix.dirname(nearest[0]);
1352
+ const ownerManager = [...manifests].filter(([ownerFile, ownerManifest]) => typeof ownerManifest["packageManager"] === "string" && isAncestor(node_path.default.posix.dirname(ownerFile), node_path.default.posix.dirname(file))).sort(([a], [b]) => directoryDepth(node_path.default.posix.dirname(b)) - directoryDepth(node_path.default.posix.dirname(a))).at(0)?.[1]["packageManager"];
1353
+ const selectedLocks = candidates.filter(([candidate]) => node_path.default.posix.dirname(candidate) === lockDirectory).filter(([candidate]) => {
1354
+ if (typeof ownerManager !== "string") return true;
1355
+ const lockName = node_path.default.posix.basename(candidate);
1356
+ if (ownerManager.startsWith("bun@")) return lockName === "bun.lock";
1357
+ if (ownerManager.startsWith("pnpm@")) return lockName === "pnpm-lock.yaml";
1358
+ if (ownerManager.startsWith("yarn@")) return lockName === "yarn.lock";
1359
+ if (ownerManager.startsWith("npm@")) return lockName === "package-lock.json" || lockName === "npm-shrinkwrap.json";
1360
+ return false;
1361
+ });
1362
+ const selected = selectedLocks.at(0);
1363
+ if (selectedLocks.length !== 1 || !selected) throw new Error(`Ambiguous or missing effective lockfile: ${file}:${dependency}`);
1364
+ const [lockFile, lock] = selected;
1365
+ const resolved = boundResolution({
1366
+ lockFile,
1367
+ lock,
1368
+ file,
1369
+ manifest,
1370
+ dependency,
1371
+ name
1372
+ });
1373
+ if (tool === "typescript" && resolved.some((item) => item.name !== name)) throw new Error(`Compiler lock name does not match effective declaration: ${file}:${dependency}`);
1374
+ if (tool === "typescript" && nonRegistryResolution(sourceSpecifier) && resolved.some((item) => item.sourceProof.type !== "immutable")) throw new Error(`Compiler source has no immutable identity: ${file}:${dependency}`);
1375
+ if (tool === "typescript" && /^(?:git(?:\+[^:]+)?:|github:|gitlab:|bitbucket:|ssh:|git@)/i.test(sourceSpecifier) && resolved.some((item) => !/(?:git(?:\+[^:]+)?:|github:|gitlab:|bitbucket:|ssh:|git@|\.git(?:#|["\\]))/i.test(item.identity))) throw new Error(`Compiler Git source has no matching immutable identity: ${file}:${dependency}`);
1376
+ const range = effectiveSpecifier.startsWith("npm:") ? effectiveSpecifier.slice(effectiveSpecifier.lastIndexOf("@") + 1) : effectiveSpecifier;
1377
+ if ((0, semver.validRange)(range) && !resolved.some((item) => (0, semver.valid)(item.version) !== null && (0, semver.satisfies)(item.version, range))) throw new Error(`Toolchain lock resolution does not satisfy manifest: ${file}:${dependency}`);
1378
+ for (const item of resolved) tools[tool].add(`${file}:${dependency}=>${item.location}:${item.name}@${item.version}:${item.identity}`);
1379
+ }
1380
+ }
1381
+ }
1382
+ for (const [lockFile, lock] of locks) {
1383
+ for (const item of lock.resolutions) {
1384
+ tools[item.tool].add(`${lockFile}:${item.location}:${item.dependency}=>${item.name}@${item.version}:${item.identity}`);
1385
+ if (item.tool === "typescript") typescript.add(`${item.name}@${item.version}`);
1386
+ }
1387
+ if (node_path.default.posix.basename(lockFile) !== "pnpm-lock.yaml") continue;
1388
+ for (const [directory, dependencies] of Object.entries(lock.importers)) for (const [dependency, version] of Object.entries(dependencies)) {
1389
+ const namedTool = changedPackageTool(dependency);
1390
+ const nonRegistry = nonRegistryResolution(version);
1391
+ const registryName = importerRegistryName({
1392
+ dependency,
1393
+ version
1394
+ });
1395
+ const tool = (registryName === void 0 ? void 0 : changedPackageTool(registryName)) ?? namedTool ?? (nonRegistry ? void 0 : lock.resolutions.find((item) => item.dependency === dependency)?.tool);
1396
+ if (tool !== void 0) tools[tool].add(`${lockFile}:importer:${directory}:${dependency}:${version}`);
1397
+ }
1398
+ }
1399
+ if (bunVersions.size > 1) throw new Error("Conflicting Bun runtime versions in tracked declarations");
1400
+ const bun = [...bunVersions].sort();
1401
+ if (bunRanges.length > 0 && bun.length === 0) throw new Error("Bun engine range has no exact runtime declaration");
1402
+ for (const range of bunRanges) if (!bun.every((version) => (0, semver.satisfies)(version, range))) throw new Error("Bun runtime does not satisfy tracked engine range");
1403
+ return {
1404
+ tools,
1405
+ current: {
1406
+ bun,
1407
+ typescript: [...typescript].sort()
1408
+ }
1409
+ };
1410
+ };
1411
+ const detectToolchainChanges = async ({ repo, since }) => {
1412
+ try {
1413
+ const refs = await Promise.all([since, "HEAD"].map(async (ref) => {
1414
+ const { stdout } = await git("git", [
1415
+ "rev-parse",
1416
+ "--verify",
1417
+ "--end-of-options",
1418
+ `${ref}^{commit}`
1419
+ ], {
1420
+ cwd: repo,
1421
+ encoding: "utf8"
1422
+ });
1423
+ const oid = stdout.trim();
1424
+ if (!/^[a-f0-9]{40,64}$/.test(oid)) throw new Error("Invalid Git commit response");
1425
+ return oid;
1426
+ }));
1427
+ const previousRef = refs.at(0);
1428
+ const currentRef = refs.at(1);
1429
+ if (!previousRef || !currentRef) throw new Error("Missing Git comparison snapshot");
1430
+ const [previousEntries, currentEntries] = await Promise.all([readTree({
1431
+ repo,
1432
+ ref: previousRef
1433
+ }), readTree({
1434
+ repo,
1435
+ ref: currentRef
1436
+ })]);
1437
+ const snapshots = await readSnapshots({
1438
+ repo,
1439
+ trees: [previousEntries, currentEntries]
1440
+ });
1441
+ const previousSnapshot = snapshots.at(0);
1442
+ const currentSnapshot = snapshots.at(1);
1443
+ if (!previousSnapshot || !currentSnapshot) throw new Error("Missing toolchain snapshot");
1444
+ const previous = parseSnapshot(previousSnapshot);
1445
+ const current = parseSnapshot(currentSnapshot);
1446
+ const tools = toolchainChangedTools.filter((tool) => stableJson([...previous.tools[tool]].sort()) !== stableJson([...current.tools[tool]].sort()));
1447
+ return {
1448
+ status: "compared",
1449
+ changed: tools.length > 0,
1450
+ tools,
1451
+ current: current.current
1452
+ };
1453
+ } catch (error) {
1454
+ return {
1455
+ status: "unreadable",
1456
+ changed: true,
1457
+ tools: [...toolchainChangedTools],
1458
+ error: error instanceof Error ? error.message : "Unreadable toolchain Git snapshot"
1459
+ };
1460
+ }
1461
+ };
1462
+ //#endregion
1463
+ Object.defineProperty(exports, "detectToolchainChanges", {
1464
+ enumerable: true,
1465
+ get: function() {
1466
+ return detectToolchainChanges;
1467
+ }
1468
+ });
1469
+
1470
+ //# sourceMappingURL=toolchain-changed.cjs.map