@starci/hfs 4.0.8 → 4.0.9

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 (667) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +81 -39
  3. package/emit/contracts.mjs +56 -9
  4. package/emit/db-types.mjs +169 -0
  5. package/emit/events.mjs +41 -0
  6. package/emit/operations-worker.mjs +1 -1
  7. package/emit/operations.mjs +2 -2
  8. package/emit/schema-worker.mjs +1 -1
  9. package/lint/bound-sys.cjs +50 -0
  10. package/lint/run.mjs +53 -19
  11. package/package.json +14 -9
  12. package/report/sonar.mjs +8 -8
  13. package/runtime/engine/config.mjs +494 -0
  14. package/runtime/engine/invalid-config.mjs +15 -0
  15. package/runtime/engine/orca-config.mjs +27 -0
  16. package/runtime/knowledge/hfs/canon-pins.yaml +160 -29
  17. package/runtime/knowledge/hfs/peer-integrations.yaml +2 -2
  18. package/runtime/knowledge/hfs/rules.yaml +2059 -0
  19. package/runtime/knowledge/hfs/slots.yaml +912 -120
  20. package/runtime/knowledge/patterns/be/api.yaml +369 -0
  21. package/runtime/knowledge/patterns/be/cli.yaml +297 -0
  22. package/runtime/knowledge/patterns/be/domain.yaml +113 -0
  23. package/runtime/knowledge/patterns/be/event-bus.yaml +285 -0
  24. package/runtime/knowledge/patterns/be/jobs.yaml +261 -0
  25. package/runtime/knowledge/patterns/be/projections.yaml +178 -0
  26. package/runtime/knowledge/patterns/be/queues.yaml +236 -0
  27. package/runtime/knowledge/patterns/be/reactors.yaml +140 -0
  28. package/runtime/knowledge/patterns/be/realtime.yaml +222 -0
  29. package/runtime/knowledge/patterns/be/saga.yaml +248 -0
  30. package/runtime/knowledge/patterns/be/webhooks.yaml +221 -0
  31. package/runtime/knowledge/patterns/fe/folder.yaml +131 -25
  32. package/runtime/knowledge/sonar-gate.yaml +52 -18
  33. package/runtime/modules/kernel/failure-codes.yaml +571 -51
  34. package/runtime/modules/models/registry.yaml +591 -0
  35. package/runtime/modules/models/runtimes.yaml +701 -0
  36. package/runtime/scripts/api/fs/forbidden-root.mjs +51 -0
  37. package/runtime/scripts/api/fs/is-link-like.mjs +25 -0
  38. package/runtime/scripts/api/fs/lib.mjs +10 -2
  39. package/runtime/scripts/api/fs/safe-remove.mjs +12 -132
  40. package/runtime/scripts/api/git/config-get.mjs +10 -0
  41. package/runtime/scripts/api/git/diff.mjs +5 -0
  42. package/runtime/scripts/api/git/lib.mjs +16 -26
  43. package/runtime/scripts/api/git/log.mjs +5 -0
  44. package/runtime/scripts/api/git/ls-files.mjs +6 -0
  45. package/runtime/scripts/api/git/ls-tree.mjs +5 -0
  46. package/runtime/scripts/api/git/merge-base.mjs +9 -0
  47. package/runtime/scripts/api/git/remote.mjs +5 -0
  48. package/runtime/scripts/api/git/rev-parse-query.mjs +5 -0
  49. package/runtime/scripts/api/git/show.mjs +6 -0
  50. package/runtime/scripts/api/sops/decrypt.mjs +9 -5
  51. package/runtime/scripts/api/sops/lib.mjs +35 -0
  52. package/runtime/scripts/api/sops/seal.mjs +18 -0
  53. package/runtime/scripts/hfs/architecture/ast-walks.mjs +220 -0
  54. package/runtime/scripts/hfs/architecture/backend.mjs +16 -23
  55. package/runtime/scripts/hfs/architecture/background-unowned.mjs +16 -34
  56. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +3 -1
  57. package/runtime/scripts/hfs/architecture/clones.mjs +4 -4
  58. package/runtime/scripts/hfs/architecture/config.mjs +9 -6
  59. package/runtime/scripts/hfs/architecture/connection-map.mjs +20 -8
  60. package/runtime/scripts/hfs/architecture/context-coupling.mjs +88 -0
  61. package/runtime/scripts/hfs/architecture/context-map.mjs +237 -0
  62. package/runtime/scripts/hfs/architecture/context-owner.mjs +82 -0
  63. package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -0
  64. package/runtime/scripts/hfs/architecture/context-transaction.mjs +60 -0
  65. package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +4 -10
  66. package/runtime/scripts/hfs/architecture/contracts.mjs +33 -162
  67. package/runtime/scripts/hfs/architecture/dead-exports.mjs +58 -29
  68. package/runtime/scripts/hfs/architecture/doc-language.mjs +1 -1
  69. package/runtime/scripts/hfs/architecture/entrypoint.mjs +2 -1
  70. package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +1 -1
  71. package/runtime/scripts/hfs/architecture/feature-shape.mjs +5 -2
  72. package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
  73. package/runtime/scripts/hfs/architecture/frontend.mjs +17 -43
  74. package/runtime/scripts/hfs/architecture/hfs.mjs +24 -39
  75. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +1 -1
  76. package/runtime/scripts/hfs/architecture/index.mjs +17 -4
  77. package/runtime/scripts/hfs/architecture/machine-ast.mjs +44 -11
  78. package/runtime/scripts/hfs/architecture/managed-scripts.mjs +27 -0
  79. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +30 -6
  80. package/runtime/scripts/hfs/architecture/next-data.mjs +22 -63
  81. package/runtime/scripts/hfs/architecture/owners.mjs +35 -15
  82. package/runtime/scripts/hfs/architecture/register-once.mjs +2 -1
  83. package/runtime/scripts/hfs/architecture/registration.mjs +23 -81
  84. package/runtime/scripts/hfs/architecture/required-files.mjs +20 -12
  85. package/runtime/scripts/hfs/architecture/schema-owner.mjs +17 -114
  86. package/runtime/scripts/hfs/architecture/server-action.mjs +24 -0
  87. package/runtime/scripts/hfs/architecture/side-boundary.mjs +16 -0
  88. package/runtime/scripts/hfs/architecture/source-names.mjs +27 -147
  89. package/runtime/scripts/hfs/architecture/sql-owner.mjs +59 -18
  90. package/runtime/scripts/hfs/architecture/sql-returning.mjs +64 -0
  91. package/runtime/scripts/hfs/architecture/supabase-ast.mjs +111 -0
  92. package/runtime/scripts/hfs/architecture/supabase-be.mjs +131 -0
  93. package/runtime/scripts/hfs/architecture/supabase-tables.mjs +42 -0
  94. package/runtime/scripts/hfs/architecture/supabase.mjs +434 -0
  95. package/runtime/scripts/hfs/architecture/surface.mjs +9 -3
  96. package/runtime/scripts/hfs/architecture/test-world-files.mjs +9 -19
  97. package/runtime/scripts/hfs/architecture/tiers.mjs +47 -5
  98. package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -3
  99. package/runtime/scripts/hfs/architecture/typescript.mjs +12 -26
  100. package/runtime/scripts/hfs/architecture.mjs +4 -4
  101. package/runtime/scripts/hfs/check.mjs +75 -72
  102. package/runtime/scripts/hfs/coverage-scope.mjs +162 -0
  103. package/runtime/scripts/hfs/declaration-shape.mjs +62 -0
  104. package/runtime/scripts/hfs/declaration-slots.mjs +75 -0
  105. package/runtime/scripts/hfs/edition-slots.mjs +66 -0
  106. package/runtime/scripts/hfs/manifest-shape.mjs +109 -32
  107. package/runtime/scripts/hfs/param-names.mjs +18 -0
  108. package/runtime/scripts/hfs/path-findings.mjs +1 -1
  109. package/runtime/scripts/hfs/repo-identity.mjs +9 -6
  110. package/runtime/scripts/hfs/rule-params-shape.mjs +50 -0
  111. package/runtime/scripts/hfs/rules/app-root.mjs +51 -0
  112. package/runtime/scripts/hfs/rules/cli.mjs +37 -0
  113. package/runtime/scripts/hfs/rules/contract-compat.mjs +71 -0
  114. package/runtime/scripts/hfs/rules/contract.mjs +10 -9
  115. package/runtime/scripts/hfs/rules/database-config.mjs +102 -0
  116. package/runtime/scripts/hfs/rules/database-constants.mjs +13 -0
  117. package/runtime/scripts/hfs/rules/database-migrations.mjs +111 -0
  118. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +233 -0
  119. package/runtime/scripts/hfs/rules/database-sql.mjs +336 -0
  120. package/runtime/scripts/hfs/rules/database.mjs +68 -0
  121. package/runtime/scripts/hfs/rules/deps.mjs +1 -1
  122. package/runtime/scripts/hfs/rules/docker.mjs +259 -0
  123. package/runtime/scripts/hfs/rules/edition.mjs +111 -0
  124. package/runtime/scripts/hfs/rules/event-bus.mjs +153 -0
  125. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -1
  126. package/runtime/scripts/hfs/rules/fe-no-tests.mjs +6 -5
  127. package/runtime/scripts/hfs/rules/frontend-tree.mjs +9 -9
  128. package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -6
  129. package/runtime/scripts/hfs/rules/kinds.mjs +84 -0
  130. package/runtime/scripts/hfs/rules/lint-suppression.mjs +1 -1
  131. package/runtime/scripts/hfs/rules/monorepo.mjs +179 -0
  132. package/runtime/scripts/hfs/rules/peer-integrations.mjs +3 -3
  133. package/runtime/scripts/hfs/rules/pipeline.mjs +11 -21
  134. package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -2
  135. package/runtime/scripts/hfs/rules/read.mjs +1 -1
  136. package/runtime/scripts/hfs/rules/repo-local-checks.mjs +2 -2
  137. package/runtime/scripts/hfs/rules/saga.mjs +170 -0
  138. package/runtime/scripts/hfs/rules/secrets.mjs +3 -3
  139. package/runtime/scripts/hfs/rules/services.mjs +220 -0
  140. package/runtime/scripts/hfs/rules/spec-placement.mjs +1 -1
  141. package/runtime/scripts/hfs/rules/stacks.mjs +3 -3
  142. package/runtime/scripts/hfs/rules/supabase-secrets.mjs +89 -0
  143. package/runtime/scripts/hfs/rules/test-topology.mjs +2 -2
  144. package/runtime/scripts/hfs/secret.mjs +175 -0
  145. package/runtime/scripts/hfs/slots.mjs +78 -108
  146. package/runtime/scripts/hfs/sql/pg-parse.mjs +46 -0
  147. package/runtime/scripts/hfs/test-world-slot.mjs +6 -0
  148. package/runtime/scripts/hfs/tree.mjs +5 -4
  149. package/runtime/scripts/hfs/typescript-programs.mjs +0 -5
  150. package/runtime/scripts/hfs/view.mjs +3 -2
  151. package/runtime/scripts/lib/dockerfile.mjs +124 -0
  152. package/runtime/scripts/lib/env.mjs +10 -0
  153. package/runtime/scripts/lib/event-contract.mjs +103 -0
  154. package/runtime/scripts/lib/git.mjs +36 -0
  155. package/runtime/scripts/lib/graphql-contract.mjs +1 -1
  156. package/runtime/scripts/lib/i18n.mjs +86 -0
  157. package/runtime/scripts/lib/json.mjs +13 -3
  158. package/runtime/scripts/lib/language.mjs +24 -5
  159. package/runtime/scripts/lib/list.mjs +20 -0
  160. package/runtime/scripts/lib/package-at.mjs +37 -0
  161. package/runtime/scripts/lib/path-key.mjs +57 -2
  162. package/runtime/scripts/lib/read-text.mjs +34 -0
  163. package/runtime/scripts/lib/regex.mjs +4 -0
  164. package/runtime/scripts/lib/same-text.mjs +5 -0
  165. package/runtime/scripts/lib/secret-patterns.mjs +4 -3
  166. package/runtime/scripts/lib/sleep-sync.mjs +2 -1
  167. package/runtime/scripts/lib/sops-envelope.mjs +58 -0
  168. package/runtime/scripts/lib/stack-services.mjs +7 -31
  169. package/runtime/scripts/lib/ts-ast.mjs +35 -0
  170. package/runtime/scripts/lib/walk.mjs +4 -3
  171. package/scaffold/add-app.mjs +174 -0
  172. package/scaffold/add-cli-lite.mjs +257 -0
  173. package/scaffold/add-inbox-lite.mjs +314 -0
  174. package/scaffold/add-table.mjs +344 -0
  175. package/scaffold/add.mjs +299 -0
  176. package/scaffold/app.mjs +358 -69
  177. package/scaffold/edition-gate.mjs +25 -0
  178. package/scaffold/image.mjs +19 -0
  179. package/scaffold/lite-exports.mjs +186 -0
  180. package/scaffold/service.mjs +32 -26
  181. package/scaffold/supabase-ports.mjs +26 -0
  182. package/src/main.mjs +327 -0
  183. package/sync/cli.mjs +6 -9
  184. package/sync/format.mjs +1 -1
  185. package/sync/hygiene.mjs +10 -10
  186. package/sync/index.mjs +167 -104
  187. package/sync/managed.mjs +19 -11
  188. package/sync/sonar-key.mjs +5 -0
  189. package/templates/app/ci-workflows/github/workflows/ci.yml +14 -14
  190. package/templates/app/ci-workflows/github/workflows/images.yml +37 -0
  191. package/templates/app/ci-workflows/sonar-steps.yml +9 -0
  192. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +66 -0
  193. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +31 -0
  194. package/templates/app/ci-workflows-lite/github/workflows/images.yml +37 -0
  195. package/templates/app/codegen-lite/scripts/codegen.mjs +32 -0
  196. package/templates/app/docker-ignore/dockerignore +50 -0
  197. package/templates/app/gitignore-lite +35 -0
  198. package/templates/app/hooks/husky/pre-commit +3 -8
  199. package/templates/app/hooks/husky/pre-push +45 -6
  200. package/templates/app/package-scripts/core.json +8 -0
  201. package/templates/app/package-scripts/package.json +3 -10
  202. package/templates/app/package-scripts-lite/package.json +15 -0
  203. package/templates/app/quality-config/codecov.yml +15 -3
  204. package/templates/app/quality-config/sonar-base.properties +4 -0
  205. package/templates/app/quality-config/sonar-project.properties +1 -4
  206. package/templates/app/quality-config-lite/sonar-project.properties +5 -0
  207. package/templates/app/skeleton/.starcistacks/dev/seeds/primary-notes.sql +5 -0
  208. package/templates/app/skeleton/.starciwork/features/system-health/index.yaml +4 -0
  209. package/templates/app/skeleton/.starciwork/{features/index.yaml → index.yaml} +2 -2
  210. package/templates/app/skeleton/README.md +7 -5
  211. package/templates/app/skeleton-lite/.sops.yaml +3 -0
  212. package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +5 -0
  213. package/templates/app/skeleton-lite/.starcistacks/dev/secrets/.gitkeep +1 -0
  214. package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +55 -0
  215. package/templates/app/skeleton-lite/README.md +40 -0
  216. package/templates/app/skeleton-lite/supabase/config.toml +41 -0
  217. package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +56 -0
  218. package/templates/app/skeleton-lite/supabase/seed.sql +0 -0
  219. package/templates/app/task-graph/turbo.json +23 -0
  220. package/templates/app/tool-config-lite/prettierignore +2 -0
  221. package/templates/app/tool-config-lite/prettierrc +1 -0
  222. package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +38 -0
  223. package/templates/app/upgrade-full/.starcistacks/dev/README.md +10 -0
  224. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +13 -0
  225. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +3 -0
  226. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +12 -0
  227. package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +7 -0
  228. package/templates/be/common/main-listen.ts.partial +14 -0
  229. package/templates/be/common/sql-ident.ts.partial +7 -0
  230. package/templates/be/common/sql-tag.ts.partial +5 -0
  231. package/templates/be/image/api/Dockerfile +38 -0
  232. package/templates/be/image/cli/Dockerfile +38 -0
  233. package/templates/be/image/worker/Dockerfile +36 -0
  234. package/templates/be/inbox/database-client.ts.tpl +50 -0
  235. package/templates/be/inbox/index.ts.tpl +2 -0
  236. package/templates/be/inbox/migration.sql.tpl +29 -0
  237. package/templates/be/inbox/module-definition.ts.tpl +6 -0
  238. package/templates/be/inbox/module.ts.tpl +14 -0
  239. package/templates/be/inbox/options.ts.tpl +2 -0
  240. package/templates/be/inbox/service.ts.tpl +33 -0
  241. package/templates/be/inbox/sql.ts.tpl +16 -0
  242. package/templates/be/inbox/webhook-door.ts.tpl +36 -0
  243. package/templates/be/patterns/api/application.command.http.ts.tpl +10 -0
  244. package/templates/be/patterns/api/application.command.ts.tpl +10 -0
  245. package/templates/be/patterns/api/application.contracts.ts.tpl +11 -0
  246. package/templates/be/patterns/api/application.handler.http.ts.tpl +22 -0
  247. package/templates/be/patterns/api/application.handler.ts.tpl +22 -0
  248. package/templates/be/patterns/api/feature.index.http.ts.tpl +1 -0
  249. package/templates/be/patterns/api/feature.index.ts.tpl +1 -0
  250. package/templates/be/patterns/api/feature.module.ts.tpl +6 -0
  251. package/templates/be/patterns/api/graphql.input.ts.tpl +13 -0
  252. package/templates/be/patterns/api/graphql.mapper.ts.tpl +9 -0
  253. package/templates/be/patterns/api/graphql.module.ts.tpl +7 -0
  254. package/templates/be/patterns/api/graphql.resolver.ts.tpl +27 -0
  255. package/templates/be/patterns/api/graphql.type.ts.tpl +9 -0
  256. package/templates/be/patterns/api/http.controller.ts.tpl +27 -0
  257. package/templates/be/patterns/api/http.mapper.ts.tpl +9 -0
  258. package/templates/be/patterns/api/http.module.ts.tpl +7 -0
  259. package/templates/be/patterns/api/http.request.ts.tpl +10 -0
  260. package/templates/be/patterns/api/http.response.ts.tpl +5 -0
  261. package/templates/be/patterns/cli/group.cli.spec.ts.tpl +15 -0
  262. package/templates/be/patterns/cli/group.cli.ts.tpl +11 -0
  263. package/templates/be/patterns/cli/group.module.ts.tpl +7 -0
  264. package/templates/be/patterns/cli/sub.run.cli.spec.ts.tpl +17 -0
  265. package/templates/be/patterns/cli/sub.run.cli.ts.tpl +15 -0
  266. package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +22 -0
  267. package/templates/be/patterns/event-bus/platform/event-bus.config.ts.tpl +19 -0
  268. package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +58 -0
  269. package/templates/be/patterns/event-bus/platform/event-bus.decorators.ts.tpl +46 -0
  270. package/templates/be/patterns/event-bus/platform/event-bus.log-events.ts.tpl +12 -0
  271. package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +10 -0
  272. package/templates/be/patterns/event-bus/platform/event-bus.module.ts.tpl +52 -0
  273. package/templates/be/patterns/event-bus/platform/event-bus.options.ts.tpl +19 -0
  274. package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +28 -0
  275. package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +165 -0
  276. package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +93 -0
  277. package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +90 -0
  278. package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +58 -0
  279. package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +136 -0
  280. package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +56 -0
  281. package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +272 -0
  282. package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +128 -0
  283. package/templates/be/patterns/event-bus/platform/event-transport.port.ts.tpl +180 -0
  284. package/templates/be/patterns/event-bus/platform/event.policy.spec.ts.tpl +67 -0
  285. package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +38 -0
  286. package/templates/be/patterns/event-bus/platform/index.ts.tpl +11 -0
  287. package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.spec.ts.tpl +348 -0
  288. package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +241 -0
  289. package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +15 -0
  290. package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +8 -0
  291. package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +37 -0
  292. package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +11 -0
  293. package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +12 -0
  294. package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +26 -0
  295. package/templates/be/patterns/jobs/job.index.ts.tpl +1 -0
  296. package/templates/be/patterns/jobs/job.module.ts.tpl +6 -0
  297. package/templates/be/patterns/jobs/job.processor.ts.tpl +20 -0
  298. package/templates/be/patterns/jobs/job.queue.module.ts.tpl +20 -0
  299. package/templates/be/patterns/jobs/job.step.ts.tpl +19 -0
  300. package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +31 -0
  301. package/templates/be/patterns/jobs/platform/fenced.processor.ts.tpl +13 -0
  302. package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +150 -0
  303. package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +100 -0
  304. package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +138 -0
  305. package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +69 -0
  306. package/templates/be/patterns/jobs/platform/jobs.config.ts.tpl +11 -0
  307. package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +62 -0
  308. package/templates/be/patterns/jobs/platform/jobs.decorators.ts.tpl +31 -0
  309. package/templates/be/patterns/jobs/platform/jobs.error.ts.tpl +22 -0
  310. package/templates/be/patterns/jobs/platform/jobs.index.ts.tpl +11 -0
  311. package/templates/be/patterns/jobs/platform/jobs.log-events.ts.tpl +9 -0
  312. package/templates/be/patterns/jobs/platform/jobs.messages.ts.tpl +15 -0
  313. package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +10 -0
  314. package/templates/be/patterns/jobs/platform/jobs.module.ts.tpl +32 -0
  315. package/templates/be/patterns/jobs/platform/jobs.options.ts.tpl +11 -0
  316. package/templates/be/patterns/jobs/platform/jobs.port.ts.tpl +36 -0
  317. package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +8 -0
  318. package/templates/be/patterns/jobs/platform/persistence.job.entity.ts.tpl +53 -0
  319. package/templates/be/patterns/jobs/platform/persistence.jobs.rows.ts.tpl +13 -0
  320. package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +33 -0
  321. package/templates/be/patterns/jobs/platform/persistence.migration.ts.tpl +29 -0
  322. package/templates/be/patterns/outbox/platform/index.ts.tpl +2 -0
  323. package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +96 -0
  324. package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +78 -0
  325. package/templates/be/patterns/outbox/platform/outbox.log-events.ts.tpl +5 -0
  326. package/templates/be/patterns/projections/migration.ts.tpl +18 -0
  327. package/templates/be/patterns/projections/persistence.connection.ts.tpl +8 -0
  328. package/templates/be/patterns/projections/projection.entity.ts.tpl +9 -0
  329. package/templates/be/patterns/projections/projection.index.ts.tpl +3 -0
  330. package/templates/be/patterns/projections/projection.module-definition.ts.tpl +7 -0
  331. package/templates/be/patterns/projections/projection.module.ts.tpl +18 -0
  332. package/templates/be/patterns/projections/projection.options.ts.tpl +2 -0
  333. package/templates/be/patterns/projections/projection.projection.spec.ts.tpl +55 -0
  334. package/templates/be/patterns/projections/projection.projection.ts.tpl +21 -0
  335. package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +142 -0
  336. package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +81 -0
  337. package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +8 -0
  338. package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +25 -0
  339. package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +25 -0
  340. package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +9 -0
  341. package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +12 -0
  342. package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +48 -0
  343. package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +17 -0
  344. package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +128 -0
  345. package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +48 -0
  346. package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +115 -0
  347. package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +91 -0
  348. package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +45 -0
  349. package/templates/be/patterns/queues/platform/queue.config.ts.tpl +15 -0
  350. package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +31 -0
  351. package/templates/be/patterns/queues/platform/queue.decorators.ts.tpl +45 -0
  352. package/templates/be/patterns/queues/platform/queue.index.ts.tpl +7 -0
  353. package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +5 -0
  354. package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +10 -0
  355. package/templates/be/patterns/queues/platform/queue.module.ts.tpl +62 -0
  356. package/templates/be/patterns/queues/platform/queue.options.ts.tpl +22 -0
  357. package/templates/be/patterns/queues/platform/queue.policy.spec.ts.tpl +19 -0
  358. package/templates/be/patterns/queues/platform/queue.policy.ts.tpl +11 -0
  359. package/templates/be/patterns/queues/platform/queue.port.ts.tpl +14 -0
  360. package/templates/be/patterns/queues/queue.index.ts.tpl +3 -0
  361. package/templates/be/patterns/queues/queue.module.ts.tpl +6 -0
  362. package/templates/be/patterns/queues/queue.queue.ts.tpl +23 -0
  363. package/templates/be/patterns/reactors/application.command.ts.tpl +10 -0
  364. package/templates/be/patterns/reactors/application.contracts.ts.tpl +12 -0
  365. package/templates/be/patterns/reactors/application.handler.ts.tpl +22 -0
  366. package/templates/be/patterns/reactors/consumer.ts.tpl +19 -0
  367. package/templates/be/patterns/reactors/message.module.ts.tpl +20 -0
  368. package/templates/be/patterns/reactors/reactor.index.ts.tpl +1 -0
  369. package/templates/be/patterns/reactors/reactor.module.ts.tpl +6 -0
  370. package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +6 -0
  371. package/templates/be/patterns/realtime/channel.index.ts.tpl +1 -0
  372. package/templates/be/patterns/realtime/channel.input.ts.tpl +12 -0
  373. package/templates/be/patterns/realtime/channel.subscription.spec.ts.tpl +51 -0
  374. package/templates/be/patterns/realtime/channel.subscription.ts.tpl +32 -0
  375. package/templates/be/patterns/realtime/channel.type.ts.tpl +9 -0
  376. package/templates/be/patterns/saga/saga.compensate.command.ts.tpl +11 -0
  377. package/templates/be/patterns/saga/saga.compensate.contracts.ts.tpl +7 -0
  378. package/templates/be/patterns/saga/saga.compensate.handler.ts.tpl +22 -0
  379. package/templates/be/patterns/saga/saga.compensation.ts.tpl +21 -0
  380. package/templates/be/patterns/saga/saga.complete.command.ts.tpl +11 -0
  381. package/templates/be/patterns/saga/saga.complete.contracts.ts.tpl +7 -0
  382. package/templates/be/patterns/saga/saga.complete.handler.ts.tpl +22 -0
  383. package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +21 -0
  384. package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +21 -0
  385. package/templates/be/patterns/saga/saga.index.ts.tpl +1 -0
  386. package/templates/be/patterns/saga/saga.message.module.ts.tpl +23 -0
  387. package/templates/be/patterns/saga/saga.module.ts.tpl +20 -0
  388. package/templates/be/patterns/saga/saga.service.spec.ts.tpl +70 -0
  389. package/templates/be/patterns/saga/saga.service.ts.tpl +44 -0
  390. package/templates/be/patterns/saga/saga.state.ts.tpl +14 -0
  391. package/templates/be/patterns/saga/saga.step.ts.tpl +11 -0
  392. package/templates/be/patterns/saga/saga.undo.command.ts.tpl +10 -0
  393. package/templates/be/patterns/saga/saga.undo.contracts.ts.tpl +5 -0
  394. package/templates/be/patterns/saga/saga.undo.handler.ts.tpl +21 -0
  395. package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +72 -0
  396. package/templates/be/patterns/webhooks/webhook.door.ts.tpl +36 -0
  397. package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +6 -0
  398. package/templates/be/patterns/webhooks/webhook.index.ts.tpl +1 -0
  399. package/templates/be/patterns/webhooks/webhook.request.ts.tpl +9 -0
  400. package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +3 -0
  401. package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -2
  402. package/templates/be/skeleton/apps/__app__/src/main.ts +7 -15
  403. package/templates/be/skeleton/apps/cli/src/app.module.ts +30 -0
  404. package/templates/be/skeleton/apps/cli/src/cli.options.ts +22 -0
  405. package/templates/be/skeleton/apps/cli/src/main.ts +24 -0
  406. package/templates/be/skeleton/src/features/cli/cli.module.ts +7 -0
  407. package/templates/be/skeleton/src/features/cli/index.ts +1 -0
  408. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +17 -0
  409. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +15 -0
  410. package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +7 -0
  411. package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.spec.ts +17 -0
  412. package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.ts +18 -0
  413. package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +17 -0
  414. package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +15 -0
  415. package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +7 -0
  416. package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +33 -0
  417. package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.ts +34 -0
  418. package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +13 -0
  419. package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +40 -0
  420. package/templates/be/skeleton/src/modules/domain/identity/messages/identity.messages.ts +1 -1
  421. package/templates/be/skeleton/src/modules/domain/note/errors/note.error.ts +16 -0
  422. package/templates/be/skeleton/src/modules/domain/note/index.ts +3 -0
  423. package/templates/be/skeleton/src/modules/domain/note/note.contracts.ts +19 -0
  424. package/templates/be/skeleton/src/modules/domain/note/note.decorators.ts +9 -0
  425. package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +7 -0
  426. package/templates/be/skeleton/src/modules/domain/note/note.module.ts +19 -0
  427. package/templates/be/skeleton/src/modules/domain/note/note.options.ts +2 -0
  428. package/templates/be/skeleton/src/modules/domain/note/note.service.spec.ts +76 -0
  429. package/templates/be/skeleton/src/modules/domain/note/note.service.ts +37 -0
  430. package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +8 -0
  431. package/templates/be/skeleton/src/modules/domain/note/persistence/entities/note.entity.ts +17 -0
  432. package/templates/be/skeleton/src/modules/domain/note/persistence/migrations/1790000000000-init-note.ts +22 -0
  433. package/templates/be/skeleton/src/modules/domain/note/persistence/note.rows.ts +17 -0
  434. package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +4 -0
  435. package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +20 -0
  436. package/templates/be/skeleton/src/modules/platform/config/index.ts +1 -1
  437. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +18 -0
  438. package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +41 -0
  439. package/templates/be/skeleton/src/modules/platform/database/connection-source.client.ts +17 -0
  440. package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +13 -0
  441. package/templates/be/skeleton/src/modules/platform/database/database.module.ts +77 -0
  442. package/templates/be/skeleton/src/modules/platform/database/database.options.ts +30 -0
  443. package/templates/be/skeleton/src/modules/platform/database/database.port.ts +65 -0
  444. package/templates/be/skeleton/src/modules/platform/database/database.sql.ts +47 -0
  445. package/templates/be/skeleton/src/modules/platform/database/errors/database.error.ts +16 -0
  446. package/templates/be/skeleton/src/modules/platform/database/index.ts +9 -0
  447. package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +30 -0
  448. package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.spec.ts +84 -0
  449. package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.ts +23 -0
  450. package/templates/be/skeleton/src/modules/platform/database/primary.config.ts +9 -0
  451. package/templates/be/skeleton/src/modules/platform/database/primary.connection.ts +2 -0
  452. package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +10 -0
  453. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +108 -0
  454. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +75 -0
  455. package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.spec.ts +88 -0
  456. package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.ts +29 -0
  457. package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +48 -0
  458. package/templates/be/skeleton/src/modules/platform/errors/messages/errors.messages.ts +1 -1
  459. package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +12 -0
  460. package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -0
  461. package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +8 -1
  462. package/templates/be/skeleton/src/modules/platform/http-security/http-security.decorators.ts +8 -0
  463. package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +14 -1
  464. package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +12 -0
  465. package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +10 -2
  466. package/templates/be/skeleton/src/modules/platform/http-security/origin.guard.spec.ts +63 -0
  467. package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +108 -0
  468. package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.spec.ts +39 -0
  469. package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.ts +25 -0
  470. package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +172 -0
  471. package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.ts +66 -0
  472. package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +3 -1
  473. package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +52 -0
  474. package/templates/be/skeleton-lite/apps/api/Dockerfile +37 -0
  475. package/templates/be/skeleton-lite/apps/api/src/api.options.ts +16 -0
  476. package/templates/be/skeleton-lite/apps/api/src/app.module.ts +81 -0
  477. package/templates/be/skeleton-lite/apps/api/src/main.ts +34 -0
  478. package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +14 -0
  479. package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +50 -0
  480. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +26 -0
  481. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +19 -0
  482. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +26 -0
  483. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +7 -0
  484. package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +6 -0
  485. package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +19 -0
  486. package/templates/be/skeleton-lite/src/modules/integrations/supabase/index.ts +10 -0
  487. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.ts +24 -0
  488. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.config.ts +8 -0
  489. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.decorators.ts +17 -0
  490. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.jwks.ts +32 -0
  491. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.options.ts +24 -0
  492. package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +4 -0
  493. package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +54 -0
  494. package/templates/be/skeleton-lite/src/modules/platform/database/database.options.ts +23 -0
  495. package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +10 -0
  496. package/templates/be/skeleton-lite/src/modules/platform/database/database.sql.ts +49 -0
  497. package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +4 -0
  498. package/templates/be/skeleton-lite/src/modules/platform/database/migration-runner.service.ts +21 -0
  499. package/templates/be/skeleton-lite/src/modules/platform/database/primary.config.ts +11 -0
  500. package/templates/be/skeleton-lite/src/modules/platform/database/seed-runner.service.ts +28 -0
  501. package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +25 -0
  502. package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +9 -0
  503. package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +4 -0
  504. package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +5 -0
  505. package/templates/be/table/error.ts.tpl +16 -0
  506. package/templates/be/table/index.ts.tpl +2 -0
  507. package/templates/be/table/migration.sql.tpl +51 -0
  508. package/templates/be/table/module-definition.ts.tpl +7 -0
  509. package/templates/be/table/module.ts.tpl +14 -0
  510. package/templates/be/table/options.ts.tpl +2 -0
  511. package/templates/be/table/rows.ts.tpl +4 -0
  512. package/templates/be/table/service.ts.tpl +23 -0
  513. package/templates/be/table/sql.ts.tpl +4 -0
  514. package/templates/be/tool-config/jest.config.js +3 -1
  515. package/templates/be/upgrade-full/apps/cli/src/app.module.ts +14 -0
  516. package/templates/be/upgrade-full/apps/cli/src/main.ts +26 -0
  517. package/templates/be/upgrade-full/src/features/cli/cli.module.ts +6 -0
  518. package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +23 -0
  519. package/templates/common/gitignore.base +1 -2
  520. package/templates/fe/app/package.json.tpl +1 -0
  521. package/templates/fe/app/tsconfig.json +12 -0
  522. package/templates/fe/common/app-layout.tsx.partial +8 -0
  523. package/templates/fe/common/site-shell.tsx.partial +9 -0
  524. package/templates/fe/image/next/Dockerfile +42 -0
  525. package/templates/fe/skeleton/apps/app/next.config.ts +23 -0
  526. package/templates/fe/skeleton/apps/app/postcss.config.mjs +7 -0
  527. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/error.tsx +2 -2
  528. package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +22 -0
  529. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/loading.tsx +2 -2
  530. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/not-found.tsx +2 -2
  531. package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +9 -0
  532. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/global-error.tsx +2 -2
  533. package/templates/fe/skeleton/apps/app/src/app/globals.css +10 -0
  534. package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +18 -0
  535. package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/component.tsx +19 -0
  536. package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/index.tsx +11 -0
  537. package/templates/fe/skeleton/apps/app/src/features/pages/AppGlobalErrorPage/index.tsx +16 -0
  538. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/component.tsx +10 -0
  539. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +15 -0
  540. package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +8 -0
  541. package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +9 -0
  542. package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +4 -0
  543. package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +28 -0
  544. package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -0
  545. package/templates/fe/skeleton/apps/app/src/modules/routes/index.ts +4 -0
  546. package/templates/fe/skeleton/apps/app/src/proxy.ts +1 -0
  547. package/templates/fe/skeleton/apps/{__app__ → landing}/next.config.ts +4 -2
  548. package/templates/fe/skeleton/apps/landing/postcss.config.mjs +7 -0
  549. package/templates/fe/skeleton/apps/landing/src/app/[locale]/error.tsx +10 -0
  550. package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +22 -0
  551. package/templates/fe/skeleton/apps/landing/src/app/[locale]/loading.tsx +6 -0
  552. package/templates/fe/skeleton/apps/landing/src/app/[locale]/not-found.tsx +6 -0
  553. package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +9 -0
  554. package/templates/fe/skeleton/apps/landing/src/app/global-error.tsx +10 -0
  555. package/templates/fe/skeleton/apps/landing/src/app/globals.css +10 -0
  556. package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +24 -0
  557. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/component.tsx +18 -0
  558. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/index.tsx +11 -0
  559. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingGlobalErrorPage/index.tsx +16 -0
  560. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +8 -0
  561. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +9 -0
  562. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/component.tsx +24 -0
  563. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +16 -0
  564. package/templates/fe/skeleton/apps/landing/src/modules/config/index.ts +12 -0
  565. package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +7 -0
  566. package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/vi.json +31 -0
  567. package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -0
  568. package/templates/fe/skeleton/apps/landing/src/modules/routes/index.ts +4 -0
  569. package/templates/fe/skeleton/apps/landing/src/proxy.ts +1 -0
  570. package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +49 -0
  571. package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +24 -0
  572. package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +20 -0
  573. package/templates/fe/skeleton/packages/__project__-ui/src/branches/Providers/index.tsx +23 -0
  574. package/templates/fe/skeleton/packages/__project__-ui/src/composites/ErrorNotice/index.tsx +14 -0
  575. package/templates/fe/skeleton/packages/__project__-ui/src/composites/GlobalErrorNotice/index.tsx +25 -0
  576. package/templates/fe/skeleton/packages/__project__-ui/src/composites/LoadingNotice/index.tsx +9 -0
  577. package/templates/fe/skeleton/packages/__project__-ui/src/composites/LocaleShell/index.tsx +21 -0
  578. package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +19 -0
  579. package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/classNames.ts +4 -0
  580. package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/index.tsx +19 -0
  581. package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +6 -0
  582. package/templates/fe/skeleton-lite/apps/web/next.config.ts +22 -0
  583. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/error.tsx +10 -0
  584. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +37 -0
  585. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/loading.tsx +6 -0
  586. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/not-found.tsx +6 -0
  587. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +9 -0
  588. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/providers.tsx +26 -0
  589. package/templates/fe/skeleton-lite/apps/web/src/app/auth/callback/route.ts +24 -0
  590. package/templates/fe/skeleton-lite/apps/web/src/app/global-error.tsx +10 -0
  591. package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/src/app/globals.css +1 -1
  592. package/templates/fe/skeleton-lite/apps/web/src/app/health/live/route.ts +5 -0
  593. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/component.tsx +29 -0
  594. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +41 -0
  595. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/component.tsx +17 -0
  596. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/index.tsx +38 -0
  597. package/templates/fe/skeleton-lite/apps/web/src/components/composites/ErrorNotice/index.tsx +13 -0
  598. package/templates/fe/skeleton-lite/apps/web/src/components/composites/GlobalErrorNotice/index.tsx +24 -0
  599. package/templates/fe/skeleton-lite/apps/web/src/components/composites/LoadingNotice/index.tsx +7 -0
  600. package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +18 -0
  601. package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/classNames.ts +4 -0
  602. package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/index.tsx +13 -0
  603. package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +31 -0
  604. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/component.tsx +12 -0
  605. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/index.tsx +10 -0
  606. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppGlobalErrorPage/index.tsx +15 -0
  607. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +14 -0
  608. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +30 -0
  609. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +8 -0
  610. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +9 -0
  611. package/templates/fe/skeleton-lite/apps/web/src/modules/brand/brand.css +20 -0
  612. package/templates/fe/skeleton-lite/apps/web/src/modules/brand/index.ts +2 -0
  613. package/templates/fe/skeleton-lite/apps/web/src/modules/config/index.ts +20 -0
  614. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/read-session.ts +25 -0
  615. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-in.ts +25 -0
  616. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +17 -0
  617. package/templates/fe/skeleton-lite/apps/web/src/modules/db/browser.ts +14 -0
  618. package/templates/fe/skeleton-lite/apps/web/src/modules/db/index.ts +3 -0
  619. package/templates/fe/skeleton-lite/apps/web/src/modules/db/outcome.ts +59 -0
  620. package/templates/fe/skeleton-lite/apps/web/src/modules/db/principal.ts +29 -0
  621. package/templates/fe/skeleton-lite/apps/web/src/modules/db/server.ts +70 -0
  622. package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +50 -0
  623. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +8 -0
  624. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +48 -0
  625. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +16 -0
  626. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +14 -0
  627. package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +7 -0
  628. package/templates/fe/skeleton-lite/apps/web/src/modules/routes/safe-next-path.ts +26 -0
  629. package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +43 -0
  630. package/templates/fe/table/read.ts.tpl +16 -0
  631. package/templates/fe/table/write.ts.tpl +16 -0
  632. package/upgrade/check.mjs +137 -0
  633. package/upgrade/index.mjs +524 -0
  634. package/bin/hfs.mjs +0 -233
  635. package/runtime/scripts/api/fs/rmdir-link.mjs +0 -12
  636. package/runtime/scripts/lib/test-secrets.mjs +0 -116
  637. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +0 -20
  638. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/page.tsx +0 -9
  639. package/templates/fe/skeleton/apps/__app__/src/components/composites/FailureScreen/index.tsx +0 -28
  640. package/templates/fe/skeleton/apps/__app__/src/features/layouts/LocaleShell/index.tsx +0 -35
  641. package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/component.tsx +0 -19
  642. package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/index.tsx +0 -11
  643. package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/component.tsx +0 -24
  644. package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/index.tsx +0 -14
  645. package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/component.tsx +0 -21
  646. package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/index.tsx +0 -15
  647. package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/component.tsx +0 -23
  648. package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/index.tsx +0 -8
  649. package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/component.tsx +0 -28
  650. package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/index.tsx +0 -8
  651. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/config.ts +0 -8
  652. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/index.ts +0 -2
  653. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages/vi.json +0 -25
  654. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/request.ts +0 -19
  655. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.ts +0 -9
  656. package/templates/fe/skeleton/apps/__app__/src/modules/routes/index.ts +0 -4
  657. package/templates/fe/skeleton/apps/__app__/src/proxy.ts +0 -10
  658. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.contracts.ts +0 -0
  659. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.handler.ts +0 -0
  660. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.query.ts +0 -0
  661. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/index.ts +0 -0
  662. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/system-health.module.ts +0 -0
  663. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/live.controller.ts +0 -0
  664. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/system-health-http.module.ts +0 -0
  665. /package/templates/fe/skeleton/apps/{__app__ → app}/src/app/health/live/route.ts +0 -0
  666. /package/templates/fe/skeleton/apps/{__app__ → app}/src/modules/config/index.ts +0 -0
  667. /package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/postcss.config.mjs +0 -0
@@ -0,0 +1,369 @@
1
+ schema: starci/knowledge-source@1
2
+ id: be.api
3
+ title: Backend api feature
4
+ purpose: |-
5
+ Given a scenario a request or a background transport triggers, where does it live and how is it written? It is an api
6
+ feature, `be/src/features/api/<feature>/`, the trigger kind the api apps compose. Its application layer is CQRS: one
7
+ typed command or query per operation and one handler that owns the scenario, dispatched by a transport directly on the
8
+ bus. There is no use-case class and no forwarder service. Inside one bounded context the work is synchronous; an event
9
+ only crosses contexts or carries async work.
10
+ appliesTo:
11
+ - backend
12
+ family: be
13
+ # The slot ids this topic teaches (knowledge/hfs/slots.yaml); scripts/gates/read-digest.mjs inverts these
14
+ # into the READ-step slot -> pattern-topic map (knowledge/op-gate.yaml kinds), each slot's topics in index.yaml order.
15
+ slots: [be.feature, be.feature.application]
16
+ provenance:
17
+ reviewedAt: 2026-10-02
18
+ sources:
19
+ - knowledge/hfs/README.md
20
+ - knowledge/hfs/slots.yaml
21
+ - knowledge/hfs/rules.yaml
22
+ - owner-locked BE convention 2026-09-30
23
+ - owner decision 2026-10-02 (features grouped by trigger kind; synchronous inside a context)
24
+ - https://docs.nestjs.com/recipes/cqrs
25
+ limitations: |-
26
+ Static checks prove the shape of a feature, a message, its handler and the dispatch, and that SQL touches only tables its
27
+ owner may reach. They do not prove that a handler decides the right thing, that a transaction boundary is right, that a
28
+ context boundary is drawn where the business draws it or that a refusal is chosen over an exception correctly; the
29
+ service spec of the service the handler calls and the e2e run prove behavior, and design review proves the boundary.
30
+ rules:
31
+ - id: BE-CQRS-1
32
+ title: The application folder holds messages, handlers, contracts and support only
33
+ kind: mandatory
34
+ hfsRules: [R87, R29]
35
+ requirement: |-
36
+ `be/src/features/api/<feature>/application/` holds `<action>.command.ts` or `<action>.query.ts`, `<action>.handler.ts`,
37
+ `<action>.contracts.ts` and the opt-in `support/` folder of helpers local to the feature. A
38
+ `*.use-case.ts` file, a forwarder `<op>.service.ts` and a folder named after a protocol do not exist. A read is a
39
+ query and a write is a command; a handler never serves both.
40
+ rationale: |-
41
+ One shape for every operation gives one interception point and one message object, so a reader finds the scenario
42
+ in the handler and never in a wrapper around it.
43
+ cases:
44
+ - id: case-1
45
+ when: A new write operation
46
+ write: 'application/start-checkout.command.ts, start-checkout.handler.ts, start-checkout.contracts.ts (the handler has no spec: its service does).'
47
+ - id: case-2
48
+ when: A new read operation
49
+ write: 'application/get-cart.query.ts, get-cart.handler.ts, get-cart.contracts.ts (the handler has no spec: its service does).'
50
+ verification:
51
+ automated:
52
+ - BE_CQRS_SHAPE
53
+ - BE_READONLY_BOUNDARY
54
+ - BE_FEATURE_SHAPE
55
+ - BE_FEATURE_LAYOUT_INVALID
56
+ - BE_APPLICATION_IMPORTS_TRANSPORT
57
+ - BE_APPLICATION_TRANSPORT_FRAMEWORK
58
+ manual:
59
+ - Confirm each operation is a command or a query and no handler mixes both.
60
+ relatedExamples: []
61
+ - id: BE-CQRS-2
62
+ title: A message is a typed Command or Query carrying one params field
63
+ kind: mandatory
64
+ hfsRules: [R87, R37]
65
+ requirement: |-
66
+ `export class <Action>Command extends Command<<Action>Result> { constructor(readonly params: ExecuteParams<<Action>Request>) { super() } }`
67
+ (`Query<R>` for reads). One `readonly params` field, no methods, no defaults. `ExecuteParams<T> = { request: T; principal: Principal }`
68
+ for an authenticated operation and `PublicExecuteParams<T> = { request: T }` for a `@Public` one, so the type states
69
+ whether the door is public and the principal is never optional. `Principal = { id: string; roles: ReadonlyArray<Role> }`
70
+ is owned by `platform/cqrs`. An entity never travels in params.
71
+ rationale: |-
72
+ `@nestjs/cqrs` 11 types `execute` with the message's result type, so the compiler proves what the transport gets
73
+ back, and a principal that cannot be absent removes a class of authorization bugs.
74
+ cases:
75
+ - id: case-1
76
+ when: An authenticated command
77
+ write: 'export class StartCheckoutCommand extends Command<StartCheckoutResult> { constructor(readonly params: ExecuteParams<StartCheckoutRequest>) { super() } }'
78
+ - id: case-2
79
+ when: A public query
80
+ write: 'export class CheckHealthQuery extends Query<CheckHealthResult> { constructor(readonly params: PublicExecuteParams<CheckHealthRequest>) { super() } }'
81
+ verification:
82
+ automated:
83
+ - BE_CQRS_SHAPE
84
+ - BE_READONLY_BOUNDARY
85
+ - BE_ENTITY_IN_CONTRACT
86
+ manual:
87
+ - Review that the request carries what the operation needs and nothing of the transport.
88
+ relatedExamples: []
89
+ - id: BE-CQRS-3
90
+ title: A handler overrides process and returns its declared result
91
+ kind: mandatory
92
+ hfsRules: [R87, R75, R48, R203]
93
+ requirement: |-
94
+ `@CommandHandler(X)` or `@QueryHandler(X)` on a class that `extends ICQRSHandler<X, XResult>` and implements
95
+ `protected override async process(message)`; it never overrides `execute`. `ICQRSHandler.execute` is load-bearing: it
96
+ logs `OperationFailed` with the operation name and the error and rethrows, and counts refused outcomes. `<Action>Result`
97
+ is a plain projection type or an `Outcome<Value, Code>` and the return type of `process` is declared. The handler opens
98
+ the transaction (`this.entityManager.transaction(async (manager) => ...)`), stamps `this.clock.now()` once and passes
99
+ it down. The handler is thin (R203 `BE_FEATURE_THIN`): its `process` maps the message, makes one delegating call to an
100
+ injected `*.service` and returns the result, so it has no unit spec; the decisions (persisted state, the `where` that
101
+ holds ownership, nothing written on a refusal) live in the service and its `<name>.service.spec.ts` asserts them.
102
+ rationale: |-
103
+ A single template gives every operation the same failure log and metric, and the service spec pins the decision the
104
+ handler delegates.
105
+ cases:
106
+ - id: case-1
107
+ when: A handler
108
+ write: |-
109
+ @CommandHandler(StartCheckoutCommand)
110
+ /** Opens a checkout, or refuses with a purchase code. */
111
+ export class StartCheckoutHandler extends ICQRSHandler<StartCheckoutCommand, StartCheckoutResult> {
112
+ constructor(
113
+ @InjectPrimaryEntityManager() private readonly entityManager: EntityManager,
114
+ @InjectClock() private readonly clock: Clock,
115
+ private readonly purchaseService: PurchaseService,
116
+ ) { super() }
117
+
118
+ protected override async process(command: StartCheckoutCommand): Promise<StartCheckoutResult> {
119
+ const at = this.clock.now()
120
+ return this.entityManager.transaction(async (manager) => this.purchaseService.open({ manager, learnerId: command.params.principal.id, offerId: command.params.request.offerId, at }))
121
+ }
122
+ }
123
+ verification:
124
+ automated:
125
+ - BE_CQRS_SHAPE
126
+ - BE_READONLY_BOUNDARY
127
+ - BE_RETURN_TYPE
128
+ - BE_SPEC_QUALITY
129
+ - BE_FEATURE_THIN
130
+ manual:
131
+ - Confirm the service spec the handler calls asserts refusal paths and ownership, not only that a mock was called.
132
+ relatedExamples: []
133
+ - id: BE-CQRS-4
134
+ title: The bus is the only way into a handler; nothing forwards
135
+ kind: mandatory
136
+ hfsRules: [R87, R88, R90]
137
+ requirement: |-
138
+ A handler is reached only through `commandBus.execute` or `queryBus.execute` from a transport (the transport side is
139
+ transport.yaml BE-TRANSPORT-2). No `<op>.service.ts` sits between a transport and a handler. Work that stays
140
+ inside one context is synchronous: the handler calls the domain service in one transaction. A follow-up that must happen
141
+ anyway or that crosses a context is an event published inside that transaction (event-bus.yaml, BE-API-2), never an
142
+ in-process event: `EventBus`, `@EventsHandler`, `EventEmitter2`, `@OnEvent` and NATS bridges are not used.
143
+ rationale: |-
144
+ A forwarder is pure indirection where logic creeps in, and an in-process event does not survive a crash between the
145
+ commit and the delivery.
146
+ cases:
147
+ - id: case-1
148
+ when: A resolver needs an operation
149
+ write: '`constructor(@InjectCommandBus() private readonly commandBus: CommandBus) {}` then `this.commandBus.execute(new StartCheckoutCommand({ request, principal }))`.'
150
+ - id: case-2
151
+ when: A follow-up effect in another context after a write
152
+ write: 'the domain service the handler calls publishes the event with eventBus.publish(event, tx) in its own transaction; a reactor or job consumes it (event-bus.yaml).'
153
+ verification:
154
+ automated:
155
+ - BE_CQRS_SHAPE
156
+ - BE_READONLY_BOUNDARY
157
+ - BE_TRANSPORT_SHAPE
158
+ - BE_INFRA_OWNER
159
+ manual: []
160
+ relatedExamples: []
161
+ - id: BE-API-1
162
+ title: An api feature has one fixed shape and is composed only by apps
163
+ kind: mandatory
164
+ hfsRules: [R29, R28, R30, R31, R45]
165
+ requirement: |-
166
+ An api feature is `be/src/features/api/<feature>/` (slot `be.feature`) and holds `index.ts`, `<feature>.module.ts`,
167
+ `application/` (`be.feature.application`, with the opt-in `support/`, `be.feature.application.support`),
168
+ `transport/<protocol>/` and, when it has copy, `messages/` (`be.feature.messages`), and nothing else. A protocol lives
169
+ in `transport/graphql`, `transport/http` (`be.transport.{graphql,http}`, composed by an api app) or
170
+ `transport/message` (`be.transport.message`, composed by the api app that owns it or by a worker app), each with
171
+ exactly one Nest module `<feature>-<protocol>.module.ts`, its resolvers, controllers or consumers, their `<action>.mapper.ts` and `dto/` where the protocol has DTOs (shapes in
172
+ transport.yaml). The feature imports only downward into `src/modules/*`, never another api feature and never another
173
+ trigger kind such as `features/cli`; only `be/apps/*` composes it (folder.yaml BE-FOLDER-2).
174
+ rationale: |-
175
+ Several entry protocols share one operation without importing transport DTOs into the application layer, and an app can
176
+ compose only the transports it serves.
177
+ cases:
178
+ - id: case-1
179
+ when: An operation reachable over HTTP and GraphQL
180
+ write: application/place-order.command.ts and its handler once; transport/http/place-order.controller.ts and transport/graphql/place-order.resolver.ts each dispatch it.
181
+ - id: case-2
182
+ when: A conversion has substance
183
+ write: transport/graphql/place-order.mapper.ts with toPlaceOrderType and toPlaceOrderRequest.
184
+ - id: case-3
185
+ when: A feature grows past 250 source files
186
+ write: Split it by product capability into api features, moving shared behavior into a domain capability.
187
+ - id: case-4
188
+ when: An api feature imports features/cli/seed/subs/demo.cli.ts to reuse its rows
189
+ write: 'Refused (BE_FEATURE_IMPORTS_FEATURE): both kinds import the domain capability that owns the rows instead.'
190
+ verification:
191
+ automated:
192
+ - BE_FEATURE_SHAPE
193
+ - BE_FEATURE_LAYOUT_INVALID
194
+ - BE_APPLICATION_IMPORTS_TRANSPORT
195
+ - BE_APPLICATION_TRANSPORT_FRAMEWORK
196
+ - BE_FEATURE_IMPORTS_FEATURE
197
+ - BE_PUBLIC_SURFACE
198
+ - ARCH_OWNER_EXPORT_BYPASS
199
+ - ARCH_OWNER_EXPORT_STAR
200
+ - HFS_ALIAS_REEXPORT
201
+ - ARCH_PACKAGE_EXPORT_BYPASS
202
+ - BE_FEATURE_NOT_COMPOSED
203
+ - BE_MODULE_SHAPE
204
+ - BE_MODULE_HANDLER_REGISTRATION
205
+ - BE_MODULE_PROVIDER_REREGISTRATION
206
+ manual:
207
+ - Review application contracts for request-context, ORM entity and protocol DTO leaks.
208
+ relatedExamples: []
209
+ - id: BE-API-2
210
+ title: Inside one context the work is synchronous; events only cross contexts or carry async work
211
+ kind: mandatory
212
+ hfsRules: [R87, R26, R86, R82]
213
+ requirement: |-
214
+ A context is one bounded context: one domain capability set under `src/modules/domain/`, which may own one Postgres
215
+ schema. Work inside one context is synchronous: the handler calls that context's domain service directly, in one
216
+ transaction it opens, and emits no event to reach code of the same context. An event (an outbox message consumed in
217
+ `transport/message/`) exists only to cross into another context or to carry work that runs later. A feature never reads
218
+ or writes another context's tables or state directly; it receives that context's facts through an event or reads a
219
+ projection its own context owns.
220
+ rationale: |-
221
+ An event inside one context turns one atomic decision into a chain that can half-fail, while a direct read of another
222
+ context's state couples two owners through their storage and bypasses the owner's rules.
223
+ cases:
224
+ - id: case-1
225
+ when: add-cart-item (passing)
226
+ write: 'AddCartItemHandler opens one transaction and calls `this.cartService.addItem({ manager, buyerId, productId, quantity, at })`; the cart rows and the quantity check commit together, with no event.'
227
+ - id: case-2
228
+ when: The order feature needs the payment state (violating)
229
+ write: 'Refused: PlaceOrderHandler querying the payment tables or calling PaymentService to read whether a payment settled. The payment context publishes PaymentSettled through its outbox, and an order consumer in transport/message records it in an order-owned projection the order handler reads.'
230
+ - id: case-3
231
+ when: A confirmation mail after the order commits (async)
232
+ write: 'The order handler enqueues OrderPlaced in its transaction; the mail runs later from a consumer, never inside the order transaction.'
233
+ verification:
234
+ automated:
235
+ - BE_CQRS_SHAPE
236
+ - BE_READONLY_BOUNDARY
237
+ - BE_TIER_DIRECTION
238
+ - FE_TIER_DIRECTION
239
+ - ARCH_PACKAGE_IMPORTS_APP
240
+ - BE_SQL_TABLE_OWNER
241
+ - BE_TRANSACTION_EXTERNAL_CALL
242
+ manual:
243
+ - Confirm each event crosses a context boundary or carries deferred work, and that no handler waits on an event of its own context.
244
+ relatedExamples: []
245
+ - id: BE-API-3
246
+ title: Every operation has an e2e flow on the real app
247
+ kind: mandatory
248
+ hfsRules: [R46, R41]
249
+ requirement: |-
250
+ Each resolver method and controller action of an api feature is driven by an e2e spec (`src/tests/e2e/<area>/*.e2e-spec.ts`) that boots the
251
+ app's real AppModule and calls it as a client does: the happy path, the refusal the command declares and the denial of an unauthenticated caller.
252
+ rationale: |-
253
+ A transport is thin (R203 `BE_FEATURE_THIN`) and has no logic to unit-test; what can break is the wiring, the guards and the mapping, which only the booted app exercises.
254
+ cases:
255
+ - id: case-1
256
+ when: A new mutation
257
+ write: 'checkout.e2e-spec.ts signs a shopper in, runs startCheckout, asserts the typed payload, then asserts the same call without a token is refused.'
258
+ verification:
259
+ automated:
260
+ - BE_BACKGROUND_UNOWNED
261
+ - BE_DEFAULT_DENY
262
+ manual:
263
+ - Confirm the spec asserts the error code of each refusal, not only a status.
264
+ relatedExamples: [api]
265
+ - id: BE-OPERATIONS-1
266
+ title: A versioned operation is declared once in the app's typed operation table
267
+ kind: mandatory
268
+ hfsRules: [R95, R88, R23]
269
+ requirement: |-
270
+ A versioned operation `<name>@<version>` (for example `sales.policy@1`) that an app serves over one operation route is
271
+ declared once, in the app's operation table `be/apps/<app>/src/operations.ts`, which exports `OPERATIONS`. The table is made by
272
+ `defineOperations` of the platform capability `operations` (`be/src/modules/platform/operations/`, which declares
273
+ `OperationContract<Input, Output, RefusalCode>`, `query`, `mutation`, `defineOperations`, `OperationRequest<Table>` and
274
+ `OperationReply<Table>`). `Input` and `Output` are named, closed types: never `any`, `unknown`, `Record<string, unknown>`,
275
+ `object`, an unbound generic, a function or `Date`. `RefusalCode` is a closed union of string literals, or omitted for an
276
+ operation that never refuses. The one route of the app takes `@Body() request: OperationRequest<Table>` and answers
277
+ `Promise<OperationReply<Table>>` of the same table; the wire is `{ operation, requestId, input }` in and `{ operation, requestId,
278
+ outcome }` out, `outcome` being the canon Outcome `{ kind: "ok", value }` or `{ kind: "refused", code }`. `starci app emit`
279
+ derives `be/contracts/<app>/openapi.json` (OpenAPI 3.1, `x-operation: "<name>@<version>"`, a oneOf over the operations) from the
280
+ table with the type checker, and the full `starci app check` fails when the committed file is stale.
281
+ rationale: |-
282
+ The operation set of an app is a contract the front end (`fe/`, through the root codegen) and other consumers generate a client from. A name-to-string registry with `unknown`
283
+ bodies and results forces every client to hand-type the wire; one typed table gives the client generator, the route and the
284
+ handlers the same types.
285
+ cases:
286
+ - id: case-1
287
+ when: A query with two refusals and a mutation with none
288
+ write: |-
289
+ // be/apps/core/src/operations.ts
290
+ export const OPERATIONS = defineOperations({
291
+ "sales.policy@1": query<SalesPolicyAsk, SalesPolicyView, "DENIED" | "INVALID">(),
292
+ "sales.close@1": mutation<SalesCloseAsk, SalesOpportunityView>(),
293
+ })
294
+ // features/api/sales/transport/http/operations.controller.ts
295
+ @Post("operations")
296
+ async invoke(@Body() request: OperationRequest<typeof OPERATIONS>): Promise<OperationReply<typeof OPERATIONS>> { ... }
297
+ verification:
298
+ automated:
299
+ - BE_OPERATION_CONTRACT
300
+ - BE_TRANSPORT_SHAPE
301
+ - HFS_CONTRACT_SNAPSHOT_DRIFT
302
+ manual:
303
+ - Confirm each refusal code is one the operation's owner can actually answer.
304
+ relatedExamples: [api]
305
+ files:
306
+ - path: src/features/api/<feature>/index.ts
307
+ slot: be.feature
308
+ role: The public surface of the feature; only apps import it.
309
+ rules: [BE_PUBLIC_SURFACE]
310
+ template: api/feature.index.ts.tpl
311
+ - path: src/features/api/<feature>/<feature>.module.ts
312
+ slot: be.feature
313
+ role: Composes the application handlers.
314
+ template: api/feature.module.ts.tpl
315
+ - path: src/features/api/<feature>/application/<action>.command.ts
316
+ slot: be.feature.application
317
+ role: One command (or `.query.ts`) carrying one `params` field.
318
+ template: api/application.command.ts.tpl
319
+ - path: src/features/api/<feature>/application/<action>.handler.ts
320
+ slot: be.feature.application
321
+ role: Overrides `process` and calls the domain service of its context.
322
+ template: api/application.handler.ts.tpl
323
+ - path: src/features/api/<feature>/application/<action>.contracts.ts
324
+ slot: be.feature.application
325
+ role: The plain request and result types of the action.
326
+ template: api/application.contracts.ts.tpl
327
+ - path: src/features/api/<feature>/transport/graphql/<action>.resolver.ts
328
+ slot: be.transport.graphql
329
+ role: The GraphQL door of one operation; dispatches one message.
330
+ rules: [BE_TRANSPORT_SHAPE, BE_DEFAULT_DENY]
331
+ template: api/graphql.resolver.ts.tpl
332
+ - path: src/features/api/<feature>/transport/graphql/dto/<action>.input.ts
333
+ slot: be.transport.graphql
334
+ role: The bounded input of the operation.
335
+ rules: [BE_INPUT_BOUNDED]
336
+ template: api/graphql.input.ts.tpl
337
+ - path: src/features/api/<feature>/transport/graphql/dto/<action>.type.ts
338
+ slot: be.transport.graphql
339
+ role: The typed output of the operation.
340
+ template: api/graphql.type.ts.tpl
341
+ - path: src/features/api/<feature>/transport/graphql/<action>.mapper.ts
342
+ slot: be.transport.graphql
343
+ role: Maps the input to the request and the result to the type.
344
+ template: api/graphql.mapper.ts.tpl
345
+ - path: src/features/api/<feature>/transport/graphql/<feature>-graphql.module.ts
346
+ slot: be.transport.graphql
347
+ role: One module for the whole GraphQL transport of the feature.
348
+ template: api/graphql.module.ts.tpl
349
+ - path: src/features/api/<feature>/transport/http/<action>.controller.ts
350
+ slot: be.transport.http
351
+ role: A REST door, only where GraphQL cannot serve (health, byte streams, the operation route).
352
+ optional: true
353
+ rules: [BE_TRANSPORT_SHAPE, BE_DEFAULT_DENY]
354
+ - path: src/features/api/<feature>/transport/http/<feature>-http.module.ts
355
+ slot: be.transport.http
356
+ role: One module for the whole HTTP transport of the feature.
357
+ optional: true
358
+ - path: src/features/api/<feature>/messages/<feature>.messages.ts
359
+ slot: be.feature.messages
360
+ role: The keyed vi and en text of the feature.
361
+ optional: true
362
+ - path: contracts/<app>/schema.graphql
363
+ slot: be.contract.graphql
364
+ role: The emitted GraphQL snapshot of the app; a stale file fails the full check.
365
+ optional: true
366
+ - path: src/tests/e2e/<area>/<action>.e2e-spec.ts
367
+ slot: be.tests.e2e
368
+ role: The operation on the booted app, refusal and unauthenticated call included.
369
+ optional: true
@@ -0,0 +1,297 @@
1
+ schema: starci/knowledge-source@1
2
+ id: be.cli
3
+ title: Backend cli
4
+ purpose: |-
5
+ Given a one-off action (migrate, seed, sync, backup, an operator command), where does it live and how is it written? Every
6
+ one-off action is a nest-commander command of the cli feature root `be/src/features/cli/`, compiled into the one cli app
7
+ `be/apps/cli` and shipped as one image run as `cli <group> <command>`. A command is an action runner: thin (R203
8
+ `BE_FEATURE_THIN`) like every other feature file — it maps the command line and makes one delegating call into the
9
+ modules — and it still owes its unit spec beside it (the `cli` role of `ruleParams.be.unitRoles`). Its slot is
10
+ `coverage: none`, so a command is never a coverage target.
11
+ appliesTo:
12
+ - backend
13
+ family: be
14
+ # The slot ids this topic teaches (knowledge/hfs/slots.yaml); scripts/gates/read-digest.mjs inverts these
15
+ # into the READ-step slot -> pattern-topic map (knowledge/op-gate.yaml kinds), each slot's topics in index.yaml order.
16
+ slots: [be.app.cli, be.cli]
17
+ pattern: cli
18
+ provenance:
19
+ reviewedAt: 2026-10-02
20
+ sources:
21
+ - knowledge/hfs/README.md
22
+ - knowledge/hfs/slots.yaml
23
+ - knowledge/hfs/rules.yaml
24
+ - owner decision 2026-10-02 (one cli app on nest-commander; features grouped by trigger kind)
25
+ - https://docs.nestjs.com/recipes/nest-commander
26
+ limitations: |-
27
+ Static checks prove where a command is declared, how the cli app boots, that every command has its spec beside it and
28
+ that nothing else parses the command line. They do not prove that a migration or a seed is right for live data, that a
29
+ deploy runs the migrate command first and once, or that an operator command is safe to re-run; the command's spec, the
30
+ test world's migration run and the deploy review prove those.
31
+ rules:
32
+ - id: BE-CLI-1
33
+ title: One cli app boots nest-commander and never serves
34
+ kind: mandatory
35
+ hfsRules: [R147, R148]
36
+ requirement: |-
37
+ Every one-off action of a back end runs in ONE app, `be/apps/cli` (slot `be.app.cli`, kind `cli`, named `cli`). It is
38
+ required as soon as the back end declares a connection in `hfs.json` `sides.be.connections` (its migrations run as
39
+ `cli migrate run`) or tracks a command under `src/features/cli/`, and it tracks its image `be/apps/cli/Dockerfile`: one
40
+ image, run as `cli <group> <command>`. `apps/cli/src/main.ts` boots with `CommandFactory.run(AppModule, logger)` of
41
+ nest-commander and never serves: no `NestFactory.create*`, no `.listen()`, no microservice connection, no
42
+ `@nestjs/platform-*` adapter and no `@nestjs/microservices`. `apps/cli/src/app.module.ts` composes the platform modules
43
+ the commands need (the database capability over every connection with its entities and migrations, like an api app) and
44
+ the static cli feature root module `CliModule`; `apps/cli/src/cli.options.ts` carries what the cli reads from the
45
+ environment, the connections it migrates among them.
46
+ rationale: |-
47
+ One image for every operator action gives one place to find, deploy and audit them, and a process that never listens
48
+ cannot become a second, unguarded door into the application.
49
+ cases:
50
+ - id: case-1
51
+ when: The cli entry
52
+ write: |-
53
+ // be/apps/cli/src/main.ts
54
+ async function bootstrap(): Promise<void> {
55
+ await CommandFactory.run(AppModule.register(parseCliOptions(EnvSource.fromProcess())), new JsonLinesLogger())
56
+ }
57
+ void bootstrap()
58
+ - id: case-2
59
+ when: The cli app calls `NestFactory.createApplicationContext` or `app.listen()`
60
+ write: 'Refused (BE_CLI_BOOTSTRAP): the cli app boots only through CommandFactory.run.'
61
+ - id: case-3
62
+ when: A back end declares a connection and has no apps/cli
63
+ write: 'Refused (BE_CLI_REQUIRED): add be/apps/cli with main.ts, app.module.ts, cli.options.ts and its Dockerfile, declared as { "name": "cli", "kind": "cli" } in hfs.json sides.be.apps.'
64
+ verification:
65
+ automated:
66
+ - BE_CLI_REQUIRED
67
+ - BE_CLI_BOOTSTRAP
68
+ manual:
69
+ - Confirm the deploy runs the cli image as a job and never as a long-running service.
70
+ relatedExamples: []
71
+ - id: BE-CLI-2
72
+ title: Commands live in the cli feature root as groups and sub-commands
73
+ kind: mandatory
74
+ hfsRules: [R149]
75
+ requirement: |-
76
+ A nest-commander command (`@Command` or `@SubCommand`) is declared only in the cli feature root `src/features/cli/`
77
+ (slot `be.cli`). The root holds `index.ts` and `cli.module.ts` and one folder per group: `<group>/<group>.cli.ts` (the
78
+ group `@Command` with its `subCommands`), `<group>/<group>.module.ts` and `<group>/subs/<name>.cli.ts` (a `@SubCommand`
79
+ class extending `CommandRunner`), one command per file. The cli feature root is a feature, so every module of it is a
80
+ static `@Module({ imports, providers })` with no module definition, no options file and no `register`: `CliModule`
81
+ imports each group module and a group module provides its commands. A command makes its one delegating call on an
82
+ injected runner service of the modules (the migrate and seed runners of `platform/database`) and takes its options
83
+ from a nest-commander `@Option` of its own (`cli seed run --env <name>`). The suffix is
84
+ `.cli.ts`, because `.command.ts` is the CQRS message (api.yaml).
85
+ rationale: |-
86
+ A fixed group and sub-command layout maps the command line `cli <group> <command>` to one file path, so an operator
87
+ command and its spec are found from the name alone.
88
+ cases:
89
+ - id: case-1
90
+ when: The migrate group
91
+ write: |-
92
+ // be/src/features/cli/migrate/migrate.cli.ts
93
+ @Command({ name: "migrate", subCommands: [RunCli], description: "Schema migrations of every connection" })
94
+ /** The migrate group; without a sub-command it shows its help. */
95
+ export class MigrateCli extends CommandRunner {
96
+ async run(): Promise<void> { this.command.help() }
97
+ }
98
+ - id: case-2
99
+ when: A `@SubCommand` declared in src/modules/platform/database/reindex.cli.ts or in features/api/
100
+ write: 'Refused (BE_CLI_COMMAND_SHAPE, BE_CLI_OWNER): move it to src/features/cli/<group>/subs/reindex.cli.ts and register it in the group module.'
101
+ verification:
102
+ automated:
103
+ - BE_CLI_COMMAND_SHAPE
104
+ manual: []
105
+ relatedExamples: []
106
+ - id: BE-CLI-3
107
+ title: A command is an action runner with its own unit spec
108
+ kind: mandatory
109
+ hfsRules: [R149, R47, R102, R203]
110
+ requirement: |-
111
+ A cli command is thin (R203 `BE_FEATURE_THIN`) like every other file of a feature-tier slot: `run` maps the command
112
+ line and makes one delegating call into the modules (a service, a platform capability or a modules function), with no
113
+ branch, loop, `try`, `throw` or computation. As an action runner it is exempt from the transport-shape law R88 (the
114
+ `exempt` of the `cli` entry of `ruleParams.be.unitRoles`), and it still owes its unit spec `<name>.cli.spec.ts` beside
115
+ it, built like a service spec (test.yaml BE-TEST-2), which asserts the delegation. The slot `be.cli` is
116
+ `coverage: none`, so a command is never a coverage target. A command never imports an api feature: what it shares
117
+ with a request lives in a domain capability (folder.yaml BE-FOLDER-2).
118
+ rationale: |-
119
+ An operator action runs against production data, often once; its delegation has no request path that an e2e run
120
+ exercises, so the unit spec is the only automatic proof that the command hands its inputs to the right module.
121
+ cases:
122
+ - id: case-1
123
+ when: A seed command
124
+ write: 'features/cli/seed/subs/demo.cli.ts (`@SubCommand({ name: "demo" })`, `DemoSeedCli extends CommandRunner`) reads its options and calls the seed runner of a platform or domain capability; demo.cli.spec.ts beside it asserts the delegation.'
125
+ - id: case-2
126
+ when: A command with no spec
127
+ write: 'Refused (BE_CLI_COMMAND_SHAPE, unit-test-colocated): features/cli/db/subs/backup.cli.ts without backup.cli.spec.ts beside it.'
128
+ verification:
129
+ automated:
130
+ - BE_CLI_COMMAND_SHAPE
131
+ - BE_TEST_TOPOLOGY
132
+ - BE_CONTRACT_UNGUARDED
133
+ - BE_SPEC_PLACEMENT
134
+ - BE_FEATURE_THIN
135
+ manual:
136
+ - Confirm a command that writes is safe to re-run or refuses a second run explicitly.
137
+ relatedExamples: []
138
+ - id: BE-CLI-4
139
+ title: Only the cli parses the command line
140
+ kind: mandatory
141
+ hfsRules: [R150]
142
+ requirement: |-
143
+ `nest-commander` is imported only by `be/apps/cli` and `src/features/cli/`. No back-end source parses command-line
144
+ arguments itself: `process.argv` and the parsers `commander`, `yargs` and `minimist` are refused everywhere, the cli
145
+ included. A one-off action anywhere but a cli command is a door this rule closes.
146
+ rationale: |-
147
+ A flag read in an api app's main.ts or a script beside the source is an operator door no review sees and no spec
148
+ covers.
149
+ cases:
150
+ - id: case-1
151
+ when: An api main.ts reads `process.argv.includes("--seed")` to seed before listening
152
+ write: 'Refused (BE_CLI_OWNER): the seed is `cli seed demo`, run as its own job.'
153
+ verification:
154
+ automated:
155
+ - BE_CLI_OWNER
156
+ manual: []
157
+ relatedExamples: []
158
+ - id: BE-CLI-5
159
+ title: Migrations and seeds run only as cli commands
160
+ kind: mandatory
161
+ hfsRules: [R34, R147]
162
+ requirement: |-
163
+ Migrations run only through the cli migrate command (`cli migrate run`) and through the test world over the same
164
+ connection list (`cli.options.ts`); `starci gate run --root <app>` verifies that path. Seed data never sits in a
165
+ migration: it is a sub-command of the `seed` group. Api and worker apps neither migrate nor seed (persistence.yaml
166
+ BE-PERSISTENCE-2).
167
+ rationale: |-
168
+ One runner per connection removes the race of two processes migrating, and one list shared with the test world makes
169
+ the tested schema the deployed one.
170
+ cases:
171
+ - id: case-1
172
+ when: The migrate command
173
+ write: 'features/cli/migrate/subs/run.cli.ts (`@SubCommand({ name: "run" })`, `RunCli extends CommandRunner`) makes one call on `MigrationRunnerService` of `platform/database`, which migrates every connection in order and fails the process on the first error; run.cli.spec.ts beside it.'
174
+ - id: case-2
175
+ when: A module runs `dataSource.runMigrations()` at boot of an api app
176
+ write: 'Refused (BE_SCHEMA_AUTHORITY): only the cli and the test world run migrations.'
177
+ verification:
178
+ automated:
179
+ - BE_SCHEMA_AUTHORITY
180
+ - BE_CLI_REQUIRED
181
+ manual:
182
+ - Confirm the deploy runs `cli migrate run` to completion before the api and worker apps start.
183
+ relatedExamples: []
184
+ files:
185
+ - path: apps/cli/Dockerfile
186
+ slot: repo.app-image
187
+ optional: true
188
+ role: the one cli image, run as `cli <group> <command>`
189
+ rules:
190
+ - BE_CLI_REQUIRED
191
+ - path: apps/cli/src/main.ts
192
+ slot: be.app.cli
193
+ optional: true
194
+ role: CommandFactory.run(AppModule, logger); never serves
195
+ rules:
196
+ - BE_CLI_BOOTSTRAP
197
+ - BE_ENTRYPOINT_ONLY_IN_APPS
198
+ - path: apps/cli/src/app.module.ts
199
+ slot: be.app.cli
200
+ optional: true
201
+ role: composes the platform modules and CliModule; no business code
202
+ rules:
203
+ - BE_APP_COMPOSITION_ONLY
204
+ - BE_CLI_BOOTSTRAP
205
+ - path: apps/cli/src/cli.options.ts
206
+ slot: be.app.cli
207
+ optional: true
208
+ role: what the cli reads from the environment, the connection list it migrates among them
209
+ rules:
210
+ - BE_CLI_REQUIRED
211
+ - BE_SCHEMA_AUTHORITY
212
+ - path: src/features/cli/index.ts
213
+ slot: be.cli
214
+ optional: true
215
+ role: exports CliModule to the cli app
216
+ rules:
217
+ - BE_PUBLIC_SURFACE
218
+ - path: src/features/cli/cli.module.ts
219
+ slot: be.cli
220
+ optional: true
221
+ role: the static cli feature root module; imports each group module
222
+ rules:
223
+ - BE_CLI_COMMAND_SHAPE
224
+ - BE_MODULE_SHAPE
225
+ - path: src/features/cli/migrate/migrate.cli.ts
226
+ slot: be.cli
227
+ optional: true
228
+ role: the migrate group @Command and its subCommands
229
+ rules:
230
+ - BE_CLI_COMMAND_SHAPE
231
+ - BE_CLI_OWNER
232
+ spec: src/features/cli/migrate/migrate.cli.spec.ts
233
+ - path: src/features/cli/migrate/migrate.module.ts
234
+ slot: be.cli
235
+ optional: true
236
+ role: "the static migrate group module: provides its command and sub-commands"
237
+ rules:
238
+ - BE_CLI_COMMAND_SHAPE
239
+ - BE_MODULE_SHAPE
240
+ - path: src/features/cli/migrate/subs/run.cli.ts
241
+ slot: be.cli
242
+ optional: true
243
+ role: "`cli migrate run`: one delegating call on the migration runner of `platform/database`, the one runner of the schema"
244
+ rules:
245
+ - BE_CLI_COMMAND_SHAPE
246
+ - BE_SCHEMA_AUTHORITY
247
+ spec: src/features/cli/migrate/subs/run.cli.spec.ts
248
+ - path: src/features/cli/seed/seed.cli.ts
249
+ slot: be.cli
250
+ optional: true
251
+ role: the seed group @Command and its subCommands
252
+ rules:
253
+ - BE_CLI_COMMAND_SHAPE
254
+ - BE_CLI_OWNER
255
+ spec: src/features/cli/seed/seed.cli.spec.ts
256
+ - path: src/features/cli/seed/seed.module.ts
257
+ slot: be.cli
258
+ optional: true
259
+ role: "the static seed group module: provides its command and sub-commands"
260
+ rules:
261
+ - BE_CLI_COMMAND_SHAPE
262
+ - BE_MODULE_SHAPE
263
+ - path: src/features/cli/seed/subs/run.cli.ts
264
+ slot: be.cli
265
+ optional: true
266
+ role: "`cli seed run [--env <name>]`: one delegating call on the seed runner of `platform/database`"
267
+ rules:
268
+ - BE_CLI_COMMAND_SHAPE
269
+ spec: src/features/cli/seed/subs/run.cli.spec.ts
270
+ - path: src/features/cli/<group>/<group>.cli.ts
271
+ slot: be.cli
272
+ role: the group @Command and its subCommands
273
+ template: cli/group.cli.ts.tpl
274
+ rules:
275
+ - BE_CLI_COMMAND_SHAPE
276
+ - BE_CLI_OWNER
277
+ - path: src/features/cli/<group>/<group>.cli.spec.ts
278
+ slot: be.cli
279
+ role: the unit spec of the group, beside it
280
+ template: cli/group.cli.spec.ts.tpl
281
+ - path: src/features/cli/<group>/<group>.module.ts
282
+ slot: be.cli
283
+ role: "the static group module: provides its command and sub-commands"
284
+ template: cli/group.module.ts.tpl
285
+ rules:
286
+ - BE_CLI_COMMAND_SHAPE
287
+ - BE_MODULE_SHAPE
288
+ - path: src/features/cli/<group>/subs/run.cli.ts
289
+ slot: be.cli
290
+ role: the first sub-command of the group; one delegating call on the injected runner service
291
+ template: cli/sub.run.cli.ts.tpl
292
+ rules:
293
+ - BE_CLI_COMMAND_SHAPE
294
+ - path: src/features/cli/<group>/subs/run.cli.spec.ts
295
+ slot: be.cli
296
+ role: the unit spec of the sub-command, beside it
297
+ template: cli/sub.run.cli.spec.ts.tpl