@stsepelin/checktrail 0.1.0-alpha.1

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 (385) hide show
  1. package/CONTRIBUTING.md +42 -0
  2. package/LICENSE +21 -0
  3. package/README.md +355 -0
  4. package/SECURITY.md +46 -0
  5. package/dist/src/actionlint-evidence.d.ts +2 -0
  6. package/dist/src/actionlint-evidence.js +166 -0
  7. package/dist/src/actionlint-inputs.d.ts +5 -0
  8. package/dist/src/actionlint-inputs.js +123 -0
  9. package/dist/src/actionlint-runner.d.ts +1 -0
  10. package/dist/src/actionlint-runner.js +122 -0
  11. package/dist/src/actionlint.d.ts +19 -0
  12. package/dist/src/actionlint.js +80 -0
  13. package/dist/src/adapters.d.ts +49 -0
  14. package/dist/src/adapters.js +430 -0
  15. package/dist/src/architecture.d.ts +107 -0
  16. package/dist/src/architecture.js +230 -0
  17. package/dist/src/clang-evidence.d.ts +2 -0
  18. package/dist/src/clang-evidence.js +194 -0
  19. package/dist/src/clang-protocol.d.ts +3 -0
  20. package/dist/src/clang-protocol.js +25 -0
  21. package/dist/src/clang-runner.d.ts +1 -0
  22. package/dist/src/clang-runner.js +159 -0
  23. package/dist/src/clang.d.ts +44 -0
  24. package/dist/src/clang.js +197 -0
  25. package/dist/src/cli.d.ts +2 -0
  26. package/dist/src/cli.js +338 -0
  27. package/dist/src/config.d.ts +33 -0
  28. package/dist/src/config.js +140 -0
  29. package/dist/src/contract-schema.d.ts +85 -0
  30. package/dist/src/contract-schema.js +56 -0
  31. package/dist/src/contract-worker.d.ts +1 -0
  32. package/dist/src/contract-worker.js +123 -0
  33. package/dist/src/contracts.d.ts +22 -0
  34. package/dist/src/contracts.js +123 -0
  35. package/dist/src/django-evidence.d.ts +2 -0
  36. package/dist/src/django-evidence.js +100 -0
  37. package/dist/src/django-runner.d.ts +1 -0
  38. package/dist/src/django-runner.js +80 -0
  39. package/dist/src/django.d.ts +9 -0
  40. package/dist/src/django.js +50 -0
  41. package/dist/src/dotnet-compiler.d.ts +1 -0
  42. package/dist/src/dotnet-compiler.js +87 -0
  43. package/dist/src/dotnet-evidence.d.ts +2 -0
  44. package/dist/src/dotnet-evidence.js +127 -0
  45. package/dist/src/dotnet-runner.d.ts +1 -0
  46. package/dist/src/dotnet-runner.js +167 -0
  47. package/dist/src/dotnet.d.ts +65 -0
  48. package/dist/src/dotnet.js +120 -0
  49. package/dist/src/engine.d.ts +19 -0
  50. package/dist/src/engine.js +239 -0
  51. package/dist/src/environment.d.ts +6 -0
  52. package/dist/src/environment.js +36 -0
  53. package/dist/src/eslint-evidence.d.ts +2 -0
  54. package/dist/src/eslint-evidence.js +98 -0
  55. package/dist/src/eslint-runner.d.ts +1 -0
  56. package/dist/src/eslint-runner.js +61 -0
  57. package/dist/src/eslint.d.ts +2 -0
  58. package/dist/src/eslint.js +40 -0
  59. package/dist/src/evidence.d.ts +2 -0
  60. package/dist/src/evidence.js +313 -0
  61. package/dist/src/external-adapter.d.ts +164 -0
  62. package/dist/src/external-adapter.js +265 -0
  63. package/dist/src/external-evidence.d.ts +2 -0
  64. package/dist/src/external-evidence.js +127 -0
  65. package/dist/src/external-runner.d.ts +1 -0
  66. package/dist/src/external-runner.js +122 -0
  67. package/dist/src/fastapi-evidence.d.ts +2 -0
  68. package/dist/src/fastapi-evidence.js +126 -0
  69. package/dist/src/fastapi-runner.d.ts +1 -0
  70. package/dist/src/fastapi-runner.js +84 -0
  71. package/dist/src/fastapi.d.ts +10 -0
  72. package/dist/src/fastapi.js +52 -0
  73. package/dist/src/fetch-pack.d.ts +16 -0
  74. package/dist/src/fetch-pack.js +154 -0
  75. package/dist/src/finding-policy-schema.d.ts +146 -0
  76. package/dist/src/finding-policy-schema.js +70 -0
  77. package/dist/src/finding-policy.d.ts +62 -0
  78. package/dist/src/finding-policy.js +206 -0
  79. package/dist/src/git-selection.d.ts +3 -0
  80. package/dist/src/git-selection.js +229 -0
  81. package/dist/src/go-directives.d.ts +1 -0
  82. package/dist/src/go-directives.js +32 -0
  83. package/dist/src/go-scope.d.ts +11 -0
  84. package/dist/src/go-scope.js +122 -0
  85. package/dist/src/golangci-evidence.d.ts +2 -0
  86. package/dist/src/golangci-evidence.js +92 -0
  87. package/dist/src/golangci-runner.d.ts +1 -0
  88. package/dist/src/golangci-runner.js +124 -0
  89. package/dist/src/golangci.d.ts +2 -0
  90. package/dist/src/golangci.js +35 -0
  91. package/dist/src/guidance.d.ts +98 -0
  92. package/dist/src/guidance.js +247 -0
  93. package/dist/src/index.d.ts +31 -0
  94. package/dist/src/index.js +16 -0
  95. package/dist/src/inventory.d.ts +4 -0
  96. package/dist/src/inventory.js +94 -0
  97. package/dist/src/java-compiler.d.ts +1 -0
  98. package/dist/src/java-compiler.js +110 -0
  99. package/dist/src/java-evidence.d.ts +2 -0
  100. package/dist/src/java-evidence.js +114 -0
  101. package/dist/src/java-runner.d.ts +1 -0
  102. package/dist/src/java-runner.js +79 -0
  103. package/dist/src/java.d.ts +25 -0
  104. package/dist/src/java.js +102 -0
  105. package/dist/src/jest-evidence.d.ts +2 -0
  106. package/dist/src/jest-evidence.js +117 -0
  107. package/dist/src/jest-runner.d.ts +1 -0
  108. package/dist/src/jest-runner.js +46 -0
  109. package/dist/src/jest.d.ts +2 -0
  110. package/dist/src/jest.js +36 -0
  111. package/dist/src/junit.d.ts +18 -0
  112. package/dist/src/junit.js +157 -0
  113. package/dist/src/laravel-evidence.d.ts +2 -0
  114. package/dist/src/laravel-evidence.js +175 -0
  115. package/dist/src/laravel-runner.d.ts +1 -0
  116. package/dist/src/laravel-runner.js +168 -0
  117. package/dist/src/laravel.d.ts +8 -0
  118. package/dist/src/laravel.js +50 -0
  119. package/dist/src/local-tool.d.ts +1 -0
  120. package/dist/src/local-tool.js +19 -0
  121. package/dist/src/mcp.d.ts +14 -0
  122. package/dist/src/mcp.js +362 -0
  123. package/dist/src/mutation.d.ts +118 -0
  124. package/dist/src/mutation.js +370 -0
  125. package/dist/src/mypy-evidence.d.ts +2 -0
  126. package/dist/src/mypy-evidence.js +63 -0
  127. package/dist/src/mypy.d.ts +2 -0
  128. package/dist/src/mypy.js +59 -0
  129. package/dist/src/node-reporter.d.ts +1 -0
  130. package/dist/src/node-reporter.js +28 -0
  131. package/dist/src/nuxt-evidence.d.ts +2 -0
  132. package/dist/src/nuxt-evidence.js +160 -0
  133. package/dist/src/nuxt-protocol.d.ts +52 -0
  134. package/dist/src/nuxt-protocol.js +29 -0
  135. package/dist/src/nuxt-runner.d.ts +1 -0
  136. package/dist/src/nuxt-runner.js +165 -0
  137. package/dist/src/nuxt.d.ts +24 -0
  138. package/dist/src/nuxt.js +105 -0
  139. package/dist/src/output.d.ts +3 -0
  140. package/dist/src/output.js +48 -0
  141. package/dist/src/phpstan-evidence.d.ts +2 -0
  142. package/dist/src/phpstan-evidence.js +81 -0
  143. package/dist/src/phpstan.d.ts +2 -0
  144. package/dist/src/phpstan.js +51 -0
  145. package/dist/src/phpunit-evidence.d.ts +2 -0
  146. package/dist/src/phpunit-evidence.js +53 -0
  147. package/dist/src/phpunit.d.ts +2 -0
  148. package/dist/src/phpunit.js +58 -0
  149. package/dist/src/pint-evidence.d.ts +2 -0
  150. package/dist/src/pint-evidence.js +79 -0
  151. package/dist/src/pint-runner.d.ts +1 -0
  152. package/dist/src/pint-runner.js +53 -0
  153. package/dist/src/pint.d.ts +2 -0
  154. package/dist/src/pint.js +37 -0
  155. package/dist/src/playwright-evidence.d.ts +2 -0
  156. package/dist/src/playwright-evidence.js +127 -0
  157. package/dist/src/playwright-reporter.d.ts +10 -0
  158. package/dist/src/playwright-reporter.js +37 -0
  159. package/dist/src/playwright-runner.d.ts +1 -0
  160. package/dist/src/playwright-runner.js +61 -0
  161. package/dist/src/playwright.d.ts +2 -0
  162. package/dist/src/playwright.js +35 -0
  163. package/dist/src/policy-pack.d.ts +21 -0
  164. package/dist/src/policy-pack.js +41 -0
  165. package/dist/src/pytest-evidence.d.ts +2 -0
  166. package/dist/src/pytest-evidence.js +116 -0
  167. package/dist/src/pytest-runner.d.ts +1 -0
  168. package/dist/src/pytest-runner.js +48 -0
  169. package/dist/src/pytest.d.ts +2 -0
  170. package/dist/src/pytest.js +25 -0
  171. package/dist/src/report-validation.d.ts +129 -0
  172. package/dist/src/report-validation.js +27 -0
  173. package/dist/src/review.d.ts +270 -0
  174. package/dist/src/review.js +349 -0
  175. package/dist/src/ruby.d.ts +2 -0
  176. package/dist/src/ruby.js +25 -0
  177. package/dist/src/ruff-evidence.d.ts +2 -0
  178. package/dist/src/ruff-evidence.js +99 -0
  179. package/dist/src/ruff.d.ts +2 -0
  180. package/dist/src/ruff.js +40 -0
  181. package/dist/src/runner.d.ts +8 -0
  182. package/dist/src/runner.js +117 -0
  183. package/dist/src/runtime-inventory.d.ts +112 -0
  184. package/dist/src/runtime-inventory.js +200 -0
  185. package/dist/src/rust-dep-info.d.ts +1 -0
  186. package/dist/src/rust-dep-info.js +46 -0
  187. package/dist/src/rust-evidence.d.ts +2 -0
  188. package/dist/src/rust-evidence.js +157 -0
  189. package/dist/src/rust-runner.d.ts +1 -0
  190. package/dist/src/rust-runner.js +161 -0
  191. package/dist/src/rust.d.ts +7 -0
  192. package/dist/src/rust.js +39 -0
  193. package/dist/src/sarif.d.ts +86 -0
  194. package/dist/src/sarif.js +113 -0
  195. package/dist/src/schemas.d.ts +393 -0
  196. package/dist/src/schemas.js +222 -0
  197. package/dist/src/staticcheck-evidence.d.ts +2 -0
  198. package/dist/src/staticcheck-evidence.js +75 -0
  199. package/dist/src/swift.d.ts +2 -0
  200. package/dist/src/swift.js +20 -0
  201. package/dist/src/task-store.d.ts +37 -0
  202. package/dist/src/task-store.js +349 -0
  203. package/dist/src/tool-versions.d.ts +3 -0
  204. package/dist/src/tool-versions.js +260 -0
  205. package/dist/src/types.d.ts +157 -0
  206. package/dist/src/types.js +37 -0
  207. package/dist/src/typescript-build-evidence.d.ts +2 -0
  208. package/dist/src/typescript-build-evidence.js +86 -0
  209. package/dist/src/typescript-build-runner.d.ts +1 -0
  210. package/dist/src/typescript-build-runner.js +132 -0
  211. package/dist/src/typescript-build.d.ts +2 -0
  212. package/dist/src/typescript-build.js +41 -0
  213. package/dist/src/typescript.d.ts +2 -0
  214. package/dist/src/typescript.js +54 -0
  215. package/dist/src/validation-task-worker.d.ts +1 -0
  216. package/dist/src/validation-task-worker.js +205 -0
  217. package/dist/src/validation-tasks-protocol.d.ts +69 -0
  218. package/dist/src/validation-tasks-protocol.js +57 -0
  219. package/dist/src/validation-tasks.d.ts +11 -0
  220. package/dist/src/validation-tasks.js +144 -0
  221. package/dist/src/vitest-evidence.d.ts +2 -0
  222. package/dist/src/vitest-evidence.js +112 -0
  223. package/dist/src/vitest-runner.d.ts +1 -0
  224. package/dist/src/vitest-runner.js +48 -0
  225. package/dist/src/vitest.d.ts +2 -0
  226. package/dist/src/vitest.js +36 -0
  227. package/dist/src/vue-router-capture.d.ts +42 -0
  228. package/dist/src/vue-router-capture.js +87 -0
  229. package/dist/src/vue-router-evidence.d.ts +2 -0
  230. package/dist/src/vue-router-evidence.js +126 -0
  231. package/dist/src/vue-router-protocol.d.ts +63 -0
  232. package/dist/src/vue-router-protocol.js +44 -0
  233. package/dist/src/vue-router-runner.d.ts +1 -0
  234. package/dist/src/vue-router-runner.js +88 -0
  235. package/dist/src/vue-router.d.ts +23 -0
  236. package/dist/src/vue-router.js +108 -0
  237. package/dist/src/vue-tsc-runner.d.ts +1 -0
  238. package/dist/src/vue-tsc-runner.js +24 -0
  239. package/dist/src/workspace.d.ts +7 -0
  240. package/dist/src/workspace.js +53 -0
  241. package/docs/ACCEPTANCE.md +56 -0
  242. package/docs/ACTIONLINT.md +112 -0
  243. package/docs/ARCHITECTURE-POLICY.md +82 -0
  244. package/docs/ARCHITECTURE.md +122 -0
  245. package/docs/CLANG.md +113 -0
  246. package/docs/CLIENTS.md +109 -0
  247. package/docs/CONTRACTS.md +73 -0
  248. package/docs/DEPENDENCIES.md +42 -0
  249. package/docs/DJANGO.md +70 -0
  250. package/docs/DOTNET.md +114 -0
  251. package/docs/ENVIRONMENTS.md +52 -0
  252. package/docs/ESLINT.md +68 -0
  253. package/docs/EVALUATION.md +124 -0
  254. package/docs/EXECUTION.md +250 -0
  255. package/docs/EXTERNAL-ADAPTERS.md +170 -0
  256. package/docs/EXTERNAL-EVALUATION.md +155 -0
  257. package/docs/EXTERNAL-RUFF-EVALUATION.md +85 -0
  258. package/docs/FASTAPI.md +73 -0
  259. package/docs/FINDING-POLICY.md +67 -0
  260. package/docs/GO-RACE.md +19 -0
  261. package/docs/GO-SCOPE.md +63 -0
  262. package/docs/GOLANGCI-LINT.md +64 -0
  263. package/docs/GUIDANCE.md +46 -0
  264. package/docs/IMPACT-MEASUREMENT.md +97 -0
  265. package/docs/INSTALLATION.md +102 -0
  266. package/docs/JAVA.md +79 -0
  267. package/docs/JEST.md +36 -0
  268. package/docs/JUNIT.md +31 -0
  269. package/docs/LANGUAGES.md +113 -0
  270. package/docs/LARAVEL.md +111 -0
  271. package/docs/MCP-COMPATIBILITY.md +68 -0
  272. package/docs/MUTATIONS.md +93 -0
  273. package/docs/MYPY.md +37 -0
  274. package/docs/NATIVE-CI.md +96 -0
  275. package/docs/NUXT.md +123 -0
  276. package/docs/PACK-DISTRIBUTION.md +98 -0
  277. package/docs/PERFORMANCE.md +71 -0
  278. package/docs/PEST.md +36 -0
  279. package/docs/PHPSTAN.md +48 -0
  280. package/docs/PHPUNIT.md +32 -0
  281. package/docs/PINT.md +41 -0
  282. package/docs/PLAN.md +265 -0
  283. package/docs/PLAYWRIGHT.md +73 -0
  284. package/docs/POLICY-PACKS.md +106 -0
  285. package/docs/PRIOR-WORKFLOW-EVALUATION.md +116 -0
  286. package/docs/PUBLICATION.md +28 -0
  287. package/docs/PYTEST.md +51 -0
  288. package/docs/RELEASE.md +115 -0
  289. package/docs/RENAMING.md +55 -0
  290. package/docs/REVIEW-EXCHANGE.md +136 -0
  291. package/docs/RUBY.md +50 -0
  292. package/docs/RUFF.md +39 -0
  293. package/docs/RUNTIME-INVENTORY.md +56 -0
  294. package/docs/RUST.md +74 -0
  295. package/docs/SARIF.md +64 -0
  296. package/docs/STATUS.md +530 -0
  297. package/docs/SWIFT.md +48 -0
  298. package/docs/TASK-STORAGE.md +141 -0
  299. package/docs/TOOL-IDENTITY.md +30 -0
  300. package/docs/TYPESCRIPT-BUILD.md +55 -0
  301. package/docs/VALIDATION-TASKS.md +119 -0
  302. package/docs/VITEST.md +36 -0
  303. package/docs/VUE-ROUTER.md +130 -0
  304. package/docs/VUE-TSC.md +26 -0
  305. package/docs/WORKSPACES.md +81 -0
  306. package/docs/licenses/nodable-entities-3.0.0.txt +21 -0
  307. package/docs/measurements/client-claude-before-schema-fix-darwin-arm64-node26.json +60 -0
  308. package/docs/measurements/client-claude-darwin-arm64-node26.json +55 -0
  309. package/docs/measurements/client-codex-darwin-arm64-node26.json +175 -0
  310. package/docs/measurements/evaluation-corpus-repo-verifier.json +332 -0
  311. package/docs/measurements/evaluation-darwin-arm64-node26.json +1236 -0
  312. package/docs/measurements/evaluation-linux-arm64-node22.json +1236 -0
  313. package/docs/measurements/external-eslint-darwin-arm64-node26.json +11866 -0
  314. package/docs/measurements/external-eslint-linux-arm64-node22.json +11866 -0
  315. package/docs/measurements/external-eslint-preservation.json +1527 -0
  316. package/docs/measurements/external-ruff-darwin-arm64-node26.json +1031 -0
  317. package/docs/measurements/external-ruff-linux-arm64-node22.json +1031 -0
  318. package/docs/measurements/hosted-ci-52ba415.json +1005 -0
  319. package/docs/measurements/impact-darwin-arm64-node26.json +6685 -0
  320. package/docs/measurements/impact-linux-arm64-node22.json +6690 -0
  321. package/docs/measurements/performance-darwin-arm64-node26.json +845 -0
  322. package/docs/measurements/performance-linux-arm64-node22.json +845 -0
  323. package/docs/measurements/required-native-profiles.json +500 -0
  324. package/package.json +72 -0
  325. package/packs/actionlint.json +8 -0
  326. package/packs/cpp.json +8 -0
  327. package/packs/dotnet.json +8 -0
  328. package/packs/go.json +7 -0
  329. package/packs/java.json +8 -0
  330. package/packs/javascript-node.json +7 -0
  331. package/packs/nuxt.json +7 -0
  332. package/packs/php-pest.json +7 -0
  333. package/packs/php-phpunit.json +7 -0
  334. package/packs/python-pytest.json +7 -0
  335. package/packs/ruby.json +7 -0
  336. package/packs/rust.json +7 -0
  337. package/packs/swift.json +7 -0
  338. package/packs/vue-router.json +7 -0
  339. package/packs/vue.json +11 -0
  340. package/schemas/actionlint-config.schema.json +19 -0
  341. package/schemas/architecture-policy.schema.json +51 -0
  342. package/schemas/architecture-report.schema.json +123 -0
  343. package/schemas/architecture-summary.schema.json +82 -0
  344. package/schemas/clang-database.schema.json +23 -0
  345. package/schemas/config.schema.json +69 -0
  346. package/schemas/contract-bundle.schema.json +78 -0
  347. package/schemas/contract-report.schema.json +114 -0
  348. package/schemas/contract-summary.schema.json +72 -0
  349. package/schemas/dependency-graph.schema.json +65 -0
  350. package/schemas/django-config.schema.json +16 -0
  351. package/schemas/dotnet-config.schema.json +60 -0
  352. package/schemas/external-manifest.schema.json +97 -0
  353. package/schemas/external-reference.schema.json +10 -0
  354. package/schemas/external-request.schema.json +56 -0
  355. package/schemas/external-result.schema.json +111 -0
  356. package/schemas/fastapi-config.schema.json +27 -0
  357. package/schemas/finding-baseline.schema.json +92 -0
  358. package/schemas/finding-comparison-summary.schema.json +95 -0
  359. package/schemas/finding-comparison.schema.json +121 -0
  360. package/schemas/guidance-context.schema.json +34 -0
  361. package/schemas/guidance-report.schema.json +176 -0
  362. package/schemas/guidance-summary.schema.json +68 -0
  363. package/schemas/java-config.schema.json +24 -0
  364. package/schemas/junit.schema.json +69 -0
  365. package/schemas/laravel-config.schema.json +11 -0
  366. package/schemas/mutation-recipe.schema.json +31 -0
  367. package/schemas/mutation-report.schema.json +238 -0
  368. package/schemas/mutation-summary.schema.json +98 -0
  369. package/schemas/nuxt-config.schema.json +51 -0
  370. package/schemas/plan-summary.schema.json +63 -0
  371. package/schemas/plan.schema.json +244 -0
  372. package/schemas/policy-pack.schema.json +41 -0
  373. package/schemas/report-summary.schema.json +100 -0
  374. package/schemas/report.schema.json +463 -0
  375. package/schemas/review-assessment.schema.json +133 -0
  376. package/schemas/review-context-summary.schema.json +31 -0
  377. package/schemas/review-context.schema.json +254 -0
  378. package/schemas/review-receipt-summary.schema.json +104 -0
  379. package/schemas/review-receipt.schema.json +271 -0
  380. package/schemas/review-selection.schema.json +30 -0
  381. package/schemas/runtime-comparison-summary.schema.json +68 -0
  382. package/schemas/runtime-comparison.schema.json +127 -0
  383. package/schemas/runtime-inventory.schema.json +99 -0
  384. package/schemas/vue-router-config.schema.json +67 -0
  385. package/server.json +47 -0
@@ -0,0 +1,370 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, mkdtemp, readFile, realpath, rm, writeFile, } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
5
+ import { z } from "zod";
6
+ import { createPlan, validate } from "./engine.js";
7
+ import { inventory, withinRoot } from "./inventory.js";
8
+ import { VERSION } from "./types.js";
9
+ const count = z.number().int().nonnegative();
10
+ const digest = z.string().regex(/^[a-f0-9]{64}$/);
11
+ const relative = z
12
+ .string()
13
+ .min(1)
14
+ .max(512)
15
+ .refine((value) => !value.includes("\\") &&
16
+ !value.includes("\0") &&
17
+ !path.posix.isAbsolute(value) &&
18
+ value
19
+ .split("/")
20
+ .every((part) => part !== "" && part !== "." && part !== ".."));
21
+ export const mutationRecipeSchema = z.strictObject({
22
+ schemaVersion: z.literal(1),
23
+ profile: z.literal("node-flat-tests"),
24
+ mutations: z
25
+ .array(z.strictObject({
26
+ id: z
27
+ .string()
28
+ .min(1)
29
+ .max(128)
30
+ .regex(/^[a-z0-9][a-z0-9.-]*$/),
31
+ file: relative,
32
+ expected: z.string().min(1).max(4096),
33
+ replacement: z.string().max(4096),
34
+ }))
35
+ .min(1)
36
+ .max(8),
37
+ });
38
+ const testsSchema = z.strictObject({
39
+ total: count,
40
+ passed: count,
41
+ failed: count,
42
+ skipped: count,
43
+ });
44
+ const observationSchema = z.strictObject({
45
+ runId: z.string().uuid(),
46
+ outcome: z.enum(["passed", "failed", "incomplete"]),
47
+ durationMs: count,
48
+ sourceFingerprint: digest,
49
+ sourceChanged: z.boolean(),
50
+ sourceError: z.boolean(),
51
+ tests: testsSchema.optional(),
52
+ });
53
+ const trialSchema = z.strictObject({
54
+ id: z.string(),
55
+ file: relative,
56
+ status: z.enum(["killed", "survived", "invalid", "inconclusive", "not-run"]),
57
+ reason: z.string(),
58
+ observation: observationSchema.optional(),
59
+ });
60
+ const metadata = {
61
+ schemaVersion: z.literal(1),
62
+ engineVersion: z.string(),
63
+ profile: z.literal("node-flat-tests"),
64
+ channel: z.literal("advisory"),
65
+ provenance: z.literal("temporary-copy-mutation-experiment"),
66
+ recipeDigest: digest,
67
+ sourceFingerprint: digest,
68
+ finalSourceFingerprint: digest.nullable(),
69
+ complete: z.boolean(),
70
+ reason: z.string(),
71
+ durationMs: count,
72
+ runtime: z.strictObject({ name: z.literal("node"), version: z.string() }),
73
+ counts: z.strictObject({
74
+ total: count,
75
+ killed: count,
76
+ survived: count,
77
+ invalid: count,
78
+ inconclusive: count,
79
+ notRun: count,
80
+ }),
81
+ };
82
+ export const mutationReportSchema = z.strictObject({
83
+ ...metadata,
84
+ excluded: z.array(z.string()),
85
+ baseline: observationSchema.optional(),
86
+ trials: z.array(trialSchema).max(8),
87
+ });
88
+ export const mutationSummarySchema = z.strictObject(metadata);
89
+ function observation(report) {
90
+ return {
91
+ runId: report.runId,
92
+ outcome: report.outcome,
93
+ durationMs: Math.round(report.durationMs),
94
+ sourceFingerprint: report.sourceFingerprint,
95
+ sourceChanged: report.sourceChanged,
96
+ sourceError: report.sourceError,
97
+ ...(report.checks[0]?.tests ? { tests: report.checks[0].tests } : {}),
98
+ };
99
+ }
100
+ function testIdentities(report, root) {
101
+ const check = report.checks[0];
102
+ if (report.sourceChanged ||
103
+ report.sourceError ||
104
+ report.checks.length !== 1 ||
105
+ check?.id !== "javascript.node-test" ||
106
+ !check.tests ||
107
+ check.tests.skipped ||
108
+ check.processes.length !== 1 ||
109
+ check.tools?.some((tool) => tool.status !== "identified"))
110
+ return undefined;
111
+ const identities = [];
112
+ let assertionFailures = 0;
113
+ try {
114
+ for (const line of check.processes[0].stdout.split("\n").filter(Boolean)) {
115
+ const event = JSON.parse(line);
116
+ if (event.type !== "test:pass" && event.type !== "test:fail")
117
+ continue;
118
+ const data = event.data;
119
+ if (!data ||
120
+ data.nesting !== 0 ||
121
+ data.details?.type !== "test" ||
122
+ typeof data.file !== "string" ||
123
+ typeof data.name !== "string" ||
124
+ !Number.isSafeInteger(data.line) ||
125
+ !Number.isSafeInteger(data.column))
126
+ return undefined;
127
+ const file = path.relative(root, data.file).split(path.sep).join("/");
128
+ if (!check.scope.includes(file))
129
+ return undefined;
130
+ identities.push(JSON.stringify([file, data.name, data.line, data.column]));
131
+ if (event.type === "test:fail") {
132
+ const error = data.details.error;
133
+ if (error?.failureType !== "testCodeFailure" ||
134
+ error?.cause?.name !== "AssertionError" ||
135
+ error?.cause?.code !== "ERR_ASSERTION")
136
+ return undefined;
137
+ assertionFailures++;
138
+ }
139
+ }
140
+ }
141
+ catch {
142
+ return undefined;
143
+ }
144
+ if (!identities.length ||
145
+ new Set(identities).size !== identities.length ||
146
+ identities.length !== check.tests.total ||
147
+ assertionFailures !== check.tests.failed)
148
+ return undefined;
149
+ return { identities: identities.sort(), assertionFailures };
150
+ }
151
+ export async function runMutations(root, input, options) {
152
+ if (!options.trusted)
153
+ throw new Error("Mutation experiments require operator execution trust");
154
+ const serialized = JSON.stringify(input);
155
+ if (serialized === undefined || Buffer.byteLength(serialized) > 128 * 1024)
156
+ throw new Error("Mutation recipe exceeds input limits");
157
+ const recipe = mutationRecipeSchema.parse(input);
158
+ if (new Set(recipe.mutations.map((mutation) => mutation.id)).size !==
159
+ recipe.mutations.length)
160
+ throw new Error("Mutation IDs must be unique");
161
+ const timeoutMs = options.timeoutMs ?? 30_000;
162
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 120_000)
163
+ throw new Error("Timeout must be between 1 and 120000 milliseconds");
164
+ const started = performance.now();
165
+ const remaining = () => Math.floor(timeoutMs - (performance.now() - started));
166
+ const { source, plan } = await createPlan(root);
167
+ if (plan.projects.length !== 1 ||
168
+ plan.projects[0]?.path !== "." ||
169
+ plan.checks.length !== 1 ||
170
+ plan.checks[0]?.id !== "javascript.node-test" ||
171
+ plan.checks[0]?.unavailableReason ||
172
+ plan.checks[0]?.environment?.length)
173
+ throw new Error("Mutation profile requires one root Node test project without additional checks or environment requirements");
174
+ const manifest = JSON.parse(await readFile(await withinRoot(source.root, "package.json"), "utf8"));
175
+ for (const key of [
176
+ "dependencies",
177
+ "devDependencies",
178
+ "optionalDependencies",
179
+ "peerDependencies",
180
+ ])
181
+ if (manifest[key] && Object.keys(manifest[key]).length)
182
+ throw new Error("Mutation profile requires a dependency-free Node project");
183
+ const snapshot = new Map();
184
+ let snapshotBytes = 0;
185
+ for (const file of source.files) {
186
+ const bytes = await readFile(await withinRoot(source.root, file));
187
+ snapshotBytes += bytes.length;
188
+ if (bytes.length > 8 * 1024 * 1024 || snapshotBytes > 64 * 1024 * 1024)
189
+ throw new Error("Mutation snapshot exceeds inventory limits");
190
+ snapshot.set(file, bytes);
191
+ }
192
+ if ((await inventory(source.root)).fingerprint !== source.fingerprint)
193
+ throw new Error("Source changed while preparing mutation copies");
194
+ const trials = [];
195
+ const edits = new Map();
196
+ for (const mutation of recipe.mutations) {
197
+ const bytes = snapshot.get(mutation.file);
198
+ let reason = "";
199
+ if (!bytes ||
200
+ !/\.[cm]?js$/.test(mutation.file) ||
201
+ plan.checks[0].scope.includes(mutation.file)) {
202
+ reason =
203
+ "Target must be inventoried JavaScript source outside the selected test files";
204
+ }
205
+ else {
206
+ try {
207
+ const original = new TextDecoder("utf-8", {
208
+ fatal: true,
209
+ ignoreBOM: true,
210
+ }).decode(bytes);
211
+ const first = original.indexOf(mutation.expected);
212
+ if (first < 0 ||
213
+ original.indexOf(mutation.expected, first + 1) !== -1 ||
214
+ mutation.expected === mutation.replacement)
215
+ reason =
216
+ "Mutation must replace one unique occurrence with different text";
217
+ else
218
+ edits.set(mutation.id, original.slice(0, first) +
219
+ mutation.replacement +
220
+ original.slice(first + mutation.expected.length));
221
+ }
222
+ catch {
223
+ reason = "Mutation target must contain valid UTF-8";
224
+ }
225
+ }
226
+ trials.push({
227
+ id: mutation.id,
228
+ file: mutation.file,
229
+ status: reason ? "invalid" : "not-run",
230
+ reason: reason || "Experiment has not run",
231
+ });
232
+ }
233
+ let baseline;
234
+ let reason = "Requested experiments finished";
235
+ const temporary = await mkdtemp(path.join(tmpdir(), "checktrail-mutations-"));
236
+ try {
237
+ async function trialCopy(name, edit) {
238
+ if (remaining() <= 0 || options.signal?.aborted)
239
+ return undefined;
240
+ const copy = path.join(temporary, name);
241
+ await mkdir(copy, { mode: 0o700 });
242
+ try {
243
+ for (const [file, bytes] of snapshot) {
244
+ if (remaining() <= 0 || options.signal?.aborted)
245
+ return undefined;
246
+ const destination = path.join(copy, file);
247
+ await mkdir(path.dirname(destination), { recursive: true });
248
+ await writeFile(destination, edit?.file === file ? edit.contents : bytes);
249
+ }
250
+ const time = remaining();
251
+ if (time <= 0 || options.signal?.aborted)
252
+ return undefined;
253
+ return {
254
+ report: await validate(copy, {
255
+ trusted: true,
256
+ timeoutMs: Math.min(time, 120_000),
257
+ ...(options.signal ? { signal: options.signal } : {}),
258
+ }),
259
+ root: await realpath(copy),
260
+ };
261
+ }
262
+ finally {
263
+ await rm(copy, { recursive: true, force: true });
264
+ }
265
+ }
266
+ if (!edits.size)
267
+ reason = "No valid mutation target was supplied";
268
+ else {
269
+ const initial = await trialCopy("baseline");
270
+ baseline = initial?.report;
271
+ const baselineTests = initial && testIdentities(initial.report, initial.root);
272
+ if (baseline?.outcome !== "passed" || !baselineTests)
273
+ reason =
274
+ "A passing baseline with complete flat-test evidence is required";
275
+ else {
276
+ for (const [index, mutation] of recipe.mutations.entries()) {
277
+ const contents = edits.get(mutation.id);
278
+ if (contents === undefined)
279
+ continue;
280
+ const trial = trials[index];
281
+ const run = await trialCopy(`trial-${index}`, {
282
+ file: mutation.file,
283
+ contents,
284
+ });
285
+ if (!run) {
286
+ reason = "Experiment interrupted or total time budget exhausted";
287
+ break;
288
+ }
289
+ trial.observation = observation(run.report);
290
+ const evidence = testIdentities(run.report, run.root);
291
+ if (!evidence ||
292
+ JSON.stringify(evidence.identities) !==
293
+ JSON.stringify(baselineTests.identities)) {
294
+ trial.status = "inconclusive";
295
+ trial.reason =
296
+ "Missing or changed test evidence, non-assertion failure, or source mutation during execution";
297
+ }
298
+ else if (run.report.outcome === "passed") {
299
+ trial.status = "survived";
300
+ trial.reason = "The same observed tests passed with this mutation";
301
+ }
302
+ else if (run.report.outcome === "failed" &&
303
+ evidence.assertionFailures > 0) {
304
+ trial.status = "killed";
305
+ trial.reason =
306
+ "The same observed tests completed with native assertion failures";
307
+ }
308
+ else {
309
+ trial.status = "inconclusive";
310
+ trial.reason =
311
+ "Native validation did not provide conclusive mutation evidence";
312
+ }
313
+ }
314
+ }
315
+ }
316
+ }
317
+ finally {
318
+ await rm(temporary, { recursive: true, force: true });
319
+ }
320
+ let finalSourceFingerprint = null;
321
+ try {
322
+ finalSourceFingerprint = (await inventory(source.root)).fingerprint;
323
+ }
324
+ catch {
325
+ /* Retain experiment observations when final source inspection fails. */
326
+ }
327
+ const counts = {
328
+ total: trials.length,
329
+ killed: 0,
330
+ survived: 0,
331
+ invalid: 0,
332
+ inconclusive: 0,
333
+ notRun: 0,
334
+ };
335
+ for (const trial of trials)
336
+ counts[trial.status === "not-run" ? "notRun" : trial.status]++;
337
+ const complete = finalSourceFingerprint === source.fingerprint &&
338
+ counts.invalid + counts.inconclusive + counts.notRun === 0;
339
+ if (finalSourceFingerprint !== source.fingerprint)
340
+ reason =
341
+ "Original source changed or could not be inspected after the experiment";
342
+ else if (!complete && reason === "Requested experiments finished")
343
+ reason = "Some requested experiments lack conclusive evidence";
344
+ return mutationReportSchema.parse({
345
+ schemaVersion: 1,
346
+ engineVersion: VERSION,
347
+ profile: recipe.profile,
348
+ channel: "advisory",
349
+ provenance: "temporary-copy-mutation-experiment",
350
+ recipeDigest: createHash("sha256")
351
+ .update(JSON.stringify(recipe))
352
+ .digest("hex"),
353
+ sourceFingerprint: source.fingerprint,
354
+ finalSourceFingerprint,
355
+ excluded: source.excluded,
356
+ complete,
357
+ reason,
358
+ durationMs: Math.round(performance.now() - started),
359
+ runtime: { name: "node", version: process.versions.node },
360
+ counts,
361
+ ...(baseline ? { baseline: observation(baseline) } : {}),
362
+ trials,
363
+ });
364
+ }
365
+ export function projectMutations(report, detailed) {
366
+ const parsed = mutationReportSchema.parse(report);
367
+ if (detailed)
368
+ return parsed;
369
+ return mutationSummarySchema.parse(Object.fromEntries(Object.entries(parsed).filter(([key]) => key !== "excluded" && key !== "baseline" && key !== "trials")));
370
+ }
@@ -0,0 +1,2 @@
1
+ import type { Check, CheckResult, ProcessResult } from "./types.js";
2
+ export declare function mypyEvidence(check: Check, processes: ProcessResult[], root: string | undefined): Pick<CheckResult, "status" | "reason">;
@@ -0,0 +1,63 @@
1
+ import path from "node:path";
2
+ import { z } from "zod";
3
+ const schema = z.object({
4
+ format: z.literal("checktrail-mypy-1"),
5
+ version: z.literal("2.3.1"),
6
+ files: z.array(z.string()),
7
+ suppressedFiles: z.array(z.string()),
8
+ stdout: z.string(),
9
+ stderr: z.string(),
10
+ exitCode: z.number().int().min(0).max(2),
11
+ });
12
+ export function mypyEvidence(check, processes, root) {
13
+ const incomplete = {
14
+ status: "inconclusive",
15
+ reason: "Mypy evidence is incomplete, suppresses module errors, or does not account for every planned source.",
16
+ };
17
+ if (!root || processes.length !== 1 || !check.scope.length)
18
+ return incomplete;
19
+ const process = processes[0];
20
+ let value;
21
+ try {
22
+ value = JSON.parse(process.stdout);
23
+ }
24
+ catch {
25
+ return incomplete;
26
+ }
27
+ const unavailable = z
28
+ .object({
29
+ format: z.literal("checktrail-mypy-1"),
30
+ unavailable: z.string().min(1),
31
+ })
32
+ .safeParse(value);
33
+ if (process.exitCode === 3 && unavailable.success)
34
+ return { status: "unavailable", reason: unavailable.data.unavailable };
35
+ const parsed = schema.safeParse(value);
36
+ if (!parsed.success || parsed.data.exitCode !== process.exitCode)
37
+ return incomplete;
38
+ const report = parsed.data;
39
+ if (report.exitCode === 2)
40
+ return {
41
+ status: "error",
42
+ reason: "Mypy reported a configuration or execution error.",
43
+ };
44
+ if (report.exitCode === 1)
45
+ return {
46
+ status: "failed",
47
+ reason: "Mypy reported type-checking diagnostics.",
48
+ };
49
+ const expected = new Set(check.scope.map((file) => path.resolve(root, check.project, file)));
50
+ const summary = /^Success: no issues found in (\d+) source files?\s*$/.exec(report.stdout);
51
+ if (report.stderr.trim() ||
52
+ !summary ||
53
+ Number(summary[1]) !== expected.size ||
54
+ report.files.length !== expected.size ||
55
+ new Set(report.files).size !== expected.size ||
56
+ report.files.some((file) => !expected.has(file)) ||
57
+ report.suppressedFiles.length)
58
+ return incomplete;
59
+ return {
60
+ status: "passed",
61
+ reason: "Mypy checked every explicit source and reported no diagnostics or broad module-error suppression.",
62
+ };
63
+ }
@@ -0,0 +1,2 @@
1
+ import type { Check, Project } from "./types.js";
2
+ export declare function mypyCheck(project: Project): Check;
@@ -0,0 +1,59 @@
1
+ const runner = String.raw `
2
+ import contextlib
3
+ import io
4
+ import json
5
+ import os
6
+ import pathlib
7
+ import sys
8
+
9
+ def unavailable(reason):
10
+ print(json.dumps({"format": "checktrail-mypy-1", "unavailable": reason}))
11
+ sys.exit(3)
12
+
13
+ try:
14
+ from mypy import api
15
+ from mypy.version import __version__
16
+ except ModuleNotFoundError as error:
17
+ if error.name != "mypy":
18
+ raise
19
+ unavailable("mypy is not installed")
20
+ if __version__ != "2.3.1":
21
+ unavailable("mypy version is outside the verified options API contract: " + __version__)
22
+
23
+ from mypy.main import process_options
24
+
25
+ files = [str(pathlib.Path(file).resolve()) for file in sys.argv[1:]]
26
+ config = next((name for name in ["mypy.ini", ".mypy.ini", "pyproject.toml", "setup.cfg"] if pathlib.Path(name).is_file()), os.devnull)
27
+ args = ["--config-file", config, "--no-incremental", "--cache-dir", os.devnull, "--no-install-types", "--check-untyped-defs", "--warn-unused-ignores", "--no-pretty", "--no-color-output", "--error-summary", "--", *files]
28
+ capture = io.StringIO()
29
+ with contextlib.redirect_stdout(sys.stderr):
30
+ sources, options = process_options(args, stdout=capture, stderr=capture)
31
+ suppressed = [str(pathlib.Path(source.path).resolve()) for source in sources if source.path and options.clone_for_module(source.module).ignore_errors]
32
+ stdout, stderr, code = api.run(args)
33
+ print(json.dumps({"format": "checktrail-mypy-1", "version": __version__, "files": [str(pathlib.Path(source.path).resolve()) for source in sources if source.path], "suppressedFiles": suppressed, "stdout": stdout, "stderr": capture.getvalue() + stderr, "exitCode": code}))
34
+ sys.exit(code)
35
+ `;
36
+ export function mypyCheck(project) {
37
+ const files = project.files.filter((file) => /\.pyi?$/.test(file));
38
+ const check = {
39
+ id: "python.mypy",
40
+ adapter: project.adapter,
41
+ project: project.path,
42
+ scope: files,
43
+ kind: "analysis",
44
+ parser: "mypy-json",
45
+ commands: [
46
+ {
47
+ executable: "python3",
48
+ args: ["-c", runner, ...files],
49
+ cwd: project.path,
50
+ env: { PYTHONDONTWRITEBYTECODE: "1" },
51
+ },
52
+ ],
53
+ reason: "Run installed mypy with explicit source accounting, disabled stub installation/cache writes and broad error-suppression detection.",
54
+ };
55
+ if (!files.length)
56
+ check.unavailableReason =
57
+ "No Python source or stub files were inventoried.";
58
+ return check;
59
+ }
@@ -0,0 +1 @@
1
+ export default function reporter(events: AsyncIterable<unknown>): AsyncGenerator<string>;
@@ -0,0 +1,28 @@
1
+ export default async function* reporter(events) {
2
+ for await (const event of events) {
3
+ yield `${JSON.stringify(event, (_key, value) => value instanceof Error
4
+ ? {
5
+ name: value.name,
6
+ message: value.message,
7
+ stack: value.stack,
8
+ ...("code" in value && typeof value.code === "string"
9
+ ? { code: value.code }
10
+ : {}),
11
+ ...("failureType" in value && typeof value.failureType === "string"
12
+ ? { failureType: value.failureType }
13
+ : {}),
14
+ ...(value.cause instanceof Error
15
+ ? {
16
+ cause: {
17
+ name: value.cause.name,
18
+ ...("code" in value.cause &&
19
+ typeof value.cause.code === "string"
20
+ ? { code: value.cause.code }
21
+ : {}),
22
+ },
23
+ }
24
+ : {}),
25
+ }
26
+ : value)}\n`;
27
+ }
28
+ }
@@ -0,0 +1,2 @@
1
+ import type { Check, CheckResult, ProcessResult } from "./types.js";
2
+ export declare function nuxtEvidence(check: Check, processes: ProcessResult[]): Partial<CheckResult>;