@stll/oxlint-config 3.0.0 → 3.1.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.
@@ -31,9 +31,9 @@ const repositoryBun = (floor, manifest) => {
31
31
  };
32
32
  //#endregion
33
33
  //#region src/toolchain-consumer-checks.ts
34
- const consumerRunnerVersion = "3.0.0";
34
+ const consumerRunnerVersion = "3.1.0";
35
35
  const consumerCheckWorkflow = "stella/.github/.github/workflows/package-consumer-compat.yml";
36
- const record$10 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36
+ const record$11 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
37
37
  const repositoryDirectory = (value) => typeof value === "string" && value !== "" && !value.includes("\\") && !value.includes("${{") && !value.split("").some((character) => {
38
38
  const code = character.charCodeAt(0);
39
39
  return code < 32 || code === 127;
@@ -119,12 +119,12 @@ const validConsumerCron = (text) => {
119
119
  });
120
120
  };
121
121
  const nightlySchedule = (value) => {
122
- if (!record$10(value)) return false;
122
+ if (!record$11(value)) return false;
123
123
  const schedule = value["schedule"];
124
124
  if (!Array.isArray(schedule) || schedule.length === 0) return false;
125
125
  const coveredDays = /* @__PURE__ */ new Set();
126
126
  for (const entry of schedule) {
127
- if (!record$10(entry) || typeof entry["cron"] !== "string" || !validConsumerCron(entry["cron"])) return false;
127
+ if (!record$11(entry) || typeof entry["cron"] !== "string" || !validConsumerCron(entry["cron"])) return false;
128
128
  const fields = entry["cron"].trim().split(/\s+/);
129
129
  const selections = cronFields.map((field, index) => {
130
130
  const text = fields.at(index);
@@ -140,7 +140,7 @@ const parseConsumerChecks = (input) => {
140
140
  if (!Array.isArray(input)) throw new Error("consumerChecks must be an array");
141
141
  const targets = /* @__PURE__ */ new Set();
142
142
  return input.map((entry) => {
143
- if (!record$10(entry) || Object.keys(entry).some((key) => ![
143
+ if (!record$11(entry) || Object.keys(entry).some((key) => ![
144
144
  "workflow",
145
145
  "job",
146
146
  "packages",
@@ -178,9 +178,9 @@ const checkConsumerChecks = ({ declarations, files, policy }) => {
178
178
  });
179
179
  if (document.errors.length > 0) throw new Error("invalid workflow YAML");
180
180
  const source = document.toJS({ maxAliasCount: 100 });
181
- if (!record$10(source) || !record$10(source["jobs"])) continue;
181
+ if (!record$11(source) || !record$11(source["jobs"])) continue;
182
182
  for (const [jobName, job] of Object.entries(source["jobs"])) {
183
- if (!record$10(job) || typeof job["uses"] !== "string") continue;
183
+ if (!record$11(job) || typeof job["uses"] !== "string") continue;
184
184
  const [workflowPath, ref] = job["uses"].split("@");
185
185
  if (workflowPath?.toLowerCase() !== "stella/.github/.github/workflows/package-consumer-compat.yml") continue;
186
186
  const declaration = declarations.find((entry) => entry.workflow === workflow && entry.job === jobName);
@@ -194,7 +194,7 @@ const checkConsumerChecks = ({ declarations, files, policy }) => {
194
194
  const approved = Object.entries(policy.actions).find(([name]) => name.toLowerCase() === consumerCheckWorkflow)?.[1];
195
195
  if (job["uses"].split("@").length !== 2 || ref === void 0 || !/^[a-f0-9]{40}$/.test(ref) || approved === void 0 || ref !== approved.sha || "steps" in job || "runs-on" in job) throw new Error(`${workflow}:${jobName} must invoke the approved immutable consumer workflow as a reusable job`);
196
196
  const inputs = job["with"];
197
- if (!record$10(inputs)) throw new Error(`${workflow}:${jobName} requires consumer inputs`);
197
+ if (!record$11(inputs)) throw new Error(`${workflow}:${jobName} requires consumer inputs`);
198
198
  const names = /* @__PURE__ */ new Set();
199
199
  for (const key of Object.keys(inputs)) {
200
200
  const normalized = key.toLowerCase();
@@ -264,7 +264,7 @@ const locatorValidators = {
264
264
  "bun-source": /^(?:jobs\.[\w-]+|runs)\.steps\.[\w-]+$/
265
265
  };
266
266
  const knownKind = (value) => typeof value === "string" && Object.hasOwn(locatorValidators, value);
267
- const record$9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
267
+ const record$10 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
268
268
  /** One reviewed declaration identifies one unresolved selector, never a rule. */
269
269
  const parseDynamicSelectors = (input) => {
270
270
  if (input === void 0) return [];
@@ -272,7 +272,7 @@ const parseDynamicSelectors = (input) => {
272
272
  const result = [];
273
273
  const keys = /* @__PURE__ */ new Set();
274
274
  for (const entry of input) {
275
- if (!record$9(entry) || Object.keys(entry).some((key) => ![
275
+ if (!record$10(entry) || Object.keys(entry).some((key) => ![
276
276
  "path",
277
277
  "kind",
278
278
  "reason",
@@ -308,7 +308,7 @@ const parseDynamicSelectors = (input) => {
308
308
  };
309
309
  //#endregion
310
310
  //#region src/toolchain-engine-floors.ts
311
- const record$8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
311
+ const record$9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
312
312
  const repositoryPath = (value) => typeof value === "string" && value !== "" && !/^[A-Za-z]:/.test(value) && !/[\\\s]/.test(value) && !Array.from(value).some((character) => character.charCodeAt(0) < 32) && value.split("/").every((part) => part !== "" && part !== "." && part !== "..");
313
313
  const parseEngineFloors = (input) => {
314
314
  if (input === void 0) return [];
@@ -316,7 +316,7 @@ const parseEngineFloors = (input) => {
316
316
  const result = [];
317
317
  const jobs = /* @__PURE__ */ new Set();
318
318
  for (const entry of input) {
319
- if (!record$8(entry) || Object.keys(entry).some((key) => ![
319
+ if (!record$9(entry) || Object.keys(entry).some((key) => ![
320
320
  "package",
321
321
  "workflow",
322
322
  "job"
@@ -337,16 +337,16 @@ const resolveEngineFloor = (entry, files) => {
337
337
  const workflowText = files[entry.workflow];
338
338
  if (manifestText === void 0 || workflowText === void 0) throw new Error("engine floor package and workflow must be tracked");
339
339
  const manifest = JSON.parse(manifestText);
340
- if (!record$8(manifest) || manifest["private"] === true || typeof manifest["name"] !== "string" || manifest["name"].trim() === "" || !record$8(manifest["engines"]) || typeof manifest["engines"]["node"] !== "string") throw new Error("engine floor package must be published and declare engines.node");
340
+ if (!record$9(manifest) || manifest["private"] === true || typeof manifest["name"] !== "string" || manifest["name"].trim() === "" || !record$9(manifest["engines"]) || typeof manifest["engines"]["node"] !== "string") throw new Error("engine floor package must be published and declare engines.node");
341
341
  const range = manifest["engines"]["node"];
342
342
  const minimum = validRange(range) === null ? null : minVersion(range);
343
343
  if (minimum === null || minimum.prerelease.length !== 0) throw new Error("engine floor engines.node must declare a stable minimum");
344
344
  const document = parseDocument(workflowText, { merge: true });
345
345
  if (document.errors.length !== 0) throw new Error("engine floor workflow must be valid YAML");
346
346
  const workflow = document.toJS({ maxAliasCount: 100 });
347
- if (!record$8(workflow) || !record$8(workflow["jobs"])) throw new Error("engine floor job must exist in its tracked workflow");
347
+ if (!record$9(workflow) || !record$9(workflow["jobs"])) throw new Error("engine floor job must exist in its tracked workflow");
348
348
  const job = workflow["jobs"][entry.job];
349
- if (!record$8(job)) throw new Error("engine floor job must exist in its tracked workflow");
349
+ if (!record$9(job)) throw new Error("engine floor job must exist in its tracked workflow");
350
350
  if (!Array.isArray(job["steps"]) || Object.hasOwn(job, "uses")) throw new Error("engine floor job must declare steps and cannot reuse a workflow");
351
351
  return {
352
352
  ...entry,
@@ -376,7 +376,7 @@ const installSections = [
376
376
  "optionalDependencies"
377
377
  ];
378
378
  const allowlistFile = "stll-toolchain.json";
379
- const record$7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
379
+ const record$8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
380
380
  /** `npm:typescript@7.0.2` installs a compiler under another name; returns its version. */
381
381
  const typescriptAliasVersion = (spec) => {
382
382
  if (typeof spec !== "string") return void 0;
@@ -406,7 +406,7 @@ const parseTypescriptAllowlist = (input) => {
406
406
  return input.map((entry) => {
407
407
  const shown = JSON.stringify(entry);
408
408
  const text = (key) => {
409
- const value = record$7(entry) ? entry[key] : void 0;
409
+ const value = record$8(entry) ? entry[key] : void 0;
410
410
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
411
411
  };
412
412
  const workspace = text("workspace");
@@ -414,7 +414,7 @@ const parseTypescriptAllowlist = (input) => {
414
414
  const spec = text("spec");
415
415
  const dependency = text("dependency");
416
416
  const reason = text("reason");
417
- if (!record$7(entry) || Object.keys(entry).length !== allowlistKeys.length || workspace === void 0 || name === void 0 || spec === void 0 || dependency === void 0 || reason === void 0) throw new Error(`typescriptAllowlist entry ${shown} must have exactly workspace, name, spec, dependency and reason as nonempty strings: ${allowlistFormat}`);
417
+ if (!record$8(entry) || Object.keys(entry).length !== allowlistKeys.length || workspace === void 0 || name === void 0 || spec === void 0 || dependency === void 0 || reason === void 0) throw new Error(`typescriptAllowlist entry ${shown} must have exactly workspace, name, spec, dependency and reason as nonempty strings: ${allowlistFormat}`);
418
418
  if (workspace !== "." && (path.posix.normalize(workspace) !== workspace || workspace.startsWith("/") || workspace.endsWith("/") || workspace === ".." || workspace.startsWith("../"))) throw new Error(`typescriptAllowlist entry ${shown}: workspace must be "." for the root or a normalized relative package directory such as "apps/web"`);
419
419
  if (!exactTypescriptSpec(spec)) throw new Error(`typescriptAllowlist entry ${shown}: spec must be the exact spec the manifest resolves to, such as "6.0.3" or "npm:typescript@6.0.3", not a range or catalog reference`);
420
420
  if (!isTypescriptInstall(name, spec)) throw new Error(`typescriptAllowlist entry ${shown}: name must be typescript or @typescript/native, or an alias whose spec is npm:typescript@<version> or npm:@typescript/native@<version>`);
@@ -990,10 +990,10 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
990
990
  const document = pnpm ? parseDocument(text, { lineCounter }) : void 0;
991
991
  if (document !== void 0 && document.errors.length > 0) throw new Error("invalid YAML");
992
992
  const json = document === void 0 ? JSON.parse(text) : document.toJS();
993
- if (!record$7(json)) throw new Error("manifest must be an object");
993
+ if (!record$8(json)) throw new Error("manifest must be an object");
994
994
  if (pnpm) {
995
- if (json["catalog"] !== void 0 && !record$7(json["catalog"])) throw new Error("catalog must be a mapping");
996
- if (json["catalogs"] !== void 0 && (!record$7(json["catalogs"]) || Object.values(json["catalogs"]).some((catalog) => !record$7(catalog)))) throw new Error("named catalogs must be mappings");
995
+ if (json["catalog"] !== void 0 && !record$8(json["catalog"])) throw new Error("catalog must be a mapping");
996
+ if (json["catalogs"] !== void 0 && (!record$8(json["catalogs"]) || Object.values(json["catalogs"]).some((catalog) => !record$8(catalog)))) throw new Error("named catalogs must be mappings");
997
997
  if (json["packages"] !== void 0 && (!Array.isArray(json["packages"]) || !json["packages"].every((pattern) => typeof pattern === "string"))) throw new Error("workspace packages must be patterns");
998
998
  }
999
999
  manifests.set(file, json);
@@ -1007,13 +1007,13 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1007
1007
  catalog: json["overrides"],
1008
1008
  prefix: ["overrides"]
1009
1009
  },
1010
- ...record$7(json["catalogs"]) ? Object.entries(json["catalogs"]).map(([name, catalog]) => ({
1010
+ ...record$8(json["catalogs"]) ? Object.entries(json["catalogs"]).map(([name, catalog]) => ({
1011
1011
  catalog,
1012
1012
  prefix: ["catalogs", name]
1013
1013
  })) : []
1014
1014
  ];
1015
1015
  for (const { catalog, prefix } of catalogEntries) {
1016
- if (!record$7(catalog)) continue;
1016
+ if (!record$8(catalog)) continue;
1017
1017
  for (const [name, value] of Object.entries(catalog)) {
1018
1018
  const node = document.getIn([...prefix, name], true);
1019
1019
  if (!isNode(node) || node.range === void 0 || node.range === null) continue;
@@ -1032,7 +1032,7 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1032
1032
  });
1033
1033
  }
1034
1034
  }
1035
- const catalogSources = (json) => [json, ...record$7(json["workspaces"]) ? [json["workspaces"]] : []];
1035
+ const catalogSources = (json) => [json, ...record$8(json["workspaces"]) ? [json["workspaces"]] : []];
1036
1036
  const pnpmWorkspaceFor = (file) => {
1037
1037
  let directory = path.posix.dirname(file);
1038
1038
  while (true) {
@@ -1055,8 +1055,8 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1055
1055
  patterns: workspace["packages"],
1056
1056
  rootMembership: "implicit"
1057
1057
  })) return void 0;
1058
- if (name === "" || name === "default") return workspace["catalog"] ?? (record$7(workspace["catalogs"]) ? workspace["catalogs"]["default"] : void 0);
1059
- return record$7(workspace["catalogs"]) ? workspace["catalogs"][name] : void 0;
1058
+ if (name === "" || name === "default") return workspace["catalog"] ?? (record$8(workspace["catalogs"]) ? workspace["catalogs"]["default"] : void 0);
1059
+ return record$8(workspace["catalogs"]) ? workspace["catalogs"][name] : void 0;
1060
1060
  }
1061
1061
  let directory = path.posix.dirname(file);
1062
1062
  while (true) {
@@ -1064,7 +1064,7 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1064
1064
  if (manifest !== void 0) {
1065
1065
  for (const source of catalogSources(manifest)) {
1066
1066
  if (name === "" && source["catalog"] !== void 0) return source["catalog"];
1067
- if (record$7(source["catalogs"]) && source["catalogs"][name] !== void 0) return source["catalogs"][name];
1067
+ if (record$8(source["catalogs"]) && source["catalogs"][name] !== void 0) return source["catalogs"][name];
1068
1068
  }
1069
1069
  if (manifest["workspaces"] !== void 0) return void 0;
1070
1070
  }
@@ -1082,7 +1082,7 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1082
1082
  };
1083
1083
  visited.add(value);
1084
1084
  const catalog = catalogFor(file, value.slice(8));
1085
- if (!record$7(catalog)) return {
1085
+ if (!record$8(catalog)) return {
1086
1086
  value: void 0,
1087
1087
  catalogs
1088
1088
  };
@@ -1107,7 +1107,7 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1107
1107
  const workspaces = manifests.get(path.posix.join(directory, "package.json"))?.["workspaces"];
1108
1108
  if (workspaces !== void 0) return {
1109
1109
  directory,
1110
- patterns: record$7(workspaces) ? workspaces["packages"] : workspaces,
1110
+ patterns: record$8(workspaces) ? workspaces["packages"] : workspaces,
1111
1111
  source: "json"
1112
1112
  };
1113
1113
  if (directory === ".") return void 0;
@@ -1141,7 +1141,7 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1141
1141
  if (json === void 0) continue;
1142
1142
  for (const section of installSections) {
1143
1143
  const entries = json[section];
1144
- if (!record$7(entries) || !Object.hasOwn(entries, entry.name)) continue;
1144
+ if (!record$8(entries) || !Object.hasOwn(entries, entry.name)) continue;
1145
1145
  const { value, catalogs } = resolveThrough(file, entry.name, entries[entry.name]);
1146
1146
  if (value !== entry.spec) continue;
1147
1147
  for (const catalog of catalogs) {
@@ -1186,7 +1186,7 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1186
1186
  message: `packageManager Bun version must be ${policy.bun} (one exact release at or above the shared minimum, the same in every manifest), found ${JSON.stringify(manager)}`
1187
1187
  });
1188
1188
  const checkEntries = (entries, scope, location) => {
1189
- if (!record$7(entries)) return;
1189
+ if (!record$8(entries)) return;
1190
1190
  for (const [name, raw] of Object.entries(entries)) {
1191
1191
  const value = resolve(file, name, raw);
1192
1192
  if (isTypescriptInstall(name, value)) {
@@ -1226,13 +1226,13 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1226
1226
  }
1227
1227
  };
1228
1228
  for (const key of installSections) checkEntries(json[key], "install");
1229
- if (record$7(json["peerDependencies"])) for (const [name, value] of Object.entries(json["peerDependencies"])) {
1229
+ if (record$8(json["peerDependencies"])) for (const [name, value] of Object.entries(json["peerDependencies"])) {
1230
1230
  const resolved = resolve(file, name, value);
1231
1231
  if (isTypescriptInstall(name, resolved) && !peerRange(resolved)) checkEntries({ [name]: value }, "peer");
1232
1232
  }
1233
1233
  for (const source of catalogSources(json)) {
1234
1234
  checkEntries(source["catalog"], "catalog");
1235
- if (!record$7(source["catalogs"])) continue;
1235
+ if (!record$8(source["catalogs"])) continue;
1236
1236
  for (const catalog of Object.values(source["catalogs"])) checkEntries(catalog, "catalog");
1237
1237
  }
1238
1238
  const ownedNames = /* @__PURE__ */ new Set([...pins.keys(), ...typescriptPackages]);
@@ -1244,11 +1244,11 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1244
1244
  "optionalDependencies"
1245
1245
  ]) {
1246
1246
  const entries = resolutionRoot[section];
1247
- if (!record$7(entries)) continue;
1247
+ if (!record$8(entries)) continue;
1248
1248
  for (const [name, value] of Object.entries(entries)) resolutionDependencies[name] = resolve(file, name, value);
1249
1249
  }
1250
1250
  const checkResolutionMap = (entries, parent) => {
1251
- if (!record$7(entries)) return;
1251
+ if (!record$8(entries)) return;
1252
1252
  for (const [selector, replacement] of Object.entries(entries)) {
1253
1253
  const target = selector === "." ? parent : selector;
1254
1254
  if (target === void 0) continue;
@@ -1267,7 +1267,7 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1267
1267
  });
1268
1268
  continue;
1269
1269
  }
1270
- if (record$7(replacement)) {
1270
+ if (record$8(replacement)) {
1271
1271
  checkResolutionMap(replacement, target);
1272
1272
  continue;
1273
1273
  }
@@ -1287,8 +1287,8 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1287
1287
  };
1288
1288
  checkResolutionMap(json["overrides"]);
1289
1289
  checkResolutionMap(json["resolutions"]);
1290
- if (record$7(json["pnpm"])) checkResolutionMap(json["pnpm"]["overrides"]);
1291
- const command = record$7(json["scripts"]) ? json["scripts"]["typecheck"] : void 0;
1290
+ if (record$8(json["pnpm"])) checkResolutionMap(json["pnpm"]["overrides"]);
1291
+ const command = record$8(json["scripts"]) ? json["scripts"]["typecheck"] : void 0;
1292
1292
  if (typeof command === "string") for (const words of compilerCommandSegments(command)) {
1293
1293
  if (!invokesCompiler(words)) continue;
1294
1294
  const normalized = words.join(" ");
@@ -1302,7 +1302,7 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1302
1302
  });
1303
1303
  break;
1304
1304
  }
1305
- if (record$7(json["peerDependencies"])) for (const [name, raw] of Object.entries(json["peerDependencies"])) {
1305
+ if (record$8(json["peerDependencies"])) for (const [name, raw] of Object.entries(json["peerDependencies"])) {
1306
1306
  if (!typescriptPackages.has(name)) continue;
1307
1307
  const range = resolve(file, name, raw);
1308
1308
  if (!peerRange(range)) continue;
@@ -1326,7 +1326,7 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1326
1326
  message: `${name} peer support range must include ${release}, found ${range}`
1327
1327
  });
1328
1328
  }
1329
- if (record$7(json["engines"]) && json["engines"]["node"] !== void 0 && (typeof json["engines"]["node"] !== "string" || !nodeSupportRangeMatches(policy.node, json["engines"]["node"]))) add({
1329
+ if (record$8(json["engines"]) && json["engines"]["node"] !== void 0 && (typeof json["engines"]["node"] !== "string" || !nodeSupportRangeMatches(policy.node, json["engines"]["node"]))) add({
1330
1330
  file,
1331
1331
  rule: "node-engine",
1332
1332
  key: "node",
@@ -1340,7 +1340,7 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1340
1340
  if (json === void 0) return `${file} is missing or invalid; remove the entry`;
1341
1341
  const declares = (name) => installSections.some((section) => {
1342
1342
  const entries = json[section];
1343
- return record$7(entries) && Object.hasOwn(entries, name);
1343
+ return record$8(entries) && Object.hasOwn(entries, name);
1344
1344
  });
1345
1345
  if (!declares(entry.name)) return `${file} no longer declares ${entry.name} in dependencies, devDependencies or optionalDependencies; remove the entry`;
1346
1346
  if (!declares(entry.dependency)) return `${file} no longer depends on ${entry.dependency}; remove the entry and the ${entry.name} declaration, or name the dependency that still needs it`;
@@ -1379,9 +1379,9 @@ const extensionKinds = new Map(Object.entries({
1379
1379
  const scriptSourceKind = (file) => extensionKinds.get(extname(file));
1380
1380
  //#endregion
1381
1381
  //#region src/toolchain-script-lifecycle.ts
1382
- const record$6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1382
+ const record$7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1383
1383
  const declaredManager = (value) => typeof value === "string" ? /^(bun|npm|pnpm|yarn)@(\d+\.\d+\.\d+)(?:\+\S+)?$/u.exec(value) : null;
1384
- const rootScriptManager = (manifest, context) => declaredManager(context.packageManager ?? (record$6(manifest) ? manifest["packageManager"] : void 0))?.at(1);
1384
+ const rootScriptManager = (manifest, context) => declaredManager(context.packageManager ?? (record$7(manifest) ? manifest["packageManager"] : void 0))?.at(1);
1385
1385
  const npmHooksEnabled = (file) => {
1386
1386
  if (file?.type === "unknown") throw new Error("npm lifecycle configuration must be a tracked regular file");
1387
1387
  if (file?.type !== "tracked") return true;
@@ -1412,12 +1412,12 @@ const yamlSettings = (file, manager) => {
1412
1412
  } catch {
1413
1413
  throw new Error(`Cannot parse ${manager} lifecycle configuration`);
1414
1414
  }
1415
- if (!record$6(settings)) throw new Error(`${manager} lifecycle configuration must be a mapping`);
1415
+ if (!record$7(settings)) throw new Error(`${manager} lifecycle configuration must be a mapping`);
1416
1416
  return settings;
1417
1417
  };
1418
1418
  const pnpmHooksEnabled = (manifest, context) => {
1419
1419
  const declared = declaredManager(context.packageManager ?? manifest["packageManager"]);
1420
- const dependency = [manifest["dependencies"], manifest["devDependencies"]].flatMap((field) => record$6(field) ? [field["pnpm"]] : []).find((value) => typeof value === "string");
1420
+ const dependency = [manifest["dependencies"], manifest["devDependencies"]].flatMap((field) => record$7(field) ? [field["pnpm"]] : []).find((value) => typeof value === "string");
1421
1421
  if ((declared?.at(1) === "pnpm" ? declared.at(2) : dependency) !== "12.9.1") throw new Error("pnpm lifecycle discovery requires declared pnpm@12.9.1");
1422
1422
  const enabled = yamlSettings(context.pnpmWorkspace, "pnpm")["enablePrePostScripts"];
1423
1423
  if (enabled === void 0) return true;
@@ -1461,7 +1461,7 @@ const scriptLifecycleStages = ({ name, manager, manifest, scripts, context }) =>
1461
1461
  };
1462
1462
  //#endregion
1463
1463
  //#region src/toolchain-lint-command.ts
1464
- const record$5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1464
+ const record$6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1465
1465
  const scriptManagers = /* @__PURE__ */ new Set([
1466
1466
  "bun",
1467
1467
  "npm",
@@ -1578,7 +1578,7 @@ const lintInventoryCommands = (manifest, forceEnabled = false, context = {}) =>
1578
1578
  const errors = [];
1579
1579
  const workspaceTasks = /* @__PURE__ */ new Set();
1580
1580
  const filteredBuilds = /* @__PURE__ */ new Set();
1581
- const declaresEngine = record$5(manifest) && [manifest["dependencies"], manifest["devDependencies"]].some((dependencies) => record$5(dependencies) && "oxlint-tsgolint" in dependencies);
1581
+ const declaresEngine = record$6(manifest) && [manifest["dependencies"], manifest["devDependencies"]].some((dependencies) => record$6(dependencies) && "oxlint-tsgolint" in dependencies);
1582
1582
  const enabled = forceEnabled || declaresEngine;
1583
1583
  if (!enabled) return {
1584
1584
  commands,
@@ -1586,14 +1586,14 @@ const lintInventoryCommands = (manifest, forceEnabled = false, context = {}) =>
1586
1586
  enabled,
1587
1587
  workspaceTask: false
1588
1588
  };
1589
- if (!record$5(manifest)) return {
1589
+ if (!record$6(manifest)) return {
1590
1590
  commands,
1591
1591
  errors: ["lint inventory requires an object package manifest"],
1592
1592
  enabled,
1593
1593
  workspaceTask: false
1594
1594
  };
1595
1595
  const scripts = manifest["scripts"];
1596
- if (!record$5(scripts)) return {
1596
+ if (!record$6(scripts)) return {
1597
1597
  commands,
1598
1598
  errors: ["type-aware linting requires a root lint script"],
1599
1599
  enabled,
@@ -1735,7 +1735,7 @@ const checkProjectOwnership = ({ trackedFiles, lintedFiles, projectFiles }) => [
1735
1735
  line: 1,
1736
1736
  message: "type-aware lint would use an inferred project; include this file in a declared tsconfig project (for Node scripts, add scripts/tsconfig.json extending @stll/typescript-config/node.json; see the Node scripts recipe in stella/tooling README)"
1737
1737
  }));
1738
- const record$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1738
+ const record$5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1739
1739
  const stringList = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
1740
1740
  const readCommand = ({ root, executable, args, runtime }) => {
1741
1741
  const result = spawnSync(runtime, [
@@ -1779,7 +1779,7 @@ const resolveProjectApi = (root) => {
1779
1779
  for (const base of [import.meta.url, path.join(root, "package.json")]) try {
1780
1780
  return pathToFileURL(createRequire(base).resolve("tsrs/unstable/async")).href;
1781
1781
  } catch (error) {
1782
- const code = record$4(error) ? error["code"] : void 0;
1782
+ const code = record$5(error) ? error["code"] : void 0;
1783
1783
  if (code !== "MODULE_NOT_FOUND" && code !== "ERR_MODULE_NOT_FOUND") throw error;
1784
1784
  }
1785
1785
  throw new Error("configuration discovery needs tsrs: reinstall @stll/oxlint-config, which depends on it");
@@ -1802,7 +1802,7 @@ const readProjectConfig = (root, config) => {
1802
1802
  if (result.error !== void 0) throw result.error;
1803
1803
  if (result.status !== 0) throw new Error(`configuration discovery failed: ${(result.stderr + result.stdout).trim()}`);
1804
1804
  const parsed = JSON.parse(result.stdout);
1805
- if (!record$4(parsed) || !stringList(parsed["files"]) || !stringList(parsed["references"]) || typeof parsed["allowJs"] !== "boolean" || typeof parsed["checkJs"] !== "boolean") throw new Error("compiler configuration must list files and references");
1805
+ if (!record$5(parsed) || !stringList(parsed["files"]) || !stringList(parsed["references"]) || typeof parsed["allowJs"] !== "boolean" || typeof parsed["checkJs"] !== "boolean") throw new Error("compiler configuration must list files and references");
1806
1806
  return {
1807
1807
  files: parsed["files"],
1808
1808
  references: parsed["references"],
@@ -1827,7 +1827,7 @@ const lifecycleFile = (root, file, tracked) => {
1827
1827
  };
1828
1828
  /** Inventory and config expansion only: neither subprocess lints or typechecks. */
1829
1829
  const checkNodeProjects = ({ root, manifest, trackedFiles }) => {
1830
- const packageManager = record$4(manifest) ? manifest["packageManager"] : void 0;
1830
+ const packageManager = record$5(manifest) ? manifest["packageManager"] : void 0;
1831
1831
  const managerUsesPnpmWorkspace = packageManager === void 0 || typeof packageManager === "string" && packageManager.startsWith("pnpm@");
1832
1832
  const discovered = /* @__PURE__ */ new Map();
1833
1833
  const workspaceMembers = (pnpmMembership) => {
@@ -1863,7 +1863,7 @@ const checkNodeProjects = ({ root, manifest, trackedFiles }) => {
1863
1863
  return nestedLifecycle;
1864
1864
  };
1865
1865
  const lifecycle = {
1866
- packageManager: record$4(manifest) ? manifest["packageManager"] : void 0,
1866
+ packageManager: record$5(manifest) ? manifest["packageManager"] : void 0,
1867
1867
  npmrc: lifecycleFile(root, ".npmrc", trackedFiles),
1868
1868
  pnpmWorkspace: lifecycleFile(root, "pnpm-workspace.yaml", trackedFiles),
1869
1869
  yarnrc: lifecycleFile(root, ".yarnrc.yml", trackedFiles),
@@ -1874,7 +1874,7 @@ const checkNodeProjects = ({ root, manifest, trackedFiles }) => {
1874
1874
  if (member === void 0) throw new Error("Missing selected workspace");
1875
1875
  const [directory, pkg] = member;
1876
1876
  const scripts = pkg["scripts"];
1877
- if (!record$4(scripts) || typeof scripts["build"] !== "string") throw new Error("Filtered Bun build requires a literal build script");
1877
+ if (!record$5(scripts) || typeof scripts["build"] !== "string") throw new Error("Filtered Bun build requires a literal build script");
1878
1878
  if (["prebuild", "postbuild"].some((name) => scripts[name] !== void 0 && scripts[name] !== "")) throw new Error("Filtered Bun build lifecycle hooks are unsupported for lint inventory; invoke lint scopes explicitly");
1879
1879
  if (activeBuilds.has(directory)) throw new Error("Cyclic filtered Bun build delegation");
1880
1880
  activeBuilds.add(directory);
@@ -1916,7 +1916,7 @@ const checkNodeProjects = ({ root, manifest, trackedFiles }) => {
1916
1916
  for (const [directory, workspace] of workspaceMembers(managerUsesPnpmWorkspace)) {
1917
1917
  if (directory === ".") continue;
1918
1918
  const file = `${directory}/package.json`;
1919
- if (!record$4(workspace["scripts"]) || workspace["scripts"]["lint"] === void 0) continue;
1919
+ if (!record$5(workspace["scripts"]) || workspace["scripts"]["lint"] === void 0) continue;
1920
1920
  const nestedLifecycle = workspaceLifecycle(directory);
1921
1921
  const nested = lintInventoryCommands(workspace, true, nestedLifecycle);
1922
1922
  if (nested.workspaceTask) throw new Error(`nested workspace lint delegation is unsupported: ${file}`);
@@ -2025,7 +2025,7 @@ const checkNodeProjects = ({ root, manifest, trackedFiles }) => {
2025
2025
  };
2026
2026
  //#endregion
2027
2027
  //#region src/toolchain-rust.ts
2028
- const record$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2028
+ const record$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2029
2029
  /**
2030
2030
  * Exceptions authorize one immutable release, never a channel or an entire
2031
2031
  * rule. Expiry is scoped separately by checkRustExceptionExpiry.
@@ -2035,7 +2035,7 @@ const parseRustExceptions = (input) => {
2035
2035
  if (!Array.isArray(input)) throw new Error("rustExceptions must be an array");
2036
2036
  const pins = /* @__PURE__ */ new Set();
2037
2037
  return input.map((entry) => {
2038
- if (!record$3(entry) || Object.keys(entry).length !== 4 || Object.keys(entry).some((key) => ![
2038
+ if (!record$4(entry) || Object.keys(entry).length !== 4 || Object.keys(entry).some((key) => ![
2039
2039
  "pin",
2040
2040
  "reason",
2041
2041
  "tracking",
@@ -2053,8 +2053,8 @@ const parseRustExceptions = (input) => {
2053
2053
  /** Whether the base stll-toolchain.json declares this exception with the same expiry. */
2054
2054
  const declaresException = (baseText, { pin, expires }) => {
2055
2055
  const base = JSON.parse(baseText);
2056
- const exceptions = record$3(base) ? base["rustExceptions"] : void 0;
2057
- return Array.isArray(exceptions) && exceptions.some((entry) => record$3(entry) && entry["pin"] === pin && entry["expires"] === expires);
2056
+ const exceptions = record$4(base) ? base["rustExceptions"] : void 0;
2057
+ return Array.isArray(exceptions) && exceptions.some((entry) => record$4(entry) && entry["pin"] === pin && entry["expires"] === expires);
2058
2058
  };
2059
2059
  /** An exception expires on its date; the scope decides whether that fails this run. */
2060
2060
  const checkRustExceptionExpiry = ({ exceptions, now = /* @__PURE__ */ new Date(), scope }) => {
@@ -2192,7 +2192,9 @@ const checkRustReferences = (options) => {
2192
2192
  }
2193
2193
  if (token.value.startsWith("-")) continue;
2194
2194
  selected++;
2195
- select(token.value, "rustup toolchain", token.line);
2195
+ const installFixture = options.type === "file" && isShellScript(file, text) && subcommand === "toolchain" && invocation.at(1)?.value === "install" && !invocation.some((word) => word.value === "--default") ? options.installFixtures?.find((entry) => entry.path === file && entry.pin === token.value) : void 0;
2196
+ if (installFixture !== void 0) options.onInstallFixture?.(installFixture);
2197
+ else select(token.value, "rustup toolchain", token.line);
2196
2198
  if (firstSelectorOnly) break;
2197
2199
  }
2198
2200
  if (selected === 0 && requiresSelector) select(void 0, "rustup toolchain", firstLine);
@@ -2213,7 +2215,7 @@ const checkRustReferences = (options) => {
2213
2215
  if (isShellScript(file, text)) shell(text);
2214
2216
  if (/(?:^|\/)package\.json$/.test(file)) try {
2215
2217
  const object = JSON.parse(text);
2216
- if (record$3(object) && record$3(object["scripts"])) {
2218
+ if (record$4(object) && record$4(object["scripts"])) {
2217
2219
  for (const [name, script] of Object.entries(object["scripts"])) if (typeof script === "string") shell(script, lineOf(`"${name}"`));
2218
2220
  }
2219
2221
  } catch {}
@@ -2224,7 +2226,7 @@ const checkRustReferences = (options) => {
2224
2226
  const version = tag === void 0 ? void 0 : rustImageVersion(tag);
2225
2227
  select(version, "Rust service image", lineOf(value));
2226
2228
  } else if (Array.isArray(value)) value.forEach(images);
2227
- else if (record$3(value)) {
2229
+ else if (record$4(value)) {
2228
2230
  for (const [key, entry] of Object.entries(value)) if (key !== "name") images(entry);
2229
2231
  }
2230
2232
  };
@@ -2248,13 +2250,13 @@ const checkRustReferences = (options) => {
2248
2250
  value.forEach(visit);
2249
2251
  return;
2250
2252
  }
2251
- if (!record$3(value)) return;
2252
- if (record$3(value["env"]) && value["env"]["RUSTUP_TOOLCHAIN"] !== void 0) select(value["env"]["RUSTUP_TOOLCHAIN"], "RUSTUP_TOOLCHAIN", lineOf("RUSTUP_TOOLCHAIN"));
2253
+ if (!record$4(value)) return;
2254
+ if (record$4(value["env"]) && value["env"]["RUSTUP_TOOLCHAIN"] !== void 0) select(value["env"]["RUSTUP_TOOLCHAIN"], "RUSTUP_TOOLCHAIN", lineOf("RUSTUP_TOOLCHAIN"));
2253
2255
  if (typeof value["run"] === "string") shell(value["run"], lineOf("run:"));
2254
2256
  const uses = value["uses"];
2255
2257
  if (typeof uses === "string") {
2256
2258
  const action = uses.split("@").at(0)?.toLowerCase();
2257
- const inputs = record$3(value["with"]) ? value["with"] : {};
2259
+ const inputs = record$4(value["with"]) ? value["with"] : {};
2258
2260
  if (action === "dtolnay/rust-toolchain" || action === "actions-rust-lang/setup-rust-toolchain") {
2259
2261
  const entries = Object.entries(inputs).filter(([key]) => key.toLowerCase() === "toolchain");
2260
2262
  if (entries.length !== 1) select(void 0, `${action} requires an explicit exact toolchain input`, lineOf(uses));
@@ -2268,8 +2270,8 @@ const checkRustReferences = (options) => {
2268
2270
  }
2269
2271
  }
2270
2272
  for (const [key, child] of Object.entries(value)) {
2271
- if (key === "workflow_call" && record$3(child) && record$3(child["inputs"])) {
2272
- for (const [input, declaration] of Object.entries(child["inputs"])) if (["toolchain", "rust-toolchain"].includes(input) && record$3(declaration) && declaration["default"] !== void 0) select(declaration["default"], `reusable workflow default ${input}`, lineOf("default:"));
2273
+ if (key === "workflow_call" && record$4(child) && record$4(child["inputs"])) {
2274
+ for (const [input, declaration] of Object.entries(child["inputs"])) if (["toolchain", "rust-toolchain"].includes(input) && record$4(declaration) && declaration["default"] !== void 0) select(declaration["default"], `reusable workflow default ${input}`, lineOf("default:"));
2273
2275
  }
2274
2276
  visit(child);
2275
2277
  }
@@ -2280,7 +2282,7 @@ const checkRustReferences = (options) => {
2280
2282
  };
2281
2283
  //#endregion
2282
2284
  //#region src/toolchain-workflow-trust.ts
2283
- const record$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2285
+ const record$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2284
2286
  const booleanParts = (source, operator) => {
2285
2287
  const parts = [];
2286
2288
  let depth = 0;
@@ -2530,7 +2532,7 @@ const safeAction = (uses, inputs) => {
2530
2532
  const approved = Object.entries(workflowTrustBootstrapCatalog.actions).find(([name]) => name === repository);
2531
2533
  if (approved === void 0) return false;
2532
2534
  if (inputs === void 0) return repository === "actions/checkout";
2533
- if (!record$2(inputs)) return false;
2535
+ if (!record$3(inputs)) return false;
2534
2536
  if (repository !== "actions/checkout") {
2535
2537
  const version = inputs["bun-version"];
2536
2538
  if (typeof version !== "string" || !/^v?[0-9]+(?:\.(?:[0-9]+|x)){0,2}$/u.test(version)) return false;
@@ -2543,7 +2545,7 @@ const safeAction = (uses, inputs) => {
2543
2545
  ].includes(typeof value) && (name !== "submodules" || value === false || value === "false"));
2544
2546
  };
2545
2547
  const safeShell = (shell) => shell === void 0 || shell === "bash" || shell === "sh";
2546
- const safeEnvironment = (value) => value === void 0 || record$2(value) && Object.keys(value).every((name) => [
2548
+ const safeEnvironment = (value) => value === void 0 || record$3(value) && Object.keys(value).every((name) => [
2547
2549
  "HEAD_REPO",
2548
2550
  "BASE_REPO",
2549
2551
  "HAS_RUN_CI_LABEL",
@@ -2557,25 +2559,25 @@ const safeEnvironment = (value) => value === void 0 || record$2(value) && Object
2557
2559
  "HAWK_VERSION",
2558
2560
  "HAWK_X86_64_UNKNOWN_LINUX_GNU_SHA256"
2559
2561
  ].includes(name));
2560
- const configuredShell = (value) => record$2(value) && record$2(value["run"]) ? value["run"]["shell"] : void 0;
2562
+ const configuredShell = (value) => record$3(value) && record$3(value["run"]) ? value["run"]["shell"] : void 0;
2561
2563
  const workflowTrustRules = ["workflow-trust"];
2562
2564
  const workflowEvents = (triggers) => {
2563
2565
  if (typeof triggers === "string") return [triggers];
2564
2566
  if (Array.isArray(triggers)) return triggers;
2565
- if (record$2(triggers)) return Object.keys(triggers);
2567
+ if (record$3(triggers)) return Object.keys(triggers);
2566
2568
  return [];
2567
2569
  };
2568
2570
  /** Enforce a workflow's declared trust boundary before repository dependency execution. */
2569
2571
  const checkWorkflowTrust = (workflow) => {
2570
2572
  const findings = [];
2571
- if (!record$2(workflow) || !record$2(workflow["jobs"])) return findings;
2573
+ if (!record$3(workflow) || !record$3(workflow["jobs"])) return findings;
2572
2574
  const jobs = workflow["jobs"];
2573
- if (!record$2(jobs["trust-check"])) return findings;
2575
+ if (!record$3(jobs["trust-check"])) return findings;
2574
2576
  const triggers = workflow["on"];
2575
2577
  const pullRequests = workflowEvents(triggers).filter((event) => event === "pull_request" || event === "pull_request_target");
2576
2578
  if (pullRequests.length === 0) return findings;
2577
2579
  for (const [name, job] of Object.entries(jobs)) {
2578
- if (!record$2(job)) continue;
2580
+ if (!record$3(job)) continue;
2579
2581
  const needs = job["needs"];
2580
2582
  const ordered = name !== "trust-check" && (needs === "trust-check" || Array.isArray(needs) && needs.includes("trust-check"));
2581
2583
  const permitsExecution = (stepCondition) => {
@@ -2612,7 +2614,7 @@ const checkWorkflowTrust = (workflow) => {
2612
2614
  message
2613
2615
  });
2614
2616
  const services = job["services"];
2615
- if (services !== void 0 && (!record$2(services) || Object.keys(services).length > 0)) findings.push({
2617
+ if (services !== void 0 && (!record$3(services) || Object.keys(services).length > 0)) findings.push({
2616
2618
  type: "job",
2617
2619
  property: "services",
2618
2620
  job: name,
@@ -2621,7 +2623,7 @@ const checkWorkflowTrust = (workflow) => {
2621
2623
  }
2622
2624
  if (!Array.isArray(job["steps"])) continue;
2623
2625
  for (const [index, step] of job["steps"].entries()) {
2624
- if (!record$2(step)) continue;
2626
+ if (!record$3(step)) continue;
2625
2627
  const run = step["run"];
2626
2628
  const uses = step["uses"];
2627
2629
  const unsafeRun = typeof run === "string" && (invokesRepositoryCode(run) || !safeShell(step["shell"] ?? configuredShell(job["defaults"]) ?? configuredShell(workflow["defaults"])) || ![
@@ -2659,7 +2661,7 @@ const runtimeRules = [
2659
2661
  "action-pins",
2660
2662
  ...workflowTrustRules
2661
2663
  ];
2662
- const record$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2664
+ const record$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2663
2665
  const dockerEnvironmentAssignments = ({ source, escape }) => {
2664
2666
  const words = [];
2665
2667
  let word = "";
@@ -2792,13 +2794,15 @@ const pythonRequirementMatches = (requirement, version) => {
2792
2794
  });
2793
2795
  };
2794
2796
  /** Check runtime declarations only in the files that declare or configure them. */
2795
- const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, repository, onDelegated, dynamicSelectors = [], onDynamicSelector, rustExceptions = [], engineFloors = [], onEngineFloor }) => {
2797
+ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, repository, onDelegated, dynamicSelectors = [], onDynamicSelector, rustExceptions = [], rustInstallFixtures = [], onRustInstallFixture, engineFloors = [], onEngineFloor }) => {
2796
2798
  const diagnostics = checkRustReferences({
2797
2799
  type: "file",
2798
2800
  file,
2799
2801
  text,
2800
2802
  rust: policy.rust,
2801
- exceptions: rustExceptions
2803
+ exceptions: rustExceptions,
2804
+ installFixtures: rustInstallFixtures,
2805
+ onInstallFixture: onRustInstallFixture
2802
2806
  });
2803
2807
  const name = path.posix.basename(file);
2804
2808
  const lines = text.split(/\r?\n/);
@@ -2894,7 +2898,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2894
2898
  const toolchain = parsed["toolchain"];
2895
2899
  pin({
2896
2900
  rule: "rust-version",
2897
- value: record$1(toolchain) ? toolchain["channel"] : void 0,
2901
+ value: record$2(toolchain) ? toolchain["channel"] : void 0,
2898
2902
  expected: policy.rust,
2899
2903
  label: "Rust channel",
2900
2904
  line: lineOf("channel")
@@ -2922,15 +2926,15 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2922
2926
  };
2923
2927
  if (parsed !== void 0) {
2924
2928
  const workspace = parsed["workspace"];
2925
- const defaults = record$1(workspace) ? workspace["package"] : void 0;
2926
- if (record$1(defaults) && defaults["rust-version"] !== void 0) rustFloor(defaults["rust-version"]);
2929
+ const defaults = record$2(workspace) ? workspace["package"] : void 0;
2930
+ if (record$2(defaults) && defaults["rust-version"] !== void 0) rustFloor(defaults["rust-version"]);
2927
2931
  const pkg = parsed["package"];
2928
- const version = record$1(pkg) ? pkg["rust-version"] : void 0;
2932
+ const version = record$2(pkg) ? pkg["rust-version"] : void 0;
2929
2933
  if (version !== void 0) {
2930
- if (!record$1(version)) rustFloor(version);
2934
+ if (!record$2(version)) rustFloor(version);
2931
2935
  else {
2932
2936
  let inherited;
2933
- if (version["workspace"] === true && record$1(pkg)) {
2937
+ if (version["workspace"] === true && record$2(pkg)) {
2934
2938
  const explicit = pkg["workspace"];
2935
2939
  let directory = path.posix.dirname(file);
2936
2940
  const explicitPath = typeof explicit === "string" && !explicit.includes("\\") && !explicit.includes(":") && !explicit.includes("${{") && !path.posix.isAbsolute(explicit) ? path.posix.normalize(path.posix.join(directory, explicit)) : void 0;
@@ -2938,9 +2942,9 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2938
2942
  if (explicitPath !== void 0) directory = explicitPath;
2939
2943
  while (true) {
2940
2944
  const table = readManifest(path.posix.join(directory, "Cargo.toml"))?.["workspace"];
2941
- if (record$1(table)) {
2945
+ if (record$2(table)) {
2942
2946
  const fields = table["package"];
2943
- inherited = record$1(fields) ? fields["rust-version"] : void 0;
2947
+ inherited = record$2(fields) ? fields["rust-version"] : void 0;
2944
2948
  break;
2945
2949
  }
2946
2950
  if (explicit !== void 0 || directory === ".") break;
@@ -2955,7 +2959,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2955
2959
  }
2956
2960
  if (name === "Pipfile") {
2957
2961
  const requires = toml("python-version")?.["requires"];
2958
- if (record$1(requires)) {
2962
+ if (record$2(requires)) {
2959
2963
  const minor = requires["python_version"];
2960
2964
  if (minor !== void 0 && minor !== policy.python.split(".").slice(0, 2).join(".")) add({
2961
2965
  rule: "python-version",
@@ -2974,7 +2978,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2974
2978
  const parsed = toml("python-version");
2975
2979
  if (parsed !== void 0) {
2976
2980
  const project = parsed["project"];
2977
- if (record$1(project) && project["requires-python"] !== void 0) {
2981
+ if (record$2(project) && project["requires-python"] !== void 0) {
2978
2982
  if (!pythonRequirementMatches(project["requires-python"], policy.python)) add({
2979
2983
  rule: "python-version",
2980
2984
  line: lineOf("requires-python"),
@@ -2982,9 +2986,9 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2982
2986
  });
2983
2987
  }
2984
2988
  const tool = parsed["tool"];
2985
- const configuredUv = record$1(tool) ? tool["uv"] : void 0;
2989
+ const configuredUv = record$2(tool) ? tool["uv"] : void 0;
2986
2990
  const uv = name === "uv.toml" ? parsed : configuredUv;
2987
- if (record$1(uv) && uv["python"] !== void 0) pin({
2991
+ if (record$2(uv) && uv["python"] !== void 0) pin({
2988
2992
  rule: "python-version",
2989
2993
  value: uv["python"],
2990
2994
  expected: policy.python,
@@ -2995,11 +2999,11 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2995
2999
  }
2996
3000
  if (isMiseConfigPath(file)) {
2997
3001
  const tools = toml("runtime-manager")?.["tools"];
2998
- if (record$1(tools)) for (const [tool, expected] of Object.entries(versions)) {
3002
+ if (record$2(tools)) for (const [tool, expected] of Object.entries(versions)) {
2999
3003
  const configured = tools[tool];
3000
3004
  if (configured !== void 0) pin({
3001
3005
  rule: tool === "rust" || tool === "core:rust" ? "rust-version" : "runtime-manager",
3002
- value: record$1(configured) ? configured["version"] : configured,
3006
+ value: record$2(configured) ? configured["version"] : configured,
3003
3007
  expected,
3004
3008
  selector: nodeTools.has(tool) ? "node" : void 0,
3005
3009
  label: `mise ${tool}`,
@@ -3347,7 +3351,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
3347
3351
  return diagnostics;
3348
3352
  }
3349
3353
  const nodeLine = (node) => {
3350
- if (!record$1(node) || !Array.isArray(node["range"])) return 1;
3354
+ if (!record$2(node) || !Array.isArray(node["range"])) return 1;
3351
3355
  const offset = node["range"].at(0);
3352
3356
  return typeof offset === "number" ? text.slice(0, offset).split("\n").length : 1;
3353
3357
  };
@@ -3461,7 +3465,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
3461
3465
  const manifest = JSON.parse(content ?? "");
3462
3466
  pin({
3463
3467
  rule,
3464
- value: record$1(manifest) ? manifest["packageManager"] : void 0,
3468
+ value: record$2(manifest) ? manifest["packageManager"] : void 0,
3465
3469
  expected: `bun@${policy.bun}`,
3466
3470
  label: "setup-bun version file packageManager",
3467
3471
  line
@@ -3661,10 +3665,21 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
3661
3665
  const selector = staticRepositoryPath(value) ? path.posix.normalize(value) : void 0;
3662
3666
  const providers = checkoutProvidersFor(selector);
3663
3667
  const binding = selector === void 0 ? void 0 : providers.at(-1);
3664
- if (selector === void 0 || unknownCheckoutDestination || binding !== void 0 && binding.source !== "tracked" || hasAmbiguousCheckoutWriters(selector, providers)) add({
3665
- rule: "action-pins",
3668
+ if (selector === void 0 || unknownCheckoutDestination || binding?.source === "untrusted" || binding?.source === "delegated" && !/^(?:inputs\.[A-Za-z_][A-Za-z0-9_]*|github\.event\.workflow_run\.head_sha)$/.test(dynamicRefBody(binding.ref) ?? "") || hasAmbiguousCheckoutWriters(selector, providers)) {
3669
+ add({
3670
+ rule: "action-pins",
3671
+ line,
3672
+ message: "local actions require an unambiguous checkout of the current source snapshot"
3673
+ });
3674
+ return;
3675
+ }
3676
+ if (binding?.source === "delegated") onDelegated?.({
3677
+ path: file,
3666
3678
  line,
3667
- message: "local actions require an unambiguous checkout of the current source snapshot"
3679
+ tool: "action",
3680
+ selector,
3681
+ checkoutPath: binding.path,
3682
+ ref: binding.ref
3668
3683
  });
3669
3684
  return;
3670
3685
  }
@@ -3962,6 +3977,54 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
3962
3977
  return diagnostics;
3963
3978
  };
3964
3979
  //#endregion
3980
+ //#region src/toolchain-rust-fixtures.ts
3981
+ const record$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3982
+ const date = (value) => /^\d{4}-\d{2}-\d{2}$/.test(value) && Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value;
3983
+ /** Historical install probes do not authorize using an older compiler. */
3984
+ const parseRustInstallFixtures = (input) => {
3985
+ if (input === void 0) return [];
3986
+ if (!Array.isArray(input)) throw new Error("rustInstallFixtures must be an array");
3987
+ const identities = /* @__PURE__ */ new Set();
3988
+ return input.map((entry) => {
3989
+ if (!record$1(entry) || Object.keys(entry).length !== 5 || Object.keys(entry).some((key) => ![
3990
+ "path",
3991
+ "pin",
3992
+ "reason",
3993
+ "tracking",
3994
+ "expires"
3995
+ ].includes(key)) || typeof entry["path"] !== "string" || !/^(?:[A-Za-z0-9_-]+\/)*[A-Za-z0-9_-]+\.sh$/.test(entry["path"]) || typeof entry["pin"] !== "string" || !(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/.test(entry["pin"]) || entry["pin"].startsWith("nightly-") && date(entry["pin"].slice(8))) || typeof entry["reason"] !== "string" || entry["reason"].trim() === "" || typeof entry["tracking"] !== "string" || !/^https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/(?:issues|pull)\/[1-9]\d*$/.test(entry["tracking"]) || typeof entry["expires"] !== "string" || !date(entry["expires"])) throw new Error("each Rust install fixture requires an exact shell path, stable or dated nightly pin, reason, GitHub tracking URL and valid expiry date");
3996
+ const identity = `${entry["path"]}:${entry["pin"]}`;
3997
+ if (identities.has(identity)) throw new Error("Rust install fixture path/pin pairs must be unique");
3998
+ identities.add(identity);
3999
+ return {
4000
+ path: entry["path"],
4001
+ pin: entry["pin"],
4002
+ reason: entry["reason"],
4003
+ tracking: entry["tracking"],
4004
+ expires: entry["expires"]
4005
+ };
4006
+ });
4007
+ };
4008
+ const checkRustInstallFixtureExpiry = ({ fixtures, scope, now = /* @__PURE__ */ new Date() }) => {
4009
+ const errors = [];
4010
+ const warnings = [];
4011
+ for (const fixture of fixtures) {
4012
+ if (fixture.expires > now.toISOString().slice(0, 10)) continue;
4013
+ const verdict = overdueVerdict(scope, "stll-toolchain.json", (text) => {
4014
+ const base = JSON.parse(text);
4015
+ const declarations = record$1(base) ? base["rustInstallFixtures"] : void 0;
4016
+ return Array.isArray(declarations) && declarations.some((entry) => record$1(entry) && entry["path"] === fixture.path && entry["pin"] === fixture.pin && entry["expires"] === fixture.expires);
4017
+ });
4018
+ const message = `Rust install fixture ${fixture.path}:${fixture.pin} expired on ${fixture.expires}; remove it or renew the documented fixture boundary`;
4019
+ if (!verdict.fail) warnings.push(message);
4020
+ else errors.push(verdict.unavailable === void 0 ? message : `${message}; cannot determine whether this change adds it: ${verdict.unavailable}`);
4021
+ }
4022
+ return {
4023
+ errors,
4024
+ warnings
4025
+ };
4026
+ };
4027
+ //#endregion
3965
4028
  //#region src/toolchain-guard.ts
3966
4029
  const toolchainRules = [.../* @__PURE__ */ new Set([
3967
4030
  ...packageRules,
@@ -3976,7 +4039,7 @@ const record = (value) => typeof value === "object" && value !== null && !Array.
3976
4039
  const array = (value) => Array.isArray(value);
3977
4040
  /** Repository-owned declarations are explicit; malformed decisions fail closed. */
3978
4041
  const parseToolchainConfiguration = (input) => {
3979
- if (!record(input) || Object.keys(input).some((key) => key !== "optOuts" && key !== "cloud" && key !== "engineFloors" && key !== "dynamicSelectors" && key !== "consumerChecks" && key !== "rustExceptions" && key !== "dependabotIgnores" && key !== "typescriptAllowlist") || input["optOuts"] !== void 0 && !array(input["optOuts"])) throw new Error("stll-toolchain.json accepts only optOuts, cloud, engineFloors, dynamicSelectors, consumerChecks, rustExceptions, dependabotIgnores and typescriptAllowlist declarations");
4042
+ if (!record(input) || Object.keys(input).some((key) => key !== "optOuts" && key !== "cloud" && key !== "engineFloors" && key !== "dynamicSelectors" && key !== "consumerChecks" && key !== "rustExceptions" && key !== "rustInstallFixtures" && key !== "dependabotIgnores" && key !== "typescriptAllowlist") || input["optOuts"] !== void 0 && !array(input["optOuts"])) throw new Error("stll-toolchain.json accepts only optOuts, cloud, engineFloors, dynamicSelectors, consumerChecks, rustExceptions, rustInstallFixtures, dependabotIgnores and typescriptAllowlist declarations");
3980
4043
  const disabled = /* @__PURE__ */ new Set();
3981
4044
  const optOuts = input["optOuts"] ?? [];
3982
4045
  if (!array(optOuts)) throw new Error("optOuts must be an array");
@@ -3993,6 +4056,7 @@ const parseToolchainConfiguration = (input) => {
3993
4056
  return {
3994
4057
  disabled,
3995
4058
  rustExceptions: parseRustExceptions(input["rustExceptions"]),
4059
+ rustInstallFixtures: parseRustInstallFixtures(input["rustInstallFixtures"]),
3996
4060
  cloud: parseCloudSetup(input["cloud"]),
3997
4061
  engineFloors: parseEngineFloors(input["engineFloors"]),
3998
4062
  dynamicSelectors: parseDynamicSelectors(input["dynamicSelectors"]),
@@ -4085,11 +4149,20 @@ const checkToolchain = ({ root, policy: sharedPolicy, onDelegated, onWarning, no
4085
4149
  if (files["stll-toolchain.json"] !== void 0) try {
4086
4150
  configuration = parseToolchainConfiguration(JSON.parse(files["stll-toolchain.json"]));
4087
4151
  dynamicSelectors = configuration.dynamicSelectors;
4088
- const expiry = checkRustExceptionExpiry({
4152
+ const rustExpiry = checkRustExceptionExpiry({
4089
4153
  exceptions: configuration.rustExceptions,
4090
4154
  now,
4091
4155
  scope: expiryScope
4092
4156
  });
4157
+ const fixtureExpiry = checkRustInstallFixtureExpiry({
4158
+ fixtures: configuration.rustInstallFixtures,
4159
+ now,
4160
+ scope: expiryScope
4161
+ });
4162
+ const expiry = {
4163
+ errors: [...rustExpiry.errors, ...fixtureExpiry.errors],
4164
+ warnings: [...rustExpiry.warnings, ...fixtureExpiry.warnings]
4165
+ };
4093
4166
  for (const message of expiry.errors) diagnostics.push({
4094
4167
  rule: "configuration",
4095
4168
  path: "stll-toolchain.json",
@@ -4223,6 +4296,7 @@ const checkToolchain = ({ root, policy: sharedPolicy, onDelegated, onWarning, no
4223
4296
  trackedFiles
4224
4297
  }));
4225
4298
  }
4299
+ const usedRustFixtures = /* @__PURE__ */ new Set();
4226
4300
  for (const [file, text] of Object.entries(files)) diagnostics.push(...checkRuntimeFile({
4227
4301
  file,
4228
4302
  text,
@@ -4235,6 +4309,8 @@ const checkToolchain = ({ root, policy: sharedPolicy, onDelegated, onWarning, no
4235
4309
  },
4236
4310
  trackedFiles,
4237
4311
  rustExceptions: configuration.rustExceptions,
4312
+ rustInstallFixtures: configuration.rustInstallFixtures,
4313
+ onRustInstallFixture: (entry) => usedRustFixtures.add(entry),
4238
4314
  dynamicSelectors: dynamicSelectors.filter((entry) => entry.path === file),
4239
4315
  onDynamicSelector: (entry) => selectorMatches.set(entry, (selectorMatches.get(entry) ?? 0) + 1),
4240
4316
  repository,
@@ -4246,6 +4322,12 @@ const checkToolchain = ({ root, policy: sharedPolicy, onDelegated, onWarning, no
4246
4322
  },
4247
4323
  readFile: (target) => files[target]
4248
4324
  }));
4325
+ for (const entry of configuration.rustInstallFixtures) if (!usedRustFixtures.has(entry)) diagnostics.push({
4326
+ rule: "configuration",
4327
+ path: "stll-toolchain.json",
4328
+ line: 1,
4329
+ message: `Rust install fixture ${entry.path}:${entry.pin} must match a tracked rustup toolchain install probe`
4330
+ });
4249
4331
  for (const entry of engineFloors) if (!exercisedFloors.has(entry) || mismatchedFloors.has(entry)) diagnostics.push({
4250
4332
  rule: "configuration",
4251
4333
  path: "stll-toolchain.json",