@stll/oxlint-config 1.9.0 → 2.0.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 (67) hide show
  1. package/README.md +93 -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/dependabot.d.cts +0 -19
  7. package/dist/dependabot.d.mts +0 -19
  8. package/dist/git-tracked-output.cjs +1 -1
  9. package/dist/git-tracked-output.mjs +1 -1
  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 +2 -2
  13. package/dist/publish-contract-cli.mjs +2 -2
  14. package/dist/quarantine-cli.cjs +3 -2
  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/toolchain-changed-cli.cjs +1477 -2
  25. package/dist/toolchain-changed-cli.cjs.map +1 -1
  26. package/dist/toolchain-changed-cli.mjs +1474 -1
  27. package/dist/toolchain-changed-cli.mjs.map +1 -1
  28. package/dist/toolchain-check-cli.cjs +2 -0
  29. package/dist/toolchain-check-cli.cjs.map +1 -1
  30. package/dist/toolchain-check-cli.mjs +2 -0
  31. package/dist/toolchain-check-cli.mjs.map +1 -1
  32. package/dist/toolchain-guard.cjs +351 -242
  33. package/dist/toolchain-guard.cjs.map +1 -1
  34. package/dist/toolchain-guard.mjs +347 -244
  35. package/dist/toolchain-guard.mjs.map +1 -1
  36. package/dist/toolchain-inputs.cjs +13 -40
  37. package/dist/toolchain-inputs.cjs.map +1 -1
  38. package/dist/toolchain-inputs.mjs +13 -40
  39. package/dist/toolchain-inputs.mjs.map +1 -1
  40. package/dist/toolchain-schema.cjs +2 -44
  41. package/dist/toolchain-schema.cjs.map +1 -1
  42. package/dist/toolchain-schema.mjs +2 -44
  43. package/dist/toolchain-schema.mjs.map +1 -1
  44. package/dist/typecheck-probe-cli.cjs +40 -2
  45. package/dist/typecheck-probe-cli.cjs.map +1 -1
  46. package/dist/typecheck-probe-cli.mjs +39 -1
  47. package/dist/typecheck-probe-cli.mjs.map +1 -1
  48. package/package.json +3 -4
  49. package/toolchain.json +2 -28
  50. package/dist/toolchain-bun.cjs +0 -34
  51. package/dist/toolchain-bun.cjs.map +0 -1
  52. package/dist/toolchain-bun.mjs +0 -23
  53. package/dist/toolchain-bun.mjs.map +0 -1
  54. package/dist/toolchain-changed.cjs +0 -1484
  55. package/dist/toolchain-changed.cjs.map +0 -1
  56. package/dist/toolchain-changed.mjs +0 -1477
  57. package/dist/toolchain-changed.mjs.map +0 -1
  58. package/dist/typecheck-parity-cli.cjs +0 -71
  59. package/dist/typecheck-parity-cli.cjs.map +0 -1
  60. package/dist/typecheck-parity-cli.d.cts +0 -1
  61. package/dist/typecheck-parity-cli.d.mts +0 -1
  62. package/dist/typecheck-parity-cli.mjs +0 -71
  63. package/dist/typecheck-parity-cli.mjs.map +0 -1
  64. package/dist/typecheck-parity.cjs +0 -908
  65. package/dist/typecheck-parity.cjs.map +0 -1
  66. package/dist/typecheck-parity.mjs +0 -897
  67. package/dist/typecheck-parity.mjs.map +0 -1
@@ -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":";;;;;;;AAMA,MAAM,sBAAsB,MAAc,SAAiB;CACzD,MAAM,aAAa,KAAK,WAAW,MAAM,GAAG;CAC5C,MAAM,UAAU,4CAA4C,KAAK,UAAU;CAC3E,MAAM,YACJ,+GAA+G,KAC7G,UACF;CACF,MAAM,UAAU,SAAS,GAAG,CAAC,KAAK,WAAW,GAAG,CAAC;CACjD,OAAO,YAAY,KAAA,IACf,SAAS,MAAM,QAAQ,MAAM,IAAI,CAAC,CAAC,CAAC,WAAW,MAAM,GAAG,IACxD,SAAS;AACf;AACA,MAAM,uBAAuB,MAAc,YACzC,cAAc,KAAK,GAAG,QAAQ,KAAK,CAAC,CAAC,WAAW,QAAQ,GAAG;;AAG7D,MAAa,iBAAiB,QAAgB,SAA2B;CACvE,MAAM,QAAQ,yBAAyB,MAAM;CAC7C,MAAM,8BAAc,IAAI,IAAY;CACpC,KAAK,MAAM,SAAS,MAAM,SAAS,sCAAsC,GAAG;EAC1E,MAAM,aAAa,IAAI,IACrB,CAAC,IAAI,MAAM,GAAG,CAAC,KAAK,GAAA,CAAI,SAAS,kBAAkB,CAAC,CAAC,CAAC,KAAK,UAAU,CACnE,MAAM,GAAG,CAAC,GACV,MAAM,GAAG,CAAC,CACZ,CAAC,CACH;EACA,MAAM,OAAO,WAAW,IAAI,MAAM;EAClC,MAAM,MAAM,WAAW,IAAI,MAAM;EACjC,MAAM,OAAO,WAAW,IAAI,MAAM;EAClC,IAAI,SAAS,KAAA,KAAa,CAAC,UAAU,KAAK,IAAI,GAAG;EACjD,IAAI,SAAS,KAAA,KAAa,QAAQ,KAAA,KAAa,QAAQ,KACrD,YAAY,IACV,GAAG,mBAAmB,MAAM,IAAI,EAAE,GAAG,IAAI,GAAG,KAAK,MAAM,CAAC,GAC1D;OACG;GACH,MAAM,WACH,MAAM,GAAG,CAAC,KAAK,GAAA,CAAI,MAAM,uBAAuB,CAAC,CAAC,GAAG,CAAC,KAAK;GAC9D,YAAY,IAAI,oBAAoB,KAAK,MAAM,CAAC,GAAG,OAAO,CAAC;EAC7D;CACF;CACA,KAAK,MAAM,WAAW,MAAM,MAAM,IAAI,GAAG;EACvC,MAAM,OAAO,QAAQ,QACnB,oGACA,EACF;EACA,IAAI,KAAK,WAAW,SAAS,GAAG;EAChC,MAAM,QACJ,mDAAmD,KAAK,IAAI,KAC5D,qDAAqD,KAAK,IAAI;EAChE,IAAI,OAAO;GACT,MAAM,GAAG,OAAO,IAAI,MAAM,IAAI,OAAO,MAAM;GAC3C,YAAY,IAAI,GAAG,mBAAmB,MAAM,IAAI,EAAE,GAAG,IAAI,GAAG,MAAM;EACpE,OAAO;GACL,MAAM,SAAS,qCAAqC,KAAK,IAAI;GAC7D,IAAI,QACF,YAAY,IACV,oBAAoB,OAAO,GAAG,CAAC,KAAK,IAAI,OAAO,GAAG,CAAC,KAAK,EAAE,CAC5D;EACJ;CACF;CACA,OAAO,CAAC,GAAG,WAAW,CAAC,CAAC,KAAK;AAC/B;AASA,MAAM,mBAAmB,EAAE,SAAS,KAAK,aACvC,IAAI,SAA6B,MAAM,WAAW;CAChD,MAAM,aAAa,QAAQ,GAAG,CAAC;CAC/B,IAAI,eAAe,KAAA,GAAW;EAC5B,uBAAO,IAAI,MAAM,kCAAkC,CAAC;EACpD;CACF;CACA,MAAM,QAAQ,MAAM,YAAY,QAAQ,MAAM,CAAC,GAAG;EAChD;EACA;EACA,OAAO;EACP,OAAO;GAAC;GAAU;GAAQ;EAAM;CAClC,CAAC;CACD,IAAI,SAAS;CACb,MAAM,OAAO,GAAG,SAAS,UAAkB;EACzC,UAAU,MAAM,SAAS;CAC3B,CAAC;CACD,MAAM,OAAO,GAAG,SAAS,UAAkB;EACzC,UAAU,MAAM,SAAS;CAC3B,CAAC;CACD,MAAM,KAAK,SAAS,MAAM;CAC1B,MAAM,KAAK,UAAU,WAAW,KAAK;EAAE;EAAQ;CAAO,CAAC,CAAC;AAC1D,CAAC;AAQH,MAAa,mBAAmB,EAC9B,MACA,WACA,QAAQ,WACyB;CACjC,MAAM,WAAW,MAAM,SAAS,MAAM,SAAS;CAC/C,OACE,aAAa,QACb,CAAC,SAAS,WAAW,OAAO,MAAM,GAAG,KACrC,CAAC,MAAM,WAAW,QAAQ;AAE9B;AAWA,MAAa,oBAAoB,OAAO,EACtC,MACA,SACA,eACA,SACA,QACA,MAAM,sBACwB;CAC9B,MAAM,aAAa,QAAQ,GAAG,CAAC;CAC/B,IACE,eAAe,KAAA,KACf,eAAe,MACf,QAAQ,MAAM,aAAa,SAAS,SAAS,IAAI,CAAC,GAElD,MAAM,IAAI,MACR,oEACF;CACF,IAAI,YAAY,IACd,MAAM,IAAI,MAAM,mDAAmD;CACrE,MAAM,OAAO,MAAM,SAAS,IAAI;CAChC,MAAM,YAAY,QAAQ,MAAM,OAAO;CACvC,IAAI,CAAC,gBAAgB;EAAE;EAAM,WAAW;CAAU,CAAC,GACjD,MAAM,IAAI,MACR,sDAAsD,SACxD;CACF,MAAM,SAAS,MAAM,SAAS,SAAS;CACvC,IAAI,CAAC,gBAAgB;EAAE;EAAM,WAAW;CAAO,CAAC,GAC9C,MAAM,IAAI,MACR,4DAA4D,SAC9D;CACF,IAAI,EAAE,MAAM,KAAK,MAAM,EAAA,CAAG,OAAO,GAC/B,MAAM,IAAI,MAAM,2CAA2C,SAAS;CACtE,IAAI,aAAa,QAAQ,MAAM;CAC/B,IAAI,kBAAkB,KAAA,GAAW;EAC/B,IAAI,kBAAkB,MAAM,WAAW,aAAa,GAClD,MAAM,IAAI,MACR,4DACF;EACF,MAAM,sBAAsB,QAAQ,MAAM,aAAa;EACvD,IAAI,CAAC,gBAAgB;GAAE;GAAM,WAAW;EAAoB,CAAC,GAC3D,MAAM,IAAI,MACR,6DAA6D,eAC/D;EACF,aAAa,MAAM,SAAS,mBAAmB;EAC/C,IACE,CAAC,gBAAgB;GAAE;GAAM,WAAW;EAAW,CAAC,KAChD,CAAC,gBAAgB;GAAE,MAAM,QAAQ,MAAM;GAAG,WAAW;EAAW,CAAC,GAEjE,MAAM,IAAI,MACR,uEAAuE,eACzE;EACF,IAAI,EAAE,MAAM,KAAK,UAAU,EAAA,CAAG,YAAY,GACxC,MAAM,IAAI,MACR,uDAAuD,eACzD;CACJ;CACA,MAAM,OAAO,KAAK,YAAY,wBAAwB,WAAW,EAAE,IAAI;CACvE,MAAM,eAAe,SAAS,MAAM,IAAI,CAAC,CAAC,WAAW,MAAM,GAAG;CAC9D,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,aAAa,WACjB,WAAW,sBAAM,IAAI,MAAM,kCAAkC,QAAQ,CAAC;CACxE,MAAM,oBAAoB,UAAU,QAAQ;CAC5C,MAAM,oBAAoB,UAAU,SAAS;CAC7C,QAAQ,GAAG,UAAU,WAAW;CAChC,QAAQ,GAAG,WAAW,WAAW;CACjC,IAAI,UAAU;CACd,MAAM,aAAa,YAAY;EAC7B,IAAI,CAAC,SAAS;EACd,MAAM,OAAO,IAAI;EACjB,UAAU;CACZ;CACA,MAAM,UAAU,YAAY;EAC1B,WAAW,OAAO,eAAe;EACjC,IAAI;EACJ,MAAM,UAAU,IAAI,SAAgB,OAAO,WAAW;GACpD,gBAAgB,OAAO,WAAW,OAAO,MAAM;GAC/C,WAAW,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EACrE,CAAC;EACD,IAAI;GACF,MAAM,SAAS,MAAM,QAAQ,KAAK,CAChC,IAAI;IAAE;IAAS,KAAK;IAAM,QAAQ,WAAW;GAAO,CAAC,GACrD,OACF,CAAC;GACD,WAAW,OAAO,eAAe;GACjC,SAAS,OAAO,MAAM;GACtB,OAAO;EACT,UAAU;GACR,IAAI,YAAY,KAAA,GACd,WAAW,OAAO,oBAAoB,SAAS,OAAO;EAC1D;CACF;CACA,IAAI;EACF,WAAW,OAAO,eAAe;EACjC,MAAM,OAAO,MAAM,KAAK,MAAM,MAAM,GAAK;EACzC,UAAU;EACV,IAAI;GACF,MAAM,KAAK,UAAU,oDAAkD;EACzE,UAAU;GACR,MAAM,KAAK,MAAM;EACnB;EACA,MAAM,SAAS,MAAM,QAAQ;EAC7B,IAAI,OAAO,WAAW,QAAQ,OAAO,WAAW,GAC9C,MAAM,IAAI,MACR,wDAAwD,cAC1D;EASF,IAAI,CALe,CAAC,mBAAG,IADH,IAAI,CAAC,MAAM,QAAQ,MAAM,CAAC,CACjB,CAAC,CAAC,CAAC,MAAM,WACpC,cAAc,OAAO,QAAQ,MAAM,CAAC,CAAC,SACnC,GAAG,SAAS,QAAQ,IAAI,CAAC,CAAC,WAAW,MAAM,GAAG,EAAE,QAClD,CAEY,GACZ,MAAM,IAAI,MACR,iDAAiD,aAAa,4CAChE;EACF,MAAM,WAAW;EACjB,MAAM,QAAQ,MAAM,QAAQ;EAC5B,IAAI,MAAM,WAAW,GACnB,MAAM,IAAI,MACR,oDAAoD,MAAM,UAAU,UAAU,IAAI,SACpF;CACJ,UAAU;EACR,IAAI;GACF,MAAM,WAAW;EACnB,UAAU;GACR,QAAQ,eAAe,UAAU,WAAW;GAC5C,QAAQ,eAAe,WAAW,WAAW;EAC/C;CACF;AACF;;;ACnQA,MAAa,aACX;AAEF,MAAa,uBAAuB,SAA4B;CAC9D,IAAI,KAAK,WAAW,KAAK,KAAK,GAAG,CAAC,MAAM,UACtC,OAAO,EAAE,MAAM,OAAO;CACxB,MAAM,UAAU,KAAK,GAAG,CAAC;CACzB,MAAM,gBAAgB,KAAK,GAAG,CAAC,MAAM,eAAe,KAAK,GAAG,CAAC,IAAI,KAAA;CACjE,MAAM,YAAY,kBAAkB,KAAA,IAAY,IAAI;CACpD,MAAM,UAAU,KAAK,MAAM,YAAY,CAAC;CACxC,IACE,KAAK,GAAG,CAAC,MAAM,eACf,YAAY,KAAA,KACZ,QAAQ,KAAK,MAAM,MACnB,QAAQ,WAAW,GAAG,KACtB,KAAK,GAAG,SAAS,MAAM,QACtB,kBAAkB,KAAA,MAChB,cAAc,KAAK,MAAM,MAAM,cAAc,WAAW,GAAG,MAC9D,QAAQ,WAAW,KACnB,QAAQ,MAAM,UAAU,MAAM,KAAK,MAAM,EAAE,GAE3C,MAAM,IAAI,MAAM,UAAU;CAC5B,OAAO;EACL,MAAM;EACN;EACA;EACA,GAAI,kBAAkB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc;CACzD;AACF;;;ACxBA,MAAM,OAAO,YAAY;CACvB,MAAM,OAAO,oBAAoB,QAAQ,KAAK,MAAM,CAAC,CAAC;CACtD,IAAI,KAAK,SAAS,QAAQ;EACxB,QAAQ,OAAO,MAAM,GAAG,WAAW,GAAG;EACtC;CACF;CACA,MAAM,kBAAkB;EACtB,MAAM,QAAQ,IAAI;EAClB,SAAS,KAAK;EACd,SAAS,KAAK;EACd,eAAe,KAAK;EACpB,SAAS,SAAS,QAAQ,OAAO,MAAM,IAAI;CAC7C,CAAC;CACD,QAAQ,OAAO,MACb,6EACF;AACF;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,OAAO,MACb,oBAAoB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GAC7E;CACA,QAAQ,WAAW;AACrB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/oxlint-config",
3
- "version": "1.9.0",
3
+ "version": "2.0.0",
4
4
  "description": "Shared general oxlint configuration for stella packages.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -17,7 +17,6 @@
17
17
  "stll-quarantine": "./dist/quarantine-cli.mjs",
18
18
  "stll-toolchain-changed": "./dist/toolchain-changed-cli.mjs",
19
19
  "stll-toolchain-check": "./dist/toolchain-check-cli.mjs",
20
- "stll-typecheck-parity": "./dist/typecheck-parity-cli.mjs",
21
20
  "stll-typecheck-probe": "./dist/typecheck-probe-cli.mjs",
22
21
  "stll-normalize-wasi-loaders": "./dist/wasi-loader-normalizer-cli.mjs"
23
22
  },
@@ -125,7 +124,7 @@
125
124
  },
126
125
  "scripts": {
127
126
  "build": "tsdown",
128
- "typecheck": "bun check",
127
+ "typecheck": "bun check --no-pretty --all --project=tsconfig.json",
129
128
  "test": "bun test"
130
129
  },
131
130
  "dependencies": {
@@ -138,6 +137,7 @@
138
137
  "picomatch": "4.0.7",
139
138
  "semver": "7.8.5",
140
139
  "smol-toml": "1.9.1",
140
+ "tsrs": "0.18.0-ts7.1.0-dev.20260929",
141
141
  "tsx": "4.23.15",
142
142
  "yaml": "2.9.1"
143
143
  },
@@ -146,7 +146,6 @@
146
146
  "@types/picomatch": "4.0.3",
147
147
  "@types/semver": "7.8.0",
148
148
  "oxlint": "1.87.0",
149
- "typescript": "7.0.2",
150
149
  "unplugin-dts": "1.1.2",
151
150
  "vite": "8.3.4",
152
151
  "vite-plugin-dts": "5.1.2"
package/toolchain.json CHANGED
@@ -55,34 +55,8 @@
55
55
  }
56
56
  }
57
57
  },
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"
72
- }
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
- },
84
- "@stll/oxlint-plugin": "0.6.2",
85
- "lefthook": "2.1.16",
58
+ "@stll/oxlint-plugin": "0.6.3",
59
+ "lefthook": "2.2.2",
86
60
  "node": "26.x",
87
61
  "postgres": "18",
88
62
  "valkey": "9.1.1",
@@ -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"}