@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,46 @@
1
+ import path from "node:path";
2
+ import { createRequire } from "node:module";
3
+ import { withinRoot } from "./inventory.js";
4
+ async function main() {
5
+ const [entry, root, ...files] = process.argv.slice(2);
6
+ if (!entry || !root || !files.length)
7
+ throw new Error("Invalid Jest runner arguments");
8
+ const tool = await withinRoot(root, path.relative(root, entry));
9
+ const { runCLI } = createRequire(import.meta.url)(tool);
10
+ const paths = [];
11
+ for (const file of files)
12
+ paths.push(await withinRoot(root, path.relative(root, path.resolve(file))));
13
+ const { results } = await runCLI({
14
+ $0: "checktrail",
15
+ _: paths,
16
+ ci: true,
17
+ runInBand: true,
18
+ runTestsByPath: true,
19
+ watch: false,
20
+ watchAll: false,
21
+ updateSnapshot: false,
22
+ cache: false,
23
+ collectTests: false,
24
+ listTests: false,
25
+ passWithNoTests: false,
26
+ onlyChanged: false,
27
+ onlyFailures: false,
28
+ findRelatedTests: false,
29
+ testNamePattern: "",
30
+ testResultsProcessor: "",
31
+ filter: "",
32
+ bail: 0,
33
+ json: false,
34
+ useStderr: true,
35
+ reporters: ["default"],
36
+ runner: "jest-runner",
37
+ testRunner: "jest-circus/runner",
38
+ }, [process.cwd()]);
39
+ const evidence = { ...results, coverageMap: undefined };
40
+ process.stdout.write(`${JSON.stringify(evidence)}\n`);
41
+ process.exitCode = results.success ? 0 : 1;
42
+ }
43
+ main().catch((error) => {
44
+ process.stderr.write(`${error instanceof Error ? error.message : "Jest execution failed"}\n`);
45
+ process.exitCode = 2;
46
+ });
@@ -0,0 +1,2 @@
1
+ import type { Check, Inventory, Project } from "./types.js";
2
+ export declare function jestCheck(source: Inventory, project: Project): Promise<Check>;
@@ -0,0 +1,36 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import { localTool } from "./local-tool.js";
3
+ export async function jestCheck(source, project) {
4
+ const files = project.files.filter((file) => /(?:\.(?:test|spec)\.(?:[cm]?[jt]s|[jt]sx)$|(?:^|\/)__tests__\/.*\.(?:[cm]?[jt]s|[jt]sx)$)/.test(file));
5
+ const tool = await localTool(source.root, project.path, "jest/build/index.js");
6
+ const check = {
7
+ id: "javascript.jest",
8
+ adapter: project.adapter,
9
+ project: project.path,
10
+ scope: files,
11
+ kind: "test",
12
+ parser: "jest-json",
13
+ reason: "Run installed Jest once and account for every planned test file; require passing non-skipped assertions and consistent native JSON evidence.",
14
+ commands: tool
15
+ ? [
16
+ {
17
+ executable: process.execPath,
18
+ args: [
19
+ fileURLToPath(new URL("./jest-runner.js", import.meta.url)),
20
+ tool,
21
+ source.root,
22
+ ...files,
23
+ ],
24
+ cwd: project.path,
25
+ env: { CI: "1" },
26
+ },
27
+ ]
28
+ : [],
29
+ };
30
+ if (!files.length)
31
+ check.unavailableReason = "No .test or .spec JS/TS files were inventoried.";
32
+ else if (!tool)
33
+ check.unavailableReason =
34
+ "Jest is not installed within the configured root.";
35
+ return check;
36
+ }
@@ -0,0 +1,18 @@
1
+ import type { TestEvidence } from "./types.js";
2
+ export interface JUnitCase {
3
+ name: string;
4
+ file?: string;
5
+ status: "passed" | "failed" | "skipped";
6
+ assertions?: number;
7
+ }
8
+ export interface JUnitImport {
9
+ schemaVersion: 1;
10
+ engineVersion: string;
11
+ format: "junit";
12
+ provenance: "imported-report";
13
+ outcome: "passed" | "failed" | "incomplete";
14
+ reason: string;
15
+ tests?: TestEvidence;
16
+ cases?: JUnitCase[];
17
+ }
18
+ export declare function importJUnit(xml: string): JUnitImport;
@@ -0,0 +1,157 @@
1
+ import { XMLParser, XMLValidator } from "fast-xml-parser";
2
+ import { VERSION } from "./types.js";
3
+ function object(value) {
4
+ if (typeof value !== "object" || value === null || Array.isArray(value))
5
+ throw new Error("Expected XML element");
6
+ return value;
7
+ }
8
+ const array = (value) => value === undefined ? [] : Array.isArray(value) ? value : [value];
9
+ function count(value) {
10
+ if (typeof value !== "string" ||
11
+ !/^\d+$/.test(value) ||
12
+ !Number.isSafeInteger(Number(value)))
13
+ throw new Error("Invalid JUnit counter");
14
+ return Number(value);
15
+ }
16
+ export function importJUnit(xml) {
17
+ const base = {
18
+ schemaVersion: 1,
19
+ engineVersion: VERSION,
20
+ format: "junit",
21
+ provenance: "imported-report",
22
+ outcome: "incomplete",
23
+ reason: "JUnit evidence is malformed, inconsistent, empty, or entirely skipped; an import does not establish execution freshness.",
24
+ };
25
+ if (Buffer.byteLength(xml) > 8 * 1024 * 1024 ||
26
+ /<!\s*(?:DOCTYPE|ENTITY)/i.test(xml))
27
+ return base;
28
+ try {
29
+ if (XMLValidator.validate(xml) !== true)
30
+ return base;
31
+ const parser = new XMLParser({
32
+ ignoreAttributes: false,
33
+ parseAttributeValue: false,
34
+ parseTagValue: false,
35
+ ignoreDeclaration: true,
36
+ processEntities: true,
37
+ htmlEntities: false,
38
+ });
39
+ const root = object(parser.parse(xml));
40
+ if (Object.keys(root).length !== 1 ||
41
+ !("testsuite" in root || "testsuites" in root))
42
+ return base;
43
+ const cases = [];
44
+ const identities = new Set();
45
+ const walk = (value, depth, trail, wrapper = false) => {
46
+ if (depth > 32)
47
+ throw new Error("JUnit nesting limit");
48
+ const suite = object(value);
49
+ if (wrapper && suite.testcase !== undefined)
50
+ throw new Error("Test case outside suite");
51
+ if (Object.keys(suite).some((key) => !key.startsWith("@_") &&
52
+ ![
53
+ "testsuite",
54
+ "testcase",
55
+ "properties",
56
+ "system-out",
57
+ "system-err",
58
+ ].includes(key)))
59
+ throw new Error("Unknown JUnit suite element");
60
+ const names = [
61
+ ...trail,
62
+ typeof suite["@_name"] === "string" ? suite["@_name"] : "",
63
+ ];
64
+ const counts = { total: 0, failures: 0, errors: 0, skipped: 0 };
65
+ for (const value of array(suite.testcase)) {
66
+ if (cases.length >= 100_000)
67
+ throw new Error("JUnit case limit");
68
+ const entry = object(value);
69
+ if (Object.keys(entry).some((key) => !key.startsWith("@_") &&
70
+ ![
71
+ "failure",
72
+ "error",
73
+ "skipped",
74
+ "properties",
75
+ "system-out",
76
+ "system-err",
77
+ ].includes(key)))
78
+ throw new Error("Unknown JUnit case element");
79
+ if (typeof entry["@_name"] !== "string" || !entry["@_name"].length)
80
+ throw new Error("Missing test name");
81
+ const identity = JSON.stringify([
82
+ names,
83
+ entry["@_file"],
84
+ entry["@_classname"],
85
+ entry["@_class"],
86
+ entry["@_name"],
87
+ ]);
88
+ if (identities.has(identity))
89
+ throw new Error("Duplicate JUnit case");
90
+ identities.add(identity);
91
+ const failures = array(entry.failure).length;
92
+ const errors = array(entry.error).length;
93
+ const skipped = array(entry.skipped).length;
94
+ if (failures + errors + skipped > 1)
95
+ throw new Error("Contradictory JUnit case states");
96
+ const item = {
97
+ name: entry["@_name"],
98
+ status: failures || errors ? "failed" : skipped ? "skipped" : "passed",
99
+ };
100
+ if (entry["@_file"] !== undefined) {
101
+ if (typeof entry["@_file"] !== "string" || !entry["@_file"].length)
102
+ throw new Error("Invalid JUnit file");
103
+ item.file = entry["@_file"];
104
+ }
105
+ if (entry["@_assertions"] !== undefined)
106
+ item.assertions = count(entry["@_assertions"]);
107
+ cases.push(item);
108
+ counts.total++;
109
+ counts.failures += failures;
110
+ counts.errors += errors;
111
+ counts.skipped += skipped;
112
+ }
113
+ for (const child of array(suite.testsuite)) {
114
+ const nested = walk(child, depth + 1, names);
115
+ for (const key of ["total", "failures", "errors", "skipped"])
116
+ counts[key] += nested[key];
117
+ }
118
+ for (const [attribute, key] of [
119
+ ["tests", "total"],
120
+ ["failures", "failures"],
121
+ ["errors", "errors"],
122
+ ["skipped", "skipped"],
123
+ ]) {
124
+ const declared = suite[`@_${attribute}`];
125
+ if (declared !== undefined && count(declared) !== counts[key])
126
+ throw new Error("JUnit counts disagree");
127
+ if (!wrapper && attribute === "tests" && declared === undefined)
128
+ throw new Error("Missing suite count");
129
+ }
130
+ return counts;
131
+ };
132
+ const value = "testsuites" in root
133
+ ? walk(root.testsuites, 0, [], true)
134
+ : walk(root.testsuite, 0, []);
135
+ const tests = {
136
+ total: value.total,
137
+ passed: value.total - value.failures - value.errors - value.skipped,
138
+ failed: value.failures + value.errors,
139
+ skipped: value.skipped,
140
+ };
141
+ const outcome = tests.failed
142
+ ? "failed"
143
+ : tests.passed
144
+ ? "passed"
145
+ : "incomplete";
146
+ return {
147
+ ...base,
148
+ outcome,
149
+ tests,
150
+ cases,
151
+ reason: "Imported JUnit counts describe this report only; source identity, scope and execution freshness are not established.",
152
+ };
153
+ }
154
+ catch {
155
+ return base;
156
+ }
157
+ }
@@ -0,0 +1,2 @@
1
+ import type { Check, CheckResult, ProcessResult } from "./types.js";
2
+ export declare function laravelEvidence(check: Check, processes: ProcessResult[]): Pick<CheckResult, "status" | "reason" | "runtime">;
@@ -0,0 +1,175 @@
1
+ import { z } from "zod";
2
+ import { runtimeInventorySchema } from "./runtime-inventory.js";
3
+ import { laravelConfigSchema } from "./laravel.js";
4
+ const text = z.string();
5
+ const hash = text.regex(/^[a-f0-9]{64}$/);
6
+ const attributes = {
7
+ routes: z.strictObject({
8
+ domain: text.nullable(),
9
+ method: text.min(1),
10
+ uri: text.min(1),
11
+ name: text.nullable(),
12
+ handler: text.min(1),
13
+ middleware: z.array(text),
14
+ constraintsHash: hash,
15
+ defaultsHash: hash,
16
+ }),
17
+ middleware: z.discriminatedUnion("type", [
18
+ z.strictObject({
19
+ type: z.enum(["global", "priority"]),
20
+ stack: z.array(text),
21
+ }),
22
+ z.strictObject({
23
+ type: z.literal("group"),
24
+ name: text,
25
+ stack: z.array(text),
26
+ }),
27
+ z.strictObject({ type: z.literal("alias"), name: text, target: text }),
28
+ ]),
29
+ listeners: z.strictObject({
30
+ type: z.enum(["exact", "wildcard"]),
31
+ event: text,
32
+ listener: text,
33
+ }),
34
+ schedules: z.strictObject({
35
+ type: z.enum(["callback", "command"]),
36
+ target: text,
37
+ expression: text,
38
+ repeatSeconds: z.number().int().nullable(),
39
+ user: text.nullable(),
40
+ environments: z.array(text),
41
+ evenInMaintenanceMode: z.boolean(),
42
+ evenWhenPaused: z.boolean(),
43
+ withoutOverlapping: z.boolean(),
44
+ releaseOnTerminationSignals: z.boolean(),
45
+ onOneServer: z.boolean(),
46
+ expiresAt: z.number().int(),
47
+ runInBackground: z.boolean(),
48
+ description: text.nullable(),
49
+ output: text,
50
+ shouldAppendOutput: z.boolean(),
51
+ timezone: text.nullable(),
52
+ parametersHash: hash,
53
+ filters: z.array(text),
54
+ rejects: z.array(text),
55
+ beforeCallbacks: z.array(text),
56
+ afterCallbacks: z.array(text),
57
+ mutex: text,
58
+ mutexNameResolver: text.nullable(),
59
+ attributesHash: hash,
60
+ }),
61
+ bindings: z.discriminatedUnion("type", [
62
+ z.strictObject({
63
+ type: z.literal("factory"),
64
+ abstract: text,
65
+ target: text,
66
+ shared: z.boolean(),
67
+ scoped: z.boolean(),
68
+ }),
69
+ z.strictObject({
70
+ type: z.enum(["alias", "instance"]),
71
+ abstract: text,
72
+ target: text,
73
+ }),
74
+ z.strictObject({
75
+ type: z.literal("contextual"),
76
+ consumer: text,
77
+ abstract: text,
78
+ target: text,
79
+ }),
80
+ ]),
81
+ };
82
+ const schema = z.strictObject({
83
+ version: z.literal(1),
84
+ laravelVersion: z.literal("13.32.0"),
85
+ entryCount: z.number().int().nonnegative().max(20_000),
86
+ runtime: runtimeInventorySchema,
87
+ });
88
+ export function laravelEvidence(check, processes) {
89
+ const incomplete = {
90
+ status: "inconclusive",
91
+ reason: "Laravel assembly projection is unsupported, malformed, empty or incomplete.",
92
+ };
93
+ if (processes.length !== 1)
94
+ return incomplete;
95
+ if (processes[0].exitCode === 3) {
96
+ try {
97
+ z.strictObject({
98
+ unavailable: z.literal("laravel-runtime"),
99
+ reason: z.literal("unsupported-version"),
100
+ }).parse(JSON.parse(processes[0].stdout));
101
+ return {
102
+ status: "unavailable",
103
+ reason: "The pinned Laravel runtime profile is not installed.",
104
+ };
105
+ }
106
+ catch {
107
+ return incomplete;
108
+ }
109
+ }
110
+ if (processes[0].exitCode !== 0)
111
+ return {
112
+ status: "error",
113
+ reason: "Laravel bootstrap or runtime collector failed.",
114
+ };
115
+ try {
116
+ const result = schema.parse(JSON.parse(processes[0].stdout));
117
+ const profile = laravelConfigSchema.parse(JSON.parse(check.commands[0].args[3]));
118
+ const runtime = result.runtime;
119
+ if (runtime.sourceFingerprint !== check.commands[0].args[4] ||
120
+ runtime.producer.name !== "checktrail.laravel-runtime" ||
121
+ runtime.producer.version !== "1.0.0" ||
122
+ runtime.assembly.name !== profile.assembly ||
123
+ runtime.assembly.environment !== profile.environment ||
124
+ runtime.collections.length !== 5 ||
125
+ new Set(runtime.collections.map((item) => item.kind)).size !== 5 ||
126
+ runtime.collections.reduce((sum, item) => sum + item.entries.length, 0) !== result.entryCount)
127
+ return incomplete;
128
+ for (const collection of runtime.collections) {
129
+ if (collection.ordered !== (collection.kind !== "bindings"))
130
+ return incomplete;
131
+ for (const entry of collection.entries) {
132
+ const value = attributes[collection.kind].parse(entry.attributes);
133
+ if (collection.kind === "routes") {
134
+ const route = attributes.routes.parse(value);
135
+ if (JSON.stringify(JSON.parse(entry.key)) !==
136
+ JSON.stringify([route.domain, route.method, route.uri]))
137
+ return incomplete;
138
+ }
139
+ else {
140
+ const record = value;
141
+ const expected = collection.kind === "listeners"
142
+ ? `${record.type}:${record.event}`
143
+ : collection.kind === "schedules"
144
+ ? `${record.type}:${record.target}`
145
+ : collection.kind === "bindings"
146
+ ? record.type === "contextual"
147
+ ? `contextual:${record.consumer}:${record.abstract}`
148
+ : `${record.type}:${record.abstract}`
149
+ : record.type === "global" || record.type === "priority"
150
+ ? record.type
151
+ : `${record.type}:${record.name}`;
152
+ if (entry.key !== expected)
153
+ return incomplete;
154
+ }
155
+ }
156
+ }
157
+ if (!runtime.collections.find((item) => item.kind === "routes").entries
158
+ .length ||
159
+ !runtime.collections.find((item) => item.kind === "bindings").entries
160
+ .length ||
161
+ ["global", "priority"].some((key) => runtime.collections
162
+ .find((item) => item.kind === "middleware")
163
+ .entries.filter((entry) => entry.key === key).length !== 1) ||
164
+ runtime.collections.some((item) => !item.complete))
165
+ return { ...incomplete, runtime };
166
+ return {
167
+ status: "passed",
168
+ reason: "Laravel testing bootstrap completed and all five declared assembly projections were captured; compare inventories to evaluate wiring changes.",
169
+ runtime,
170
+ };
171
+ }
172
+ catch {
173
+ return incomplete;
174
+ }
175
+ }
@@ -0,0 +1 @@
1
+ export declare const laravelRunner: string;
@@ -0,0 +1,168 @@
1
+ export const laravelRunner = String.raw `
2
+ function rv_property(object $object, string $property): mixed {
3
+ return (new ReflectionProperty($object, $property))->getValue($object);
4
+ }
5
+ function rv_identity(mixed $value): string {
6
+ static $sources = [];
7
+ static $sourceBytes = 0;
8
+ if (is_string($value)) return $value;
9
+ if (is_array($value) && count($value) === 2 && isset($value[0], $value[1]) && is_string($value[1])) {
10
+ return (is_object($value[0]) ? get_class($value[0]) : $value[0]).'@'.$value[1];
11
+ }
12
+ if ($value instanceof Closure) {
13
+ $reflection = new ReflectionFunction($value);
14
+ $file = $reflection->getFileName();
15
+ if (!$file || !is_file($file)) throw new RuntimeException('Unsupported closure source');
16
+ $prefix = getcwd().DIRECTORY_SEPARATOR;
17
+ $vendor = dirname($GLOBALS['rv_autoload']).DIRECTORY_SEPARATOR;
18
+ $location = str_starts_with($file, $prefix) ? substr($file, strlen($prefix)) : (str_starts_with($file, $vendor) ? 'vendor/'.substr($file, strlen($vendor)) : null);
19
+ if ($location === null) throw new RuntimeException('Closure source outside project and local vendor');
20
+ if (!isset($sources[$file])) {
21
+ $bytes = file_get_contents($file, false, null, 0, 8 * 1024 * 1024 + 1);
22
+ $sourceBytes += strlen($bytes);
23
+ if (strlen($bytes) > 8 * 1024 * 1024 || $sourceBytes > 64 * 1024 * 1024) throw new RuntimeException('Closure source limit exceeded');
24
+ $sources[$file] = explode("\n", $bytes);
25
+ }
26
+ $lines = $sources[$file];
27
+ $source = implode("\n", array_slice($lines, $reflection->getStartLine() - 1, $reflection->getEndLine() - $reflection->getStartLine() + 1));
28
+ return 'closure:'.$location.':'.$reflection->getStartLine().':'.$reflection->getEndLine().':'.hash('sha256', $source);
29
+ }
30
+ if (is_object($value)) return get_class($value).'@__invoke';
31
+ throw new RuntimeException('Unsupported callable registration');
32
+ }
33
+ function rv_scalar_hash(mixed $value, int $depth = 0): string {
34
+ $walk = function (mixed $value, int $depth) use (&$walk): mixed {
35
+ if ($depth > 32) throw new RuntimeException('Registration data too deep');
36
+ if (is_array($value)) {
37
+ $result = [];
38
+ foreach ($value as $key => $item) $result[$key] = $walk($item, $depth + 1);
39
+ return $result;
40
+ }
41
+ if (is_null($value) || is_scalar($value)) return $value;
42
+ throw new RuntimeException('Unsupported registration data');
43
+ };
44
+ return hash('sha256', json_encode($walk($value, $depth), JSON_THROW_ON_ERROR));
45
+ }
46
+ function rv_collect(string $kind, bool $ordered, callable $read): array {
47
+ $entries = [];
48
+ try {
49
+ $read($entries);
50
+ return ['kind' => $kind, 'complete' => true, 'ordered' => $ordered, 'entries' => $entries];
51
+ } catch (Throwable $error) {
52
+ fwrite(STDERR, $kind.': '.get_class($error).': '.$error->getMessage().PHP_EOL);
53
+ return ['kind' => $kind, 'complete' => false, 'ordered' => $ordered, 'entries' => $entries];
54
+ }
55
+ }
56
+ function rv_add(array &$entries, string $key, array $attributes): void {
57
+ if (++$GLOBALS['rv_count'] > 20000) throw new RuntimeException('Assembly entry limit exceeded');
58
+ $entries[] = ['key' => $key, 'attributes' => (object) $attributes];
59
+ }
60
+ $rv_count = 0;
61
+ $rv_temporary = null;
62
+ $rv_autoload = $argv[1];
63
+ ob_start(function ($text) { fwrite(STDERR, $text); return ''; }, 1);
64
+ try {
65
+ require $rv_autoload;
66
+ if (!class_exists(Illuminate\Foundation\Application::class) || Illuminate\Foundation\Application::VERSION !== '13.32.0') {
67
+ fwrite(STDOUT, json_encode(['unavailable' => 'laravel-runtime', 'reason' => 'unsupported-version']));
68
+ exit(3);
69
+ }
70
+ $config = json_decode($argv[2], true, flags: JSON_THROW_ON_ERROR);
71
+ $rv_temporary = sys_get_temp_dir().'/checktrail-laravel-'.bin2hex(random_bytes(16));
72
+ if (!mkdir($rv_temporary, 0700)) throw new RuntimeException('Cannot create temporary cache directory');
73
+ register_shutdown_function(function () use ($rv_temporary) {
74
+ foreach (glob($rv_temporary.'/*') as $file) if (is_file($file) || is_link($file)) @unlink($file);
75
+ @rmdir($rv_temporary);
76
+ });
77
+ $cachePaths = [];
78
+ foreach (['CONFIG', 'ROUTES', 'EVENTS', 'SERVICES', 'PACKAGES'] as $kind) {
79
+ $key = 'APP_'.$kind.'_CACHE';
80
+ $cachePaths[$kind] = $rv_temporary.'/'.strtolower($kind).'.php';
81
+ putenv($key.'='.$cachePaths[$kind]);
82
+ $_ENV[$key] = $_SERVER[$key] = $cachePaths[$kind];
83
+ }
84
+ $app = require getcwd().'/bootstrap/app.php';
85
+ if (!$app instanceof Illuminate\Foundation\Application || realpath($app->basePath()) !== getcwd() || $app->hasBeenBootstrapped()) throw new RuntimeException('Unsupported application bootstrap');
86
+ $app->useEnvironmentPath($rv_temporary)->loadEnvironmentFrom('.env');
87
+ $http = $app->make(Illuminate\Contracts\Http\Kernel::class);
88
+ $console = $app->make(Illuminate\Contracts\Console\Kernel::class);
89
+ $console->bootstrap();
90
+ $console->all();
91
+ $http->bootstrap();
92
+ if ($app->environment() !== 'testing') throw new RuntimeException('Expected isolated testing environment');
93
+ foreach (['CONFIG' => 'getCachedConfigPath', 'ROUTES' => 'getCachedRoutesPath', 'EVENTS' => 'getCachedEventsPath', 'SERVICES' => 'getCachedServicesPath', 'PACKAGES' => 'getCachedPackagesPath'] as $kind => $method) {
94
+ if ($app->$method() !== $cachePaths[$kind]) throw new RuntimeException('Application changed protected cache paths');
95
+ }
96
+ $router = $app->make('router');
97
+ $dispatcher = $app->make('events');
98
+ $schedule = $app->make(Illuminate\Console\Scheduling\Schedule::class);
99
+ $collections = [];
100
+ $collections[] = rv_collect('routes', true, function (&$entries) use ($router) {
101
+ if (get_class($router) !== Illuminate\Routing\Router::class || get_class($router->getRoutes()) !== Illuminate\Routing\RouteCollection::class) throw new RuntimeException('Unsupported router or cached route collection');
102
+ foreach ($router->getRoutes() as $route) {
103
+ if (get_class($route) !== Illuminate\Routing\Route::class) throw new RuntimeException('Unsupported route class');
104
+ $middleware = array_map('rv_identity', $router->gatherRouteMiddleware($route));
105
+ foreach ($route->methods() as $method) {
106
+ rv_add($entries, json_encode([$route->getDomain(), $method, $route->uri()], JSON_THROW_ON_ERROR), [
107
+ 'domain' => $route->getDomain(), 'method' => $method, 'uri' => $route->uri(),
108
+ 'name' => $route->getName(), 'handler' => rv_identity($route->getAction('uses')),
109
+ 'middleware' => array_values($middleware), 'constraintsHash' => rv_scalar_hash($route->wheres), 'defaultsHash' => rv_scalar_hash($route->defaults),
110
+ ]);
111
+ }
112
+ }
113
+ if (!$entries) throw new RuntimeException('No served routes');
114
+ });
115
+ $collections[] = rv_collect('middleware', true, function (&$entries) use ($http) {
116
+ foreach (['global' => $http->getGlobalMiddleware(), 'priority' => $http->getMiddlewarePriority()] as $kind => $values) rv_add($entries, $kind, ['type' => $kind, 'stack' => array_values(array_map('rv_identity', $values))]);
117
+ foreach ($http->getMiddlewareGroups() as $name => $values) rv_add($entries, 'group:'.$name, ['type' => 'group', 'name' => $name, 'stack' => array_values(array_map('rv_identity', $values))]);
118
+ foreach ($http->getMiddlewareAliases() as $name => $value) rv_add($entries, 'alias:'.$name, ['type' => 'alias', 'name' => $name, 'target' => rv_identity($value)]);
119
+ });
120
+ $collections[] = rv_collect('listeners', true, function (&$entries) use ($dispatcher) {
121
+ if (get_class($dispatcher) !== Illuminate\Events\Dispatcher::class) throw new RuntimeException('Unsupported event dispatcher');
122
+ foreach (['exact' => $dispatcher->getRawListeners(), 'wildcard' => rv_property($dispatcher, 'wildcards')] as $kind => $events) {
123
+ foreach ($events as $event => $listeners) foreach ($listeners as $listener) rv_add($entries, $kind.':'.$event, ['type' => $kind, 'event' => $event, 'listener' => rv_identity($listener)]);
124
+ }
125
+ });
126
+ $collections[] = rv_collect('schedules', true, function (&$entries) use ($schedule) {
127
+ if (get_class($schedule) !== Illuminate\Console\Scheduling\Schedule::class) throw new RuntimeException('Unsupported scheduler');
128
+ foreach ($schedule->events() as $event) {
129
+ if (!in_array(get_class($event), [Illuminate\Console\Scheduling\Event::class, Illuminate\Console\Scheduling\CallbackEvent::class], true)) throw new RuntimeException('Unsupported scheduled event');
130
+ $callback = $event instanceof Illuminate\Console\Scheduling\CallbackEvent;
131
+ $target = $callback ? rv_identity(rv_property($event, 'callback')) : $event->command;
132
+ $attributes = ['type' => $callback ? 'callback' : 'command', 'target' => $target];
133
+ foreach (['expression', 'repeatSeconds', 'user', 'environments', 'evenInMaintenanceMode', 'evenWhenPaused', 'withoutOverlapping', 'releaseOnTerminationSignals', 'onOneServer', 'expiresAt', 'runInBackground', 'description', 'output', 'shouldAppendOutput'] as $field) $attributes[$field] = $event->$field;
134
+ $attributes['timezone'] = $event->timezone instanceof DateTimeZone ? $event->timezone->getName() : $event->timezone;
135
+ $attributes['parametersHash'] = rv_scalar_hash($callback ? rv_property($event, 'parameters') : []);
136
+ foreach (['filters', 'rejects', 'beforeCallbacks', 'afterCallbacks'] as $field) $attributes[$field] = array_values(array_map('rv_identity', rv_property($event, $field)));
137
+ $attributes['mutex'] = get_class($event->mutex);
138
+ $attributes['mutexNameResolver'] = $event->mutexNameResolver === null ? null : rv_identity($event->mutexNameResolver);
139
+ $attributes['attributesHash'] = rv_scalar_hash($event->attributes);
140
+ rv_add($entries, ($callback ? 'callback:' : 'command:').$target, $attributes);
141
+ }
142
+ });
143
+ $collections[] = rv_collect('bindings', false, function (&$entries) use ($app) {
144
+ $factoryMethod = new ReflectionMethod(Illuminate\Container\Container::class, 'getClosure');
145
+ $scoped = rv_property($app, 'scopedInstances');
146
+ foreach ($app->getBindings() as $abstract => $binding) {
147
+ $factory = $binding['concrete'];
148
+ $identity = rv_identity($factory);
149
+ if ($factory instanceof Closure) {
150
+ $reflection = new ReflectionFunction($factory);
151
+ if ($reflection->getFileName() === $factoryMethod->getFileName() && $reflection->getStartLine() >= $factoryMethod->getStartLine() && $reflection->getEndLine() <= $factoryMethod->getEndLine()) $identity = $reflection->getStaticVariables()['concrete'];
152
+ }
153
+ rv_add($entries, 'factory:'.$abstract, ['type' => 'factory', 'abstract' => $abstract, 'target' => $identity, 'shared' => $binding['shared'], 'scoped' => in_array($abstract, $scoped, true)]);
154
+ }
155
+ foreach (rv_property($app, 'aliases') as $alias => $abstract) rv_add($entries, 'alias:'.$alias, ['type' => 'alias', 'abstract' => $alias, 'target' => $abstract]);
156
+ foreach ($app->contextual as $consumer => $bindings) foreach ($bindings as $abstract => $value) rv_add($entries, 'contextual:'.$consumer.':'.$abstract, ['type' => 'contextual', 'consumer' => $consumer, 'abstract' => $abstract, 'target' => rv_identity($value)]);
157
+ foreach (rv_property($app, 'instances') as $abstract => $instance) rv_add($entries, 'instance:'.$abstract, ['type' => 'instance', 'abstract' => $abstract, 'target' => is_object($instance) ? get_class($instance) : 'scalar:'.rv_scalar_hash($instance)]);
158
+ });
159
+ $result = ['version' => 1, 'laravelVersion' => Illuminate\Foundation\Application::VERSION, 'entryCount' => array_sum(array_map(fn ($collection) => count($collection['entries']), $collections)), 'runtime' => [
160
+ 'schemaVersion' => 1, 'format' => 'runtime-inventory', 'producer' => ['name' => 'checktrail.laravel-runtime', 'version' => '1.0.0'],
161
+ 'assembly' => ['name' => $config['assembly'], 'environment' => $config['environment']], 'sourceFingerprint' => $argv[3], 'capturedAt' => gmdate('Y-m-d\TH:i:s\Z'), 'collections' => $collections,
162
+ ]];
163
+ fwrite(STDOUT, json_encode($result, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
164
+ } catch (Throwable $error) {
165
+ fwrite(STDERR, get_class($error).': '.$error->getMessage().PHP_EOL);
166
+ exit(2);
167
+ }
168
+ `;
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ import type { Check, Inventory, Project } from "./types.js";
3
+ export declare const laravelConfigSchema: z.ZodObject<{
4
+ schemaVersion: z.ZodLiteral<1>;
5
+ assembly: z.ZodString;
6
+ environment: z.ZodLiteral<"testing">;
7
+ }, z.core.$strict>;
8
+ export declare function laravelCheck(source: Inventory, project: Project): Promise<Check>;