@cyanautomation/kaseki-agent 1.4.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 (459) hide show
  1. package/.dockerignore +54 -0
  2. package/.eslintignore +11 -0
  3. package/.eslintrc.json +95 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +53 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +53 -0
  6. package/.github/ISSUE_TEMPLATE/security.md +51 -0
  7. package/.github/PULL_REQUEST_TEMPLATE/default.md +71 -0
  8. package/.github/dependabot.yml +38 -0
  9. package/.github/skills/dependency-cache-optimization/SKILL.md +526 -0
  10. package/.github/skills/docker-image-management/SKILL.md +532 -0
  11. package/.github/skills/frontend-design/SKILL.md +782 -0
  12. package/.github/skills/prompt-engineering/SKILL.md +360 -0
  13. package/.github/skills/quality-gate-config/SKILL.md +591 -0
  14. package/.github/skills/result-report-analysis/SKILL.md +576 -0
  15. package/.github/skills/test-automation/SKILL.md +593 -0
  16. package/.github/skills/workflow-diagnosis/SKILL.md +468 -0
  17. package/.github/workflows/build-docker-image.yml +453 -0
  18. package/.github/workflows/release.yml +68 -0
  19. package/.releaserc.json +135 -0
  20. package/CHANGELOG.md +117 -0
  21. package/CLAUDE.md +336 -0
  22. package/CONTRIBUTING.md +339 -0
  23. package/Dockerfile +217 -0
  24. package/README.md +1527 -0
  25. package/STYLE.md +521 -0
  26. package/add-js-extensions.d.ts +9 -0
  27. package/add-js-extensions.d.ts.map +1 -0
  28. package/add-js-extensions.js.map +1 -0
  29. package/dist/add-js-extensions.d.ts +9 -0
  30. package/dist/add-js-extensions.d.ts.map +1 -0
  31. package/dist/add-js-extensions.js +52 -0
  32. package/dist/add-js-extensions.js.map +1 -0
  33. package/dist/ansi-colors.d.ts +26 -0
  34. package/dist/ansi-colors.d.ts.map +1 -0
  35. package/dist/ansi-colors.js +51 -0
  36. package/dist/ansi-colors.js.map +1 -0
  37. package/dist/cli/BaseCommand.d.ts +18 -0
  38. package/dist/cli/BaseCommand.d.ts.map +1 -0
  39. package/dist/cli/BaseCommand.js +31 -0
  40. package/dist/cli/BaseCommand.js.map +1 -0
  41. package/dist/cli/KasekiCLI.d.ts +30 -0
  42. package/dist/cli/KasekiCLI.d.ts.map +1 -0
  43. package/dist/cli/KasekiCLI.js +134 -0
  44. package/dist/cli/KasekiCLI.js.map +1 -0
  45. package/dist/cli/commands/ConfigCommand.d.ts +13 -0
  46. package/dist/cli/commands/ConfigCommand.d.ts.map +1 -0
  47. package/dist/cli/commands/ConfigCommand.js +131 -0
  48. package/dist/cli/commands/ConfigCommand.js.map +1 -0
  49. package/dist/cli/commands/DoctorCommand.d.ts +45 -0
  50. package/dist/cli/commands/DoctorCommand.d.ts.map +1 -0
  51. package/dist/cli/commands/DoctorCommand.js +309 -0
  52. package/dist/cli/commands/DoctorCommand.js.map +1 -0
  53. package/dist/cli/commands/ListCommand.d.ts +9 -0
  54. package/dist/cli/commands/ListCommand.d.ts.map +1 -0
  55. package/dist/cli/commands/ListCommand.js +81 -0
  56. package/dist/cli/commands/ListCommand.js.map +1 -0
  57. package/dist/cli/commands/ReportCommand.d.ts +9 -0
  58. package/dist/cli/commands/ReportCommand.d.ts.map +1 -0
  59. package/dist/cli/commands/ReportCommand.js +98 -0
  60. package/dist/cli/commands/ReportCommand.js.map +1 -0
  61. package/dist/cli/commands/RunCommand.d.ts +13 -0
  62. package/dist/cli/commands/RunCommand.d.ts.map +1 -0
  63. package/dist/cli/commands/RunCommand.js +191 -0
  64. package/dist/cli/commands/RunCommand.js.map +1 -0
  65. package/dist/cli/commands/SecretsCommand.d.ts +9 -0
  66. package/dist/cli/commands/SecretsCommand.d.ts.map +1 -0
  67. package/dist/cli/commands/SecretsCommand.js +109 -0
  68. package/dist/cli/commands/SecretsCommand.js.map +1 -0
  69. package/dist/cli/commands/ServeCommand.d.ts +9 -0
  70. package/dist/cli/commands/ServeCommand.d.ts.map +1 -0
  71. package/dist/cli/commands/ServeCommand.js +50 -0
  72. package/dist/cli/commands/ServeCommand.js.map +1 -0
  73. package/dist/cli/commands/SetupCommand.d.ts +42 -0
  74. package/dist/cli/commands/SetupCommand.d.ts.map +1 -0
  75. package/dist/cli/commands/SetupCommand.js +249 -0
  76. package/dist/cli/commands/SetupCommand.js.map +1 -0
  77. package/dist/cli.d.ts +9 -0
  78. package/dist/cli.d.ts.map +1 -0
  79. package/dist/cli.js +130 -0
  80. package/dist/cli.js.map +1 -0
  81. package/dist/config/ConfigManager.d.ts +395 -0
  82. package/dist/config/ConfigManager.d.ts.map +1 -0
  83. package/dist/config/ConfigManager.js +446 -0
  84. package/dist/config/ConfigManager.js.map +1 -0
  85. package/dist/docker/DockerManager.d.ts +69 -0
  86. package/dist/docker/DockerManager.d.ts.map +1 -0
  87. package/dist/docker/DockerManager.js +266 -0
  88. package/dist/docker/DockerManager.js.map +1 -0
  89. package/dist/event-aggregator.d.ts +71 -0
  90. package/dist/event-aggregator.d.ts.map +1 -0
  91. package/dist/event-aggregator.js +95 -0
  92. package/dist/event-aggregator.js.map +1 -0
  93. package/dist/github-app-token.d.ts +16 -0
  94. package/dist/github-app-token.d.ts.map +1 -0
  95. package/dist/github-app-token.js +148 -0
  96. package/dist/github-app-token.js.map +1 -0
  97. package/dist/idempotency-store.d.ts +61 -0
  98. package/dist/idempotency-store.d.ts.map +1 -0
  99. package/dist/idempotency-store.js +321 -0
  100. package/dist/idempotency-store.js.map +1 -0
  101. package/dist/index.d.ts +25 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +31 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/instance/InstanceManager.d.ts +81 -0
  106. package/dist/instance/InstanceManager.d.ts.map +1 -0
  107. package/dist/instance/InstanceManager.js +220 -0
  108. package/dist/instance/InstanceManager.js.map +1 -0
  109. package/dist/instance-metadata-reader.d.ts +48 -0
  110. package/dist/instance-metadata-reader.d.ts.map +1 -0
  111. package/dist/instance-metadata-reader.js +94 -0
  112. package/dist/instance-metadata-reader.js.map +1 -0
  113. package/dist/instance-state-derivation.d.ts +42 -0
  114. package/dist/instance-state-derivation.d.ts.map +1 -0
  115. package/dist/instance-state-derivation.js +133 -0
  116. package/dist/instance-state-derivation.js.map +1 -0
  117. package/dist/job-scheduler.d.ts +124 -0
  118. package/dist/job-scheduler.d.ts.map +1 -0
  119. package/dist/job-scheduler.js +992 -0
  120. package/dist/job-scheduler.js.map +1 -0
  121. package/dist/kaseki-api-client.d.ts +89 -0
  122. package/dist/kaseki-api-client.d.ts.map +1 -0
  123. package/dist/kaseki-api-client.js +405 -0
  124. package/dist/kaseki-api-client.js.map +1 -0
  125. package/dist/kaseki-api-config.d.ts +34 -0
  126. package/dist/kaseki-api-config.d.ts.map +1 -0
  127. package/dist/kaseki-api-config.js +113 -0
  128. package/dist/kaseki-api-config.js.map +1 -0
  129. package/dist/kaseki-api-routes.d.ts +13 -0
  130. package/dist/kaseki-api-routes.d.ts.map +1 -0
  131. package/dist/kaseki-api-routes.js +559 -0
  132. package/dist/kaseki-api-routes.js.map +1 -0
  133. package/dist/kaseki-api-service-wrapper.d.ts +43 -0
  134. package/dist/kaseki-api-service-wrapper.d.ts.map +1 -0
  135. package/dist/kaseki-api-service-wrapper.js +150 -0
  136. package/dist/kaseki-api-service-wrapper.js.map +1 -0
  137. package/dist/kaseki-api-service.d.ts +16 -0
  138. package/dist/kaseki-api-service.d.ts.map +1 -0
  139. package/dist/kaseki-api-service.js +143 -0
  140. package/dist/kaseki-api-service.js.map +1 -0
  141. package/dist/kaseki-api-types.d.ts +440 -0
  142. package/dist/kaseki-api-types.d.ts.map +1 -0
  143. package/dist/kaseki-api-types.js +64 -0
  144. package/dist/kaseki-api-types.js.map +1 -0
  145. package/dist/kaseki-cli-lib.d.ts +219 -0
  146. package/dist/kaseki-cli-lib.d.ts.map +1 -0
  147. package/dist/kaseki-cli-lib.js +523 -0
  148. package/dist/kaseki-cli-lib.js.map +1 -0
  149. package/dist/kaseki-cli.d.ts +38 -0
  150. package/dist/kaseki-cli.d.ts.map +1 -0
  151. package/dist/kaseki-cli.js +559 -0
  152. package/dist/kaseki-cli.js.map +1 -0
  153. package/dist/kaseki-report.d.ts +3 -0
  154. package/dist/kaseki-report.d.ts.map +1 -0
  155. package/dist/kaseki-report.js +140 -0
  156. package/dist/kaseki-report.js.map +1 -0
  157. package/dist/lib/subprocess-helpers.d.ts +98 -0
  158. package/dist/lib/subprocess-helpers.d.ts.map +1 -0
  159. package/dist/lib/subprocess-helpers.js +136 -0
  160. package/dist/lib/subprocess-helpers.js.map +1 -0
  161. package/dist/logger.d.ts +39 -0
  162. package/dist/logger.d.ts.map +1 -0
  163. package/dist/logger.js +79 -0
  164. package/dist/logger.js.map +1 -0
  165. package/dist/metrics.d.ts +19 -0
  166. package/dist/metrics.d.ts.map +1 -0
  167. package/dist/metrics.js +59 -0
  168. package/dist/metrics.js.map +1 -0
  169. package/dist/middleware/job-lookup.d.ts +27 -0
  170. package/dist/middleware/job-lookup.d.ts.map +1 -0
  171. package/dist/middleware/job-lookup.js +28 -0
  172. package/dist/middleware/job-lookup.js.map +1 -0
  173. package/dist/pi-event-filter.d.ts +3 -0
  174. package/dist/pi-event-filter.d.ts.map +1 -0
  175. package/dist/pi-event-filter.js +126 -0
  176. package/dist/pi-event-filter.js.map +1 -0
  177. package/dist/pi-progress-stream.d.ts +3 -0
  178. package/dist/pi-progress-stream.d.ts.map +1 -0
  179. package/dist/pi-progress-stream.js +205 -0
  180. package/dist/pi-progress-stream.js.map +1 -0
  181. package/dist/pi-progress-summarizer.d.ts +61 -0
  182. package/dist/pi-progress-summarizer.d.ts.map +1 -0
  183. package/dist/pi-progress-summarizer.js +246 -0
  184. package/dist/pi-progress-summarizer.js.map +1 -0
  185. package/dist/pre-flight-validator.d.ts +72 -0
  186. package/dist/pre-flight-validator.d.ts.map +1 -0
  187. package/dist/pre-flight-validator.js +513 -0
  188. package/dist/pre-flight-validator.js.map +1 -0
  189. package/dist/progress-stream-utils.d.ts +3 -0
  190. package/dist/progress-stream-utils.d.ts.map +1 -0
  191. package/dist/progress-stream-utils.js +15 -0
  192. package/dist/progress-stream-utils.js.map +1 -0
  193. package/dist/result-cache.d.ts +52 -0
  194. package/dist/result-cache.d.ts.map +1 -0
  195. package/dist/result-cache.js +134 -0
  196. package/dist/result-cache.js.map +1 -0
  197. package/dist/routes/artifact-routes.d.ts +10 -0
  198. package/dist/routes/artifact-routes.d.ts.map +1 -0
  199. package/dist/routes/artifact-routes.js +126 -0
  200. package/dist/routes/artifact-routes.js.map +1 -0
  201. package/dist/routes/log-routes.d.ts +8 -0
  202. package/dist/routes/log-routes.d.ts.map +1 -0
  203. package/dist/routes/log-routes.js +345 -0
  204. package/dist/routes/log-routes.js.map +1 -0
  205. package/dist/routes/status-routes.d.ts +8 -0
  206. package/dist/routes/status-routes.d.ts.map +1 -0
  207. package/dist/routes/status-routes.js +82 -0
  208. package/dist/routes/status-routes.js.map +1 -0
  209. package/dist/routes/webhook-routes.d.ts +6 -0
  210. package/dist/routes/webhook-routes.d.ts.map +1 -0
  211. package/dist/routes/webhook-routes.js +86 -0
  212. package/dist/routes/webhook-routes.js.map +1 -0
  213. package/dist/run-artifact-metadata-cache.d.ts +42 -0
  214. package/dist/run-artifact-metadata-cache.d.ts.map +1 -0
  215. package/dist/run-artifact-metadata-cache.js +139 -0
  216. package/dist/run-artifact-metadata-cache.js.map +1 -0
  217. package/dist/secret-value-cache.d.ts +13 -0
  218. package/dist/secret-value-cache.d.ts.map +1 -0
  219. package/dist/secret-value-cache.js +44 -0
  220. package/dist/secret-value-cache.js.map +1 -0
  221. package/dist/secrets/SecretsManager.d.ts +80 -0
  222. package/dist/secrets/SecretsManager.d.ts.map +1 -0
  223. package/dist/secrets/SecretsManager.js +306 -0
  224. package/dist/secrets/SecretsManager.js.map +1 -0
  225. package/dist/test-utils.d.ts +55 -0
  226. package/dist/test-utils.d.ts.map +1 -0
  227. package/dist/test-utils.js +48 -0
  228. package/dist/test-utils.js.map +1 -0
  229. package/dist/timestamp-tracker.d.ts +75 -0
  230. package/dist/timestamp-tracker.d.ts.map +1 -0
  231. package/dist/timestamp-tracker.js +121 -0
  232. package/dist/timestamp-tracker.js.map +1 -0
  233. package/dist/utils/failure-artifact-writer.d.ts +29 -0
  234. package/dist/utils/failure-artifact-writer.d.ts.map +1 -0
  235. package/dist/utils/failure-artifact-writer.js +157 -0
  236. package/dist/utils/failure-artifact-writer.js.map +1 -0
  237. package/dist/utils/file-helpers.d.ts +41 -0
  238. package/dist/utils/file-helpers.d.ts.map +1 -0
  239. package/dist/utils/file-helpers.js +143 -0
  240. package/dist/utils/file-helpers.js.map +1 -0
  241. package/dist/utils/http-client-factory.d.ts +46 -0
  242. package/dist/utils/http-client-factory.d.ts.map +1 -0
  243. package/dist/utils/http-client-factory.js +114 -0
  244. package/dist/utils/http-client-factory.js.map +1 -0
  245. package/dist/utils/progress-normalizer.d.ts +13 -0
  246. package/dist/utils/progress-normalizer.d.ts.map +1 -0
  247. package/dist/utils/progress-normalizer.js +57 -0
  248. package/dist/utils/progress-normalizer.js.map +1 -0
  249. package/dist/utils/response-helpers.d.ts +34 -0
  250. package/dist/utils/response-helpers.d.ts.map +1 -0
  251. package/dist/utils/response-helpers.js +78 -0
  252. package/dist/utils/response-helpers.js.map +1 -0
  253. package/dist/utils/route-helpers.d.ts +17 -0
  254. package/dist/utils/route-helpers.d.ts.map +1 -0
  255. package/dist/utils/route-helpers.js +22 -0
  256. package/dist/utils/route-helpers.js.map +1 -0
  257. package/dist/utils/status-response-builder.d.ts +23 -0
  258. package/dist/utils/status-response-builder.d.ts.map +1 -0
  259. package/dist/utils/status-response-builder.js +144 -0
  260. package/dist/utils/status-response-builder.js.map +1 -0
  261. package/dist/utils/type-guards.d.ts +37 -0
  262. package/dist/utils/type-guards.d.ts.map +1 -0
  263. package/dist/utils/type-guards.js +45 -0
  264. package/dist/utils/type-guards.js.map +1 -0
  265. package/dist/utils/utf8-helpers.d.ts +32 -0
  266. package/dist/utils/utf8-helpers.d.ts.map +1 -0
  267. package/dist/utils/utf8-helpers.js +97 -0
  268. package/dist/utils/utf8-helpers.js.map +1 -0
  269. package/dist/utils/webhook-event-builder.d.ts +26 -0
  270. package/dist/utils/webhook-event-builder.d.ts.map +1 -0
  271. package/dist/utils/webhook-event-builder.js +77 -0
  272. package/dist/utils/webhook-event-builder.js.map +1 -0
  273. package/dist/webhook-manager.d.ts +56 -0
  274. package/dist/webhook-manager.d.ts.map +1 -0
  275. package/dist/webhook-manager.js +359 -0
  276. package/dist/webhook-manager.js.map +1 -0
  277. package/docker/workspace-cache/package-lock.json +13 -0
  278. package/docker/workspace-cache/package.json +7 -0
  279. package/docker-compose.yml +53 -0
  280. package/docs/API.md +708 -0
  281. package/docs/BACKLOG.md +19 -0
  282. package/docs/BUILD_STRATEGY.md +404 -0
  283. package/docs/CLI.md +569 -0
  284. package/docs/DEPLOYMENT.md +521 -0
  285. package/docs/DEVELOPMENT.md +459 -0
  286. package/docs/DOCKER_SETUP.md +522 -0
  287. package/docs/ENHANCED_PROGRESS_LOGS.md +264 -0
  288. package/docs/IMPLEMENTATION_SUMMARY.md +549 -0
  289. package/docs/INTEGRATION_EXAMPLE.md +217 -0
  290. package/docs/NPM_SETUP.md +468 -0
  291. package/docs/PHASE1-4_IMPLEMENTATION.md +302 -0
  292. package/docs/PHASE1_COMPLETION.md +192 -0
  293. package/docs/PHASE2_COMPLETION.md +134 -0
  294. package/docs/PHASE6_MIGRATION.md +392 -0
  295. package/docs/PRINTF_SAFETY_FIX.md +282 -0
  296. package/docs/QUALITY_GATES.md +369 -0
  297. package/docs/SETUP_GUIDE.md +482 -0
  298. package/docs/TASK_PROMPT_TEMPLATES.md +533 -0
  299. package/docs/VALIDATION_FIX.md +139 -0
  300. package/docs/VERIFICATION_CHECKLIST.md +335 -0
  301. package/docs/repo-maturity.md +760 -0
  302. package/fix-tests.d.ts +9 -0
  303. package/fix-tests.d.ts.map +1 -0
  304. package/fix-tests.js.map +1 -0
  305. package/fix-tests.ts +53 -0
  306. package/jest.config.ts +31 -0
  307. package/kaseki +183 -0
  308. package/kaseki-agent.sh +1961 -0
  309. package/ops/logrotate/kaseki +10 -0
  310. package/package.json +83 -0
  311. package/perf/README.md +54 -0
  312. package/perf/pi-event-filter.benchmark.test.ts +98 -0
  313. package/run-kaseki-json.test.sh +106 -0
  314. package/run-kaseki.sh +990 -0
  315. package/scripts/allowlist-helper.sh +56 -0
  316. package/scripts/cleanup-kaseki.sh +168 -0
  317. package/scripts/deploy-pi-template.sh +293 -0
  318. package/scripts/docker-entrypoint.sh +71 -0
  319. package/scripts/dry-run-allowlist.sh +161 -0
  320. package/scripts/kaseki-activate.sh +396 -0
  321. package/scripts/kaseki-api.service +62 -0
  322. package/scripts/kaseki-container-entrypoint-wrapper.sh +119 -0
  323. package/scripts/kaseki-container-setup-remote.sh +172 -0
  324. package/scripts/kaseki-container-setup.sh +193 -0
  325. package/scripts/kaseki-healthcheck.sh +95 -0
  326. package/scripts/kaseki-install.sh +50 -0
  327. package/scripts/kaseki-maturity-score.sh +291 -0
  328. package/scripts/kaseki-performance-metrics.sh +122 -0
  329. package/scripts/kaseki-preflight.sh +270 -0
  330. package/scripts/kaseki-setup.sh +265 -0
  331. package/scripts/pi-setup-remote.sh +213 -0
  332. package/scripts/setup-github-labels.sh +42 -0
  333. package/scripts/suggest-allowlist.sh +68 -0
  334. package/scripts/templates/MULTI_HOST_DISTRIBUTED.md +337 -0
  335. package/scripts/templates/REST_API_SERVICE.md +490 -0
  336. package/scripts/templates/SINGLE_HOST_CLI.md +194 -0
  337. package/scripts/test-github-app.sh +248 -0
  338. package/src/add-js-extensions.ts +61 -0
  339. package/src/ansi-colors.test.ts +62 -0
  340. package/src/ansi-colors.ts +67 -0
  341. package/src/cli/BaseCommand.ts +40 -0
  342. package/src/cli/KasekiCLI.ts +154 -0
  343. package/src/cli/commands/ConfigCommand.ts +145 -0
  344. package/src/cli/commands/DoctorCommand.ts +329 -0
  345. package/src/cli/commands/ListCommand.ts +105 -0
  346. package/src/cli/commands/ReportCommand.ts +110 -0
  347. package/src/cli/commands/RunCommand.ts +218 -0
  348. package/src/cli/commands/SecretsCommand.ts +120 -0
  349. package/src/cli/commands/ServeCommand.ts +62 -0
  350. package/src/cli/commands/SetupCommand.ts +301 -0
  351. package/src/cli.ts +138 -0
  352. package/src/config/ConfigManager.ts +476 -0
  353. package/src/docker/DockerManager.ts +319 -0
  354. package/src/docker-entrypoint-packaging.test.ts +33 -0
  355. package/src/event-aggregator.test.ts +117 -0
  356. package/src/event-aggregator.ts +126 -0
  357. package/src/github-app-token.ts +215 -0
  358. package/src/idempotency-store.test.ts +117 -0
  359. package/src/idempotency-store.ts +385 -0
  360. package/src/index.ts +89 -0
  361. package/src/instance/InstanceManager.ts +285 -0
  362. package/src/instance-metadata-reader.test.ts +190 -0
  363. package/src/instance-metadata-reader.ts +129 -0
  364. package/src/instance-state-derivation.test.ts +263 -0
  365. package/src/instance-state-derivation.ts +148 -0
  366. package/src/job-scheduler.test.ts +1236 -0
  367. package/src/job-scheduler.ts +1117 -0
  368. package/src/kaseki-api-client.ts +488 -0
  369. package/src/kaseki-api-config.test.ts +315 -0
  370. package/src/kaseki-api-config.ts +175 -0
  371. package/src/kaseki-api-routes.test.ts +1615 -0
  372. package/src/kaseki-api-routes.ts +643 -0
  373. package/src/kaseki-api-service-wrapper.ts +188 -0
  374. package/src/kaseki-api-service.test.ts +418 -0
  375. package/src/kaseki-api-service.ts +192 -0
  376. package/src/kaseki-api-types.ts +320 -0
  377. package/src/kaseki-cli-lib.test.ts +552 -0
  378. package/src/kaseki-cli-lib.ts +760 -0
  379. package/src/kaseki-cli.ts +682 -0
  380. package/src/kaseki-report.test.ts +118 -0
  381. package/src/kaseki-report.ts +192 -0
  382. package/src/lib/subprocess-helpers.ts +177 -0
  383. package/src/logger.ts +114 -0
  384. package/src/metrics.ts +66 -0
  385. package/src/middleware/job-lookup.test.ts +113 -0
  386. package/src/middleware/job-lookup.ts +45 -0
  387. package/src/pi-event-filter.test.ts +183 -0
  388. package/src/pi-event-filter.ts +183 -0
  389. package/src/pi-progress-stream.ts +287 -0
  390. package/src/pi-progress-summarizer.test.ts +302 -0
  391. package/src/pi-progress-summarizer.ts +287 -0
  392. package/src/pre-flight-validator.test.ts +512 -0
  393. package/src/pre-flight-validator.ts +618 -0
  394. package/src/progress-stream-utils.test.ts +35 -0
  395. package/src/progress-stream-utils.ts +14 -0
  396. package/src/result-cache.test.ts +195 -0
  397. package/src/result-cache.ts +181 -0
  398. package/src/routes/artifact-routes.ts +169 -0
  399. package/src/routes/log-routes.ts +391 -0
  400. package/src/routes/status-routes.ts +92 -0
  401. package/src/routes/webhook-routes.ts +97 -0
  402. package/src/run-artifact-metadata-cache.test.ts +80 -0
  403. package/src/run-artifact-metadata-cache.ts +184 -0
  404. package/src/secret-value-cache.test.ts +66 -0
  405. package/src/secret-value-cache.ts +55 -0
  406. package/src/secrets/SecretsManager.ts +343 -0
  407. package/src/test-utils.ts +81 -0
  408. package/src/timestamp-tracker.test.ts +134 -0
  409. package/src/timestamp-tracker.ts +132 -0
  410. package/src/utils/failure-artifact-writer.ts +187 -0
  411. package/src/utils/file-helpers.test.ts +235 -0
  412. package/src/utils/file-helpers.ts +150 -0
  413. package/src/utils/http-client-factory.test.ts +245 -0
  414. package/src/utils/http-client-factory.ts +157 -0
  415. package/src/utils/progress-normalizer.test.ts +442 -0
  416. package/src/utils/progress-normalizer.ts +68 -0
  417. package/src/utils/response-helpers.test.ts +122 -0
  418. package/src/utils/response-helpers.ts +101 -0
  419. package/src/utils/route-helpers.ts +30 -0
  420. package/src/utils/status-response-builder.ts +159 -0
  421. package/src/utils/type-guards.ts +52 -0
  422. package/src/utils/utf8-helpers.ts +102 -0
  423. package/src/utils/webhook-event-builder.test.ts +143 -0
  424. package/src/utils/webhook-event-builder.ts +87 -0
  425. package/src/webhook-manager.test.ts +152 -0
  426. package/src/webhook-manager.ts +445 -0
  427. package/templates/allowlist-api-route.txt +7 -0
  428. package/templates/allowlist-comprehensive.txt +8 -0
  429. package/templates/allowlist-parser-fix.txt +6 -0
  430. package/templates/allowlist-ui-component.txt +9 -0
  431. package/templates/allowlist-utility.txt +9 -0
  432. package/test/actual-model-metadata.test.sh +102 -0
  433. package/test/dry-run.test.sh +131 -0
  434. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-0.json +1 -0
  435. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-1.json +1 -0
  436. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-invalid.json +1 -0
  437. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-str-0.json +1 -0
  438. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-str-1.json +1 -0
  439. package/test/kaseki-api.integration.test.sh +165 -0
  440. package/test/pi-event-filter-failure.test.sh +83 -0
  441. package/test/printf-safety-focused.test.sh +99 -0
  442. package/test/printf-safety-results/results/restoration.jsonl +10 -0
  443. package/test/printf-safety-results/results/test.jsonl +0 -0
  444. package/test/printf-safety.test.sh +297 -0
  445. package/test/validation-fix.test.sh +79 -0
  446. package/test/validation-integration.test.sh +109 -0
  447. package/tests/allowlist-glob.test.sh +61 -0
  448. package/tests/dependency-cache-key.test.sh +48 -0
  449. package/tests/dependency-restore-mode.test.sh +48 -0
  450. package/tests/doctor-template-parity.test.sh +95 -0
  451. package/tests/github-operations.test.sh +142 -0
  452. package/tests/npm-install-flags.test.sh +58 -0
  453. package/tests/quality-gates.test.sh +178 -0
  454. package/tests/repo-memory.test.sh +103 -0
  455. package/tests/restore-disallowed-changes.test.sh +80 -0
  456. package/tests/validation-missing-npm-scripts.test.sh +93 -0
  457. package/tests/validation-strict-mode.test.sh +118 -0
  458. package/tsconfig.changed.json +7 -0
  459. package/tsconfig.json +39 -0
@@ -0,0 +1,512 @@
1
+ import { PreFlightValidator, globToRegex, testPathAgainstPatterns, validateAllowlistPatternMatching } from './pre-flight-validator';
2
+ import type { RunRequest } from './kaseki-api-types';
3
+
4
+ describe('PreFlightValidator validation logic', () => {
5
+ let validator: PreFlightValidator;
6
+
7
+ beforeEach(() => {
8
+ delete process.env.KASEKI_PREFLIGHT_CACHE_TTL_SECONDS;
9
+ delete process.env.KASEKI_PREFLIGHT_CACHE_MAX_ENTRIES;
10
+ validator = new PreFlightValidator();
11
+ });
12
+
13
+ describe('validateCommandsSyntax', () => {
14
+ test('rejects empty validation commands', async () => {
15
+ const request: RunRequest = {
16
+ repoUrl: 'https://github.com/example/repo',
17
+ ref: 'main',
18
+ validationCommands: ['', 'npm test'],
19
+ };
20
+
21
+ const response = await validator.validate(request);
22
+ const cmdCheck = response.checks.find((c) => c.name === 'commands-syntax');
23
+ expect(cmdCheck?.status).toBe('fail');
24
+ expect(cmdCheck?.message).toContain('Invalid validation commands');
25
+ });
26
+
27
+ test('warns about dangerous validation commands', async () => {
28
+ const request: RunRequest = {
29
+ repoUrl: 'https://github.com/example/repo',
30
+ ref: 'main',
31
+ validationCommands: ['rm -rf / --no-preserve-root'],
32
+ };
33
+
34
+ const response = await validator.validate(request);
35
+ const cmdCheck = response.checks.find((c) => c.name === 'commands-syntax');
36
+ expect(cmdCheck?.status).toBe('fail');
37
+ expect(cmdCheck?.message).toContain('Dangerous command');
38
+ });
39
+
40
+ test('accepts legitimate commands', async () => {
41
+ const request: RunRequest = {
42
+ repoUrl: 'https://github.com/example/repo',
43
+ ref: 'main',
44
+ validationCommands: ['npm test', 'npm run build'],
45
+ };
46
+
47
+ const response = await validator.validate(request);
48
+ const cmdCheck = response.checks.find((c) => c.name === 'commands-syntax');
49
+ expect(cmdCheck?.status).toBe('pass');
50
+ });
51
+ });
52
+
53
+ describe('validateAllowlistPatterns', () => {
54
+ test('accepts valid file patterns', async () => {
55
+ const request: RunRequest = {
56
+ repoUrl: 'https://github.com/example/repo',
57
+ ref: 'main',
58
+ changedFilesAllowlist: ['src/**/*.ts', 'tests/**/*.test.ts', 'package.json'],
59
+ };
60
+
61
+ const response = await validator.validate(request);
62
+ const patternCheck = response.checks.find((c) => c.name === 'allowlist-patterns');
63
+ expect(patternCheck?.status).toBe('pass');
64
+ });
65
+
66
+ test('warns about overly broad patterns', async () => {
67
+ const request: RunRequest = {
68
+ repoUrl: 'https://github.com/example/repo',
69
+ ref: 'main',
70
+ changedFilesAllowlist: ['*'],
71
+ };
72
+
73
+ const response = await validator.validate(request);
74
+ const patternCheck = response.checks.find((c) => c.name === 'allowlist-patterns');
75
+ expect(patternCheck?.status).toBe('warning');
76
+ expect(patternCheck?.message).toContain('very broad');
77
+ });
78
+
79
+ test('allows exact file paths in allowlist', async () => {
80
+ const request: RunRequest = {
81
+ repoUrl: 'https://github.com/example/repo',
82
+ ref: 'main',
83
+ changedFilesAllowlist: ['src/index.ts', 'src/utils.ts', 'README.md'],
84
+ };
85
+
86
+ const response = await validator.validate(request);
87
+ const patternCheck = response.checks.find((c) => c.name === 'allowlist-patterns');
88
+ expect(patternCheck?.status).toBe('pass');
89
+ });
90
+ });
91
+
92
+ describe('validateDiffBytes', () => {
93
+ test('passes reasonable max diff bytes values', async () => {
94
+ const request: RunRequest = {
95
+ repoUrl: 'https://github.com/example/repo',
96
+ ref: 'main',
97
+ maxDiffBytes: 200000, // 200 KB
98
+ };
99
+
100
+ const response = await validator.validate(request);
101
+ const diffCheck = response.checks.find((c) => c.name === 'max-diff-bytes');
102
+ expect(diffCheck?.status).toBe('pass');
103
+ });
104
+
105
+ test('warns about very small max diff bytes', async () => {
106
+ const request: RunRequest = {
107
+ repoUrl: 'https://github.com/example/repo',
108
+ ref: 'main',
109
+ maxDiffBytes: 1000, // 1 KB - too small
110
+ };
111
+
112
+ const response = await validator.validate(request);
113
+ const diffCheck = response.checks.find((c) => c.name === 'max-diff-bytes');
114
+ expect(diffCheck?.status).toBe('warning');
115
+ expect(diffCheck?.message).toContain('very small');
116
+ });
117
+
118
+ test('uses default when max diff bytes not specified', async () => {
119
+ const request: RunRequest = {
120
+ repoUrl: 'https://github.com/example/repo',
121
+ ref: 'main',
122
+ };
123
+
124
+ const response = await validator.validate(request);
125
+ const diffCheck = response.checks.find((c) => c.name === 'max-diff-bytes');
126
+ expect(diffCheck?.status).toBe('pass');
127
+ expect(diffCheck?.message).toContain('default');
128
+ });
129
+ });
130
+
131
+ describe('full validation response structure', () => {
132
+ test('returns valid response for basic request', async () => {
133
+ const request: RunRequest = {
134
+ repoUrl: 'https://github.com/example/repo',
135
+ ref: 'main',
136
+ validationCommands: ['npm test'],
137
+ maxDiffBytes: 200000,
138
+ };
139
+
140
+ const response = await validator.validate(request);
141
+
142
+ expect(response).toHaveProperty('isValid');
143
+ expect(response).toHaveProperty('checks');
144
+ expect(response).toHaveProperty('warnings');
145
+ expect(response).toHaveProperty('errors');
146
+ expect(Array.isArray(response.checks)).toBe(true);
147
+ expect(response.checks.length).toBeGreaterThan(0);
148
+
149
+ // All checks should have required fields
150
+ for (const check of response.checks) {
151
+ expect(check).toHaveProperty('name');
152
+ expect(check).toHaveProperty('status');
153
+ expect(check).toHaveProperty('message');
154
+ }
155
+ });
156
+
157
+ test('collects multiple validation checks', async () => {
158
+ const request: RunRequest = {
159
+ repoUrl: 'https://github.com/example/repo',
160
+ ref: 'main',
161
+ validationCommands: ['npm test', 'npm build'],
162
+ changedFilesAllowlist: ['src/**'],
163
+ };
164
+
165
+ const response = await validator.validate(request);
166
+
167
+ // Should include at least: repo-reachable, repo-size, commands-syntax, allowlist-patterns, max-diff-bytes
168
+ expect(response.checks.length).toBeGreaterThanOrEqual(5);
169
+ });
170
+
171
+ test('marks validation invalid when there are errors', async () => {
172
+ const request: RunRequest = {
173
+ repoUrl: 'https://github.com/example/repo',
174
+ ref: 'main',
175
+ validationCommands: ['', 'rm -rf /'],
176
+ };
177
+
178
+ const response = await validator.validate(request);
179
+
180
+ if (response.errors.length > 0) {
181
+ expect(response.isValid).toBe(false);
182
+ }
183
+ });
184
+ });
185
+
186
+ describe('preflight result cache', () => {
187
+ const baseTime = new Date('2026-05-06T00:00:00.000Z');
188
+ const successfulGitResult = {
189
+ code: 0,
190
+ durationMs: 12,
191
+ output: 'abc123\trefs/heads/main\ndef456\trefs/heads/dev\n',
192
+ timedOut: false,
193
+ };
194
+
195
+ beforeEach(() => {
196
+ jest.useFakeTimers();
197
+ jest.setSystemTime(baseTime);
198
+ process.env.KASEKI_PREFLIGHT_CACHE_TTL_SECONDS = '60';
199
+ process.env.KASEKI_PREFLIGHT_CACHE_MAX_ENTRIES = '10';
200
+ validator = new PreFlightValidator();
201
+ });
202
+
203
+ afterEach(() => {
204
+ jest.useRealTimers();
205
+ jest.restoreAllMocks();
206
+ });
207
+
208
+ test('returns a cached response for repeated repoUrl/ref validations', async () => {
209
+ const gitSpy = jest
210
+ .spyOn(validator as any, 'lsRemoteHeadsAndTags')
211
+ .mockResolvedValue(successfulGitResult);
212
+ const request: RunRequest = {
213
+ repoUrl: 'https://github.com/example/cache-hit',
214
+ ref: 'main',
215
+ maxDiffBytes: 200000,
216
+ };
217
+
218
+ const first = await validator.validate(request);
219
+ const second = await validator.validate(request);
220
+
221
+ expect(gitSpy).toHaveBeenCalledTimes(1);
222
+ expect(second).toEqual(first);
223
+ expect(second.checks.find((c) => c.name === 'ref-exists')?.status).toBe('pass');
224
+ });
225
+
226
+ test('refreshes validation after cache expiry', async () => {
227
+ process.env.KASEKI_PREFLIGHT_CACHE_TTL_SECONDS = '1';
228
+ validator = new PreFlightValidator();
229
+ const gitSpy = jest
230
+ .spyOn(validator as any, 'lsRemoteHeadsAndTags')
231
+ .mockResolvedValue(successfulGitResult);
232
+ const request: RunRequest = {
233
+ repoUrl: 'https://github.com/example/cache-expiry',
234
+ ref: 'main',
235
+ };
236
+
237
+ await validator.validate(request);
238
+ jest.setSystemTime(new Date(baseTime.getTime() + 1001));
239
+ await validator.validate(request);
240
+
241
+ expect(gitSpy).toHaveBeenCalledTimes(2);
242
+ });
243
+
244
+ test('coalesces concurrent validations for the same cache key', async () => {
245
+ let resolveGitResult: (result: typeof successfulGitResult) => void = () => undefined;
246
+ const gitResultPromise = new Promise<typeof successfulGitResult>((resolve) => {
247
+ resolveGitResult = resolve;
248
+ });
249
+ const gitSpy = jest
250
+ .spyOn(validator as any, 'lsRemoteHeadsAndTags')
251
+ .mockReturnValue(gitResultPromise);
252
+ const request: RunRequest = {
253
+ repoUrl: 'https://github.com/example/coalesce',
254
+ ref: 'main',
255
+ };
256
+
257
+ const firstPromise = validator.validate(request);
258
+ const secondPromise = validator.validate(request);
259
+
260
+ expect(gitSpy).toHaveBeenCalledTimes(1);
261
+ resolveGitResult(successfulGitResult);
262
+ const [first, second] = await Promise.all([firstPromise, secondPromise]);
263
+
264
+ expect(first).toEqual(second);
265
+ expect(gitSpy).toHaveBeenCalledTimes(1);
266
+ });
267
+
268
+ test('uses distinct cache keys for distinct refs', async () => {
269
+ const gitSpy = jest
270
+ .spyOn(validator as any, 'lsRemoteHeadsAndTags')
271
+ .mockResolvedValue(successfulGitResult);
272
+ const repoUrl = 'https://github.com/example/distinct-refs';
273
+
274
+ await validator.validate({ repoUrl, ref: 'main' });
275
+ await validator.validate({ repoUrl, ref: 'dev' });
276
+ await validator.validate({ repoUrl, ref: 'main' });
277
+
278
+ expect(gitSpy).toHaveBeenCalledTimes(2);
279
+ });
280
+ });
281
+
282
+ describe('git validation (git operations)', () => {
283
+ test('marks repository reachable when git ls-remote succeeds', async () => {
284
+ const request: RunRequest = {
285
+ repoUrl: 'https://github.com/example/repo',
286
+ ref: 'main',
287
+ };
288
+ jest.spyOn(validator as any, 'lsRemoteHeadsAndTags').mockResolvedValue({
289
+ code: 0,
290
+ durationMs: 23,
291
+ output: 'abc123\trefs/heads/main\n',
292
+ timedOut: false,
293
+ });
294
+
295
+ const response = await validator.validate(request);
296
+
297
+ const reachableCheck = response.checks.find((c) => c.name === 'repo-reachable');
298
+ expect(reachableCheck).toEqual({
299
+ name: 'repo-reachable',
300
+ status: 'pass',
301
+ message: 'Git repository is reachable (23ms)',
302
+ });
303
+ expect(response.isValid).toBe(true);
304
+ expect(response.errors).not.toContain('Git repository is reachable (23ms)');
305
+ });
306
+
307
+ test('marks repository unreachable when git ls-remote fails', async () => {
308
+ const repoUrl = 'https://github.com/example/missing-repo';
309
+ const request: RunRequest = {
310
+ repoUrl,
311
+ ref: 'main',
312
+ };
313
+ jest.spyOn(validator as any, 'lsRemoteHeadsAndTags').mockResolvedValue({
314
+ code: 128,
315
+ durationMs: 31,
316
+ output: '',
317
+ timedOut: false,
318
+ });
319
+
320
+ const response = await validator.validate(request);
321
+
322
+ const reachableCheck = response.checks.find((c) => c.name === 'repo-reachable');
323
+ expect(reachableCheck).toEqual({
324
+ name: 'repo-reachable',
325
+ status: 'fail',
326
+ message: 'Git repository is not reachable or invalid URL',
327
+ detail: repoUrl,
328
+ });
329
+ expect(response.isValid).toBe(false);
330
+ expect(response.errors).toContain('Git repository is not reachable or invalid URL');
331
+ expect(response.checks.find((c) => c.name === 'ref-exists')).toBeUndefined();
332
+ });
333
+
334
+ test('includes ref existence check if repo is reachable', async () => {
335
+ const request: RunRequest = {
336
+ repoUrl: 'https://github.com/example/repo',
337
+ ref: 'feature/my-branch',
338
+ };
339
+ jest.spyOn(validator as any, 'lsRemoteHeadsAndTags').mockResolvedValue({
340
+ code: 0,
341
+ durationMs: 15,
342
+ output: 'abc123\trefs/heads/feature/my-branch\n',
343
+ timedOut: false,
344
+ });
345
+
346
+ const response = await validator.validate(request);
347
+
348
+ const reachableCheck = response.checks.find((c) => c.name === 'repo-reachable');
349
+ expect(reachableCheck?.status).toBe('pass');
350
+ const refCheck = response.checks.find((c) => c.name === 'ref-exists');
351
+ expect(refCheck).toEqual({
352
+ name: 'ref-exists',
353
+ status: 'pass',
354
+ message: "Git ref 'feature/my-branch' exists",
355
+ });
356
+ });
357
+
358
+ test('passes repo-size check for acceptable repository size', async () => {
359
+ const request: RunRequest = {
360
+ repoUrl: 'https://github.com/example/repo',
361
+ ref: 'main',
362
+ };
363
+ jest.spyOn(validator as any, 'lsRemoteHeadsAndTags').mockResolvedValue({
364
+ code: 0,
365
+ durationMs: 17,
366
+ output: [
367
+ 'abc123\trefs/heads/main',
368
+ 'def456\trefs/heads/dev',
369
+ 'fedcba\trefs/tags/v1.0.0',
370
+ ].join('\n'),
371
+ timedOut: false,
372
+ });
373
+
374
+ const response = await validator.validate(request);
375
+
376
+ const sizeCheck = response.checks.find((c) => c.name === 'repo-size');
377
+ expect(sizeCheck).toEqual({
378
+ name: 'repo-size',
379
+ status: 'pass',
380
+ message: 'Repository size is reasonable (3 refs)',
381
+ });
382
+ expect(sizeCheck?.detail).toBeUndefined();
383
+ expect(response.isValid).toBe(true);
384
+ expect(response.warnings).not.toContain(sizeCheck?.message);
385
+ expect(response.errors).not.toContain(sizeCheck?.message);
386
+ });
387
+
388
+ test('warns for oversized repository without invalidating response', async () => {
389
+ const request: RunRequest = {
390
+ repoUrl: 'https://github.com/example/large-repo',
391
+ ref: 'main',
392
+ };
393
+ const oversizedRefs = [
394
+ 'abc123\trefs/heads/main',
395
+ ...Array.from({ length: 1001 }, (_, index) => `def${index}\trefs/tags/v${index}`),
396
+ ].join('\n');
397
+ jest.spyOn(validator as any, 'lsRemoteHeadsAndTags').mockResolvedValue({
398
+ code: 0,
399
+ durationMs: 42,
400
+ output: oversizedRefs,
401
+ timedOut: false,
402
+ });
403
+
404
+ const response = await validator.validate(request);
405
+
406
+ const sizeCheck = response.checks.find((c) => c.name === 'repo-size');
407
+ expect(sizeCheck).toEqual({
408
+ name: 'repo-size',
409
+ status: 'warning',
410
+ message: 'Repository appears very large (1002+ refs detected); consider using shallow clone',
411
+ });
412
+ expect(sizeCheck?.detail).toBeUndefined();
413
+ expect(response.isValid).toBe(true);
414
+ expect(response.warnings).toContain(sizeCheck?.message);
415
+ expect(response.errors).not.toContain(sizeCheck?.message);
416
+ });
417
+ });
418
+
419
+ describe('pattern matching functions', () => {
420
+ describe('globToRegex', () => {
421
+ test('converts simple glob patterns to regex', () => {
422
+ const regex = globToRegex('src/*.ts');
423
+ expect(regex.test('src/index.ts')).toBe(true);
424
+ expect(regex.test('src/foo.ts')).toBe(true);
425
+ expect(regex.test('src/subdir/index.ts')).toBe(false);
426
+ expect(regex.test('src/index.tsx')).toBe(false);
427
+ });
428
+
429
+ test('handles ** for multi-level matching', () => {
430
+ const regex = globToRegex('src/**/*.ts');
431
+ expect(regex.test('src/index.ts')).toBe(true);
432
+ expect(regex.test('src/lib/parser.ts')).toBe(true);
433
+ expect(regex.test('src/lib/sub/deep/file.ts')).toBe(true);
434
+ expect(regex.test('src/file.tsx')).toBe(false);
435
+ });
436
+
437
+ test('matches exact file paths', () => {
438
+ const regex = globToRegex('package.json');
439
+ expect(regex.test('package.json')).toBe(true);
440
+ expect(regex.test('src/package.json')).toBe(false);
441
+ });
442
+
443
+ test('handles ? single-character wildcard', () => {
444
+ const regex = globToRegex('src/index.t?s');
445
+ expect(regex.test('src/index.tas')).toBe(true);
446
+ expect(regex.test('src/index.tbs')).toBe(true);
447
+ expect(regex.test('src/index.tXs')).toBe(true);
448
+ expect(regex.test('src/index.ts')).toBe(false); // ? requires one character
449
+ expect(regex.test('src/index.tjss')).toBe(false);
450
+ });
451
+ });
452
+
453
+ describe('testPathAgainstPatterns', () => {
454
+ test('returns true if path matches any pattern', () => {
455
+ const patterns = ['src/**/*.ts', 'tests/**/*.ts'];
456
+ expect(testPathAgainstPatterns('src/index.ts', patterns)).toBe(true);
457
+ expect(testPathAgainstPatterns('tests/unit/foo.test.ts', patterns)).toBe(true);
458
+ expect(testPathAgainstPatterns('docs/README.md', patterns)).toBe(false);
459
+ });
460
+
461
+ test('returns true for empty pattern list', () => {
462
+ expect(testPathAgainstPatterns('any/file.ts', [])).toBe(true);
463
+ });
464
+
465
+ test('handles multiple patterns correctly', () => {
466
+ const patterns = ['src/lib/parser.ts', 'tests/parser.validation.ts'];
467
+ expect(testPathAgainstPatterns('src/lib/parser.ts', patterns)).toBe(true);
468
+ expect(testPathAgainstPatterns('tests/parser.validation.ts', patterns)).toBe(true);
469
+ expect(testPathAgainstPatterns('src/index.ts', patterns)).toBe(false);
470
+ });
471
+ });
472
+
473
+ describe('validateAllowlistPatternMatching', () => {
474
+ test('returns valid for reasonable patterns', () => {
475
+ const result = validateAllowlistPatternMatching(['src/lib/parser.ts', 'tests/parser.validation.ts']);
476
+ expect(result.isValid).toBe(true);
477
+ expect(result.warnings.length).toBe(0);
478
+ });
479
+
480
+ test('warns about empty patterns', () => {
481
+ const result = validateAllowlistPatternMatching(['', 'src/**/*.ts']);
482
+ expect(result.warnings).toContain('Empty pattern detected');
483
+ });
484
+
485
+ test('warns about overly broad patterns', () => {
486
+ const result = validateAllowlistPatternMatching(['*']);
487
+ expect(result.warnings.length).toBeGreaterThan(0);
488
+ expect(result.warnings.some((w) => w.includes('very broad'))).toBe(true);
489
+ });
490
+
491
+ test('warns about patterns matching no sample files', () => {
492
+ const result = validateAllowlistPatternMatching(['xyz/**/*.nonexistent']);
493
+ expect(result.warnings.length).toBeGreaterThan(0);
494
+ expect(result.warnings.some((w) => w.includes("doesn't match any sample files"))).toBe(true);
495
+ });
496
+
497
+ test('returns test results for each pattern', () => {
498
+ const result = validateAllowlistPatternMatching(['src/**/*.ts', 'tests/**/*.ts']);
499
+ expect(result.testResults.length).toBe(2);
500
+ expect(result.testResults[0].pattern).toBe('src/**/*.ts');
501
+ expect(result.testResults[0].matches).toBeGreaterThan(0);
502
+ });
503
+
504
+ test('returns empty result for empty pattern list', () => {
505
+ const result = validateAllowlistPatternMatching([]);
506
+ expect(result.isValid).toBe(true);
507
+ expect(result.warnings.length).toBe(0);
508
+ expect(result.testResults.length).toBe(0);
509
+ });
510
+ });
511
+ });
512
+ });