@stll/oxlint-config 1.9.0 → 2.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.
Files changed (74) hide show
  1. package/README.md +110 -63
  2. package/dist/cloud-setup-cli.cjs +1 -2
  3. package/dist/cloud-setup-cli.cjs.map +1 -1
  4. package/dist/cloud-setup-cli.mjs +1 -2
  5. package/dist/cloud-setup-cli.mjs.map +1 -1
  6. package/dist/consumer-node-support.cjs +2 -2
  7. package/dist/consumer-node-support.mjs +1 -1
  8. package/dist/dependabot.d.cts +8 -19
  9. package/dist/dependabot.d.mts +8 -19
  10. package/dist/publish-artifact-syntax.cjs +1 -1
  11. package/dist/publish-artifact-syntax.mjs +1 -1
  12. package/dist/publish-contract-cli.cjs +1 -1
  13. package/dist/publish-contract-cli.mjs +1 -1
  14. package/dist/quarantine-cli.cjs +2 -1
  15. package/dist/quarantine-cli.cjs.map +1 -1
  16. package/dist/quarantine-cli.mjs +3 -2
  17. package/dist/quarantine-cli.mjs.map +1 -1
  18. package/dist/quarantine-policy.d.cts +25 -2
  19. package/dist/quarantine-policy.d.mts +25 -2
  20. package/dist/quarantine-policy2.cjs +177 -5
  21. package/dist/quarantine-policy2.cjs.map +1 -1
  22. package/dist/quarantine-policy2.mjs +166 -6
  23. package/dist/quarantine-policy2.mjs.map +1 -1
  24. package/dist/rust-policy-cli.cjs +481 -0
  25. package/dist/rust-policy-cli.cjs.map +1 -0
  26. package/dist/rust-policy-cli.mjs +479 -0
  27. package/dist/rust-policy-cli.mjs.map +1 -0
  28. package/dist/toolchain-changed-cli.cjs +1477 -2
  29. package/dist/toolchain-changed-cli.cjs.map +1 -1
  30. package/dist/toolchain-changed-cli.mjs +1474 -1
  31. package/dist/toolchain-changed-cli.mjs.map +1 -1
  32. package/dist/toolchain-check-cli.cjs +2 -0
  33. package/dist/toolchain-check-cli.cjs.map +1 -1
  34. package/dist/toolchain-check-cli.mjs +2 -0
  35. package/dist/toolchain-check-cli.mjs.map +1 -1
  36. package/dist/toolchain-dependabot.cjs +1 -1
  37. package/dist/toolchain-dependabot.mjs +2 -2
  38. package/dist/toolchain-dependabot.mjs.map +1 -1
  39. package/dist/toolchain-guard.cjs +353 -244
  40. package/dist/toolchain-guard.cjs.map +1 -1
  41. package/dist/toolchain-guard.mjs +348 -245
  42. package/dist/toolchain-guard.mjs.map +1 -1
  43. package/dist/toolchain-inputs.cjs +108 -56
  44. package/dist/toolchain-inputs.cjs.map +1 -1
  45. package/dist/toolchain-inputs.mjs +102 -56
  46. package/dist/toolchain-inputs.mjs.map +1 -1
  47. package/dist/toolchain-schema.cjs +8 -46
  48. package/dist/toolchain-schema.cjs.map +1 -1
  49. package/dist/toolchain-schema.mjs +8 -46
  50. package/dist/toolchain-schema.mjs.map +1 -1
  51. package/dist/typecheck-probe-cli.cjs +40 -2
  52. package/dist/typecheck-probe-cli.cjs.map +1 -1
  53. package/dist/typecheck-probe-cli.mjs +39 -1
  54. package/dist/typecheck-probe-cli.mjs.map +1 -1
  55. package/package.json +4 -4
  56. package/toolchain.json +13 -27
  57. package/dist/toolchain-bun.cjs +0 -34
  58. package/dist/toolchain-bun.cjs.map +0 -1
  59. package/dist/toolchain-bun.mjs +0 -23
  60. package/dist/toolchain-bun.mjs.map +0 -1
  61. package/dist/toolchain-changed.cjs +0 -1484
  62. package/dist/toolchain-changed.cjs.map +0 -1
  63. package/dist/toolchain-changed.mjs +0 -1477
  64. package/dist/toolchain-changed.mjs.map +0 -1
  65. package/dist/typecheck-parity-cli.cjs +0 -71
  66. package/dist/typecheck-parity-cli.cjs.map +0 -1
  67. package/dist/typecheck-parity-cli.mjs +0 -71
  68. package/dist/typecheck-parity-cli.mjs.map +0 -1
  69. package/dist/typecheck-parity.cjs +0 -908
  70. package/dist/typecheck-parity.cjs.map +0 -1
  71. package/dist/typecheck-parity.mjs +0 -897
  72. package/dist/typecheck-parity.mjs.map +0 -1
  73. /package/dist/{typecheck-parity-cli.d.cts → rust-policy-cli.d.cts} +0 -0
  74. /package/dist/{typecheck-parity-cli.d.mts → rust-policy-cli.d.mts} +0 -0
@@ -1,13 +1,51 @@
1
1
  #!/usr/bin/env node
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_rolldown_runtime = require("./rolldown-runtime.cjs");
4
- const require_typecheck_parity = require("./typecheck-parity.cjs");
5
4
  let node_fs_promises = require("node:fs/promises");
6
5
  let node_path = require("node:path");
7
6
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
8
7
  let node_child_process = require("node:child_process");
9
8
  let node_crypto = require("node:crypto");
9
+ let node_util = require("node:util");
10
10
  //#region src/typecheck-probe.ts
11
+ const diagnosticLocation = (file, repo) => {
12
+ const normalized = file.replaceAll("\\", "/");
13
+ const bundled = /^bundled:\/\/\/libs\/(lib\.[^/]+\.d\.ts)$/.exec(normalized);
14
+ const installed = /(?:^|\/)node_modules\/(?:typescript|@typescript\/(?:native|typescript-[^/]+))\/(?:.*\/)?(lib\.[^/]+\.d\.ts)$/.exec(normalized);
15
+ const library = bundled?.at(1) ?? installed?.at(1);
16
+ return library === void 0 ? (0, node_path.relative)(repo, (0, node_path.resolve)(repo, file)).replaceAll("\\", "/") : `<lib>/${library}`;
17
+ };
18
+ const globalDiagnosticKey = (code, message) => `<config>:0:${code}:${message.trim().replaceAll(/\s+/g, " ")}`;
19
+ /** Normalizes checker output to sorted `file:line:code` keys. */
20
+ const diagnosticSet = (output, repo) => {
21
+ const clean = (0, node_util.stripVTControlCharacters)(output);
22
+ const diagnostics = /* @__PURE__ */ new Set();
23
+ for (const block of clean.matchAll(/<error\b([^>]*)>([\s\S]*?)<\/error>/g)) {
24
+ const attributes = new Map([...(block.at(1) ?? "").matchAll(/(\w+)="([^"]*)"/g)].map((match) => [match.at(1), match.at(2)]));
25
+ const file = attributes.get("file");
26
+ const row = attributes.get("line");
27
+ const code = attributes.get("code");
28
+ if (code === void 0 || !/^TS\d+$/.test(code)) continue;
29
+ if (file !== void 0 && row !== void 0 && row !== "0") diagnostics.add(`${diagnosticLocation(file, repo)}:${row}:${code.slice(2)}`);
30
+ else {
31
+ const message = (block.at(2) ?? "").split(/<(?:source|related)\b/).at(0) ?? "";
32
+ diagnostics.add(globalDiagnosticKey(code.slice(2), message));
33
+ }
34
+ }
35
+ for (const rawLine of clean.split("\n")) {
36
+ const line = rawLine.replace(/^(?!(?:error|warning) TS\d+:)(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]* [a-zA-Z0-9_.:-]+: /, "");
37
+ if (line.startsWith("<error ")) continue;
38
+ const match = /^(.*?)\((\d+),\d+\):\s*(?:error|warning) TS(\d+)/.exec(line) ?? /^(.*?):(\d+):\d+:\s*(?:error|warning) (?:TS)?(\d+)/.exec(line);
39
+ if (match) {
40
+ const [, file = "", row = "", code = ""] = match;
41
+ diagnostics.add(`${diagnosticLocation(file, repo)}:${row}:${code}`);
42
+ } else {
43
+ const global = /^(?:error|warning) TS(\d+):\s*(.*)/.exec(line);
44
+ if (global) diagnostics.add(globalDiagnosticKey(global.at(1) ?? "", global.at(2) ?? ""));
45
+ }
46
+ }
47
+ return [...diagnostics].sort();
48
+ };
11
49
  const runProbeCommand = ({ command, cwd, signal }) => new Promise((done, reject) => {
12
50
  const executable = command.at(0);
13
51
  if (executable === void 0) {
@@ -120,7 +158,7 @@ const runTypecheckProbe = async ({ repo, project, seedDirectory, command, output
120
158
  }
121
159
  const seeded = await execute();
122
160
  if (seeded.status === null || seeded.status === 0) throw new Error(`typecheck probe expected the seeded command to fail: ${seedLocation}`);
123
- if (![.../* @__PURE__ */ new Set([root, (0, node_path.dirname)(config)])].some((origin) => require_typecheck_parity.diagnosticSet(seeded.output, origin).includes(`${(0, node_path.relative)(origin, seed).replaceAll("\\", "/")}:1:2322`))) throw new Error(`typecheck probe expected TS2322 attributed to ${seedLocation}; the declared project may exclude the seed`);
161
+ if (![.../* @__PURE__ */ new Set([root, (0, node_path.dirname)(config)])].some((origin) => diagnosticSet(seeded.output, origin).includes(`${(0, node_path.relative)(origin, seed).replaceAll("\\", "/")}:1:2322`))) throw new Error(`typecheck probe expected TS2322 attributed to ${seedLocation}; the declared project may exclude the seed`);
124
162
  await removeSeed();
125
163
  const clean = await execute();
126
164
  if (clean.status !== 0) throw new Error(`typecheck probe clean command failed with status ${clean.status ?? "unknown"}: ${project}`);
@@ -1 +1 @@
1
- {"version":3,"file":"typecheck-probe-cli.cjs","names":["spawn","path","realpath","resolve","stat","dirname","isAbsolute","join","randomUUID","relative","unlink","open","diagnosticSet"],"sources":["../src/typecheck-probe.ts","../src/typecheck-probe-args.ts","../src/typecheck-probe-cli.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { open, realpath, stat, unlink } from \"node:fs/promises\";\nimport path, { dirname, isAbsolute, join, relative, resolve } from \"node:path\";\n\nimport { diagnosticSet } from \"./typecheck-parity\";\n\ntype ProbeCommandOptions = {\n command: readonly string[];\n cwd: string;\n signal: AbortSignal;\n};\ntype ProbeCommandResult = { status: number | null; output: string };\n\nconst runProbeCommand = ({ command, cwd, signal }: ProbeCommandOptions) =>\n new Promise<ProbeCommandResult>((done, reject) => {\n const executable = command.at(0);\n if (executable === undefined) {\n reject(new Error(\"typecheck probe command is empty\"));\n return;\n }\n const child = spawn(executable, command.slice(1), {\n cwd,\n signal,\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n let output = \"\";\n child.stdout.on(\"data\", (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.once(\"error\", reject);\n child.once(\"close\", (status) => done({ status, output }));\n });\n\ntype ProbePathContainmentOptions = {\n root: string;\n candidate: string;\n paths?: Pick<typeof path, \"relative\" | \"isAbsolute\" | \"sep\">;\n};\n\nexport const insideProbePath = ({\n root,\n candidate,\n paths = path,\n}: ProbePathContainmentOptions) => {\n const location = paths.relative(root, candidate);\n return (\n location !== \"..\" &&\n !location.startsWith(\"..\" + paths.sep) &&\n !paths.isAbsolute(location)\n );\n};\n\ntype RunTypecheckProbeOptions = {\n repo: string;\n project: string;\n seedDirectory?: string | undefined;\n command: readonly string[];\n output?: (text: string) => void;\n run?: (options: ProbeCommandOptions) => Promise<ProbeCommandResult>;\n};\n\nexport const runTypecheckProbe = async ({\n repo,\n project,\n seedDirectory,\n command,\n output,\n run = runProbeCommand,\n}: RunTypecheckProbeOptions) => {\n const executable = command.at(0);\n if (\n executable === undefined ||\n executable === \"\" ||\n command.some((argument) => argument.includes(\"\\0\"))\n )\n throw new Error(\n \"typecheck probe requires a nonempty command argv without NUL bytes\",\n );\n if (project === \"\")\n throw new Error(\"typecheck probe requires an explicit project file\");\n const root = await realpath(repo);\n const requested = resolve(root, project);\n if (!insideProbePath({ root, candidate: requested }))\n throw new Error(\n `typecheck probe project is outside the repository: ${project}`,\n );\n const config = await realpath(requested);\n if (!insideProbePath({ root, candidate: config }))\n throw new Error(\n `typecheck probe project resolves outside the repository: ${project}`,\n );\n if (!(await stat(config)).isFile())\n throw new Error(`typecheck probe project must be a file: ${project}`);\n let seedParent = dirname(config);\n if (seedDirectory !== undefined) {\n if (seedDirectory === \"\" || isAbsolute(seedDirectory))\n throw new Error(\n \"typecheck probe seed directory must be repository-relative\",\n );\n const requestedSeedParent = resolve(root, seedDirectory);\n if (!insideProbePath({ root, candidate: requestedSeedParent }))\n throw new Error(\n `typecheck probe seed directory is outside the repository: ${seedDirectory}`,\n );\n seedParent = await realpath(requestedSeedParent);\n if (\n !insideProbePath({ root, candidate: seedParent }) ||\n !insideProbePath({ root: dirname(config), candidate: seedParent })\n )\n throw new Error(\n `typecheck probe seed directory must be inside the declared project: ${seedDirectory}`,\n );\n if (!(await stat(seedParent)).isDirectory())\n throw new Error(\n `typecheck probe seed directory must be a directory: ${seedDirectory}`,\n );\n }\n const seed = join(seedParent, `stll-typecheck-probe-${randomUUID()}.ts`);\n const seedLocation = relative(root, seed).replaceAll(\"\\\\\", \"/\");\n const controller = new AbortController();\n const interrupt = (signal: string) =>\n controller.abort(new Error(`typecheck probe interrupted by ${signal}`));\n const onInterrupt = () => interrupt(\"SIGINT\");\n const onTerminate = () => interrupt(\"SIGTERM\");\n process.on(\"SIGINT\", onInterrupt);\n process.on(\"SIGTERM\", onTerminate);\n let created = false;\n const removeSeed = async () => {\n if (!created) return;\n await unlink(seed);\n created = false;\n };\n const execute = async () => {\n controller.signal.throwIfAborted();\n let onAbort: (() => void) | undefined;\n const aborted = new Promise<never>((_done, reject) => {\n onAbort = () => reject(controller.signal.reason);\n controller.signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n try {\n const result = await Promise.race([\n run({ command, cwd: root, signal: controller.signal }),\n aborted,\n ]);\n controller.signal.throwIfAborted();\n output?.(result.output);\n return result;\n } finally {\n if (onAbort !== undefined)\n controller.signal.removeEventListener(\"abort\", onAbort);\n }\n };\n try {\n controller.signal.throwIfAborted();\n const file = await open(seed, \"wx\", 0o600);\n created = true;\n try {\n await file.writeFile('export const typecheckProbe: number = \"wrong\";\\n');\n } finally {\n await file.close();\n }\n const seeded = await execute();\n if (seeded.status === null || seeded.status === 0)\n throw new Error(\n `typecheck probe expected the seeded command to fail: ${seedLocation}`,\n );\n // Workspace runners may report compiler paths from the declared project.\n // Only the exact owned seed under either known command origin is accepted.\n const origins = new Set([root, dirname(config)]);\n const attributed = [...origins].some((origin) =>\n diagnosticSet(seeded.output, origin).includes(\n `${relative(origin, seed).replaceAll(\"\\\\\", \"/\")}:1:2322`,\n ),\n );\n if (!attributed)\n throw new Error(\n `typecheck probe expected TS2322 attributed to ${seedLocation}; the declared project may exclude the seed`,\n );\n await removeSeed();\n const clean = await execute();\n if (clean.status !== 0)\n throw new Error(\n `typecheck probe clean command failed with status ${clean.status ?? \"unknown\"}: ${project}`,\n );\n } finally {\n try {\n await removeSeed();\n } finally {\n process.removeListener(\"SIGINT\", onInterrupt);\n process.removeListener(\"SIGTERM\", onTerminate);\n }\n }\n};\n","export const probeUsage =\n \"Usage: stll-typecheck-probe --project <tsconfig> [--seed-dir <directory>] -- <command> [args...]\\n stll-typecheck-probe --help\";\n\nexport const parseProbeArguments = (args: readonly string[]) => {\n if (args.length === 1 && args.at(0) === \"--help\")\n return { mode: \"help\" } as const;\n const project = args.at(1);\n const seedDirectory = args.at(2) === \"--seed-dir\" ? args.at(3) : undefined;\n const separator = seedDirectory === undefined ? 2 : 4;\n const command = args.slice(separator + 1);\n if (\n args.at(0) !== \"--project\" ||\n project === undefined ||\n project.trim() === \"\" ||\n project.startsWith(\"-\") ||\n args.at(separator) !== \"--\" ||\n (seedDirectory !== undefined &&\n (seedDirectory.trim() === \"\" || seedDirectory.startsWith(\"-\"))) ||\n command.length === 0 ||\n command.some((value) => value.trim() === \"\")\n )\n throw new Error(probeUsage);\n return {\n mode: \"probe\",\n project,\n command,\n ...(seedDirectory === undefined ? {} : { seedDirectory }),\n } as const;\n};\n","#!/usr/bin/env node\nimport { runTypecheckProbe } from \"./typecheck-probe\";\nimport { parseProbeArguments, probeUsage } from \"./typecheck-probe-args\";\n\nconst main = async () => {\n const args = parseProbeArguments(process.argv.slice(2));\n if (args.mode === \"help\") {\n process.stdout.write(`${probeUsage}\\n`);\n return;\n }\n await runTypecheckProbe({\n repo: process.cwd(),\n project: args.project,\n command: args.command,\n seedDirectory: args.seedDirectory,\n output: (text) => process.stdout.write(text),\n });\n process.stdout.write(\n \"typecheck probe passed: seeded TS2322 detected and clean typecheck passed\\n\",\n );\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `typecheck-probe: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"typecheck-probe-cli.cjs","names":["relative","resolve","stripVTControlCharacters","spawn","path","realpath","stat","dirname","isAbsolute","join","randomUUID","unlink","open"],"sources":["../src/typecheck-probe.ts","../src/typecheck-probe-args.ts","../src/typecheck-probe-cli.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { open, realpath, stat, unlink } from \"node:fs/promises\";\nimport path, { dirname, isAbsolute, join, relative, resolve } from \"node:path\";\nimport { stripVTControlCharacters } from \"node:util\";\n\nconst diagnosticLocation = (file: string, repo: string) => {\n const normalized = file.replaceAll(\"\\\\\", \"/\");\n const bundled = /^bundled:\\/\\/\\/libs\\/(lib\\.[^/]+\\.d\\.ts)$/.exec(normalized);\n const installed =\n /(?:^|\\/)node_modules\\/(?:typescript|@typescript\\/(?:native|typescript-[^/]+))\\/(?:.*\\/)?(lib\\.[^/]+\\.d\\.ts)$/.exec(\n normalized,\n );\n const library = bundled?.at(1) ?? installed?.at(1);\n return library === undefined\n ? relative(repo, resolve(repo, file)).replaceAll(\"\\\\\", \"/\")\n : `<lib>/${library}`;\n};\nconst globalDiagnosticKey = (code: string, message: string) =>\n `<config>:0:${code}:${message.trim().replaceAll(/\\s+/g, \" \")}`;\n\n/** Normalizes checker output to sorted `file:line:code` keys. */\nexport const diagnosticSet = (output: string, repo: string): string[] => {\n const clean = stripVTControlCharacters(output);\n const diagnostics = new Set<string>();\n for (const block of clean.matchAll(/<error\\b([^>]*)>([\\s\\S]*?)<\\/error>/g)) {\n const attributes = new Map(\n [...(block.at(1) ?? \"\").matchAll(/(\\w+)=\"([^\"]*)\"/g)].map((match) => [\n match.at(1),\n match.at(2),\n ]),\n );\n const file = attributes.get(\"file\");\n const row = attributes.get(\"line\");\n const code = attributes.get(\"code\");\n if (code === undefined || !/^TS\\d+$/.test(code)) continue;\n if (file !== undefined && row !== undefined && row !== \"0\")\n diagnostics.add(\n `${diagnosticLocation(file, repo)}:${row}:${code.slice(2)}`,\n );\n else {\n const message =\n (block.at(2) ?? \"\").split(/<(?:source|related)\\b/).at(0) ?? \"\";\n diagnostics.add(globalDiagnosticKey(code.slice(2), message));\n }\n }\n for (const rawLine of clean.split(\"\\n\")) {\n const line = rawLine.replace(\n /^(?!(?:error|warning) TS\\d+:)(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]* [a-zA-Z0-9_.:-]+: /,\n \"\",\n );\n if (line.startsWith(\"<error \")) continue;\n const match =\n /^(.*?)\\((\\d+),\\d+\\):\\s*(?:error|warning) TS(\\d+)/.exec(line) ??\n /^(.*?):(\\d+):\\d+:\\s*(?:error|warning) (?:TS)?(\\d+)/.exec(line);\n if (match) {\n const [, file = \"\", row = \"\", code = \"\"] = match;\n diagnostics.add(`${diagnosticLocation(file, repo)}:${row}:${code}`);\n } else {\n const global = /^(?:error|warning) TS(\\d+):\\s*(.*)/.exec(line);\n if (global)\n diagnostics.add(\n globalDiagnosticKey(global.at(1) ?? \"\", global.at(2) ?? \"\"),\n );\n }\n }\n return [...diagnostics].sort();\n};\n\ntype ProbeCommandOptions = {\n command: readonly string[];\n cwd: string;\n signal: AbortSignal;\n};\ntype ProbeCommandResult = { status: number | null; output: string };\n\nconst runProbeCommand = ({ command, cwd, signal }: ProbeCommandOptions) =>\n new Promise<ProbeCommandResult>((done, reject) => {\n const executable = command.at(0);\n if (executable === undefined) {\n reject(new Error(\"typecheck probe command is empty\"));\n return;\n }\n const child = spawn(executable, command.slice(1), {\n cwd,\n signal,\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n let output = \"\";\n child.stdout.on(\"data\", (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.once(\"error\", reject);\n child.once(\"close\", (status) => done({ status, output }));\n });\n\ntype ProbePathContainmentOptions = {\n root: string;\n candidate: string;\n paths?: Pick<typeof path, \"relative\" | \"isAbsolute\" | \"sep\">;\n};\n\nexport const insideProbePath = ({\n root,\n candidate,\n paths = path,\n}: ProbePathContainmentOptions) => {\n const location = paths.relative(root, candidate);\n return (\n location !== \"..\" &&\n !location.startsWith(\"..\" + paths.sep) &&\n !paths.isAbsolute(location)\n );\n};\n\ntype RunTypecheckProbeOptions = {\n repo: string;\n project: string;\n seedDirectory?: string | undefined;\n command: readonly string[];\n output?: (text: string) => void;\n run?: (options: ProbeCommandOptions) => Promise<ProbeCommandResult>;\n};\n\nexport const runTypecheckProbe = async ({\n repo,\n project,\n seedDirectory,\n command,\n output,\n run = runProbeCommand,\n}: RunTypecheckProbeOptions) => {\n const executable = command.at(0);\n if (\n executable === undefined ||\n executable === \"\" ||\n command.some((argument) => argument.includes(\"\\0\"))\n )\n throw new Error(\n \"typecheck probe requires a nonempty command argv without NUL bytes\",\n );\n if (project === \"\")\n throw new Error(\"typecheck probe requires an explicit project file\");\n const root = await realpath(repo);\n const requested = resolve(root, project);\n if (!insideProbePath({ root, candidate: requested }))\n throw new Error(\n `typecheck probe project is outside the repository: ${project}`,\n );\n const config = await realpath(requested);\n if (!insideProbePath({ root, candidate: config }))\n throw new Error(\n `typecheck probe project resolves outside the repository: ${project}`,\n );\n if (!(await stat(config)).isFile())\n throw new Error(`typecheck probe project must be a file: ${project}`);\n let seedParent = dirname(config);\n if (seedDirectory !== undefined) {\n if (seedDirectory === \"\" || isAbsolute(seedDirectory))\n throw new Error(\n \"typecheck probe seed directory must be repository-relative\",\n );\n const requestedSeedParent = resolve(root, seedDirectory);\n if (!insideProbePath({ root, candidate: requestedSeedParent }))\n throw new Error(\n `typecheck probe seed directory is outside the repository: ${seedDirectory}`,\n );\n seedParent = await realpath(requestedSeedParent);\n if (\n !insideProbePath({ root, candidate: seedParent }) ||\n !insideProbePath({ root: dirname(config), candidate: seedParent })\n )\n throw new Error(\n `typecheck probe seed directory must be inside the declared project: ${seedDirectory}`,\n );\n if (!(await stat(seedParent)).isDirectory())\n throw new Error(\n `typecheck probe seed directory must be a directory: ${seedDirectory}`,\n );\n }\n const seed = join(seedParent, `stll-typecheck-probe-${randomUUID()}.ts`);\n const seedLocation = relative(root, seed).replaceAll(\"\\\\\", \"/\");\n const controller = new AbortController();\n const interrupt = (signal: string) =>\n controller.abort(new Error(`typecheck probe interrupted by ${signal}`));\n const onInterrupt = () => interrupt(\"SIGINT\");\n const onTerminate = () => interrupt(\"SIGTERM\");\n process.on(\"SIGINT\", onInterrupt);\n process.on(\"SIGTERM\", onTerminate);\n let created = false;\n const removeSeed = async () => {\n if (!created) return;\n await unlink(seed);\n created = false;\n };\n const execute = async () => {\n controller.signal.throwIfAborted();\n let onAbort: (() => void) | undefined;\n const aborted = new Promise<never>((_done, reject) => {\n onAbort = () => reject(controller.signal.reason);\n controller.signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n try {\n const result = await Promise.race([\n run({ command, cwd: root, signal: controller.signal }),\n aborted,\n ]);\n controller.signal.throwIfAborted();\n output?.(result.output);\n return result;\n } finally {\n if (onAbort !== undefined)\n controller.signal.removeEventListener(\"abort\", onAbort);\n }\n };\n try {\n controller.signal.throwIfAborted();\n const file = await open(seed, \"wx\", 0o600);\n created = true;\n try {\n await file.writeFile('export const typecheckProbe: number = \"wrong\";\\n');\n } finally {\n await file.close();\n }\n const seeded = await execute();\n if (seeded.status === null || seeded.status === 0)\n throw new Error(\n `typecheck probe expected the seeded command to fail: ${seedLocation}`,\n );\n // Workspace runners may report compiler paths from the declared project.\n // Only the exact owned seed under either known command origin is accepted.\n const origins = new Set([root, dirname(config)]);\n const attributed = [...origins].some((origin) =>\n diagnosticSet(seeded.output, origin).includes(\n `${relative(origin, seed).replaceAll(\"\\\\\", \"/\")}:1:2322`,\n ),\n );\n if (!attributed)\n throw new Error(\n `typecheck probe expected TS2322 attributed to ${seedLocation}; the declared project may exclude the seed`,\n );\n await removeSeed();\n const clean = await execute();\n if (clean.status !== 0)\n throw new Error(\n `typecheck probe clean command failed with status ${clean.status ?? \"unknown\"}: ${project}`,\n );\n } finally {\n try {\n await removeSeed();\n } finally {\n process.removeListener(\"SIGINT\", onInterrupt);\n process.removeListener(\"SIGTERM\", onTerminate);\n }\n }\n};\n","export const probeUsage =\n \"Usage: stll-typecheck-probe --project <tsconfig> [--seed-dir <directory>] -- <command> [args...]\\n stll-typecheck-probe --help\";\n\nexport const parseProbeArguments = (args: readonly string[]) => {\n if (args.length === 1 && args.at(0) === \"--help\")\n return { mode: \"help\" } as const;\n const project = args.at(1);\n const seedDirectory = args.at(2) === \"--seed-dir\" ? args.at(3) : undefined;\n const separator = seedDirectory === undefined ? 2 : 4;\n const command = args.slice(separator + 1);\n if (\n args.at(0) !== \"--project\" ||\n project === undefined ||\n project.trim() === \"\" ||\n project.startsWith(\"-\") ||\n args.at(separator) !== \"--\" ||\n (seedDirectory !== undefined &&\n (seedDirectory.trim() === \"\" || seedDirectory.startsWith(\"-\"))) ||\n command.length === 0 ||\n command.some((value) => value.trim() === \"\")\n )\n throw new Error(probeUsage);\n return {\n mode: \"probe\",\n project,\n command,\n ...(seedDirectory === undefined ? {} : { seedDirectory }),\n } as const;\n};\n","#!/usr/bin/env node\nimport { runTypecheckProbe } from \"./typecheck-probe\";\nimport { parseProbeArguments, probeUsage } from \"./typecheck-probe-args\";\n\nconst main = async () => {\n const args = parseProbeArguments(process.argv.slice(2));\n if (args.mode === \"help\") {\n process.stdout.write(`${probeUsage}\\n`);\n return;\n }\n await runTypecheckProbe({\n repo: process.cwd(),\n project: args.project,\n command: args.command,\n seedDirectory: args.seedDirectory,\n output: (text) => process.stdout.write(text),\n });\n process.stdout.write(\n \"typecheck probe passed: seeded TS2322 detected and clean typecheck passed\\n\",\n );\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `typecheck-probe: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":"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"}
@@ -1,10 +1,48 @@
1
1
  #!/usr/bin/env node
2
- import { t as diagnosticSet } from "./typecheck-parity.mjs";
3
2
  import { open, realpath, stat, unlink } from "node:fs/promises";
4
3
  import path, { dirname, isAbsolute, join, relative, resolve } from "node:path";
5
4
  import { spawn } from "node:child_process";
6
5
  import { randomUUID } from "node:crypto";
6
+ import { stripVTControlCharacters } from "node:util";
7
7
  //#region src/typecheck-probe.ts
8
+ const diagnosticLocation = (file, repo) => {
9
+ const normalized = file.replaceAll("\\", "/");
10
+ const bundled = /^bundled:\/\/\/libs\/(lib\.[^/]+\.d\.ts)$/.exec(normalized);
11
+ const installed = /(?:^|\/)node_modules\/(?:typescript|@typescript\/(?:native|typescript-[^/]+))\/(?:.*\/)?(lib\.[^/]+\.d\.ts)$/.exec(normalized);
12
+ const library = bundled?.at(1) ?? installed?.at(1);
13
+ return library === void 0 ? relative(repo, resolve(repo, file)).replaceAll("\\", "/") : `<lib>/${library}`;
14
+ };
15
+ const globalDiagnosticKey = (code, message) => `<config>:0:${code}:${message.trim().replaceAll(/\s+/g, " ")}`;
16
+ /** Normalizes checker output to sorted `file:line:code` keys. */
17
+ const diagnosticSet = (output, repo) => {
18
+ const clean = stripVTControlCharacters(output);
19
+ const diagnostics = /* @__PURE__ */ new Set();
20
+ for (const block of clean.matchAll(/<error\b([^>]*)>([\s\S]*?)<\/error>/g)) {
21
+ const attributes = new Map([...(block.at(1) ?? "").matchAll(/(\w+)="([^"]*)"/g)].map((match) => [match.at(1), match.at(2)]));
22
+ const file = attributes.get("file");
23
+ const row = attributes.get("line");
24
+ const code = attributes.get("code");
25
+ if (code === void 0 || !/^TS\d+$/.test(code)) continue;
26
+ if (file !== void 0 && row !== void 0 && row !== "0") diagnostics.add(`${diagnosticLocation(file, repo)}:${row}:${code.slice(2)}`);
27
+ else {
28
+ const message = (block.at(2) ?? "").split(/<(?:source|related)\b/).at(0) ?? "";
29
+ diagnostics.add(globalDiagnosticKey(code.slice(2), message));
30
+ }
31
+ }
32
+ for (const rawLine of clean.split("\n")) {
33
+ const line = rawLine.replace(/^(?!(?:error|warning) TS\d+:)(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]* [a-zA-Z0-9_.:-]+: /, "");
34
+ if (line.startsWith("<error ")) continue;
35
+ const match = /^(.*?)\((\d+),\d+\):\s*(?:error|warning) TS(\d+)/.exec(line) ?? /^(.*?):(\d+):\d+:\s*(?:error|warning) (?:TS)?(\d+)/.exec(line);
36
+ if (match) {
37
+ const [, file = "", row = "", code = ""] = match;
38
+ diagnostics.add(`${diagnosticLocation(file, repo)}:${row}:${code}`);
39
+ } else {
40
+ const global = /^(?:error|warning) TS(\d+):\s*(.*)/.exec(line);
41
+ if (global) diagnostics.add(globalDiagnosticKey(global.at(1) ?? "", global.at(2) ?? ""));
42
+ }
43
+ }
44
+ return [...diagnostics].sort();
45
+ };
8
46
  const runProbeCommand = ({ command, cwd, signal }) => new Promise((done, reject) => {
9
47
  const executable = command.at(0);
10
48
  if (executable === void 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"typecheck-probe-cli.mjs","names":[],"sources":["../src/typecheck-probe.ts","../src/typecheck-probe-args.ts","../src/typecheck-probe-cli.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { open, realpath, stat, unlink } from \"node:fs/promises\";\nimport path, { dirname, isAbsolute, join, relative, resolve } from \"node:path\";\n\nimport { diagnosticSet } from \"./typecheck-parity\";\n\ntype ProbeCommandOptions = {\n command: readonly string[];\n cwd: string;\n signal: AbortSignal;\n};\ntype ProbeCommandResult = { status: number | null; output: string };\n\nconst runProbeCommand = ({ command, cwd, signal }: ProbeCommandOptions) =>\n new Promise<ProbeCommandResult>((done, reject) => {\n const executable = command.at(0);\n if (executable === undefined) {\n reject(new Error(\"typecheck probe command is empty\"));\n return;\n }\n const child = spawn(executable, command.slice(1), {\n cwd,\n signal,\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n let output = \"\";\n child.stdout.on(\"data\", (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.once(\"error\", reject);\n child.once(\"close\", (status) => done({ status, output }));\n });\n\ntype ProbePathContainmentOptions = {\n root: string;\n candidate: string;\n paths?: Pick<typeof path, \"relative\" | \"isAbsolute\" | \"sep\">;\n};\n\nexport const insideProbePath = ({\n root,\n candidate,\n paths = path,\n}: ProbePathContainmentOptions) => {\n const location = paths.relative(root, candidate);\n return (\n location !== \"..\" &&\n !location.startsWith(\"..\" + paths.sep) &&\n !paths.isAbsolute(location)\n );\n};\n\ntype RunTypecheckProbeOptions = {\n repo: string;\n project: string;\n seedDirectory?: string | undefined;\n command: readonly string[];\n output?: (text: string) => void;\n run?: (options: ProbeCommandOptions) => Promise<ProbeCommandResult>;\n};\n\nexport const runTypecheckProbe = async ({\n repo,\n project,\n seedDirectory,\n command,\n output,\n run = runProbeCommand,\n}: RunTypecheckProbeOptions) => {\n const executable = command.at(0);\n if (\n executable === undefined ||\n executable === \"\" ||\n command.some((argument) => argument.includes(\"\\0\"))\n )\n throw new Error(\n \"typecheck probe requires a nonempty command argv without NUL bytes\",\n );\n if (project === \"\")\n throw new Error(\"typecheck probe requires an explicit project file\");\n const root = await realpath(repo);\n const requested = resolve(root, project);\n if (!insideProbePath({ root, candidate: requested }))\n throw new Error(\n `typecheck probe project is outside the repository: ${project}`,\n );\n const config = await realpath(requested);\n if (!insideProbePath({ root, candidate: config }))\n throw new Error(\n `typecheck probe project resolves outside the repository: ${project}`,\n );\n if (!(await stat(config)).isFile())\n throw new Error(`typecheck probe project must be a file: ${project}`);\n let seedParent = dirname(config);\n if (seedDirectory !== undefined) {\n if (seedDirectory === \"\" || isAbsolute(seedDirectory))\n throw new Error(\n \"typecheck probe seed directory must be repository-relative\",\n );\n const requestedSeedParent = resolve(root, seedDirectory);\n if (!insideProbePath({ root, candidate: requestedSeedParent }))\n throw new Error(\n `typecheck probe seed directory is outside the repository: ${seedDirectory}`,\n );\n seedParent = await realpath(requestedSeedParent);\n if (\n !insideProbePath({ root, candidate: seedParent }) ||\n !insideProbePath({ root: dirname(config), candidate: seedParent })\n )\n throw new Error(\n `typecheck probe seed directory must be inside the declared project: ${seedDirectory}`,\n );\n if (!(await stat(seedParent)).isDirectory())\n throw new Error(\n `typecheck probe seed directory must be a directory: ${seedDirectory}`,\n );\n }\n const seed = join(seedParent, `stll-typecheck-probe-${randomUUID()}.ts`);\n const seedLocation = relative(root, seed).replaceAll(\"\\\\\", \"/\");\n const controller = new AbortController();\n const interrupt = (signal: string) =>\n controller.abort(new Error(`typecheck probe interrupted by ${signal}`));\n const onInterrupt = () => interrupt(\"SIGINT\");\n const onTerminate = () => interrupt(\"SIGTERM\");\n process.on(\"SIGINT\", onInterrupt);\n process.on(\"SIGTERM\", onTerminate);\n let created = false;\n const removeSeed = async () => {\n if (!created) return;\n await unlink(seed);\n created = false;\n };\n const execute = async () => {\n controller.signal.throwIfAborted();\n let onAbort: (() => void) | undefined;\n const aborted = new Promise<never>((_done, reject) => {\n onAbort = () => reject(controller.signal.reason);\n controller.signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n try {\n const result = await Promise.race([\n run({ command, cwd: root, signal: controller.signal }),\n aborted,\n ]);\n controller.signal.throwIfAborted();\n output?.(result.output);\n return result;\n } finally {\n if (onAbort !== undefined)\n controller.signal.removeEventListener(\"abort\", onAbort);\n }\n };\n try {\n controller.signal.throwIfAborted();\n const file = await open(seed, \"wx\", 0o600);\n created = true;\n try {\n await file.writeFile('export const typecheckProbe: number = \"wrong\";\\n');\n } finally {\n await file.close();\n }\n const seeded = await execute();\n if (seeded.status === null || seeded.status === 0)\n throw new Error(\n `typecheck probe expected the seeded command to fail: ${seedLocation}`,\n );\n // Workspace runners may report compiler paths from the declared project.\n // Only the exact owned seed under either known command origin is accepted.\n const origins = new Set([root, dirname(config)]);\n const attributed = [...origins].some((origin) =>\n diagnosticSet(seeded.output, origin).includes(\n `${relative(origin, seed).replaceAll(\"\\\\\", \"/\")}:1:2322`,\n ),\n );\n if (!attributed)\n throw new Error(\n `typecheck probe expected TS2322 attributed to ${seedLocation}; the declared project may exclude the seed`,\n );\n await removeSeed();\n const clean = await execute();\n if (clean.status !== 0)\n throw new Error(\n `typecheck probe clean command failed with status ${clean.status ?? \"unknown\"}: ${project}`,\n );\n } finally {\n try {\n await removeSeed();\n } finally {\n process.removeListener(\"SIGINT\", onInterrupt);\n process.removeListener(\"SIGTERM\", onTerminate);\n }\n }\n};\n","export const probeUsage =\n \"Usage: stll-typecheck-probe --project <tsconfig> [--seed-dir <directory>] -- <command> [args...]\\n stll-typecheck-probe --help\";\n\nexport const parseProbeArguments = (args: readonly string[]) => {\n if (args.length === 1 && args.at(0) === \"--help\")\n return { mode: \"help\" } as const;\n const project = args.at(1);\n const seedDirectory = args.at(2) === \"--seed-dir\" ? args.at(3) : undefined;\n const separator = seedDirectory === undefined ? 2 : 4;\n const command = args.slice(separator + 1);\n if (\n args.at(0) !== \"--project\" ||\n project === undefined ||\n project.trim() === \"\" ||\n project.startsWith(\"-\") ||\n args.at(separator) !== \"--\" ||\n (seedDirectory !== undefined &&\n (seedDirectory.trim() === \"\" || seedDirectory.startsWith(\"-\"))) ||\n command.length === 0 ||\n command.some((value) => value.trim() === \"\")\n )\n throw new Error(probeUsage);\n return {\n mode: \"probe\",\n project,\n command,\n ...(seedDirectory === undefined ? {} : { seedDirectory }),\n } as const;\n};\n","#!/usr/bin/env node\nimport { runTypecheckProbe } from \"./typecheck-probe\";\nimport { parseProbeArguments, probeUsage } from \"./typecheck-probe-args\";\n\nconst main = async () => {\n const args = parseProbeArguments(process.argv.slice(2));\n if (args.mode === \"help\") {\n process.stdout.write(`${probeUsage}\\n`);\n return;\n }\n await runTypecheckProbe({\n repo: process.cwd(),\n project: args.project,\n command: args.command,\n seedDirectory: args.seedDirectory,\n output: (text) => process.stdout.write(text),\n });\n process.stdout.write(\n \"typecheck probe passed: seeded TS2322 detected and clean typecheck passed\\n\",\n );\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `typecheck-probe: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"typecheck-probe-cli.mjs","names":[],"sources":["../src/typecheck-probe.ts","../src/typecheck-probe-args.ts","../src/typecheck-probe-cli.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { open, realpath, stat, unlink } from \"node:fs/promises\";\nimport path, { dirname, isAbsolute, join, relative, resolve } from \"node:path\";\nimport { stripVTControlCharacters } from \"node:util\";\n\nconst diagnosticLocation = (file: string, repo: string) => {\n const normalized = file.replaceAll(\"\\\\\", \"/\");\n const bundled = /^bundled:\\/\\/\\/libs\\/(lib\\.[^/]+\\.d\\.ts)$/.exec(normalized);\n const installed =\n /(?:^|\\/)node_modules\\/(?:typescript|@typescript\\/(?:native|typescript-[^/]+))\\/(?:.*\\/)?(lib\\.[^/]+\\.d\\.ts)$/.exec(\n normalized,\n );\n const library = bundled?.at(1) ?? installed?.at(1);\n return library === undefined\n ? relative(repo, resolve(repo, file)).replaceAll(\"\\\\\", \"/\")\n : `<lib>/${library}`;\n};\nconst globalDiagnosticKey = (code: string, message: string) =>\n `<config>:0:${code}:${message.trim().replaceAll(/\\s+/g, \" \")}`;\n\n/** Normalizes checker output to sorted `file:line:code` keys. */\nexport const diagnosticSet = (output: string, repo: string): string[] => {\n const clean = stripVTControlCharacters(output);\n const diagnostics = new Set<string>();\n for (const block of clean.matchAll(/<error\\b([^>]*)>([\\s\\S]*?)<\\/error>/g)) {\n const attributes = new Map(\n [...(block.at(1) ?? \"\").matchAll(/(\\w+)=\"([^\"]*)\"/g)].map((match) => [\n match.at(1),\n match.at(2),\n ]),\n );\n const file = attributes.get(\"file\");\n const row = attributes.get(\"line\");\n const code = attributes.get(\"code\");\n if (code === undefined || !/^TS\\d+$/.test(code)) continue;\n if (file !== undefined && row !== undefined && row !== \"0\")\n diagnostics.add(\n `${diagnosticLocation(file, repo)}:${row}:${code.slice(2)}`,\n );\n else {\n const message =\n (block.at(2) ?? \"\").split(/<(?:source|related)\\b/).at(0) ?? \"\";\n diagnostics.add(globalDiagnosticKey(code.slice(2), message));\n }\n }\n for (const rawLine of clean.split(\"\\n\")) {\n const line = rawLine.replace(\n /^(?!(?:error|warning) TS\\d+:)(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]* [a-zA-Z0-9_.:-]+: /,\n \"\",\n );\n if (line.startsWith(\"<error \")) continue;\n const match =\n /^(.*?)\\((\\d+),\\d+\\):\\s*(?:error|warning) TS(\\d+)/.exec(line) ??\n /^(.*?):(\\d+):\\d+:\\s*(?:error|warning) (?:TS)?(\\d+)/.exec(line);\n if (match) {\n const [, file = \"\", row = \"\", code = \"\"] = match;\n diagnostics.add(`${diagnosticLocation(file, repo)}:${row}:${code}`);\n } else {\n const global = /^(?:error|warning) TS(\\d+):\\s*(.*)/.exec(line);\n if (global)\n diagnostics.add(\n globalDiagnosticKey(global.at(1) ?? \"\", global.at(2) ?? \"\"),\n );\n }\n }\n return [...diagnostics].sort();\n};\n\ntype ProbeCommandOptions = {\n command: readonly string[];\n cwd: string;\n signal: AbortSignal;\n};\ntype ProbeCommandResult = { status: number | null; output: string };\n\nconst runProbeCommand = ({ command, cwd, signal }: ProbeCommandOptions) =>\n new Promise<ProbeCommandResult>((done, reject) => {\n const executable = command.at(0);\n if (executable === undefined) {\n reject(new Error(\"typecheck probe command is empty\"));\n return;\n }\n const child = spawn(executable, command.slice(1), {\n cwd,\n signal,\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n let output = \"\";\n child.stdout.on(\"data\", (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.once(\"error\", reject);\n child.once(\"close\", (status) => done({ status, output }));\n });\n\ntype ProbePathContainmentOptions = {\n root: string;\n candidate: string;\n paths?: Pick<typeof path, \"relative\" | \"isAbsolute\" | \"sep\">;\n};\n\nexport const insideProbePath = ({\n root,\n candidate,\n paths = path,\n}: ProbePathContainmentOptions) => {\n const location = paths.relative(root, candidate);\n return (\n location !== \"..\" &&\n !location.startsWith(\"..\" + paths.sep) &&\n !paths.isAbsolute(location)\n );\n};\n\ntype RunTypecheckProbeOptions = {\n repo: string;\n project: string;\n seedDirectory?: string | undefined;\n command: readonly string[];\n output?: (text: string) => void;\n run?: (options: ProbeCommandOptions) => Promise<ProbeCommandResult>;\n};\n\nexport const runTypecheckProbe = async ({\n repo,\n project,\n seedDirectory,\n command,\n output,\n run = runProbeCommand,\n}: RunTypecheckProbeOptions) => {\n const executable = command.at(0);\n if (\n executable === undefined ||\n executable === \"\" ||\n command.some((argument) => argument.includes(\"\\0\"))\n )\n throw new Error(\n \"typecheck probe requires a nonempty command argv without NUL bytes\",\n );\n if (project === \"\")\n throw new Error(\"typecheck probe requires an explicit project file\");\n const root = await realpath(repo);\n const requested = resolve(root, project);\n if (!insideProbePath({ root, candidate: requested }))\n throw new Error(\n `typecheck probe project is outside the repository: ${project}`,\n );\n const config = await realpath(requested);\n if (!insideProbePath({ root, candidate: config }))\n throw new Error(\n `typecheck probe project resolves outside the repository: ${project}`,\n );\n if (!(await stat(config)).isFile())\n throw new Error(`typecheck probe project must be a file: ${project}`);\n let seedParent = dirname(config);\n if (seedDirectory !== undefined) {\n if (seedDirectory === \"\" || isAbsolute(seedDirectory))\n throw new Error(\n \"typecheck probe seed directory must be repository-relative\",\n );\n const requestedSeedParent = resolve(root, seedDirectory);\n if (!insideProbePath({ root, candidate: requestedSeedParent }))\n throw new Error(\n `typecheck probe seed directory is outside the repository: ${seedDirectory}`,\n );\n seedParent = await realpath(requestedSeedParent);\n if (\n !insideProbePath({ root, candidate: seedParent }) ||\n !insideProbePath({ root: dirname(config), candidate: seedParent })\n )\n throw new Error(\n `typecheck probe seed directory must be inside the declared project: ${seedDirectory}`,\n );\n if (!(await stat(seedParent)).isDirectory())\n throw new Error(\n `typecheck probe seed directory must be a directory: ${seedDirectory}`,\n );\n }\n const seed = join(seedParent, `stll-typecheck-probe-${randomUUID()}.ts`);\n const seedLocation = relative(root, seed).replaceAll(\"\\\\\", \"/\");\n const controller = new AbortController();\n const interrupt = (signal: string) =>\n controller.abort(new Error(`typecheck probe interrupted by ${signal}`));\n const onInterrupt = () => interrupt(\"SIGINT\");\n const onTerminate = () => interrupt(\"SIGTERM\");\n process.on(\"SIGINT\", onInterrupt);\n process.on(\"SIGTERM\", onTerminate);\n let created = false;\n const removeSeed = async () => {\n if (!created) return;\n await unlink(seed);\n created = false;\n };\n const execute = async () => {\n controller.signal.throwIfAborted();\n let onAbort: (() => void) | undefined;\n const aborted = new Promise<never>((_done, reject) => {\n onAbort = () => reject(controller.signal.reason);\n controller.signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n try {\n const result = await Promise.race([\n run({ command, cwd: root, signal: controller.signal }),\n aborted,\n ]);\n controller.signal.throwIfAborted();\n output?.(result.output);\n return result;\n } finally {\n if (onAbort !== undefined)\n controller.signal.removeEventListener(\"abort\", onAbort);\n }\n };\n try {\n controller.signal.throwIfAborted();\n const file = await open(seed, \"wx\", 0o600);\n created = true;\n try {\n await file.writeFile('export const typecheckProbe: number = \"wrong\";\\n');\n } finally {\n await file.close();\n }\n const seeded = await execute();\n if (seeded.status === null || seeded.status === 0)\n throw new Error(\n `typecheck probe expected the seeded command to fail: ${seedLocation}`,\n );\n // Workspace runners may report compiler paths from the declared project.\n // Only the exact owned seed under either known command origin is accepted.\n const origins = new Set([root, dirname(config)]);\n const attributed = [...origins].some((origin) =>\n diagnosticSet(seeded.output, origin).includes(\n `${relative(origin, seed).replaceAll(\"\\\\\", \"/\")}:1:2322`,\n ),\n );\n if (!attributed)\n throw new Error(\n `typecheck probe expected TS2322 attributed to ${seedLocation}; the declared project may exclude the seed`,\n );\n await removeSeed();\n const clean = await execute();\n if (clean.status !== 0)\n throw new Error(\n `typecheck probe clean command failed with status ${clean.status ?? \"unknown\"}: ${project}`,\n );\n } finally {\n try {\n await removeSeed();\n } finally {\n process.removeListener(\"SIGINT\", onInterrupt);\n process.removeListener(\"SIGTERM\", onTerminate);\n }\n }\n};\n","export const probeUsage =\n \"Usage: stll-typecheck-probe --project <tsconfig> [--seed-dir <directory>] -- <command> [args...]\\n stll-typecheck-probe --help\";\n\nexport const parseProbeArguments = (args: readonly string[]) => {\n if (args.length === 1 && args.at(0) === \"--help\")\n return { mode: \"help\" } as const;\n const project = args.at(1);\n const seedDirectory = args.at(2) === \"--seed-dir\" ? args.at(3) : undefined;\n const separator = seedDirectory === undefined ? 2 : 4;\n const command = args.slice(separator + 1);\n if (\n args.at(0) !== \"--project\" ||\n project === undefined ||\n project.trim() === \"\" ||\n project.startsWith(\"-\") ||\n args.at(separator) !== \"--\" ||\n (seedDirectory !== undefined &&\n (seedDirectory.trim() === \"\" || seedDirectory.startsWith(\"-\"))) ||\n command.length === 0 ||\n command.some((value) => value.trim() === \"\")\n )\n throw new Error(probeUsage);\n return {\n mode: \"probe\",\n project,\n command,\n ...(seedDirectory === undefined ? {} : { seedDirectory }),\n } as const;\n};\n","#!/usr/bin/env node\nimport { runTypecheckProbe } from \"./typecheck-probe\";\nimport { parseProbeArguments, probeUsage } from \"./typecheck-probe-args\";\n\nconst main = async () => {\n const args = parseProbeArguments(process.argv.slice(2));\n if (args.mode === \"help\") {\n process.stdout.write(`${probeUsage}\\n`);\n return;\n }\n await runTypecheckProbe({\n repo: process.cwd(),\n project: args.project,\n command: args.command,\n seedDirectory: args.seedDirectory,\n output: (text) => process.stdout.write(text),\n });\n process.stdout.write(\n \"typecheck probe passed: seeded TS2322 detected and clean typecheck passed\\n\",\n );\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `typecheck-probe: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/oxlint-config",
3
- "version": "1.9.0",
3
+ "version": "2.1.0",
4
4
  "description": "Shared general oxlint configuration for stella packages.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -15,9 +15,9 @@
15
15
  "stll-engine-support": "./dist/engine-support-cli.mjs",
16
16
  "stll-publish-contract": "./dist/publish-contract-cli.mjs",
17
17
  "stll-quarantine": "./dist/quarantine-cli.mjs",
18
+ "stll-rust-policy": "./dist/rust-policy-cli.mjs",
18
19
  "stll-toolchain-changed": "./dist/toolchain-changed-cli.mjs",
19
20
  "stll-toolchain-check": "./dist/toolchain-check-cli.mjs",
20
- "stll-typecheck-parity": "./dist/typecheck-parity-cli.mjs",
21
21
  "stll-typecheck-probe": "./dist/typecheck-probe-cli.mjs",
22
22
  "stll-normalize-wasi-loaders": "./dist/wasi-loader-normalizer-cli.mjs"
23
23
  },
@@ -125,7 +125,7 @@
125
125
  },
126
126
  "scripts": {
127
127
  "build": "tsdown",
128
- "typecheck": "bun check",
128
+ "typecheck": "bun check --no-pretty --all --project=tsconfig.json",
129
129
  "test": "bun test"
130
130
  },
131
131
  "dependencies": {
@@ -138,6 +138,7 @@
138
138
  "picomatch": "4.0.7",
139
139
  "semver": "7.8.5",
140
140
  "smol-toml": "1.9.1",
141
+ "tsrs": "0.18.0-ts7.1.0-dev.20260929",
141
142
  "tsx": "4.23.15",
142
143
  "yaml": "2.9.1"
143
144
  },
@@ -146,7 +147,6 @@
146
147
  "@types/picomatch": "4.0.3",
147
148
  "@types/semver": "7.8.0",
148
149
  "oxlint": "1.87.0",
149
- "typescript": "7.0.2",
150
150
  "unplugin-dts": "1.1.2",
151
151
  "vite": "8.3.4",
152
152
  "vite-plugin-dts": "5.1.2"
package/toolchain.json CHANGED
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "bun": "1.4.4",
4
+ "bunArchiveSha256": {
5
+ "1.4.4": {
6
+ "amd64": "bf8ccbfb2b0b80159e0f9a6d15c55317add8077534dbc6130a3edb52fccb395c",
7
+ "arm64": "baa6bb36f51d6e58561e02bbea825038a9df454f673d6d776e8bbf86f8c7850e"
8
+ }
9
+ },
4
10
  "typescript": "7.0.2",
5
11
  "oxlint": "1.87.0",
6
12
  "@oxlint/plugins": "1.87.0",
@@ -55,35 +61,15 @@
55
61
  }
56
62
  }
57
63
  },
58
- "typescriptInstallLayouts": [
59
- {
60
- "type": "direct",
61
- "compilerPackage": "typescript",
62
- "compilerSpecifier": "7.0.2",
63
- "typecheckCommand": "bun check"
64
- },
65
- {
66
- "type": "split-compatibility",
67
- "compilerPackage": "@typescript/native",
68
- "compilerSpecifier": "npm:typescript@7.0.2",
69
- "compatibilityPackage": "typescript",
70
- "compatibilitySpecifier": "6.0.3",
71
- "typecheckCommand": "bun check"
64
+ "@stll/oxlint-plugin": "0.6.3",
65
+ "lefthook": "2.2.2",
66
+ "node": "26.x",
67
+ "nodeArchiveSha256": {
68
+ "26.10.0": {
69
+ "amd64": "ca70e9e349de048b9522abb3adc05b3bd6f43c5ffd3ec57916c7da292f59f022",
70
+ "arm64": "7a6353f63eb3d04765004b4adf172616243e4522434635cb1d26288658b04ab5"
72
71
  }
73
- ],
74
- "typescript6Compatibility": {
75
- "version": "6.0.3",
76
- "packageAlias": "typescript-compat",
77
- "apiConsumers": ["TypeScript compiler API"],
78
- "peerBlockers": [
79
- "@astrojs/check",
80
- "@typescript-eslint/utils",
81
- "dependency-cruiser"
82
- ]
83
72
  },
84
- "@stll/oxlint-plugin": "0.6.2",
85
- "lefthook": "2.1.16",
86
- "node": "26.x",
87
73
  "postgres": "18",
88
74
  "valkey": "9.1.1",
89
75
  "valkeyArtifacts": {
@@ -1,34 +0,0 @@
1
- let semver = require("semver");
2
- //#region src/toolchain-bun.ts
3
- const STABLE_RELEASE = /^\d+\.\d+\.\d+$/u;
4
- /**
5
- * The shared policy names the minimum Bun. A repository pins one exact
6
- * release at or above it in its root packageManager, and every other Bun
7
- * reference in the repository must equal that release.
8
- */
9
- const repositoryBun = (floor, manifest) => {
10
- let packageManager;
11
- try {
12
- const parsed = JSON.parse(manifest ?? "");
13
- if (typeof parsed === "object" && parsed !== null) packageManager = Reflect.get(parsed, "packageManager");
14
- } catch {}
15
- const version = typeof packageManager === "string" ? /^bun@(.+)$/.exec(packageManager)?.[1] : void 0;
16
- return version !== void 0 && STABLE_RELEASE.test(version) && (0, semver.valid)(version) === version && (0, semver.gte)(version, floor) ? version : floor;
17
- };
18
- /** A selected runtime satisfies the policy when it is an exact release at or above the floor. */
19
- const satisfiesBunFloor = (version, floor) => STABLE_RELEASE.test(version) && (0, semver.valid)(version) === version && (0, semver.gte)(version, floor);
20
- //#endregion
21
- Object.defineProperty(exports, "repositoryBun", {
22
- enumerable: true,
23
- get: function() {
24
- return repositoryBun;
25
- }
26
- });
27
- Object.defineProperty(exports, "satisfiesBunFloor", {
28
- enumerable: true,
29
- get: function() {
30
- return satisfiesBunFloor;
31
- }
32
- });
33
-
34
- //# sourceMappingURL=toolchain-bun.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"toolchain-bun.cjs","names":["valid","gte"],"sources":["../src/toolchain-bun.ts"],"sourcesContent":["import { gte, valid } from \"semver\";\n\n// Stable releases only: a prerelease never satisfies the shared minimum.\nconst STABLE_RELEASE = /^\\d+\\.\\d+\\.\\d+$/u;\n\n/**\n * The shared policy names the minimum Bun. A repository pins one exact\n * release at or above it in its root packageManager, and every other Bun\n * reference in the repository must equal that release.\n */\nexport const repositoryBun = (floor: string, manifest: string | undefined) => {\n let packageManager: unknown;\n try {\n const parsed: unknown = JSON.parse(manifest ?? \"\");\n if (typeof parsed === \"object\" && parsed !== null)\n packageManager = Reflect.get(parsed, \"packageManager\");\n } catch {\n // Manifest diagnostics report a malformed root package.json.\n }\n const version =\n typeof packageManager === \"string\"\n ? /^bun@(.+)$/.exec(packageManager)?.[1]\n : undefined;\n return version !== undefined &&\n STABLE_RELEASE.test(version) &&\n valid(version) === version &&\n gte(version, floor)\n ? version\n : floor;\n};\n\n/** A selected runtime satisfies the policy when it is an exact release at or above the floor. */\nexport const satisfiesBunFloor = (version: string, floor: string) =>\n STABLE_RELEASE.test(version) &&\n valid(version) === version &&\n gte(version, floor);\n"],"mappings":";;AAGA,MAAM,iBAAiB;;;;;;AAOvB,MAAa,iBAAiB,OAAe,aAAiC;CAC5E,IAAI;CACJ,IAAI;EACF,MAAM,SAAkB,KAAK,MAAM,YAAY,EAAE;EACjD,IAAI,OAAO,WAAW,YAAY,WAAW,MAC3C,iBAAiB,QAAQ,IAAI,QAAQ,gBAAgB;CACzD,QAAQ,CAER;CACA,MAAM,UACJ,OAAO,mBAAmB,WACtB,aAAa,KAAK,cAAc,CAAC,GAAG,KACpC,KAAA;CACN,OAAO,YAAY,KAAA,KACjB,eAAe,KAAK,OAAO,MAAA,GAC3BA,OAAAA,MAAAA,CAAM,OAAO,MAAM,YAAA,GACnBC,OAAAA,IAAAA,CAAI,SAAS,KAAK,IAChB,UACA;AACN;;AAGA,MAAa,qBAAqB,SAAiB,UACjD,eAAe,KAAK,OAAO,MAAA,GAC3BD,OAAAA,MAAAA,CAAM,OAAO,MAAM,YAAA,GACnBC,OAAAA,IAAAA,CAAI,SAAS,KAAK"}
@@ -1,23 +0,0 @@
1
- import { gte, valid } from "semver";
2
- //#region src/toolchain-bun.ts
3
- const STABLE_RELEASE = /^\d+\.\d+\.\d+$/u;
4
- /**
5
- * The shared policy names the minimum Bun. A repository pins one exact
6
- * release at or above it in its root packageManager, and every other Bun
7
- * reference in the repository must equal that release.
8
- */
9
- const repositoryBun = (floor, manifest) => {
10
- let packageManager;
11
- try {
12
- const parsed = JSON.parse(manifest ?? "");
13
- if (typeof parsed === "object" && parsed !== null) packageManager = Reflect.get(parsed, "packageManager");
14
- } catch {}
15
- const version = typeof packageManager === "string" ? /^bun@(.+)$/.exec(packageManager)?.[1] : void 0;
16
- return version !== void 0 && STABLE_RELEASE.test(version) && valid(version) === version && gte(version, floor) ? version : floor;
17
- };
18
- /** A selected runtime satisfies the policy when it is an exact release at or above the floor. */
19
- const satisfiesBunFloor = (version, floor) => STABLE_RELEASE.test(version) && valid(version) === version && gte(version, floor);
20
- //#endregion
21
- export { satisfiesBunFloor as n, repositoryBun as t };
22
-
23
- //# sourceMappingURL=toolchain-bun.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"toolchain-bun.mjs","names":[],"sources":["../src/toolchain-bun.ts"],"sourcesContent":["import { gte, valid } from \"semver\";\n\n// Stable releases only: a prerelease never satisfies the shared minimum.\nconst STABLE_RELEASE = /^\\d+\\.\\d+\\.\\d+$/u;\n\n/**\n * The shared policy names the minimum Bun. A repository pins one exact\n * release at or above it in its root packageManager, and every other Bun\n * reference in the repository must equal that release.\n */\nexport const repositoryBun = (floor: string, manifest: string | undefined) => {\n let packageManager: unknown;\n try {\n const parsed: unknown = JSON.parse(manifest ?? \"\");\n if (typeof parsed === \"object\" && parsed !== null)\n packageManager = Reflect.get(parsed, \"packageManager\");\n } catch {\n // Manifest diagnostics report a malformed root package.json.\n }\n const version =\n typeof packageManager === \"string\"\n ? /^bun@(.+)$/.exec(packageManager)?.[1]\n : undefined;\n return version !== undefined &&\n STABLE_RELEASE.test(version) &&\n valid(version) === version &&\n gte(version, floor)\n ? version\n : floor;\n};\n\n/** A selected runtime satisfies the policy when it is an exact release at or above the floor. */\nexport const satisfiesBunFloor = (version: string, floor: string) =>\n STABLE_RELEASE.test(version) &&\n valid(version) === version &&\n gte(version, floor);\n"],"mappings":";;AAGA,MAAM,iBAAiB;;;;;;AAOvB,MAAa,iBAAiB,OAAe,aAAiC;CAC5E,IAAI;CACJ,IAAI;EACF,MAAM,SAAkB,KAAK,MAAM,YAAY,EAAE;EACjD,IAAI,OAAO,WAAW,YAAY,WAAW,MAC3C,iBAAiB,QAAQ,IAAI,QAAQ,gBAAgB;CACzD,QAAQ,CAER;CACA,MAAM,UACJ,OAAO,mBAAmB,WACtB,aAAa,KAAK,cAAc,CAAC,GAAG,KACpC,KAAA;CACN,OAAO,YAAY,KAAA,KACjB,eAAe,KAAK,OAAO,KAC3B,MAAM,OAAO,MAAM,WACnB,IAAI,SAAS,KAAK,IAChB,UACA;AACN;;AAGA,MAAa,qBAAqB,SAAiB,UACjD,eAAe,KAAK,OAAO,KAC3B,MAAM,OAAO,MAAM,WACnB,IAAI,SAAS,KAAK"}