@starci/hfs 4.2.17 → 4.4.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 (564) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +7 -0
  3. package/db/migrate.mjs +361 -0
  4. package/db/post-cutover.mjs +136 -0
  5. package/emit/db-types.mjs +10 -1
  6. package/lint/run.mjs +1 -1
  7. package/package.json +4 -1
  8. package/release/gate.mjs +298 -0
  9. package/release/index.mjs +103 -0
  10. package/release/push.mjs +110 -0
  11. package/runtime/knowledge/hfs/canon-pins.yaml +11 -11
  12. package/runtime/knowledge/hfs/fe-stack.yaml +22 -1
  13. package/runtime/knowledge/hfs/rules.yaml +3437 -160
  14. package/runtime/knowledge/hfs/slots.yaml +430 -92
  15. package/runtime/knowledge/patterns/be/api.yaml +92 -2
  16. package/runtime/knowledge/patterns/be/cli.yaml +37 -9
  17. package/runtime/knowledge/patterns/be/event-bus.yaml +3 -3
  18. package/runtime/knowledge/patterns/be/jobs.yaml +20 -2
  19. package/runtime/knowledge/patterns/be/projections.yaml +4 -2
  20. package/runtime/knowledge/patterns/be/queues.yaml +11 -3
  21. package/runtime/knowledge/patterns/be/reactors.yaml +20 -1
  22. package/runtime/knowledge/patterns/be/realtime.yaml +25 -3
  23. package/runtime/knowledge/patterns/be/saga.yaml +20 -3
  24. package/runtime/knowledge/patterns/be/webhooks.yaml +210 -36
  25. package/runtime/knowledge/patterns/fe/folder.yaml +4 -0
  26. package/runtime/knowledge/sonar-gate.yaml +1 -1
  27. package/runtime/modules/kernel/failure-codes.yaml +3128 -47
  28. package/runtime/modules/models/runtimes.yaml +1 -1
  29. package/runtime/modules/schemas/application-stacks.schema.yaml +663 -0
  30. package/runtime/packages/cli/src/catalog.generated.mjs +14104 -0
  31. package/runtime/packages/cli/src/validate-args.mjs +213 -0
  32. package/runtime/scripts/api/git/add.mjs +5 -0
  33. package/runtime/scripts/api/git/cat-file.mjs +5 -0
  34. package/runtime/scripts/api/git/init.mjs +7 -0
  35. package/runtime/scripts/api/git/lib.mjs +3 -0
  36. package/runtime/scripts/api/git/tag.mjs +5 -0
  37. package/runtime/scripts/api/node/lib.mjs +20 -0
  38. package/runtime/scripts/api/node/run-node.mjs +6 -0
  39. package/runtime/scripts/api/sops/lib.mjs +41 -0
  40. package/runtime/scripts/api/sops/resolve-sops.mjs +7 -0
  41. package/runtime/scripts/api/sops/seal.mjs +1 -1
  42. package/runtime/scripts/api/sops/selected-age-recipient.mjs +7 -0
  43. package/runtime/scripts/api/supabase/database-dump.mjs +5 -0
  44. package/runtime/scripts/api/supabase/database-query.mjs +5 -0
  45. package/runtime/scripts/api/supabase/lib.mjs +44 -0
  46. package/runtime/scripts/api/supabase/migration-repair.mjs +5 -0
  47. package/runtime/scripts/hfs/app-wiring.mjs +201 -0
  48. package/runtime/scripts/hfs/architecture/backend-app-files.mjs +103 -0
  49. package/runtime/scripts/hfs/architecture/backend.mjs +22 -10
  50. package/runtime/scripts/hfs/architecture/background-unowned.mjs +157 -41
  51. package/runtime/scripts/hfs/architecture/canon-clones.mjs +240 -0
  52. package/runtime/scripts/hfs/architecture/canon-jsx-clones.mjs +143 -0
  53. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +6 -3
  54. package/runtime/scripts/hfs/architecture/clones.mjs +17 -8
  55. package/runtime/scripts/hfs/architecture/connection-map.mjs +14 -12
  56. package/runtime/scripts/hfs/architecture/context-map.mjs +25 -14
  57. package/runtime/scripts/hfs/architecture/context-owner.mjs +1 -2
  58. package/runtime/scripts/hfs/architecture/dead-exports-bindings.mjs +149 -0
  59. package/runtime/scripts/hfs/architecture/dead-exports.mjs +23 -50
  60. package/runtime/scripts/hfs/architecture/default-deny.mjs +8 -6
  61. package/runtime/scripts/hfs/architecture/frontend-block-render.mjs +104 -0
  62. package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +30 -11
  63. package/runtime/scripts/hfs/architecture/frontend-world.mjs +1 -1
  64. package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +153 -0
  65. package/runtime/scripts/hfs/architecture/hfs.mjs +3 -3
  66. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +16 -3
  67. package/runtime/scripts/hfs/architecture/machine-ast.mjs +17 -0
  68. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +49 -25
  69. package/runtime/scripts/hfs/architecture/next-data-inference.mjs +176 -0
  70. package/runtime/scripts/hfs/architecture/next-data-symbols.mjs +26 -0
  71. package/runtime/scripts/hfs/architecture/next-data.mjs +31 -23
  72. package/runtime/scripts/hfs/architecture/reachability.mjs +14 -0
  73. package/runtime/scripts/hfs/architecture/register-once.mjs +15 -7
  74. package/runtime/scripts/hfs/architecture/required-files.mjs +2 -1
  75. package/runtime/scripts/hfs/architecture/source-names.mjs +48 -3
  76. package/runtime/scripts/hfs/architecture/staged-consumers.mjs +37 -0
  77. package/runtime/scripts/hfs/architecture/supabase-ast.mjs +214 -17
  78. package/runtime/scripts/hfs/architecture/supabase-be.mjs +1 -6
  79. package/runtime/scripts/hfs/architecture/supabase.mjs +59 -21
  80. package/runtime/scripts/hfs/architecture/surface.mjs +2 -1
  81. package/runtime/scripts/hfs/architecture/tiers.mjs +11 -4
  82. package/runtime/scripts/hfs/architecture/typescript.mjs +1 -8
  83. package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +9 -13
  84. package/runtime/scripts/hfs/check-results.mjs +28 -0
  85. package/runtime/scripts/hfs/check.mjs +63 -72
  86. package/runtime/scripts/hfs/coverage-from.mjs +70 -0
  87. package/runtime/scripts/hfs/db-baseline-verb.mjs +276 -0
  88. package/runtime/scripts/hfs/db-plan-verb.mjs +254 -0
  89. package/runtime/scripts/hfs/db-restamp-verb.mjs +60 -0
  90. package/runtime/scripts/hfs/declaration-shape.mjs +39 -3
  91. package/runtime/scripts/hfs/doc-commands.mjs +367 -0
  92. package/runtime/scripts/hfs/edition-slots.mjs +2 -2
  93. package/runtime/scripts/hfs/example-secrets.mjs +158 -0
  94. package/runtime/scripts/hfs/manifest-shape.mjs +91 -6
  95. package/runtime/scripts/hfs/path-findings.mjs +32 -2
  96. package/runtime/scripts/hfs/refusal-codes.mjs +6 -0
  97. package/runtime/scripts/hfs/rule-catalog.mjs +156 -11
  98. package/runtime/scripts/hfs/rule-params-shape.mjs +22 -10
  99. package/runtime/scripts/hfs/rules/a15-findings.mjs +88 -0
  100. package/runtime/scripts/hfs/rules/app-env-required.mjs +20 -0
  101. package/runtime/scripts/hfs/rules/app-root.mjs +2 -2
  102. package/runtime/scripts/hfs/rules/assets.mjs +79 -0
  103. package/runtime/scripts/hfs/rules/boot-proof.mjs +16 -0
  104. package/runtime/scripts/hfs/rules/canon-copy-drift.mjs +59 -0
  105. package/runtime/scripts/hfs/rules/contract.mjs +66 -5
  106. package/runtime/scripts/hfs/rules/coverage-below.mjs +230 -0
  107. package/runtime/scripts/hfs/rules/database-access.mjs +343 -0
  108. package/runtime/scripts/hfs/rules/database-baseline.mjs +142 -0
  109. package/runtime/scripts/hfs/rules/database-config.mjs +250 -2
  110. package/runtime/scripts/hfs/rules/database-constants.mjs +9 -0
  111. package/runtime/scripts/hfs/rules/database-migrations.mjs +317 -37
  112. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +119 -9
  113. package/runtime/scripts/hfs/rules/database-queue-sources.mjs +176 -0
  114. package/runtime/scripts/hfs/rules/database-queues.mjs +361 -0
  115. package/runtime/scripts/hfs/rules/database-sql.mjs +91 -9
  116. package/runtime/scripts/hfs/rules/database.mjs +11 -2
  117. package/runtime/scripts/hfs/rules/deploy-scan.mjs +192 -0
  118. package/runtime/scripts/hfs/rules/deploy-workflow.mjs +219 -0
  119. package/runtime/scripts/hfs/rules/doc-fresh.mjs +286 -0
  120. package/runtime/scripts/hfs/rules/docker-health.mjs +249 -0
  121. package/runtime/scripts/hfs/rules/docker.mjs +24 -7
  122. package/runtime/scripts/hfs/rules/edition.mjs +11 -10
  123. package/runtime/scripts/hfs/rules/env-parity.mjs +373 -0
  124. package/runtime/scripts/hfs/rules/env-value-shape.mjs +113 -0
  125. package/runtime/scripts/hfs/rules/event-bus.mjs +3 -2
  126. package/runtime/scripts/hfs/rules/event-tables.mjs +100 -0
  127. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +115 -15
  128. package/runtime/scripts/hfs/rules/fe-stack.mjs +57 -0
  129. package/runtime/scripts/hfs/rules/feature-files.mjs +28 -0
  130. package/runtime/scripts/hfs/rules/frontend-tree.mjs +54 -35
  131. package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -2
  132. package/runtime/scripts/hfs/rules/lexicon.mjs +62 -0
  133. package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +89 -0
  134. package/runtime/scripts/hfs/rules/monorepo.mjs +14 -1
  135. package/runtime/scripts/hfs/rules/pipeline.mjs +8 -1
  136. package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -0
  137. package/runtime/scripts/hfs/rules/release-provenance.mjs +211 -0
  138. package/runtime/scripts/hfs/rules/saga-delivery.mjs +66 -0
  139. package/runtime/scripts/hfs/rules/saga.mjs +8 -16
  140. package/runtime/scripts/hfs/rules/script-hygiene.mjs +151 -0
  141. package/runtime/scripts/hfs/rules/secret-custody-only.mjs +65 -0
  142. package/runtime/scripts/hfs/rules/secrets.mjs +26 -0
  143. package/runtime/scripts/hfs/rules/services.mjs +4 -4
  144. package/runtime/scripts/hfs/rules/stack-tooling.mjs +93 -0
  145. package/runtime/scripts/hfs/rules/stacks-deploy.mjs +76 -0
  146. package/runtime/scripts/hfs/rules/stacks-ingress.mjs +87 -0
  147. package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +111 -0
  148. package/runtime/scripts/hfs/rules/stacks-release.mjs +366 -0
  149. package/runtime/scripts/hfs/rules/stacks-runtime.mjs +168 -0
  150. package/runtime/scripts/hfs/rules/stacks-swarm.mjs +417 -0
  151. package/runtime/scripts/hfs/rules/stacks.mjs +13 -8
  152. package/runtime/scripts/hfs/rules/supabase-secrets.mjs +1 -1
  153. package/runtime/scripts/hfs/rules/supply-chain.mjs +167 -0
  154. package/runtime/scripts/hfs/rules/unused-symbol-flow.mjs +101 -0
  155. package/runtime/scripts/hfs/rules/unused-symbol-ledger.mjs +77 -0
  156. package/runtime/scripts/hfs/rules/unused-symbol-translators.mjs +165 -0
  157. package/runtime/scripts/hfs/rules/unused-symbol-values.mjs +103 -0
  158. package/runtime/scripts/hfs/rules/unused-symbol.mjs +320 -0
  159. package/runtime/scripts/hfs/runtime-rules/source-ast.mjs +185 -0
  160. package/runtime/scripts/hfs/runtime-rules/workflow-source.mjs +19 -0
  161. package/runtime/scripts/hfs/sealed-members.mjs +101 -0
  162. package/runtime/scripts/hfs/secret-bootstrap-crypto.mjs +200 -0
  163. package/runtime/scripts/hfs/secret-document.mjs +97 -0
  164. package/runtime/scripts/hfs/secret-edit.mjs +170 -0
  165. package/runtime/scripts/hfs/secret-errors.mjs +20 -0
  166. package/runtime/scripts/hfs/secret-files.mjs +124 -0
  167. package/runtime/scripts/hfs/secret-init.mjs +300 -0
  168. package/runtime/scripts/hfs/secret-output.mjs +100 -0
  169. package/runtime/scripts/hfs/secret-sops.mjs +130 -0
  170. package/runtime/scripts/hfs/secret-store.mjs +163 -0
  171. package/runtime/scripts/hfs/secret.mjs +162 -193
  172. package/runtime/scripts/hfs/slot-imports.mjs +18 -3
  173. package/runtime/scripts/hfs/slot-required.mjs +13 -0
  174. package/runtime/scripts/hfs/slot-semantic-problems.mjs +2 -1
  175. package/runtime/scripts/hfs/slots.mjs +1 -0
  176. package/runtime/scripts/hfs/stack-config-kit.mjs +34 -0
  177. package/runtime/scripts/hfs/stack-deploy-config.mjs +313 -0
  178. package/runtime/scripts/hfs/stack-ingress-config.mjs +237 -0
  179. package/runtime/scripts/hfs/stacks-gate.mjs +414 -0
  180. package/runtime/scripts/hfs/starcistacks-runtime.mjs +61 -0
  181. package/runtime/scripts/hfs/tree-findings.mjs +22 -0
  182. package/runtime/scripts/lib/command-tokens.mjs +126 -0
  183. package/runtime/scripts/lib/compose-values.mjs +11 -0
  184. package/runtime/scripts/lib/deployment-findings.mjs +7 -0
  185. package/runtime/scripts/lib/json-clone.mjs +61 -0
  186. package/runtime/scripts/lib/list.mjs +4 -0
  187. package/runtime/scripts/lib/roots.mjs +59 -0
  188. package/runtime/scripts/lib/runtime-example.mjs +13 -0
  189. package/runtime/scripts/lib/secret-patterns.mjs +60 -5
  190. package/runtime/scripts/lib/sql-ast.mjs +32 -0
  191. package/runtime/scripts/lib/stack-services.mjs +1 -1
  192. package/runtime/scripts/lib/swarm-stack.mjs +126 -0
  193. package/runtime/scripts/lib/trim-end.mjs +8 -0
  194. package/runtime/scripts/lib/ts-analysis.mjs +23 -0
  195. package/runtime/scripts/lib/ts-ast.mjs +27 -2
  196. package/runtime/scripts/lib/workflow-events.mjs +8 -0
  197. package/scaffold/add-app.mjs +77 -59
  198. package/scaffold/add-cli-lite.mjs +24 -17
  199. package/scaffold/add-compose.mjs +79 -0
  200. package/scaffold/add-database-lite.mjs +14 -0
  201. package/scaffold/add-fe-overlay.mjs +122 -0
  202. package/scaffold/add-fe-page.mjs +269 -0
  203. package/scaffold/add-inbox-lite.mjs +87 -124
  204. package/scaffold/add-plan.mjs +183 -0
  205. package/scaffold/add-table.mjs +132 -70
  206. package/scaffold/add.mjs +186 -204
  207. package/scaffold/app.mjs +128 -43
  208. package/scaffold/edition-gate.mjs +3 -3
  209. package/scaffold/image.mjs +2 -2
  210. package/scaffold/lite-exports.mjs +2 -1
  211. package/scaffold/service.mjs +2 -2
  212. package/src/main.mjs +238 -119
  213. package/stack/age-install.mjs +37 -0
  214. package/stack/backup.mjs +100 -0
  215. package/stack/build-args.mjs +42 -0
  216. package/stack/build.mjs +173 -0
  217. package/stack/cli.mjs +130 -0
  218. package/stack/config.mjs +57 -0
  219. package/stack/context.mjs +57 -0
  220. package/stack/converge.mjs +107 -0
  221. package/stack/deploy.mjs +301 -0
  222. package/stack/digest.mjs +16 -0
  223. package/stack/docker-transport.mjs +53 -0
  224. package/stack/edge-alias.mjs +118 -0
  225. package/stack/errors.mjs +21 -0
  226. package/stack/image-scan.mjs +152 -0
  227. package/stack/ingress-create.mjs +114 -0
  228. package/stack/ingress-publish.mjs +273 -0
  229. package/stack/ingress.mjs +706 -0
  230. package/stack/jobs.mjs +51 -0
  231. package/stack/live-restore.mjs +177 -0
  232. package/stack/prepare.mjs +266 -0
  233. package/stack/private-file.mjs +22 -0
  234. package/stack/provision.mjs +90 -0
  235. package/stack/prune.mjs +46 -0
  236. package/stack/release-store.mjs +87 -0
  237. package/stack/remote.mjs +205 -0
  238. package/stack/render.mjs +149 -0
  239. package/stack/retire.mjs +47 -0
  240. package/stack/sealed.mjs +149 -0
  241. package/stack/task.mjs +42 -0
  242. package/stack/verify.mjs +99 -0
  243. package/sync/ci-commands.mjs +13 -0
  244. package/sync/index.mjs +127 -64
  245. package/sync/stack-images.mjs +127 -0
  246. package/templates/app/ci-workflows/github/workflows/ci.yml +5 -1
  247. package/templates/app/ci-workflows/github/workflows/images.yml +15 -3
  248. package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +148 -0
  249. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +7 -3
  250. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +38 -6
  251. package/templates/app/ci-workflows-lite/github/workflows/images.yml +14 -2
  252. package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +148 -0
  253. package/templates/app/codegen-graphql.mjs.partial +13 -2
  254. package/templates/app/codegen-lite/scripts/codegen.mjs +6 -1
  255. package/templates/app/codegen-prelude.mjs.partial +8 -0
  256. package/templates/app/gitignore +4 -0
  257. package/templates/app/gitignore-lite +4 -0
  258. package/templates/app/hooks/husky/pre-commit +7 -12
  259. package/templates/app/package-scripts/package.json +1 -0
  260. package/templates/app/package-scripts/supabase.json +7 -0
  261. package/templates/app/package-scripts-lite/package.json +1 -7
  262. package/templates/app/skeleton/.editorconfig +1 -0
  263. package/templates/app/skeleton/.gitattributes +7 -1
  264. package/templates/app/skeleton/.github/dependabot.yml +17 -0
  265. package/templates/app/skeleton/.nvmrc +1 -1
  266. package/templates/app/skeleton/.sops.yaml +1 -1
  267. package/templates/app/skeleton/.starcistacks/application-stacks.yaml +134 -1
  268. package/templates/app/skeleton/.starcistacks/dev/README.md +16 -0
  269. package/templates/app/skeleton/.starcistacks/dev/environment.json +5 -0
  270. package/templates/app/skeleton/.starcistacks/dev/infra/compose/stack.yaml +71 -0
  271. package/templates/app/skeleton/.starcistacks/dev/runtime/env/KEYS.md +14 -0
  272. package/templates/app/skeleton/.starcistacks/production/README.md +37 -0
  273. package/templates/app/skeleton/.starcistacks/production/environment.json +5 -0
  274. package/templates/app/skeleton/.starcistacks/production/infra/swarm/Caddyfile +15 -0
  275. package/templates/app/skeleton/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
  276. package/templates/app/skeleton/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
  277. package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
  278. package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/images.lock.yaml +9 -0
  279. package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
  280. package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +142 -0
  281. package/templates/app/skeleton/.starcistacks/production/runtime/env/KEYS.md +14 -0
  282. package/templates/app/skeleton/README.md +6 -6
  283. package/templates/app/skeleton/scripts/codegen.mjs +1 -2
  284. package/templates/app/skeleton-lite/.editorconfig +16 -0
  285. package/templates/app/skeleton-lite/.gitattributes +8 -0
  286. package/templates/app/skeleton-lite/.github/dependabot.yml +17 -0
  287. package/templates/app/skeleton-lite/.nvmrc +1 -0
  288. package/templates/app/skeleton-lite/.sops.yaml +3 -2
  289. package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +193 -0
  290. package/templates/app/skeleton-lite/.starcistacks/dev/README.md +20 -0
  291. package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +2 -1
  292. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +16 -0
  293. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +11 -0
  294. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +7 -0
  295. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +6 -0
  296. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +8 -0
  297. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +6 -0
  298. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +120 -0
  299. package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +18 -0
  300. package/templates/app/skeleton-lite/.starcistacks/production/README.md +38 -0
  301. package/templates/app/skeleton-lite/.starcistacks/production/environment.json +6 -0
  302. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/Caddyfile +11 -0
  303. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
  304. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
  305. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
  306. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/images.lock.yaml +8 -0
  307. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
  308. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +121 -0
  309. package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +21 -0
  310. package/templates/app/skeleton-lite/.starciwork/brand/index.yaml +12 -0
  311. package/templates/app/skeleton-lite/.starciwork/workspace.yaml +9 -0
  312. package/templates/app/skeleton-lite/README.md +8 -4
  313. package/templates/app/skeleton-lite/supabase/config.toml +8 -1
  314. package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +15 -6
  315. package/templates/app/starciwork.gitignore-lite +6 -0
  316. package/templates/app/upgrade-full/.starcistacks/dev/README.md +13 -7
  317. package/templates/be/image/worker/Dockerfile +1 -1
  318. package/templates/be/inbox/database-client.spec.ts.tpl +22 -28
  319. package/templates/be/inbox/database-client.ts.tpl +14 -28
  320. package/templates/be/inbox/delivery-recorded.event.ts.tpl +36 -0
  321. package/templates/be/inbox/event-index.ts.tpl +1 -0
  322. package/templates/be/inbox/events.json.tpl +10 -0
  323. package/templates/be/inbox/migration.sql.tpl +1 -0
  324. package/templates/be/inbox/platform/event-bus/event-bus.contracts.ts.tpl +42 -0
  325. package/templates/be/inbox/platform/event-bus/event-bus.decorators.ts.tpl +9 -0
  326. package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +6 -0
  327. package/templates/be/inbox/platform/event-bus/event-bus.module.ts.tpl +19 -0
  328. package/templates/be/inbox/platform/event-bus/event-bus.options.ts.tpl +2 -0
  329. package/templates/be/inbox/platform/event-bus/event-bus.port.ts.tpl +16 -0
  330. package/templates/be/inbox/platform/event-bus/event-bus.service.spec.ts.tpl +45 -0
  331. package/templates/be/inbox/platform/event-bus/event-bus.service.ts.tpl +15 -0
  332. package/templates/be/inbox/platform/event-bus/index.ts.tpl +5 -0
  333. package/templates/be/inbox/platform/event-bus/persistence/event-bus.sql.ts.tpl +5 -0
  334. package/templates/be/inbox/platform/inbox/inbox.decorators.ts.tpl +9 -0
  335. package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +6 -0
  336. package/templates/be/inbox/platform/inbox/inbox.module.ts.tpl +19 -0
  337. package/templates/be/inbox/platform/inbox/inbox.options.ts.tpl +2 -0
  338. package/templates/be/inbox/platform/inbox/inbox.port.ts.tpl +10 -0
  339. package/templates/be/inbox/platform/inbox/inbox.service.spec.ts.tpl +41 -0
  340. package/templates/be/inbox/platform/inbox/inbox.service.ts.tpl +19 -0
  341. package/templates/be/inbox/platform/inbox/index.ts.tpl +3 -0
  342. package/templates/be/inbox/platform/inbox/persistence/inbox.sql.ts.tpl +7 -0
  343. package/templates/be/inbox/platform/migration.sql.tpl +48 -0
  344. package/templates/be/inbox/service.spec.ts.tpl +49 -20
  345. package/templates/be/inbox/service.ts.tpl +27 -16
  346. package/templates/be/inbox/sql.ts.tpl +2 -2
  347. package/templates/be/inbox/webhook-door.spec.ts.tpl +3 -3
  348. package/templates/be/inbox/webhook-door.ts.tpl +4 -3
  349. package/templates/be/patterns/api/feature.module-definition.ts.tpl +6 -0
  350. package/templates/be/patterns/api/feature.module.ts.tpl +2 -1
  351. package/templates/be/patterns/api/feature.options.ts.tpl +2 -0
  352. package/templates/be/patterns/api/graphql.module-definition.ts.tpl +6 -0
  353. package/templates/be/patterns/api/graphql.module.ts.tpl +3 -2
  354. package/templates/be/patterns/api/graphql.options.ts.tpl +2 -0
  355. package/templates/be/patterns/cli/group.module-definition.ts.tpl +6 -0
  356. package/templates/be/patterns/cli/group.module.ts.tpl +2 -1
  357. package/templates/be/patterns/cli/group.options.ts.tpl +2 -0
  358. package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +0 -3
  359. package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +0 -2
  360. package/templates/be/patterns/jobs/job.module-definition.ts.tpl +6 -0
  361. package/templates/be/patterns/jobs/job.module.ts.tpl +2 -1
  362. package/templates/be/patterns/jobs/job.options.ts.tpl +2 -0
  363. package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +6 -0
  364. package/templates/be/patterns/jobs/job.queue.module.ts.tpl +6 -3
  365. package/templates/be/patterns/jobs/job.queue.options.ts.tpl +2 -0
  366. package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +3 -1
  367. package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +25 -9
  368. package/templates/be/patterns/queues/platform/queue.config.ts.tpl +1 -1
  369. package/templates/be/patterns/queues/platform/queue.index.ts.tpl +1 -1
  370. package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +2 -2
  371. package/templates/be/patterns/queues/platform/queue.module.ts.tpl +11 -0
  372. package/templates/be/patterns/queues/platform/queue.options.ts.tpl +10 -0
  373. package/templates/be/patterns/queues/queue.module-definition.ts.tpl +6 -0
  374. package/templates/be/patterns/queues/queue.module.ts.tpl +2 -1
  375. package/templates/be/patterns/queues/queue.options.ts.tpl +2 -0
  376. package/templates/be/patterns/reactors/message.module-definition.ts.tpl +6 -0
  377. package/templates/be/patterns/reactors/message.module.ts.tpl +6 -3
  378. package/templates/be/patterns/reactors/message.options.ts.tpl +2 -0
  379. package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +6 -0
  380. package/templates/be/patterns/reactors/reactor.module.ts.tpl +2 -1
  381. package/templates/be/patterns/reactors/reactor.options.ts.tpl +2 -0
  382. package/templates/be/patterns/realtime/channel.gateway.spec.ts.tpl +188 -0
  383. package/templates/be/patterns/realtime/channel.gateway.ts.tpl +22 -0
  384. package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +6 -0
  385. package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +2 -1
  386. package/templates/be/patterns/realtime/channel.graphql.options.ts.tpl +2 -0
  387. package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +6 -0
  388. package/templates/be/patterns/realtime/channel.websocket.module.ts.tpl +110 -0
  389. package/templates/be/patterns/realtime/channel.websocket.options.ts.tpl +7 -0
  390. package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +6 -0
  391. package/templates/be/patterns/saga/saga.message.module.ts.tpl +6 -3
  392. package/templates/be/patterns/saga/saga.message.options.ts.tpl +2 -0
  393. package/templates/be/patterns/saga/saga.module-definition.ts.tpl +6 -0
  394. package/templates/be/patterns/saga/saga.module.ts.tpl +2 -1
  395. package/templates/be/patterns/saga/saga.options.ts.tpl +2 -0
  396. package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +3 -3
  397. package/templates/be/patterns/webhooks/webhook.door.ts.tpl +3 -3
  398. package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +6 -0
  399. package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +2 -1
  400. package/templates/be/patterns/webhooks/webhook.http.options.ts.tpl +2 -0
  401. package/templates/be/platform/graphql/localize-errors.mapper.spec.ts +80 -26
  402. package/templates/be/platform/graphql/localize-errors.mapper.ts +2 -5
  403. package/templates/be/skeleton/apps/__app__/src/__app__.env.ts +21 -0
  404. package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +1 -1
  405. package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -45
  406. package/templates/be/skeleton/apps/__app__/src/compose/api.ts +4 -0
  407. package/templates/be/skeleton/apps/__app__/src/compose/domain.ts +11 -0
  408. package/templates/be/skeleton/apps/__app__/src/compose/platform.ts +44 -0
  409. package/templates/be/skeleton/apps/__app__/src/compose/system.ts +5 -0
  410. package/templates/be/skeleton/apps/__app__/src/compose/webhooks.ts +4 -0
  411. package/templates/be/skeleton/apps/__app__/src/main.ts +18 -10
  412. package/templates/be/skeleton/apps/cli/src/app.module.ts +3 -13
  413. package/templates/be/skeleton/apps/cli/src/cli.env.ts +13 -0
  414. package/templates/be/skeleton/apps/cli/src/cli.options.ts +7 -17
  415. package/templates/be/skeleton/apps/cli/src/compose/cli.ts +5 -0
  416. package/templates/be/skeleton/apps/cli/src/compose/platform.ts +24 -0
  417. package/templates/be/skeleton/apps/cli/src/main.ts +3 -2
  418. package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.contracts.ts +2 -0
  419. package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.handler.ts +22 -0
  420. package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.query.ts +11 -0
  421. package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +6 -0
  422. package/templates/be/skeleton/src/features/api/system-health/system-health.module.ts +5 -3
  423. package/templates/be/skeleton/src/features/api/system-health/system-health.options.ts +2 -0
  424. package/templates/be/skeleton/src/features/api/system-health/transport/http/ready.controller.ts +19 -0
  425. package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +6 -0
  426. package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module.ts +8 -3
  427. package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.options.ts +2 -0
  428. package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +6 -0
  429. package/templates/be/skeleton/src/features/cli/cli.module.ts +3 -2
  430. package/templates/be/skeleton/src/features/cli/cli.options.ts +2 -0
  431. package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +6 -0
  432. package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +2 -1
  433. package/templates/be/skeleton/src/features/cli/migrate/migrate.options.ts +2 -0
  434. package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +6 -0
  435. package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +2 -1
  436. package/templates/be/skeleton/src/features/cli/seed/seed.options.ts +2 -0
  437. package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +5 -6
  438. package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +56 -4
  439. package/templates/be/skeleton/src/modules/domain/identity/admission.policy.ts +36 -5
  440. package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +39 -8
  441. package/templates/be/skeleton/src/modules/domain/identity/auth.guard.ts +14 -8
  442. package/templates/be/skeleton/src/modules/domain/identity/credential.contracts.ts +26 -0
  443. package/templates/be/skeleton/src/modules/domain/identity/credential.guard.spec.ts +119 -0
  444. package/templates/be/skeleton/src/modules/domain/identity/credential.guard.ts +43 -0
  445. package/templates/be/skeleton/src/modules/domain/identity/identity.contracts.ts +16 -0
  446. package/templates/be/skeleton/src/modules/domain/identity/identity.decorators.ts +18 -1
  447. package/templates/be/skeleton/src/modules/domain/identity/identity.module.ts +22 -4
  448. package/templates/be/skeleton/src/modules/domain/identity/identity.options.ts +8 -2
  449. package/templates/be/skeleton/src/modules/domain/identity/index.ts +1 -0
  450. package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +6 -6
  451. package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +11 -4
  452. package/templates/be/skeleton/src/modules/platform/database/database-probe.service.spec.ts +32 -0
  453. package/templates/be/skeleton/src/modules/platform/database/database-probe.service.ts +14 -0
  454. package/templates/be/skeleton/src/modules/platform/database/database.module.ts +7 -1
  455. package/templates/be/skeleton/src/modules/platform/database/index.ts +2 -0
  456. package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +8 -0
  457. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +25 -6
  458. package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +16 -6
  459. package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.spec.ts +130 -0
  460. package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.ts +58 -0
  461. package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +3 -0
  462. package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -4
  463. package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +16 -10
  464. package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +3 -1
  465. package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +2 -0
  466. package/templates/be/skeleton/src/modules/platform/http-security/index.ts +5 -0
  467. package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +12 -5
  468. package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +13 -8
  469. package/templates/be/skeleton/src/modules/platform/http-security/readiness.contracts.ts +5 -0
  470. package/templates/be/skeleton/src/modules/platform/http-security/readiness.decorators.ts +7 -0
  471. package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.spec.ts +86 -0
  472. package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.ts +39 -0
  473. package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +139 -131
  474. package/templates/be/skeleton/src/modules/platform/i18n/index.ts +1 -1
  475. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +91 -67
  476. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +5 -4
  477. package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +0 -2
  478. package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +20 -6
  479. package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +16 -4
  480. package/templates/be/skeleton/src/modules/platform/retry/index.ts +3 -0
  481. package/templates/be/skeleton/src/modules/platform/retry/retry.decorators.ts +9 -0
  482. package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +7 -0
  483. package/templates/be/skeleton/src/modules/platform/retry/retry.module.ts +19 -0
  484. package/templates/be/skeleton/src/modules/platform/retry/retry.options.ts +2 -0
  485. package/templates/be/skeleton/src/modules/platform/retry/retry.port.ts +11 -0
  486. package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.spec.ts +36 -0
  487. package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.ts +10 -0
  488. package/templates/be/skeleton/src/tests/contract/http-security/http-security.contract-spec.ts +54 -0
  489. package/templates/be/skeleton-lite/apps/api/src/api.env.ts +25 -0
  490. package/templates/be/skeleton-lite/apps/api/src/api.options.ts +1 -1
  491. package/templates/be/skeleton-lite/apps/api/src/app.module.ts +18 -58
  492. package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +4 -0
  493. package/templates/be/skeleton-lite/apps/api/src/compose/domain.ts +10 -0
  494. package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +16 -0
  495. package/templates/be/skeleton-lite/apps/api/src/compose/platform.ts +47 -0
  496. package/templates/be/skeleton-lite/apps/api/src/compose/system.ts +5 -0
  497. package/templates/be/skeleton-lite/apps/api/src/compose/webhooks.ts +4 -0
  498. package/templates/be/skeleton-lite/apps/api/src/main.ts +5 -13
  499. package/templates/be/skeleton-lite/apps/cli/src/cli.env.ts +17 -0
  500. package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +12 -0
  501. package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.spec.ts +71 -5
  502. package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +29 -2
  503. package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.spec.ts +68 -15
  504. package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +14 -9
  505. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +9 -2
  506. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +16 -1
  507. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +19 -4
  508. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +5 -1
  509. package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +2 -1
  510. package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +0 -3
  511. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.spec.ts +17 -5
  512. package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +24 -13
  513. package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +5 -0
  514. package/templates/be/skeleton-lite/src/modules/platform/database/errors/database.error.ts +16 -0
  515. package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +3 -0
  516. package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +5 -2
  517. package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +4 -1
  518. package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +1 -1
  519. package/templates/be/skeleton-lite/src/modules/platform/logging/logging.log-events.ts +7 -0
  520. package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +29 -0
  521. package/templates/be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts +16 -0
  522. package/templates/be/table/api/feature.module-definition.ts.tpl +6 -0
  523. package/templates/be/table/api/feature.module.ts.tpl +2 -1
  524. package/templates/be/table/api/feature.options.ts.tpl +2 -0
  525. package/templates/be/table/api/graphql.module-definition.ts.tpl +6 -0
  526. package/templates/be/table/api/graphql.module.ts.tpl +3 -2
  527. package/templates/be/table/api/graphql.options.ts.tpl +2 -0
  528. package/templates/be/table/migration.sql.tpl +1 -0
  529. package/templates/be/upgrade-full/apps/cli/src/app.module.ts +4 -6
  530. package/templates/be/upgrade-full/apps/cli/src/cli.env.ts +7 -0
  531. package/templates/be/upgrade-full/apps/cli/src/compose/cli.ts +5 -0
  532. package/templates/be/upgrade-full/apps/cli/src/compose/platform.ts +9 -0
  533. package/templates/be/upgrade-full/apps/cli/src/main.ts +3 -0
  534. package/templates/be/upgrade-full/src/features/cli/cli.module.ts +3 -2
  535. package/templates/fe/auth/supabase-client.ts.tpl +34 -0
  536. package/templates/fe/skeleton/apps/__app__/src/app/auth/callback/route.ts +11 -0
  537. package/templates/fe/skeleton/apps/__app__/src/components/blocks/SignInForm/index.tsx +6 -1
  538. package/templates/fe/skeleton/apps/__app__/src/components/leaves/Icon/index.tsx +4 -0
  539. package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/component.tsx +31 -0
  540. package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/index.tsx +24 -0
  541. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSession.ts +6 -1
  542. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignOut.ts +3 -4
  543. package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/index.ts +1 -0
  544. package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/store.ts +15 -0
  545. package/templates/fe/skeleton/apps/__app__/src/modules/auth/pkce-client.ts +18 -0
  546. package/templates/fe/skeleton/apps/__app__/src/modules/auth/server.ts +19 -0
  547. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +7 -1
  548. package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/en.json +57 -0
  549. package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +6 -0
  550. package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/en.json +31 -0
  551. package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +1 -1
  552. package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +1 -0
  553. package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +1 -34
  554. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +7 -1
  555. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/en.json +57 -0
  556. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +30 -24
  557. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +1 -1
  558. package/templates/fe/transport/graphql/clients/clients/auth.ts +1 -1
  559. package/templates/fe/transport/graphql/clients/links/locale.ts +2 -2
  560. package/upgrade/check.mjs +3 -1
  561. package/upgrade/file-state.mjs +68 -0
  562. package/upgrade/index.mjs +58 -94
  563. package/runtime/scripts/api/git/log.mjs +0 -5
  564. package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +0 -55
@@ -0,0 +1,93 @@
1
+ // stack-tooling.mjs - R293: an app does not re-implement the Swarm deploy tooling the canon ships as `starci app stack <verb>`.
2
+ // Deploy sources (tracked files under deploy/ and scripts/, the scripts of package.json, workflows other than the canon's managed
3
+ // stack-deploy.yml) must not call a docker command a verb owns, and deploy/ holds no file named after a verb. A finding names the
4
+ // replacing verb and the file, never a snippet of the source.
5
+ import { found, readText } from './read.mjs';
6
+
7
+ export const STACK_TOOLING_REIMPLEMENTED = 'STACK_TOOLING_REIMPLEMENTED';
8
+
9
+ const MANAGED_WORKFLOW = /(?:^|\/)\.github\/workflows\/stack-deploy\.ya?ml$/;
10
+ const WORKFLOW = /(?:^|\/)\.github\/workflows\/[^/]+\.ya?ml$/;
11
+ const DEPLOY_DIR = /(?:^|\/)deploy\//;
12
+ const SCRIPTS_DIR = /(?:^|\/)scripts\//;
13
+ const PACKAGE_JSON = /(?:^|\/)package\.json$/;
14
+ const SOURCE_FILE = /(?:^|\/)[^/.]+$|\.(?:sh|bash|zsh|mjs|cjs|js|ts|mts|py|ya?ml)$/;
15
+ const TEST_FILE = /\.(?:spec|test)\.[cm]?[jt]s$/;
16
+ const CREDENTIAL_FILE = /(?:^|\/)(?:secrets?\.env(?:\.[^/]*)?|\.env(?:\.[^/]*)?|secrets\/)|\.enc$/i;
17
+ const VERB_FILE = /^(render|provision|converge|verify|prune|rollback|release|backup|prepare|doctor|jobs|public)\.(?:mjs|js|cjs|sh)$/;
18
+ const COMMENT_LINE = /^[ \t]*(?:#|\/\/).*$/gm;
19
+ const LINE_CONTINUATION = /\\\r?\n\s*/g;
20
+ const VERB_COMMAND = /\bapp\s+stack\b[^\n;&|]*/g;
21
+ const DOCKER_OPTIONS = String.raw`(?:\s+(?:--(?:context|host)(?:\s+|=)\S+|-H\s+\S+))*`;
22
+ const SHELL_CALL = new RegExp(String.raw`\bdocker${DOCKER_OPTIONS}\s+(stack|secret|config|service|swarm|volume|node)\s+([a-z]+)\b`, 'g');
23
+ const ARGV_CALL = /['"]docker['"]\s*,\s*\[\s*['"](stack|secret|config|service|swarm|volume|node)['"]\s*,\s*['"]([a-z]+)['"]/g;
24
+
25
+ /** `docker <noun> <action>` pairs the canon owns, mapped to the verb that replaces them. */
26
+ const OWNED = new Map([
27
+ ['stack deploy', 'deploy'], ['stack config', 'render'],
28
+ ['secret create', 'deploy'], ['secret rm', 'prune'], ['config create', 'deploy'], ['config rm', 'prune'],
29
+ ['service ls', 'converge'], ['service ps', 'converge'], ['service inspect', 'verify'], ['service update', 'rollback'],
30
+ ['swarm init', 'prepare'], ['volume create', 'prepare'], ['node ls', 'doctor'],
31
+ ]);
32
+
33
+ /** Comments run nothing; a `starci app stack ...` command line is the canon call itself and is never judged. */
34
+ function executableText(text) {
35
+ return text.replaceAll(COMMENT_LINE, '').replaceAll(LINE_CONTINUATION, ' ').replaceAll(VERB_COMMAND, ' ');
36
+ }
37
+
38
+ function ownedVerbs(text) {
39
+ const verbs = new Set();
40
+ for (const pattern of [SHELL_CALL, ARGV_CALL]) {
41
+ for (const [, noun, action] of text.matchAll(pattern)) {
42
+ const verb = OWNED.get(`${noun} ${action}`);
43
+ if (verb) verbs.add(verb);
44
+ }
45
+ }
46
+ return verbs;
47
+ }
48
+
49
+ function packageScripts(text) {
50
+ try {
51
+ return Object.values(JSON.parse(text)?.scripts ?? {}).filter((command) => typeof command === 'string').join('\n');
52
+ } catch { return ''; }
53
+ }
54
+
55
+ function sourceText(file, raw) {
56
+ return executableText(PACKAGE_JSON.test(file) ? packageScripts(raw) : raw);
57
+ }
58
+
59
+ function deploySource(file) {
60
+ if (MANAGED_WORKFLOW.test(file) || CREDENTIAL_FILE.test(file) || TEST_FILE.test(file)) return false;
61
+ if (PACKAGE_JSON.test(file) || WORKFLOW.test(file)) return true;
62
+ return (DEPLOY_DIR.test(file) || SCRIPTS_DIR.test(file)) && SOURCE_FILE.test(file);
63
+ }
64
+
65
+ const replacement = (file, verb) => found(
66
+ STACK_TOOLING_REIMPLEMENTED,
67
+ file,
68
+ `${file} re-implements deploy tooling the canon ships; call \`starci app stack ${verb}\` instead`,
69
+ { verb },
70
+ );
71
+
72
+ function fileFindings(repoRoot, file) {
73
+ const raw = readText(repoRoot, file);
74
+ if (raw === null) return [];
75
+ return [...ownedVerbs(sourceText(file, raw))].map((verb) => replacement(file, verb));
76
+ }
77
+
78
+ function verbNamedFile(file) {
79
+ const verb = DEPLOY_DIR.test(file) ? VERB_FILE.exec(file.slice(file.lastIndexOf('/') + 1))?.[1] : undefined;
80
+ return verb ? [replacement(file, verb)] : [];
81
+ }
82
+
83
+ /** Findings are names and paths only: no source snippet leaves this finder. One finding per file and replacing verb. */
84
+ export function stackToolingFindings({ repoRoot, files, all, repo, isRoot, inScope = () => true }) {
85
+ if (repo?.kind !== 'app' || !['lite', 'full'].includes(repo.edition) || isRoot === false) return [];
86
+ const findings = new Map();
87
+ for (const file of new Set(all ?? files)) {
88
+ if (!inScope(file)) continue;
89
+ const candidates = [...verbNamedFile(file), ...(deploySource(file) ? fileFindings(repoRoot, file) : [])];
90
+ for (const finding of candidates) findings.set(`${finding.path}\0${finding.verb}`, finding);
91
+ }
92
+ return [...findings.values()];
93
+ }
@@ -0,0 +1,76 @@
1
+ // stacks-deploy.mjs - R294: `environments.<env>.deploy` in .starcistacks/application-stacks.yaml is coherent.
2
+ // The block is judged by the same `normalizeDeploy` the `starci app stack` verbs use (scripts/hfs/stack-deploy-config.mjs), against a
3
+ // probe of the real tree: the files below .starcistacks/<env>/, the services of the first composeFiles entry and the sealed members.
4
+ // One finding per problem; an environment without a deploy block is not a finding.
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { isPlainObject as plain } from '../../../engine/plain-object.mjs';
8
+ import { findStackDeclaration, readDeclaration, STACK_ROOT } from '../../lib/stack-declaration.mjs';
9
+ import { normalizeDeploy } from '../stack-deploy-config.mjs';
10
+ import { found, readJson } from './read.mjs';
11
+
12
+ const DECLARATION = `${STACK_ROOT}/application-stacks.yaml`;
13
+ const ENV_NAME = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
14
+ const mapping = (value) => (plain(value) ? value : {});
15
+
16
+ /** Service names of the first composeFiles entry; an unreadable stack source means unknown services (empty set). */
17
+ function stackServices(envDir, spec) {
18
+ const relative = Array.isArray(spec.composeFiles) ? spec.composeFiles[0] : undefined;
19
+ if (typeof relative !== 'string' || path.isAbsolute(relative) || relative.split(/[\\/]/).includes('..')) return new Set();
20
+ const { doc } = readDeclaration(path.join(envDir, relative));
21
+ return new Set(Object.keys(mapping(doc?.services)));
22
+ }
23
+
24
+ function sealedMembers(envDir) {
25
+ try {
26
+ return new Set(fs.readdirSync(path.join(envDir, 'secrets')).filter((name) => name.endsWith('.enc')).map((name) => name.slice(0, -4)));
27
+ } catch { return new Set(); }
28
+ }
29
+
30
+ function probeOf(envDir, spec) {
31
+ return { exists: (relative) => fs.existsSync(path.join(envDir, relative)), services: stackServices(envDir, spec), members: sealedMembers(envDir) };
32
+ }
33
+
34
+ /** The shell scripts below the environment directory (node_modules aside), as absolute paths. */
35
+ function shellScripts(directory) {
36
+ return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
37
+ const file = path.join(directory, entry.name);
38
+ if (entry.isDirectory()) return entry.name === 'node_modules' ? [] : shellScripts(file);
39
+ return entry.name.endsWith('.sh') ? [file] : [];
40
+ });
41
+ }
42
+
43
+ /** A shell script with CRLF line endings fails inside the container (`set: Illegal option -`): one finding per such file. */
44
+ function lineEndingFindings({ repoRoot, envDir, environment }) {
45
+ if (!fs.existsSync(envDir)) return [];
46
+ const crlf = String.fromCodePoint(13, 10);
47
+ return shellScripts(envDir).filter((file) => fs.readFileSync(file, 'utf8').includes(crlf)).map((file) => found(
48
+ 'STACKS_DEPLOY_CONFIG_INVALID',
49
+ path.relative(repoRoot, file).split(path.sep).join('/'),
50
+ `environments.${environment}: this shell script has CRLF line endings and fails in the container; commit it with LF (.gitattributes: *.sh text eol=lf) and save it again`,
51
+ ));
52
+ }
53
+
54
+ function environmentFindings({ repoRoot, project, environment, spec }) {
55
+ const envDir = path.join(repoRoot, STACK_ROOT, environment);
56
+ const { problems } = normalizeDeploy(spec.deploy, { project, probe: probeOf(envDir, spec) });
57
+ return [...problems.map((problem) => found(
58
+ 'STACKS_DEPLOY_CONFIG_INVALID',
59
+ DECLARATION,
60
+ `environments.${environment}.${problem.at}: ${problem.message}`,
61
+ )), ...lineEndingFindings({ repoRoot, envDir, environment })];
62
+ }
63
+
64
+ /** Only docker-swarm environments that declare a deploy block are judged. */
65
+ function judged([environment, spec]) {
66
+ return ENV_NAME.test(environment) && plain(spec) && spec.runtime === 'docker-swarm' && spec.deploy !== undefined;
67
+ }
68
+
69
+ /** Findings of every declared deploy block of the app root, in both editions. */
70
+ export function stacksDeployFindings({ repoRoot, repo, isRoot }) {
71
+ if (repo?.kind !== 'app' || !['lite', 'full'].includes(repo.edition) || isRoot === false) return [];
72
+ const { doc } = findStackDeclaration(repoRoot);
73
+ const project = String(readJson(repoRoot, 'hfs.json')?.project ?? 'app');
74
+ return Object.entries(mapping(mapping(doc).environments)).filter(judged)
75
+ .flatMap(([environment, spec]) => environmentFindings({ repoRoot, project, environment, spec }));
76
+ }
@@ -0,0 +1,87 @@
1
+ // R275 and R266: each Swarm environment has one declared, digest-pinned ingress, or declares that the ingress is an external shared stack
2
+ // (`ingress.external: { stack }` with `hosts`): then the stack renders no ingress of its own and publishes no port.
3
+ import { isPlainObject as plain } from '../../../engine/plain-object.mjs';
4
+ import { repositoryOf } from '../../lib/stack-services.mjs';
5
+ import { stacksDeploymentFindings } from './stacks-runtime.mjs';
6
+
7
+ const PINNED = /@sha256:[a-f0-9]{64}$/i;
8
+ const SUPPORTED = new Set(['traefik', 'caddy', 'caddy-docker-proxy']);
9
+ const mapping = value => plain(value) ? value : {};
10
+ const present = value => typeof value === 'string' && Boolean(value.trim());
11
+ const imageName = image => present(image) ? repositoryOf(image).toLowerCase() : '';
12
+ const ingressImage = image => SUPPORTED.has(imageName(image));
13
+
14
+ function ingressIds(components, bindings, services, chosen) {
15
+ const ids = new Set();
16
+ if (present(chosen)) ids.add(chosen);
17
+ for (const id of Object.keys(bindings)) {
18
+ const component = mapping(components[id]);
19
+ if (component.role === 'ingress' || ingressImage(component.image)) ids.add(id);
20
+ }
21
+ for (const [id, entry] of Object.entries(services)) {
22
+ const service = mapping(entry), component = mapping(components[id]);
23
+ if (component.role === 'ingress' || ingressImage(service.image)) ids.add(id);
24
+ }
25
+ return [...ids];
26
+ }
27
+
28
+ /** The name of the shared external ingress stack the environment declares, or null. */
29
+ export const externalIngressStack = spec => present(spec?.ingress?.external?.stack) ? spec.ingress.external.stack : null;
30
+
31
+ function externalIssues({ spec, candidates, at }) {
32
+ const ingress = mapping(spec.ingress), errors = [];
33
+ if (externalIngressStack(spec) === null || present(ingress.component) || !Array.isArray(ingress.hosts) || ingress.hosts.length === 0)
34
+ errors.push({ code: 'STACKS_INGRESS_UNDECLARED', at,
35
+ message: 'an external ingress is declared as ingress.external.stack with ingress.hosts and without ingress.component' });
36
+ if (candidates.length > 0)
37
+ errors.push({ code: 'STACKS_SECOND_INGRESS', at,
38
+ message: 'a stack that declares an external shared ingress must not render an ingress of its own' });
39
+ return errors;
40
+ }
41
+
42
+ function declarationIssues({ chosen, components, bindings, services, candidates, at }) {
43
+ const errors = [];
44
+ if (!present(chosen) || components[chosen]?.role !== 'ingress' || !Object.hasOwn(bindings, chosen) || !Object.hasOwn(services, chosen))
45
+ errors.push({ code: 'STACKS_INGRESS_UNDECLARED', at,
46
+ message: 'Swarm ingress must name one component with role ingress that is inventoried, bound and present in the rendered model' });
47
+ const undeclared = candidates.filter(id => id !== chosen);
48
+ if (undeclared.length)
49
+ errors.push({ code: 'STACKS_INGRESS_UNDECLARED', at,
50
+ message: 'every rendered ingress must be the environment\'s declared ingress component' });
51
+ if (candidates.length > 1)
52
+ errors.push({ code: 'STACKS_SECOND_INGRESS', at,
53
+ message: 'a Swarm environment must run exactly one ingress component; a second ingress is forbidden' });
54
+ return errors;
55
+ }
56
+
57
+ function ingressImageIssues(ids, components, services) {
58
+ const errors = [];
59
+ for (const id of ids) {
60
+ for (const [prefix, source] of [['components', components], ['services', services]]) {
61
+ if (!Object.hasOwn(source, id)) continue;
62
+ const image = source[id]?.image;
63
+ if (!SUPPORTED.has(imageName(image)) || !PINNED.test(String(image)))
64
+ errors.push({ code: 'STACKS_INGRESS_UNKNOWN', at: `${prefix}.${id}.image`,
65
+ message: 'the declared ingress must use Traefik or Caddy pinned by a sha256 digest' });
66
+ }
67
+ }
68
+ return errors;
69
+ }
70
+
71
+ /** Static ingress contract shared by HFS and the rendered stacks gate. */
72
+ export function ingressIssues({ doc, environment, spec = doc?.environments?.[environment], model }) {
73
+ if (spec?.runtime !== 'docker-swarm') return [];
74
+ const components = mapping(doc?.components), bindings = mapping(spec.components), services = mapping(model?.services);
75
+ const chosen = spec.ingress?.component, candidates = ingressIds(components, bindings, services, chosen);
76
+ const at = `environments.${environment}.ingress`;
77
+ if (mapping(spec.ingress).external !== undefined) return externalIssues({ spec, candidates, at });
78
+ return [
79
+ ...declarationIssues({ chosen, components, bindings, services, candidates, at }),
80
+ ...ingressImageIssues(candidates, components, services),
81
+ ];
82
+ }
83
+
84
+ /** HFS finder over the tracked rendered Swarm environments of either edition. */
85
+ export function stacksIngressFindings(context) {
86
+ return stacksDeploymentFindings(context, ingressIssues);
87
+ }
@@ -0,0 +1,111 @@
1
+ // R276: lite uses managed Supabase backing; sidecar SQLite persists on declared named volumes.
2
+ import { isPlainObject as plain } from '../../../engine/plain-object.mjs';
3
+ import { composeEntries } from '../../lib/compose-values.mjs';
4
+ import { repositoryOf } from '../../lib/stack-services.mjs';
5
+ import { stacksDeploymentFindings } from './stacks-runtime.mjs';
6
+
7
+ const BACKING_IMAGE = /^(?:postgres(?:ql)?|postgresql-repmgr|redis(?:-stack(?:-server)?)?|mongo(?:db)?|mongodb-community-server)$/i;
8
+ const DATABASE_ADAPTER_KEYS = new Set(['db_type', 'database_type', 'db_provider', 'database_provider', 'db_client', 'database_client', 'db_adapter', 'database_adapter']);
9
+ const SQLITE_ADAPTERS = new Set(['sqlite', 'sqlite3', 'better-sqlite3', 'sql.js']);
10
+ const DATABASE_URL_KEY = /DB|DATABASE|POSTGRES|REDIS|MONGO/i;
11
+ const NON_SQLITE_URL = /^(?:postgres(?:ql)?|rediss?|mongo(?:db)?(?:\+srv)?|mysql|mariadb|mssql|sqlserver|cockroachdb):/i;
12
+ const SQLITE_URL = /^sqlite(?:3)?:/i;
13
+ const NAMED_VOLUME = /^[a-z0-9][a-z0-9_.-]*$/i;
14
+ const mapping = value => plain(value) ? value : {};
15
+ const present = value => typeof value === 'string' && Boolean(value.trim());
16
+
17
+ function imageIssues(components, services) {
18
+ const errors = [];
19
+ for (const [prefix, inventory] of [['components', components], ['services', services]]) {
20
+ for (const [id, entry] of Object.entries(inventory)) {
21
+ if (present(entry?.image) && BACKING_IMAGE.test(repositoryOf(entry.image)))
22
+ errors.push({ code: 'STACKS_LITE_SELFHOSTED_DB', at: `${prefix}.${id}.image`,
23
+ message: 'lite must not run a self-hosted Postgres, Redis or Mongo database image; use managed Supabase backing' });
24
+ }
25
+ }
26
+ return errors;
27
+ }
28
+
29
+ function unsupportedAdapter(service) {
30
+ for (const [key, value] of composeEntries(service?.environment, { stringsOnly: true })) {
31
+ const adapterKey = DATABASE_ADAPTER_KEYS.has(key.toLowerCase());
32
+ if (adapterKey && !SQLITE_ADAPTERS.has(String(value).trim().toLowerCase())) return true;
33
+ if (DATABASE_URL_KEY.test(key) && typeof value === 'string' && NON_SQLITE_URL.test(value.trim())) return true;
34
+ }
35
+ return false;
36
+ }
37
+
38
+ function sqliteAdapter(service) {
39
+ return composeEntries(service?.environment, { stringsOnly: true }).some(([key, value]) => {
40
+ const adapter = String(value).trim().toLowerCase();
41
+ return DATABASE_ADAPTER_KEYS.has(key.toLowerCase()) && SQLITE_ADAPTERS.has(adapter)
42
+ || DATABASE_URL_KEY.test(key) && SQLITE_URL.test(adapter);
43
+ });
44
+ }
45
+
46
+ function renderedAdapterIssues(components, services) {
47
+ const errors = [];
48
+ for (const [id, service] of Object.entries(services)) {
49
+ const component = mapping(components[id]);
50
+ if (component.role !== 'sidecar' && component.database === undefined) continue;
51
+ if (!unsupportedAdapter(service)) continue;
52
+ errors.push({ code: 'STACKS_SIDECAR_DB_UNSUPPORTED', at: `services.${id}.environment`,
53
+ message: 'rendered sidecar database adapter configuration must use SQLite; Postgres, Redis and Mongo dependencies are forbidden in lite' });
54
+ }
55
+ return errors;
56
+ }
57
+
58
+ function writableVolume(entry, source) {
59
+ if (typeof entry === 'string') {
60
+ const [name, target, options = ''] = entry.split(':');
61
+ return name === source && present(target) && target.startsWith('/') && !options.split(',').includes('ro');
62
+ }
63
+ return plain(entry) && entry.type === 'volume' && entry.source === source && present(entry.target) && entry.target.startsWith('/') && entry.read_only !== true;
64
+ }
65
+
66
+ function sqliteVolumeIssue({ id, component, database, services, bindings, volumes }) {
67
+ const declared = Array.isArray(component.volumes) && component.volumes.includes(database.volume);
68
+ const active = Object.hasOwn(bindings, id) || Object.hasOwn(services, id);
69
+ const mounts = Array.isArray(services[id]?.volumes) ? services[id].volumes : [];
70
+ const mounted = Object.hasOwn(volumes, database.volume) && mounts.some(entry => writableVolume(entry, database.volume));
71
+ if (present(database.volume) && NAMED_VOLUME.test(database.volume) && declared && (!active || mounted)) return [];
72
+ return [{ code: 'STACKS_SIDECAR_VOLUME_UNDECLARED', at: `components.${id}.database.volume`,
73
+ message: 'a SQLite sidecar needs a declared persistent component volume, a top-level rendered named volume and a writable mount' }];
74
+ }
75
+
76
+ function sidecarIssues({ components, services, bindings, volumes }) {
77
+ const errors = [];
78
+ for (const [id, entry] of Object.entries(components)) {
79
+ const component = mapping(entry);
80
+ if (component.database === undefined) {
81
+ if (component.role === 'sidecar' && sqliteAdapter(services[id]))
82
+ errors.push(...sqliteVolumeIssue({ id, component, database: {}, services, bindings, volumes }));
83
+ continue;
84
+ }
85
+ const database = mapping(component.database);
86
+ if (database.provider !== 'sqlite') {
87
+ errors.push({ code: 'STACKS_SIDECAR_DB_UNSUPPORTED', at: `components.${id}.database.provider`,
88
+ message: 'a lite sidecar that needs a database must use SQLite on a declared persistent volume' });
89
+ continue;
90
+ }
91
+ errors.push(...sqliteVolumeIssue({ id, component, database, services, bindings, volumes }));
92
+ }
93
+ return errors;
94
+ }
95
+
96
+ /** The lite backing contract; full keeps its existing backing choices. */
97
+ export function liteBackingIssues({ doc, environment, spec = doc?.environments?.[environment], model, repo, edition = repo?.edition }) {
98
+ if (edition !== 'lite' || spec?.runtime !== 'docker-swarm') return [];
99
+ const components = mapping(doc?.components), services = mapping(model?.services);
100
+ return [
101
+ ...imageIssues(components, services),
102
+ ...renderedAdapterIssues(components, services),
103
+ ...sidecarIssues({ components, services, bindings: mapping(spec.components), volumes: mapping(model?.volumes) }),
104
+ ];
105
+ }
106
+
107
+ /** HFS finder over tracked rendered models; unavailable evidence is reported by R267. */
108
+ export function stacksLiteBackingFindings(context) {
109
+ if (context.repo?.edition !== 'lite') return [];
110
+ return stacksDeploymentFindings(context, liteBackingIssues);
111
+ }