@intentius/chant 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (453) hide show
  1. package/dist/audit/catalog.d.ts +45 -5
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/fetch.d.ts.map +1 -1
  4. package/dist/audit/proof.d.ts +3 -0
  5. package/dist/audit/proof.d.ts.map +1 -1
  6. package/dist/audit/report-model.d.ts +4 -1
  7. package/dist/audit/report-model.d.ts.map +1 -1
  8. package/dist/audit/report.d.ts +3 -0
  9. package/dist/audit/report.d.ts.map +1 -1
  10. package/dist/audit/rules-doc.d.ts +8 -2
  11. package/dist/audit/rules-doc.d.ts.map +1 -1
  12. package/dist/cli/commands/audit.d.ts.map +1 -1
  13. package/dist/cli/commands/doctor.d.ts.map +1 -1
  14. package/dist/cli/commands/lexicon-rolling-upgrade.d.ts +37 -0
  15. package/dist/cli/commands/lexicon-rolling-upgrade.d.ts.map +1 -0
  16. package/dist/cli/commands/lexicon-surface-diff.d.ts +48 -0
  17. package/dist/cli/commands/lexicon-surface-diff.d.ts.map +1 -0
  18. package/dist/cli/commands/lint.d.ts.map +1 -1
  19. package/dist/cli/commands/pinned-upgrade.d.ts +41 -0
  20. package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -0
  21. package/dist/cli/handlers/build.d.ts.map +1 -1
  22. package/dist/cli/handlers/components.d.ts +39 -0
  23. package/dist/cli/handlers/components.d.ts.map +1 -0
  24. package/dist/cli/handlers/dev.d.ts +3 -0
  25. package/dist/cli/handlers/dev.d.ts.map +1 -1
  26. package/dist/cli/handlers/graph.d.ts +4 -2
  27. package/dist/cli/handlers/graph.d.ts.map +1 -1
  28. package/dist/cli/handlers/misc.d.ts.map +1 -1
  29. package/dist/cli/handlers/run.d.ts +49 -0
  30. package/dist/cli/handlers/run.d.ts.map +1 -1
  31. package/dist/cli/main.d.ts.map +1 -1
  32. package/dist/cli/registry.d.ts +35 -0
  33. package/dist/cli/registry.d.ts.map +1 -1
  34. package/dist/codegen/fetch.d.ts +22 -0
  35. package/dist/codegen/fetch.d.ts.map +1 -1
  36. package/dist/codegen/lexicon-regen.d.ts +110 -0
  37. package/dist/codegen/lexicon-regen.d.ts.map +1 -0
  38. package/dist/codegen/pinned-upgrade.d.ts +153 -0
  39. package/dist/codegen/pinned-upgrade.d.ts.map +1 -0
  40. package/dist/codegen/rolling-upgrade.d.ts +138 -0
  41. package/dist/codegen/rolling-upgrade.d.ts.map +1 -0
  42. package/dist/codegen/surface-snapshot.d.ts +173 -0
  43. package/dist/codegen/surface-snapshot.d.ts.map +1 -0
  44. package/dist/codegen/version-bump.d.ts +21 -0
  45. package/dist/codegen/version-bump.d.ts.map +1 -0
  46. package/dist/components/auto-release.d.ts +93 -0
  47. package/dist/components/auto-release.d.ts.map +1 -0
  48. package/dist/components/builders.d.ts +37 -0
  49. package/dist/components/builders.d.ts.map +1 -0
  50. package/dist/components/capability-plugin-loader.d.ts +111 -0
  51. package/dist/components/capability-plugin-loader.d.ts.map +1 -0
  52. package/dist/components/capability-plugin-schema.d.ts +24 -0
  53. package/dist/components/capability-plugin-schema.d.ts.map +1 -0
  54. package/dist/components/capability-plugin.d.ts +85 -0
  55. package/dist/components/capability-plugin.d.ts.map +1 -0
  56. package/dist/components/capability.d.ts +89 -0
  57. package/dist/components/capability.d.ts.map +1 -0
  58. package/dist/components/cli-support.d.ts +204 -0
  59. package/dist/components/cli-support.d.ts.map +1 -0
  60. package/dist/components/component.d.ts +179 -0
  61. package/dist/components/component.d.ts.map +1 -0
  62. package/dist/components/config-defaults.d.ts +41 -0
  63. package/dist/components/config-defaults.d.ts.map +1 -0
  64. package/dist/components/discover.d.ts +60 -0
  65. package/dist/components/discover.d.ts.map +1 -0
  66. package/dist/components/driver-output.d.ts +20 -0
  67. package/dist/components/driver-output.d.ts.map +1 -0
  68. package/dist/components/driver.d.ts +209 -0
  69. package/dist/components/driver.d.ts.map +1 -0
  70. package/dist/components/index.d.ts +22 -0
  71. package/dist/components/index.d.ts.map +1 -0
  72. package/dist/components/manifest-persistence.d.ts +79 -0
  73. package/dist/components/manifest-persistence.d.ts.map +1 -0
  74. package/dist/components/pilots/alb-ecs.pilot.d.ts +27 -0
  75. package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -0
  76. package/dist/components/pilots/dynamodb.pilot.d.ts +22 -0
  77. package/dist/components/pilots/dynamodb.pilot.d.ts.map +1 -0
  78. package/dist/components/pilots/jar-emr.pilot.d.ts +37 -0
  79. package/dist/components/pilots/jar-emr.pilot.d.ts.map +1 -0
  80. package/dist/components/pilots/lambda.pilot.d.ts +35 -0
  81. package/dist/components/pilots/lambda.pilot.d.ts.map +1 -0
  82. package/dist/components/pilots/neo4j-fanout.pilot.d.ts +17 -0
  83. package/dist/components/pilots/neo4j-fanout.pilot.d.ts.map +1 -0
  84. package/dist/components/pilots/project.d.ts +13 -0
  85. package/dist/components/pilots/project.d.ts.map +1 -0
  86. package/dist/components/presets/ecs-fargate.d.ts +59 -0
  87. package/dist/components/presets/ecs-fargate.d.ts.map +1 -0
  88. package/dist/components/presets/index.d.ts +29 -0
  89. package/dist/components/presets/index.d.ts.map +1 -0
  90. package/dist/components/presets/lambda.d.ts +35 -0
  91. package/dist/components/presets/lambda.d.ts.map +1 -0
  92. package/dist/components/presets/single-host-compose.d.ts +43 -0
  93. package/dist/components/presets/single-host-compose.d.ts.map +1 -0
  94. package/dist/components/registry.d.ts +29 -0
  95. package/dist/components/registry.d.ts.map +1 -0
  96. package/dist/components/starter-plugin.d.ts +51 -0
  97. package/dist/components/starter-plugin.d.ts.map +1 -0
  98. package/dist/components/temporal-codegen-loader.d.ts +27 -0
  99. package/dist/components/temporal-codegen-loader.d.ts.map +1 -0
  100. package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts +35 -0
  101. package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts.map +1 -0
  102. package/dist/components/verbs/__tests__/mock-process-runner.d.ts +45 -0
  103. package/dist/components/verbs/__tests__/mock-process-runner.d.ts.map +1 -0
  104. package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts +33 -0
  105. package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts.map +1 -0
  106. package/dist/components/verbs/bom-writer.d.ts +155 -0
  107. package/dist/components/verbs/bom-writer.d.ts.map +1 -0
  108. package/dist/components/verbs/build-archive.d.ts +205 -0
  109. package/dist/components/verbs/build-archive.d.ts.map +1 -0
  110. package/dist/components/verbs/build.d.ts +150 -0
  111. package/dist/components/verbs/build.d.ts.map +1 -0
  112. package/dist/components/verbs/cloud-executor.d.ts +88 -0
  113. package/dist/components/verbs/cloud-executor.d.ts.map +1 -0
  114. package/dist/components/verbs/component-bom.d.ts +103 -0
  115. package/dist/components/verbs/component-bom.d.ts.map +1 -0
  116. package/dist/components/verbs/index.d.ts +49 -0
  117. package/dist/components/verbs/index.d.ts.map +1 -0
  118. package/dist/components/verbs/license-policy.d.ts +56 -0
  119. package/dist/components/verbs/license-policy.d.ts.map +1 -0
  120. package/dist/components/verbs/lockfile-sbom-generator.d.ts +81 -0
  121. package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -0
  122. package/dist/components/verbs/process-runner.d.ts +64 -0
  123. package/dist/components/verbs/process-runner.d.ts.map +1 -0
  124. package/dist/components/verbs/reproducibility.d.ts +80 -0
  125. package/dist/components/verbs/reproducibility.d.ts.map +1 -0
  126. package/dist/components/verbs/sbom-generator.d.ts +157 -0
  127. package/dist/components/verbs/sbom-generator.d.ts.map +1 -0
  128. package/dist/components/verbs/sbom.d.ts +79 -0
  129. package/dist/components/verbs/sbom.d.ts.map +1 -0
  130. package/dist/components/verbs/shell.d.ts +35 -0
  131. package/dist/components/verbs/shell.d.ts.map +1 -0
  132. package/dist/components/verbs/sign.d.ts +222 -0
  133. package/dist/components/verbs/sign.d.ts.map +1 -0
  134. package/dist/components/verbs/stub.d.ts +18 -0
  135. package/dist/components/verbs/stub.d.ts.map +1 -0
  136. package/dist/components/verbs/tool-sbom-generator.d.ts +51 -0
  137. package/dist/components/verbs/tool-sbom-generator.d.ts.map +1 -0
  138. package/dist/components/verbs/verify.d.ts +106 -0
  139. package/dist/components/verbs/verify.d.ts.map +1 -0
  140. package/dist/components/verbs/vex.d.ts +65 -0
  141. package/dist/components/verbs/vex.d.ts.map +1 -0
  142. package/dist/components/verbs/vuln-gate.d.ts +91 -0
  143. package/dist/components/verbs/vuln-gate.d.ts.map +1 -0
  144. package/dist/components/verbs/vuln-scan.d.ts +116 -0
  145. package/dist/components/verbs/vuln-scan.d.ts.map +1 -0
  146. package/dist/components/verbs/wait-verify.d.ts +85 -0
  147. package/dist/components/verbs/wait-verify.d.ts.map +1 -0
  148. package/dist/config.d.ts +205 -0
  149. package/dist/config.d.ts.map +1 -1
  150. package/dist/index.d.ts +4 -0
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/lexicon.d.ts +78 -0
  153. package/dist/lexicon.d.ts.map +1 -1
  154. package/dist/lifecycle/build-ledger-store.d.ts +121 -0
  155. package/dist/lifecycle/build-ledger-store.d.ts.map +1 -0
  156. package/dist/lifecycle/build-ledger.d.ts +225 -0
  157. package/dist/lifecycle/build-ledger.d.ts.map +1 -0
  158. package/dist/lifecycle/git.d.ts +84 -0
  159. package/dist/lifecycle/git.d.ts.map +1 -1
  160. package/dist/lifecycle/index.d.ts +5 -0
  161. package/dist/lifecycle/index.d.ts.map +1 -1
  162. package/dist/lifecycle/oras-referrer-lookup.d.ts +41 -0
  163. package/dist/lifecycle/oras-referrer-lookup.d.ts.map +1 -0
  164. package/dist/lifecycle/release-ledger.d.ts +122 -0
  165. package/dist/lifecycle/release-ledger.d.ts.map +1 -0
  166. package/dist/lifecycle/status.d.ts +150 -0
  167. package/dist/lifecycle/status.d.ts.map +1 -0
  168. package/dist/lint/component-checks.d.ts +95 -0
  169. package/dist/lint/component-checks.d.ts.map +1 -0
  170. package/dist/lint/engine.d.ts +23 -0
  171. package/dist/lint/engine.d.ts.map +1 -1
  172. package/dist/lint/presets/strict.json +8 -1
  173. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts +4 -0
  174. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts.map +1 -0
  175. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts +4 -0
  176. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts.map +1 -0
  177. package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts +4 -0
  178. package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts.map +1 -0
  179. package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts +4 -0
  180. package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts.map +1 -0
  181. package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts +4 -0
  182. package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts.map +1 -0
  183. package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts +10 -0
  184. package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts.map +1 -0
  185. package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts +3 -0
  186. package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts.map +1 -0
  187. package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts +4 -0
  188. package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts.map +1 -0
  189. package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts +4 -0
  190. package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts.map +1 -0
  191. package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts +4 -0
  192. package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts.map +1 -0
  193. package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts +4 -0
  194. package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts.map +1 -0
  195. package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts +4 -0
  196. package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts.map +1 -0
  197. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +4 -0
  198. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -0
  199. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +4 -0
  200. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -0
  201. package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts +4 -0
  202. package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts.map +1 -0
  203. package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts +4 -0
  204. package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts.map +1 -0
  205. package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts +4 -0
  206. package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts.map +1 -0
  207. package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts +4 -0
  208. package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts.map +1 -0
  209. package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts +4 -0
  210. package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts.map +1 -0
  211. package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts +4 -0
  212. package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts.map +1 -0
  213. package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts +3 -0
  214. package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts.map +1 -0
  215. package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts +4 -0
  216. package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts.map +1 -0
  217. package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts +20 -0
  218. package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts.map +1 -0
  219. package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts +28 -0
  220. package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts.map +1 -0
  221. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +39 -0
  222. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -0
  223. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +37 -0
  224. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +1 -0
  225. package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts +43 -0
  226. package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts.map +1 -0
  227. package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts +22 -0
  228. package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts.map +1 -0
  229. package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts +33 -0
  230. package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts.map +1 -0
  231. package/dist/lint/rules/comp/index.d.ts +18 -0
  232. package/dist/lint/rules/comp/index.d.ts.map +1 -0
  233. package/dist/lint/rules/comp/support.d.ts +71 -0
  234. package/dist/lint/rules/comp/support.d.ts.map +1 -0
  235. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  236. package/dist/op/activity-registry.d.ts +11 -6
  237. package/dist/op/activity-registry.d.ts.map +1 -1
  238. package/dist/op/activity-runtime.d.ts +24 -0
  239. package/dist/op/activity-runtime.d.ts.map +1 -0
  240. package/dist/op/builders.d.ts +56 -0
  241. package/dist/op/builders.d.ts.map +1 -1
  242. package/dist/op/index.d.ts +2 -1
  243. package/dist/op/index.d.ts.map +1 -1
  244. package/dist/ownership.d.ts +20 -14
  245. package/dist/ownership.d.ts.map +1 -1
  246. package/package.json +14 -1
  247. package/src/audit/catalog.test.ts +74 -22
  248. package/src/audit/catalog.ts +74 -471
  249. package/src/audit/fetch.test.ts +112 -8
  250. package/src/audit/fetch.ts +53 -8
  251. package/src/audit/proof.ts +4 -2
  252. package/src/audit/report-html.test.ts +14 -7
  253. package/src/audit/report-model.ts +20 -9
  254. package/src/audit/report.test.ts +36 -13
  255. package/src/audit/report.ts +2 -0
  256. package/src/audit/rules-doc.test.ts +8 -6
  257. package/src/audit/rules-doc.ts +13 -5
  258. package/src/cli/commands/audit.ts +23 -18
  259. package/src/cli/commands/component-lint.test.ts +158 -0
  260. package/src/cli/commands/doctor.test.ts +116 -2
  261. package/src/cli/commands/doctor.ts +46 -0
  262. package/src/cli/commands/lexicon-rolling-upgrade.ts +198 -0
  263. package/src/cli/commands/lexicon-surface-diff.test.ts +104 -0
  264. package/src/cli/commands/lexicon-surface-diff.ts +185 -0
  265. package/src/cli/commands/lint.ts +142 -1
  266. package/src/cli/commands/pinned-upgrade.ts +204 -0
  267. package/src/cli/handlers/build.ts +58 -1
  268. package/src/cli/handlers/components.test.ts +504 -0
  269. package/src/cli/handlers/components.ts +404 -0
  270. package/src/cli/handlers/dev.ts +60 -1
  271. package/src/cli/handlers/graph.ts +47 -2
  272. package/src/cli/handlers/misc.ts +45 -1
  273. package/src/cli/handlers/run.test.ts +967 -3
  274. package/src/cli/handlers/run.ts +618 -11
  275. package/src/cli/main.test.ts +66 -0
  276. package/src/cli/main.ts +105 -2
  277. package/src/cli/registry.ts +35 -0
  278. package/src/codegen/fetch.test.ts +84 -3
  279. package/src/codegen/fetch.ts +76 -2
  280. package/src/codegen/lexicon-regen.test.ts +298 -0
  281. package/src/codegen/lexicon-regen.ts +545 -0
  282. package/src/codegen/pinned-upgrade.test.ts +399 -0
  283. package/src/codegen/pinned-upgrade.ts +423 -0
  284. package/src/codegen/rolling-upgrade.test.ts +333 -0
  285. package/src/codegen/rolling-upgrade.ts +320 -0
  286. package/src/codegen/surface-snapshot.test.ts +461 -0
  287. package/src/codegen/surface-snapshot.ts +546 -0
  288. package/src/codegen/version-bump.test.ts +47 -0
  289. package/src/codegen/version-bump.ts +39 -0
  290. package/src/components/README.md +130 -0
  291. package/src/components/SPRAWL-VALIDATION.md +181 -0
  292. package/src/components/__fixtures__/alb-ecs-service.json +51 -0
  293. package/src/components/__fixtures__/dynamodb-infra.json +32 -0
  294. package/src/components/__fixtures__/emr-job-consumer.json +26 -0
  295. package/src/components/__fixtures__/jar-lib-producer.json +16 -0
  296. package/src/components/__fixtures__/lambda-image-processor.json +28 -0
  297. package/src/components/__fixtures__/neo4j-fanout.json +34 -0
  298. package/src/components/__fixtures__/single-host-compose.json +29 -0
  299. package/src/components/auto-release.test.ts +217 -0
  300. package/src/components/auto-release.ts +216 -0
  301. package/src/components/builders.test.ts +45 -0
  302. package/src/components/builders.ts +71 -0
  303. package/src/components/capability-plugin-loader.ts +218 -0
  304. package/src/components/capability-plugin-schema.ts +56 -0
  305. package/src/components/capability-plugin.test.ts +235 -0
  306. package/src/components/capability-plugin.ts +108 -0
  307. package/src/components/capability.ts +117 -0
  308. package/src/components/cli-support.test.ts +579 -0
  309. package/src/components/cli-support.ts +481 -0
  310. package/src/components/component-schema.test.ts +265 -0
  311. package/src/components/component.schema.json +235 -0
  312. package/src/components/component.test.ts +251 -0
  313. package/src/components/component.ts +255 -0
  314. package/src/components/config-defaults.test.ts +263 -0
  315. package/src/components/config-defaults.ts +145 -0
  316. package/src/components/discover.test.ts +352 -0
  317. package/src/components/discover.ts +167 -0
  318. package/src/components/driver-output.ts +71 -0
  319. package/src/components/driver.test.ts +638 -0
  320. package/src/components/driver.ts +690 -0
  321. package/src/components/index.ts +113 -0
  322. package/src/components/manifest-persistence.test.ts +152 -0
  323. package/src/components/manifest-persistence.ts +164 -0
  324. package/src/components/pilots/README.md +33 -0
  325. package/src/components/pilots/alb-ecs.pilot.ts +63 -0
  326. package/src/components/pilots/dynamodb.pilot.ts +59 -0
  327. package/src/components/pilots/jar-emr.pilot.ts +69 -0
  328. package/src/components/pilots/lambda.pilot.ts +55 -0
  329. package/src/components/pilots/neo4j-fanout.pilot.ts +92 -0
  330. package/src/components/pilots/pilots-lint-clean.test.ts +78 -0
  331. package/src/components/pilots/pilots.test.ts +126 -0
  332. package/src/components/pilots/project.ts +13 -0
  333. package/src/components/presets/comp007-reduction.test.ts +122 -0
  334. package/src/components/presets/ecs-fargate.test.ts +84 -0
  335. package/src/components/presets/ecs-fargate.ts +103 -0
  336. package/src/components/presets/index.ts +32 -0
  337. package/src/components/presets/lambda.test.ts +35 -0
  338. package/src/components/presets/lambda.ts +53 -0
  339. package/src/components/presets/single-host-compose.test.ts +65 -0
  340. package/src/components/presets/single-host-compose.ts +64 -0
  341. package/src/components/registry.test.ts +112 -0
  342. package/src/components/registry.ts +37 -0
  343. package/src/components/starter-plugin.ts +105 -0
  344. package/src/components/temporal-codegen-loader.ts +41 -0
  345. package/src/components/verbs/__fixtures__/package-lock.json +45 -0
  346. package/src/components/verbs/__fixtures__/pom.xml +37 -0
  347. package/src/components/verbs/__fixtures__/schemas/cyclonedx-1.5.schema.json +3800 -0
  348. package/src/components/verbs/__fixtures__/schemas/cyclonedx-spdx-license.schema.json +621 -0
  349. package/src/components/verbs/__fixtures__/schemas/jsf-0.82.schema.json +240 -0
  350. package/src/components/verbs/__fixtures__/schemas/spdx-2.3.schema.json +740 -0
  351. package/src/components/verbs/__tests__/mock-cloud-executor.ts +102 -0
  352. package/src/components/verbs/__tests__/mock-process-runner.ts +79 -0
  353. package/src/components/verbs/__tests__/mock-sbom-generator.ts +85 -0
  354. package/src/components/verbs/bom-writer.test.ts +331 -0
  355. package/src/components/verbs/bom-writer.ts +386 -0
  356. package/src/components/verbs/build-archive.test.ts +251 -0
  357. package/src/components/verbs/build-archive.ts +296 -0
  358. package/src/components/verbs/build.test.ts +222 -0
  359. package/src/components/verbs/build.ts +252 -0
  360. package/src/components/verbs/cloud-executor.ts +181 -0
  361. package/src/components/verbs/component-bom.test.ts +219 -0
  362. package/src/components/verbs/component-bom.ts +189 -0
  363. package/src/components/verbs/index.ts +49 -0
  364. package/src/components/verbs/license-policy.ts +115 -0
  365. package/src/components/verbs/lockfile-sbom-generator.test.ts +195 -0
  366. package/src/components/verbs/lockfile-sbom-generator.ts +236 -0
  367. package/src/components/verbs/process-runner.test.ts +79 -0
  368. package/src/components/verbs/process-runner.ts +113 -0
  369. package/src/components/verbs/reproducibility.test.ts +24 -0
  370. package/src/components/verbs/reproducibility.ts +100 -0
  371. package/src/components/verbs/sbom-generator.test.ts +96 -0
  372. package/src/components/verbs/sbom-generator.ts +209 -0
  373. package/src/components/verbs/sbom.test.ts +184 -0
  374. package/src/components/verbs/sbom.ts +152 -0
  375. package/src/components/verbs/shell.test.ts +36 -0
  376. package/src/components/verbs/shell.ts +51 -0
  377. package/src/components/verbs/sign.test.ts +210 -0
  378. package/src/components/verbs/sign.ts +350 -0
  379. package/src/components/verbs/stub.ts +34 -0
  380. package/src/components/verbs/tool-sbom-generator.test.ts +194 -0
  381. package/src/components/verbs/tool-sbom-generator.ts +192 -0
  382. package/src/components/verbs/verify.test.ts +159 -0
  383. package/src/components/verbs/verify.ts +179 -0
  384. package/src/components/verbs/vex.ts +182 -0
  385. package/src/components/verbs/vuln-gate.test.ts +201 -0
  386. package/src/components/verbs/vuln-gate.ts +170 -0
  387. package/src/components/verbs/vuln-scan.test.ts +145 -0
  388. package/src/components/verbs/vuln-scan.ts +266 -0
  389. package/src/components/verbs/wait-verify.test.ts +111 -0
  390. package/src/components/verbs/wait-verify.ts +171 -0
  391. package/src/config.test.ts +74 -1
  392. package/src/config.ts +219 -0
  393. package/src/index.ts +32 -0
  394. package/src/lexicon.ts +88 -0
  395. package/src/lifecycle/build-ledger-store.test.ts +217 -0
  396. package/src/lifecycle/build-ledger-store.ts +165 -0
  397. package/src/lifecycle/build-ledger.test.ts +349 -0
  398. package/src/lifecycle/build-ledger.ts +305 -0
  399. package/src/lifecycle/git.test.ts +118 -0
  400. package/src/lifecycle/git.ts +149 -14
  401. package/src/lifecycle/index.ts +5 -0
  402. package/src/lifecycle/oras-referrer-lookup.test.ts +116 -0
  403. package/src/lifecycle/oras-referrer-lookup.ts +110 -0
  404. package/src/lifecycle/release-ledger.test.ts +209 -0
  405. package/src/lifecycle/release-ledger.ts +185 -0
  406. package/src/lifecycle/status.test.ts +330 -0
  407. package/src/lifecycle/status.ts +278 -0
  408. package/src/lint/component-checks.ts +148 -0
  409. package/src/lint/config.test.ts +21 -0
  410. package/src/lint/engine.ts +11 -2
  411. package/src/lint/presets/relaxed.json +8 -1
  412. package/src/lint/presets/strict.json +8 -1
  413. package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.ts +9 -0
  414. package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.ts +9 -0
  415. package/src/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.ts +11 -0
  416. package/src/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.ts +13 -0
  417. package/src/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.ts +11 -0
  418. package/src/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.ts +27 -0
  419. package/src/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.ts +10 -0
  420. package/src/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.ts +23 -0
  421. package/src/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.ts +10 -0
  422. package/src/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.ts +19 -0
  423. package/src/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.ts +10 -0
  424. package/src/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.ts +20 -0
  425. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +23 -0
  426. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +10 -0
  427. package/src/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.ts +14 -0
  428. package/src/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.ts +18 -0
  429. package/src/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.ts +10 -0
  430. package/src/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.ts +14 -0
  431. package/src/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.ts +13 -0
  432. package/src/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.ts +13 -0
  433. package/src/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.ts +12 -0
  434. package/src/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.ts +18 -0
  435. package/src/lint/rules/comp/comp.test.ts +378 -0
  436. package/src/lint/rules/comp/comp001-publishes-never-applies.ts +78 -0
  437. package/src/lint/rules/comp/comp002-dangling-wiring-ref.ts +94 -0
  438. package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +115 -0
  439. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +66 -0
  440. package/src/lint/rules/comp/comp005-capability-kind-is-noun.ts +113 -0
  441. package/src/lint/rules/comp/comp006-shell-needs-reason.ts +55 -0
  442. package/src/lint/rules/comp/comp007-composition-sprawl.ts +71 -0
  443. package/src/lint/rules/comp/index.ts +37 -0
  444. package/src/lint/rules/comp/support.ts +173 -0
  445. package/src/lsp/lexicon-providers.test.ts +55 -0
  446. package/src/lsp/lexicon-providers.ts +13 -3
  447. package/src/op/activity-registry.test.ts +38 -0
  448. package/src/op/activity-registry.ts +31 -12
  449. package/src/op/activity-runtime.ts +73 -0
  450. package/src/op/builders.ts +92 -0
  451. package/src/op/index.ts +3 -1
  452. package/src/ownership.test.ts +24 -26
  453. package/src/ownership.ts +26 -38
@@ -0,0 +1,352 @@
1
+ /**
2
+ * Discovery tests for the typed `Component` authoring form (#560, epic #551).
3
+ *
4
+ * Mirrors `../discovery/index.test.ts`'s style (write real `.ts` fixture
5
+ * files to a temp dir, run the discovery entrypoint, assert on the returned
6
+ * map) rather than `../op/discover.test.ts`'s (which discovers from the git
7
+ * root against real example files) since `discoverComponents`, like
8
+ * `discover()`, takes an explicit path rather than always walking from the
9
+ * repo root.
10
+ */
11
+
12
+ import { describe, test, expect, beforeEach, afterEach } from "vitest";
13
+ import { mkdir, writeFile, rm } from "node:fs/promises";
14
+ import { join } from "node:path";
15
+ import { tmpdir } from "node:os";
16
+ import { discoverComponents } from "./discover";
17
+
18
+ describe("discoverComponents", () => {
19
+ let testDir: string;
20
+
21
+ beforeEach(async () => {
22
+ testDir = join(tmpdir(), `chant-component-discover-test-${Date.now()}-${Math.random()}`);
23
+ await mkdir(testDir, { recursive: true });
24
+ });
25
+
26
+ afterEach(async () => {
27
+ await rm(testDir, { recursive: true, force: true });
28
+ });
29
+
30
+ test("returns empty result for an empty directory", async () => {
31
+ const result = await discoverComponents(testDir);
32
+ expect(result.components.size).toBe(0);
33
+ expect(result.sourceFiles).toEqual([]);
34
+ expect(result.errors).toEqual([]);
35
+ });
36
+
37
+ test("discovers a component from a single *.component.ts file", async () => {
38
+ await writeFile(
39
+ join(testDir, "search.component.ts"),
40
+ `
41
+ export const searchService = {
42
+ name: "search-service",
43
+ dependsOn: [],
44
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell", command: "echo ok" }] }],
45
+ };
46
+ `,
47
+ );
48
+
49
+ const result = await discoverComponents(testDir);
50
+
51
+ expect(result.errors).toEqual([]);
52
+ expect(result.components.size).toBe(1);
53
+ expect(result.components.has("search-service")).toBe(true);
54
+ expect(result.components.get("search-service")?.exportName).toBe("searchService");
55
+ expect(result.sourceFiles).toHaveLength(1);
56
+ expect(result.sourceFiles[0]).toMatch(/search\.component\.ts$/);
57
+ });
58
+
59
+ test("discovers multiple components across multiple files, keyed by component.name not export name", async () => {
60
+ await writeFile(
61
+ join(testDir, "a.component.ts"),
62
+ `
63
+ export const foo = {
64
+ name: "svc-a",
65
+ dependsOn: [],
66
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
67
+ };
68
+ `,
69
+ );
70
+ await writeFile(
71
+ join(testDir, "b.component.ts"),
72
+ `
73
+ export const bar = {
74
+ name: "svc-b",
75
+ dependsOn: ["svc-a"],
76
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
77
+ };
78
+ `,
79
+ );
80
+
81
+ const result = await discoverComponents(testDir);
82
+
83
+ expect(result.errors).toEqual([]);
84
+ expect(result.components.size).toBe(2);
85
+ expect([...result.components.keys()].sort()).toEqual(["svc-a", "svc-b"]);
86
+ expect(result.sourceFiles).toHaveLength(2);
87
+ });
88
+
89
+ test("discovers multiple components exported from one file (any export name, not just default)", async () => {
90
+ await writeFile(
91
+ join(testDir, "multi.component.ts"),
92
+ `
93
+ export const first = {
94
+ name: "first-comp",
95
+ dependsOn: [],
96
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
97
+ };
98
+ export const second = {
99
+ name: "second-comp",
100
+ dependsOn: [],
101
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
102
+ };
103
+ `,
104
+ );
105
+
106
+ const result = await discoverComponents(testDir);
107
+
108
+ expect(result.errors).toEqual([]);
109
+ expect(result.components.size).toBe(2);
110
+ expect(result.components.has("first-comp")).toBe(true);
111
+ expect(result.components.has("second-comp")).toBe(true);
112
+ });
113
+
114
+ test("ignores non-.component.ts files, even if they export a Component-shaped value", async () => {
115
+ await writeFile(
116
+ join(testDir, "not-a-component.ts"),
117
+ `
118
+ export const sneaky = {
119
+ name: "sneaky",
120
+ dependsOn: [],
121
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
122
+ };
123
+ `,
124
+ );
125
+
126
+ const result = await discoverComponents(testDir);
127
+
128
+ expect(result.components.size).toBe(0);
129
+ expect(result.sourceFiles).toEqual([]);
130
+ });
131
+
132
+ test("excludes .test.component.ts / .spec.component.ts files", async () => {
133
+ await writeFile(
134
+ join(testDir, "real.component.ts"),
135
+ `
136
+ export const real = {
137
+ name: "real",
138
+ dependsOn: [],
139
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
140
+ };
141
+ `,
142
+ );
143
+ await writeFile(
144
+ join(testDir, "fixture.test.component.ts"),
145
+ `
146
+ export const testOnly = {
147
+ name: "test-only",
148
+ dependsOn: [],
149
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
150
+ };
151
+ `,
152
+ );
153
+
154
+ const result = await discoverComponents(testDir);
155
+
156
+ expect(result.components.size).toBe(1);
157
+ expect(result.components.has("real")).toBe(true);
158
+ expect(result.components.has("test-only")).toBe(false);
159
+ expect(result.sourceFiles).toHaveLength(1);
160
+ });
161
+
162
+ test("ignores non-Component-shaped exports from a *.component.ts file", async () => {
163
+ await writeFile(
164
+ join(testDir, "mixed.component.ts"),
165
+ `
166
+ export const realComponent = {
167
+ name: "real-component",
168
+ dependsOn: [],
169
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
170
+ };
171
+ export const helper = { foo: "bar" };
172
+ export const CONST = 42;
173
+ `,
174
+ );
175
+
176
+ const result = await discoverComponents(testDir);
177
+
178
+ expect(result.components.size).toBe(1);
179
+ expect(result.components.has("real-component")).toBe(true);
180
+ });
181
+
182
+ test("recurses into nested directories", async () => {
183
+ const subDir = join(testDir, "services", "search");
184
+ await mkdir(subDir, { recursive: true });
185
+ await writeFile(
186
+ join(subDir, "search.component.ts"),
187
+ `
188
+ export const searchService = {
189
+ name: "search-service",
190
+ dependsOn: [],
191
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
192
+ };
193
+ `,
194
+ );
195
+
196
+ const result = await discoverComponents(testDir);
197
+
198
+ expect(result.components.size).toBe(1);
199
+ expect(result.components.has("search-service")).toBe(true);
200
+ expect(result.sourceFiles[0]).toMatch(/search\.component\.ts$/);
201
+ });
202
+
203
+ test("skips node_modules", async () => {
204
+ const nm = join(testDir, "node_modules", "some-pkg");
205
+ await mkdir(nm, { recursive: true });
206
+ await writeFile(
207
+ join(nm, "vendored.component.ts"),
208
+ `
209
+ export const vendored = {
210
+ name: "vendored",
211
+ dependsOn: [],
212
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
213
+ };
214
+ `,
215
+ );
216
+
217
+ const result = await discoverComponents(testDir);
218
+
219
+ expect(result.components.size).toBe(0);
220
+ expect(result.sourceFiles).toEqual([]);
221
+ });
222
+
223
+ test("reports a duplicate component name across two files as a resolution error", async () => {
224
+ await writeFile(
225
+ join(testDir, "one.component.ts"),
226
+ `
227
+ export const svc = {
228
+ name: "dup",
229
+ dependsOn: [],
230
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
231
+ };
232
+ `,
233
+ );
234
+ await writeFile(
235
+ join(testDir, "two.component.ts"),
236
+ `
237
+ export const svcAgain = {
238
+ name: "dup",
239
+ dependsOn: [],
240
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
241
+ };
242
+ `,
243
+ );
244
+
245
+ const result = await discoverComponents(testDir);
246
+
247
+ expect(result.errors.length).toBeGreaterThan(0);
248
+ expect(result.errors[0]?.type).toBe("resolution");
249
+ expect(result.errors[0]?.message).toMatch(/Duplicate component name "dup"/);
250
+ // The first one found is kept; discovery continues rather than aborting.
251
+ expect(result.components.has("dup")).toBe(true);
252
+ });
253
+
254
+ test("collects an import error (syntax error) and continues processing other files", async () => {
255
+ await writeFile(
256
+ join(testDir, "good.component.ts"),
257
+ `
258
+ export const good = {
259
+ name: "good",
260
+ dependsOn: [],
261
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
262
+ };
263
+ `,
264
+ );
265
+ await writeFile(
266
+ join(testDir, "bad.component.ts"),
267
+ `
268
+ export const bad = {
269
+ name: "bad"
270
+ dependsOn: [],
271
+ };
272
+ `,
273
+ );
274
+
275
+ const result = await discoverComponents(testDir);
276
+
277
+ expect(result.components.has("good")).toBe(true);
278
+ expect(result.errors.some((e) => e.type === "import")).toBe(true);
279
+ });
280
+
281
+ test("the first chant.config.ts encountered becomes the source root and is still scanned", async () => {
282
+ // Matches findInfraFiles's own rule: the *first* config directory found
283
+ // becomes `sourceRoot` and is descended into, same as any other
284
+ // directory — the skip only applies to a *second* nested config found
285
+ // after that (see the next test).
286
+ const childDir = join(testDir, "child-project");
287
+ await mkdir(childDir, { recursive: true });
288
+ await writeFile(join(childDir, "chant.config.ts"), `export default {};`);
289
+ await writeFile(
290
+ join(childDir, "child.component.ts"),
291
+ `
292
+ export const childSvc = {
293
+ name: "child-svc",
294
+ dependsOn: [],
295
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
296
+ };
297
+ `,
298
+ );
299
+ await writeFile(
300
+ join(testDir, "root.component.ts"),
301
+ `
302
+ export const rootSvc = {
303
+ name: "root-svc",
304
+ dependsOn: [],
305
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
306
+ };
307
+ `,
308
+ );
309
+
310
+ const result = await discoverComponents(testDir);
311
+
312
+ expect(result.components.has("root-svc")).toBe(true);
313
+ expect(result.components.has("child-svc")).toBe(true);
314
+ });
315
+
316
+ test("a second nested chant.config.ts (a true child project) is not descended into", async () => {
317
+ const firstConfigDir = join(testDir, "first-config");
318
+ const nestedChildProject = join(firstConfigDir, "nested-child-project");
319
+ await mkdir(nestedChildProject, { recursive: true });
320
+ await writeFile(join(firstConfigDir, "chant.config.ts"), `export default {};`);
321
+ await writeFile(join(nestedChildProject, "chant.config.ts"), `export default {};`);
322
+ await writeFile(
323
+ join(firstConfigDir, "first.component.ts"),
324
+ `
325
+ export const firstSvc = {
326
+ name: "first-svc",
327
+ dependsOn: [],
328
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
329
+ };
330
+ `,
331
+ );
332
+ await writeFile(
333
+ join(nestedChildProject, "nested.component.ts"),
334
+ `
335
+ export const nestedSvc = {
336
+ name: "nested-svc",
337
+ dependsOn: [],
338
+ deploy: [{ phase: "Apply", steps: [{ kind: "shell" }] }],
339
+ };
340
+ `,
341
+ );
342
+
343
+ const result = await discoverComponents(testDir);
344
+
345
+ // first-config/ has no config above it, so it becomes the source root
346
+ // and is scanned; nested-child-project/'s own chant.config.ts is the
347
+ // *second* config encountered, so it is treated as a separate project
348
+ // scope and not descended into.
349
+ expect(result.components.has("first-svc")).toBe(true);
350
+ expect(result.components.has("nested-svc")).toBe(false);
351
+ });
352
+ });
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Component discovery — Phase 2 (#560, epic #551).
3
+ *
4
+ * Mirrors chant's existing declarable discovery
5
+ * (`../discovery/index.ts`'s `discover()`, built on `findInfraFiles`) as
6
+ * closely as a structurally distinct type allows, and borrows its
7
+ * file-suffix convention from Op discovery
8
+ * (`../op/discover.ts`'s `discoverOps()`, `*.op.ts`): a component is any
9
+ * `Component`-shaped value (see `./component.ts`'s `isComponent`) exported
10
+ * (by any export name, not just `default`) from a `*.component.ts` file.
11
+ *
12
+ * Why a dedicated convention rather than folding into `discover()`:
13
+ * `Declarable` (`../declarable.ts`) is a lexicon-resource marker —
14
+ * `{ lexicon, entityType, kind, [DECLARABLE_MARKER]: true }` — and a
15
+ * `Component` has none of that shape (no `lexicon`, and its `deploy`
16
+ * composition is data the capability driver interprets, not a serializable
17
+ * lexicon resource). Reusing `collectEntities`/`isDeclarable` would mean
18
+ * either stretching the `Declarable` marker to cover an unrelated shape or
19
+ * silently ignoring components during the existing walk. A parallel,
20
+ * purpose-built collector keeps both conventions simple: `*.ts` + the
21
+ * `Declarable` marker for resources, `*.component.ts` + the `Component`
22
+ * shape for components — exactly how `*.op.ts` is already a parallel,
23
+ * purpose-built convention for Ops.
24
+ *
25
+ * Filename convention (`*.component.ts`) is documented here rather than
26
+ * inferred from export shape alone so a project can `grep` for its
27
+ * components the same way it can for `*.op.ts` files, and so discovery does
28
+ * not have to import (and risk side-effecting) every `.ts` file in the
29
+ * project looking for stray `Component`-shaped objects.
30
+ */
31
+
32
+ import { readdir } from "node:fs/promises";
33
+ import { join, resolve } from "node:path";
34
+ import { pathToFileURL } from "node:url";
35
+ import { existsSync } from "node:fs";
36
+ import { DiscoveryError } from "../errors";
37
+ import { isComponent, type Component } from "./component";
38
+
39
+ /** One discovered component, paired with the file it was exported from. */
40
+ export interface DiscoveredComponent {
41
+ component: Component;
42
+ /** The export name the component was found under (informational; components are keyed by their own `.name`, not this). */
43
+ exportName: string;
44
+ filePath: string;
45
+ }
46
+
47
+ export interface ComponentDiscoveryResult {
48
+ /** Discovered components, keyed by `component.name` (the schema identity), not by export name or file. */
49
+ components: Map<string, DiscoveredComponent>;
50
+ /** Every `*.component.ts` file that was scanned. */
51
+ sourceFiles: string[];
52
+ errors: DiscoveryError[];
53
+ }
54
+
55
+ /**
56
+ * Recursively find every `*.component.ts` file under `path`, the same
57
+ * child-project boundary rule `findInfraFiles` (`../discovery/files.ts`)
58
+ * uses: a subdirectory with its own `chant.config.ts` is a separate scope
59
+ * once the outer project's own source root has been established.
60
+ */
61
+ async function findComponentFiles(path: string): Promise<string[]> {
62
+ const files: string[] = [];
63
+ let sourceRoot: string | null = null;
64
+
65
+ async function scanDirectory(dir: string): Promise<void> {
66
+ let entries;
67
+ try {
68
+ entries = await readdir(dir, { withFileTypes: true });
69
+ } catch {
70
+ return;
71
+ }
72
+
73
+ for (const entry of entries) {
74
+ const fullPath = join(dir, entry.name);
75
+
76
+ if (entry.isDirectory()) {
77
+ // Skip dependency and tool/VCS directories: component sources never live
78
+ // in them, and descending in is both wasted work and a correctness bug —
79
+ // e.g. a CI runner like gitlab-ci-local stages a copy of the project
80
+ // under `.gitlab-ci-local/`, which discovery would otherwise pick up as
81
+ // duplicate component names alongside the originals.
82
+ if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
83
+
84
+ const configPath = join(fullPath, "chant.config.ts");
85
+ if (existsSync(configPath)) {
86
+ if (sourceRoot === null) {
87
+ sourceRoot = fullPath;
88
+ } else {
89
+ continue;
90
+ }
91
+ }
92
+ await scanDirectory(fullPath);
93
+ } else if (
94
+ entry.isFile() &&
95
+ entry.name.endsWith(".component.ts") &&
96
+ !entry.name.endsWith(".test.component.ts") &&
97
+ !entry.name.endsWith(".spec.component.ts")
98
+ ) {
99
+ files.push(fullPath);
100
+ }
101
+ }
102
+ }
103
+
104
+ await scanDirectory(path);
105
+ return files;
106
+ }
107
+
108
+ /**
109
+ * Discover every `Component` declared under `path`: scan for `*.component.ts`
110
+ * files, import each, and collect every export whose value satisfies
111
+ * `isComponent` (any export name — not just `default` — so a file may
112
+ * declare several related components, matching how a resource file can
113
+ * export several `Declarable`s). Duplicate `component.name` across files (or
114
+ * within one file) is a discovery error, matching `collectEntities`'s
115
+ * duplicate-export handling and `discoverOps`'s duplicate-Op-name handling —
116
+ * except the identity compared is the schema-level `name` field, since two
117
+ * different export bindings could otherwise declare the same component name
118
+ * and silently collide at deploy time.
119
+ */
120
+ export async function discoverComponents(path: string): Promise<ComponentDiscoveryResult> {
121
+ const errors: DiscoveryError[] = [];
122
+ const sourceFiles = await findComponentFiles(path);
123
+ const components = new Map<string, DiscoveredComponent>();
124
+ const nameToFile = new Map<string, string>();
125
+
126
+ for (const filePath of sourceFiles) {
127
+ let exports: Record<string, unknown>;
128
+ try {
129
+ // Resolve to an absolute file:// URL before importing. A relative
130
+ // `filePath` (e.g. from `chant build --components --generate`, which passes
131
+ // a bare "." path) would otherwise be treated by `import()` as a bare
132
+ // package specifier and fail with "Cannot find package 'x.component.ts'".
133
+ exports = await import(pathToFileURL(resolve(filePath)).href);
134
+ } catch (err) {
135
+ errors.push(new DiscoveryError(filePath, err instanceof Error ? err.message : String(err), "import"));
136
+ continue;
137
+ }
138
+
139
+ for (const [exportName, value] of Object.entries(exports)) {
140
+ if (!isComponent(value)) continue;
141
+
142
+ const existingFile = nameToFile.get(value.name);
143
+ if (existingFile && existingFile !== filePath) {
144
+ errors.push(
145
+ new DiscoveryError(
146
+ filePath,
147
+ `Duplicate component name "${value.name}" in ${filePath} and ${existingFile}`,
148
+ "resolution",
149
+ ),
150
+ );
151
+ continue;
152
+ }
153
+ const existing = components.get(value.name);
154
+ if (existing && existing.component !== value) {
155
+ errors.push(
156
+ new DiscoveryError(filePath, `Duplicate component name "${value.name}" in ${filePath}`, "resolution"),
157
+ );
158
+ continue;
159
+ }
160
+
161
+ nameToFile.set(value.name, filePath);
162
+ components.set(value.name, { component: value, exportName, filePath });
163
+ }
164
+ }
165
+
166
+ return { components, sourceFiles, errors };
167
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Renderers for `chant run --components` (#585) — the interpret driver's CLI
3
+ * output. Mirrors `../op/local-output.ts`'s `renderHuman`/`renderJson` pair
4
+ * for Ops: both consume the same `DriverRunResult` (`./driver.ts`);
5
+ * `renderDriverHuman` is the default (logs to stderr), `renderDriverJson`
6
+ * prints the machine-readable result as JSON on stdout and nothing else.
7
+ */
8
+
9
+ import type { DriverRunResult, DriverStepRecord } from "./driver";
10
+
11
+ type Writer = (line: string) => void;
12
+
13
+ const stderr: Writer = (line) => process.stderr.write(line + "\n");
14
+ const stdout: Writer = (line) => process.stdout.write(line + "\n");
15
+
16
+ function formatDuration(ms: number): string {
17
+ return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;
18
+ }
19
+
20
+ function statusMark(status: DriverStepRecord["status"]): string {
21
+ switch (status) {
22
+ case "ok":
23
+ return "✓";
24
+ case "fail":
25
+ return "✗";
26
+ case "rolled-back":
27
+ return "↺";
28
+ default:
29
+ return "•";
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Render an interpret-driver run result as human-readable progress. Defaults
35
+ * to stderr so stdout stays clean for piping (the `--json` renderer owns
36
+ * stdout). Groups records by component, then by phase, matching the driver's
37
+ * own wave/phase/step nesting.
38
+ */
39
+ export function renderDriverHuman(result: DriverRunResult, write: Writer = stderr): void {
40
+ for (const componentResult of result.results) {
41
+ write(`[component] ${componentResult.component}`);
42
+ let currentPhase: string | undefined;
43
+ for (const record of componentResult.records) {
44
+ if (record.phase !== currentPhase) {
45
+ currentPhase = record.phase;
46
+ write(` [phase] ${currentPhase}`);
47
+ }
48
+ const mark = statusMark(record.status);
49
+ if (record.status === "skipped") {
50
+ write(` ${mark} ${record.kind} skipped`);
51
+ } else {
52
+ write(` ${mark} ${record.kind} ${formatDuration(record.durationMs)}`);
53
+ }
54
+ if (record.error) {
55
+ write(` ${record.error}`);
56
+ }
57
+ }
58
+ write(componentResult.ok ? ` component "${componentResult.component}" completed` : ` component "${componentResult.component}" failed`);
59
+ }
60
+
61
+ if (result.ok) {
62
+ write(`interpret run completed (${result.order.length} component(s))`);
63
+ } else {
64
+ write(`interpret run failed at component "${result.failedComponent}"`);
65
+ }
66
+ }
67
+
68
+ /** Render an interpret-driver run result as JSON on stdout (and nothing else on stdout). */
69
+ export function renderDriverJson(result: DriverRunResult, write: Writer = stdout): void {
70
+ write(JSON.stringify(result));
71
+ }