@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,113 @@
1
+ schema: starci/knowledge-source@1
2
+ id: be.domain
3
+ title: Domain capabilities (module family domain)
4
+ purpose: |-
5
+ Given a business capability (orders, payments, catalog), where does its decision live? In `modules/domain/<capability>/`: services that
6
+ own the rules, the entities and migrations of its tables on one connection, its errors and its messages. The capability belongs to
7
+ one bounded context, so everything inside it is synchronous and runs in one transaction; the context talks to other contexts only
8
+ through events. Features reach it through its public `index.ts`.
9
+ appliesTo:
10
+ - backend
11
+ family: be
12
+ provenance:
13
+ reviewedAt: 2026-10-02
14
+ sources:
15
+ - knowledge/hfs/README.md
16
+ - knowledge/hfs/slots.yaml
17
+ - knowledge/hfs/rules.yaml
18
+ - knowledge/patterns/be/services.yaml
19
+ - knowledge/patterns/be/persistence.yaml
20
+ - knowledge/patterns/be/event-bus.yaml
21
+ - owner decision 2026-10-02 (bounded contexts)
22
+ limitations: |-
23
+ Static checks prove the folder shape, the one connection per context, the coupling between contexts and the one-transaction rule. They do
24
+ not prove that a rule is the right business rule; the service specs and the e2e run do.
25
+ rules:
26
+ - id: BE-DOMAIN-1
27
+ title: A capability owns its rules, its tables and its transaction
28
+ kind: mandatory
29
+ hfsRules: [R175, R176, R174, R30]
30
+ requirement: |-
31
+ A capability folder holds `index.ts`, `<capability>.module.ts`, `<capability>.service.ts` with its `<capability>.service.spec.ts` beside it,
32
+ `<capability>.contracts.ts`, `errors/`, `messages/` and `persistence/` (its entities, migrations, SQL and rows on the connection of its
33
+ context). A service opens one transaction on that connection and publishes its events inside it (`eventBus.publish(event, tx)`, event-bus.yaml).
34
+ No relation, foreign key or import reaches a capability of another context; contexts are coupled by events only.
35
+ rationale: |-
36
+ A capability that owns its rules and its tables can change them without asking another team, and a transaction that touches one connection
37
+ stays atomic.
38
+ cases:
39
+ - id: case-1
40
+ when: Order and payment must agree
41
+ write: 'The payment service commits its row and publishes PaymentSettledEvent in one transaction; an order reactor consumes it and the order service updates its own copy.'
42
+ verification:
43
+ automated:
44
+ - BE_CONTEXT_COUPLING
45
+ - BE_CONTEXT_TRANSACTION
46
+ - BE_CONTEXT_OWNER
47
+ - BE_PUBLIC_SURFACE
48
+ - ARCH_OWNER_EXPORT_BYPASS
49
+ - ARCH_OWNER_EXPORT_STAR
50
+ - HFS_ALIAS_REEXPORT
51
+ - ARCH_PACKAGE_EXPORT_BYPASS
52
+ manual:
53
+ - Confirm the capability is named after the business capability, not after a technical layer.
54
+ relatedExamples: [domain]
55
+ - id: BE-DOMAIN-2
56
+ title: Work inside one context is synchronous; an event crosses contexts or starts async work
57
+ kind: mandatory
58
+ hfsRules: [R152, R175]
59
+ requirement: |-
60
+ A handler that needs another capability of the same context calls its service in the same transaction and no event exists for it. An event is
61
+ published only when another context must react or when the work it starts is asynchronous by nature (a job, a saga step, a reactor, a projection
62
+ recompute), and only from a domain service inside its transaction.
63
+ rationale: |-
64
+ An event inside one context adds a delivery that can fail between two writes the transaction would have made atomic.
65
+ cases:
66
+ - id: case-1
67
+ when: Checkout reserves stock in the same context
68
+ write: 'The order service calls the catalog service in its transaction; PlacementCompletedEvent is published only for the billing context.'
69
+ verification:
70
+ automated:
71
+ - BE_OUTBOX_WRITE_TX
72
+ - BE_CONTEXT_COUPLING
73
+ manual:
74
+ - Confirm no published event is consumed by the context that published it only to tell itself something.
75
+ relatedExamples: [domain]
76
+ files:
77
+ - path: src/modules/domain/<capability>/index.ts
78
+ slot: be.domain
79
+ role: The public surface (services, contracts, errors and the entity and migration arrays), never an entity class used elsewhere.
80
+ rules: [BE_PUBLIC_SURFACE]
81
+ - path: src/modules/domain/<capability>/<capability>.module.ts
82
+ slot: be.domain
83
+ role: Registers the services of the capability.
84
+ - path: src/modules/domain/<capability>/<capability>.service.ts
85
+ slot: be.domain
86
+ role: The rules, in one transaction on the connection of the context.
87
+ rules: [BE_CONTEXT_TRANSACTION, BE_OUTBOX_WRITE_TX]
88
+ - path: src/modules/domain/<capability>/<capability>.service.spec.ts
89
+ slot: be.domain
90
+ role: The unit spec beside the service, with a few mocks.
91
+ - path: src/modules/domain/<capability>/<capability>.contracts.ts
92
+ slot: be.domain
93
+ role: The plain types the service takes and returns.
94
+ - path: src/modules/domain/<capability>/errors/<capability>.error.ts
95
+ slot: be.errors
96
+ role: The error family of the capability.
97
+ - path: src/modules/domain/<capability>/messages/<capability>.messages.ts
98
+ slot: be.domain.messages
99
+ role: The keyed vi and en text of the capability.
100
+ - path: src/modules/domain/<capability>/persistence/connection.ts
101
+ slot: be.persistence
102
+ role: The entity and migration arrays the apps register on the connection of the context.
103
+ - path: src/modules/domain/<capability>/persistence/entities/<table>.entity.ts
104
+ slot: be.persistence
105
+ role: One table of the capability; no relation to another context.
106
+ rules: [BE_CONTEXT_COUPLING]
107
+ - path: src/modules/domain/<capability>/persistence/migrations/<epochMs13>-<name>.ts
108
+ slot: be.persistence
109
+ role: One migration; no foreign key onto another context.
110
+ rules: [BE_CONTEXT_COUPLING]
111
+ - path: src/modules/domain/<capability>/persistence/<name>.sql.ts
112
+ slot: be.persistence
113
+ role: The statements of the capability, one owner per table.
@@ -0,0 +1,285 @@
1
+ schema: starci/knowledge-source@1
2
+ id: be.event-bus
3
+ title: Event bus
4
+ purpose: |-
5
+ Given a fact another context or a background process must react to, how does it travel? Work inside ONE context stays
6
+ synchronous: the handler calls that context's domain service in one transaction and no event exists. An event exists only to
7
+ cross contexts or to trigger asynchronous work (jobs, sagas, reactors, projections). It is a typed event class, published with
8
+ `eventBus.publish(event, tx)` by a domain service inside its transaction, and travels one durable path: transactional outbox,
9
+ relay, Kafka, consumer with retry and dead letter. `platform/event-bus` is the one owner and the only importer of `kafkajs`.
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.transport.message, be.feature.reactors, be.events, be.platform.event-bus]
16
+ pattern: event-bus
17
+ provenance:
18
+ reviewedAt: 2026-10-02
19
+ sources:
20
+ - knowledge/hfs/README.md
21
+ - knowledge/hfs/slots.yaml
22
+ - knowledge/hfs/rules.yaml
23
+ - owner decision 2026-10-02 (Kafka only, transactional outbox, typed event classes, publish(event, tx))
24
+ - https://microservices.io/patterns/data/transactional-outbox.html
25
+ limitations: |-
26
+ Static checks prove who may publish, with which transaction, and that a consumer is a named door that forwards the event
27
+ id. They do not prove broker configuration, partition keys, schema evolution or the operational handling of a dead letter;
28
+ the test-world scenarios (`event-bus/*`) prove the delivery behaviour against a real broker.
29
+ rules:
30
+ - id: BE-EVENT-BUS-1
31
+ title: Inside one context nothing is an event
32
+ kind: mandatory
33
+ hfsRules: [R152, R26]
34
+ requirement: |-
35
+ Work that stays inside one context (one connection, one set of tables) is synchronous: the handler dispatches to that
36
+ context's domain service, which does the whole change in one transaction. No event, no queue and no bus is involved, and a
37
+ caller that needs the result reads it from the return value. A fact is published as an event only when (a) another context
38
+ must react to it, or (b) the work it triggers is asynchronous by nature (a job, a saga step, a reactor, a projection
39
+ refresh). One transaction touches one context's connection only.
40
+ rationale: |-
41
+ An event inside one context buys eventual consistency, a failure mode and a broker round trip for work the transaction
42
+ already makes atomic. Events are the price of crossing a boundary, not a style.
43
+ cases:
44
+ - id: case-1
45
+ when: 'A buyer adds an item to the cart (same context, the answer is needed now)'
46
+ write: 'add-cart-item.handler.ts dispatches to CartService.addItem, which updates the cart rows in one transaction and returns the cart. No event. (passing)'
47
+ - id: case-2
48
+ when: 'Order needs to know whether a payment settled (payment is another context)'
49
+ write: 'Order never reads the payment tables or imports the payment service. PaymentService publishes PaymentSettledEvent in its transaction; an order reactor keeps a local copy of the status, or a projection serves the read. (a direct read is the violation)'
50
+ verification:
51
+ automated:
52
+ - BE_OUTBOX_WRITE_TX
53
+ - BE_TIER_DIRECTION
54
+ - FE_TIER_DIRECTION
55
+ - ARCH_PACKAGE_IMPORTS_APP
56
+ manual:
57
+ - Confirm each published event crosses a context or starts asynchronous work, and no handler publishes to tell itself something.
58
+ relatedExamples: [event-bus]
59
+ - id: BE-EVENT-BUS-2
60
+ title: An event is a typed class that equals its vendored contract
61
+ kind: mandatory
62
+ hfsRules: [R151]
63
+ requirement: |-
64
+ Events are classes in `be/src/modules/events/<service>/<event>.event.ts`, one per file, named after the event
65
+ (`order.placed` is `order-placed.event.ts`): a class extending the platform `BaseEvent` with literal
66
+ `static readonly eventName` and `static readonly version`, a payload typed in the class, a `create()` that builds it
67
+ and a `parse()` that reads a delivered payload back. Each class is an entry of the vendored contract
68
+ `be/contracts/<service>/events.json` at the same name and version and every contract entry has its class. Schemas
69
+ evolve only additively; a breaking change is a new event name (`<event>.v2`).
70
+ rationale: |-
71
+ The class is what the publisher and the consumer compile against; the contract is what another service pins. Two
72
+ sources of truth drift, so a check holds them equal.
73
+ cases:
74
+ - id: case-1
75
+ when: A new event
76
+ write: 'modules/events/order/order-placed.event.ts with eventName "order.placed" and version 1, then `starci app emit` so be/contracts/order/events.json lists it.'
77
+ verification:
78
+ automated:
79
+ - BE_EVENT_CLASS_CONTRACT
80
+ manual:
81
+ - Confirm a payload change is additive.
82
+ relatedExamples: [event-bus]
83
+ - id: BE-EVENT-BUS-3
84
+ title: Publish through the bus, in the domain transaction, to Kafka only
85
+ kind: mandatory
86
+ hfsRules: [R152, R90, R87]
87
+ requirement: |-
88
+ `eventBus.publish(event, tx)` is called only in `modules/domain`, with the manager of the transaction that makes the
89
+ domain change (`this.entityManager.transaction(async (manager) => { ...; await this.eventBus.publish(event, manager) })`).
90
+ It writes a row in the outbox table of that transaction's connection; the relay hands committed rows to Kafka (topic
91
+ `events.<service>`, key = aggregate id) and marks them sent. `kafkajs` is imported only by `platform/event-bus`. Nest's
92
+ `EventBus`, `@EventsHandler`, `@OnEvent` and `EventEmitter2` do not exist, and nothing publishes to Kafka directly.
93
+ There is no in-process or local transport: unit specs use a recording fake of the `EventBus` port, allowed in `*.spec.ts`.
94
+ rationale: |-
95
+ The outbox row commits with the domain change, so a rolled-back change never emits and a committed one is never lost;
96
+ Kafka then gives ordered, replayable, cross-service delivery.
97
+ cases:
98
+ - id: case-1
99
+ when: A domain service places an order
100
+ write: 'await this.entityManager.transaction(async (manager) => { await manager.save(...); await this.eventBus.publish(OrderPlacedEvent.create({ ... }), manager) })'
101
+ verification:
102
+ automated:
103
+ - BE_OUTBOX_WRITE_TX
104
+ - BE_INFRA_OWNER
105
+ - BE_CQRS_SHAPE
106
+ - BE_READONLY_BOUNDARY
107
+ manual: []
108
+ relatedExamples: [event-bus]
109
+ - id: BE-EVENT-BUS-4
110
+ title: A consumer is a thin door; delivery is at least once with retry and a dead letter
111
+ kind: mandatory
112
+ hfsRules: [R153, R80]
113
+ requirement: |-
114
+ A consumer is `transport/message/<event>.consumer.ts` implementing `EventConsumer<E>` with `readonly event = <Event>Event`;
115
+ `handle(delivery)` dispatches one command and forwards `delivery.eventId`. Delivery is at least once: the domain service the
116
+ command reaches claims the event id in `platform/inbox` as its first awaited expression, inside its own transaction, so a
117
+ redelivery is a no-op. A throw schedules a retry with exponential backoff on the retry topic (`events.<service>.retry`);
118
+ after the event's `attempts` the message goes to the dead-letter topic (`events.<service>.dlq`), which an operator reads
119
+ and requeues through `eventBus.deadLetters(event)` and `eventBus.requeue(id)` (a cli command). `eventBus.pendingRetries(event)`
120
+ lets a failure-injection spec wait for a retry.
121
+ rationale: |-
122
+ A claim persisted before the handler finishes would lose the event on a crash; claiming in the effect's transaction makes
123
+ the effect and the claim one atomic fact.
124
+ cases:
125
+ - id: case-1
126
+ when: A consumer of order.placed
127
+ write: 'order-placed.consumer.ts: handle(delivery) { await this.commandBus.execute(new IssueInvoiceCommand({ request: { eventId: delivery.eventId, ... } })) }'
128
+ verification:
129
+ automated:
130
+ - BE_EVENT_CONSUMER_SHAPE
131
+ - BE_INBOX_DEDUPE_MISSING
132
+ manual:
133
+ - Confirm the retry budget and backoff fit the consumer's side effects.
134
+ relatedExamples: [event-bus]
135
+ - id: BE-EVENT-BUS-5
136
+ title: A declared event bus is proven on the test world
137
+ kind: mandatory
138
+ hfsRules: [R155]
139
+ requirement: |-
140
+ An app that declares the pattern (`hfs.json` `sides.be.patterns: ["event-bus"]`) has e2e or integration tests, titled
141
+ `event-bus/<scenario>: ...`, for each scenario of `ruleParams.be.patternScenarios.event-bus`, run against a real Kafka:
142
+ `atomic-with-transaction` (the outbox row exists exactly when the domain change commits), `relay-only-after-commit` (nothing is
143
+ published for a rolled-back transaction), `redelivery-then-dead-letter` (a failing consumer is retried, then buried) and
144
+ `duplicate-delivery-is-a-noop` (a redelivered event changes nothing twice).
145
+ rationale: |-
146
+ Delivery behaviour is not visible to a linter; a real broker run is the only proof.
147
+ cases:
148
+ - id: case-1
149
+ when: The order e2e suite
150
+ write: "it('event-bus/atomic-with-transaction: the row commits with the order', ...)"
151
+ verification:
152
+ automated:
153
+ - BE_PATTERN_SPEC_MISSING
154
+ manual: []
155
+ relatedExamples: [event-bus]
156
+ files:
157
+ - path: src/modules/platform/outbox/index.ts
158
+ slot: be.platform
159
+ role: "The public surface of the shared outbox relay."
160
+ template: outbox/platform/index.ts.tpl
161
+ - path: src/modules/platform/outbox/outbox.log-events.ts
162
+ slot: be.platform
163
+ role: "The log event of a failed relay pass."
164
+ template: outbox/platform/outbox.log-events.ts.tpl
165
+ - path: src/modules/platform/outbox/outbox-relay.policy.ts
166
+ slot: be.platform
167
+ role: "The one relay loop the event bus and the queues share: read, deliver and mark in one transaction."
168
+ template: outbox/platform/outbox-relay.policy.ts.tpl
169
+ - path: src/modules/platform/outbox/outbox-relay.policy.spec.ts
170
+ slot: be.platform
171
+ template: outbox/platform/outbox-relay.policy.spec.ts.tpl
172
+ - path: src/modules/platform/event-bus/errors/event-bus.error.ts
173
+ slot: be.platform.event-bus
174
+ template: event-bus/platform/errors/event-bus.error.ts.tpl
175
+ - path: src/modules/platform/event-bus/event-bus.config.ts
176
+ slot: be.platform.event-bus
177
+ template: event-bus/platform/event-bus.config.ts.tpl
178
+ - path: src/modules/platform/event-bus/event-bus.contracts.ts
179
+ slot: be.platform.event-bus
180
+ template: event-bus/platform/event-bus.contracts.ts.tpl
181
+ - path: src/modules/platform/event-bus/event-bus.decorators.ts
182
+ slot: be.platform.event-bus
183
+ template: event-bus/platform/event-bus.decorators.ts.tpl
184
+ - path: src/modules/platform/event-bus/event-bus.log-events.ts
185
+ slot: be.platform.event-bus
186
+ template: event-bus/platform/event-bus.log-events.ts.tpl
187
+ - path: src/modules/platform/event-bus/event-bus.module-definition.ts
188
+ slot: be.platform.event-bus
189
+ template: event-bus/platform/event-bus.module-definition.ts.tpl
190
+ - path: src/modules/platform/event-bus/event-bus.module.ts
191
+ slot: be.platform.event-bus
192
+ template: event-bus/platform/event-bus.module.ts.tpl
193
+ - path: src/modules/platform/event-bus/event-bus.options.ts
194
+ slot: be.platform.event-bus
195
+ template: event-bus/platform/event-bus.options.ts.tpl
196
+ - path: src/modules/platform/event-bus/event-bus.port.ts
197
+ slot: be.platform.event-bus
198
+ template: event-bus/platform/event-bus.port.ts.tpl
199
+ - path: src/modules/platform/event-bus/event-bus.service.spec.ts
200
+ slot: be.platform.event-bus
201
+ template: event-bus/platform/event-bus.service.spec.ts.tpl
202
+ - path: src/modules/platform/event-bus/event-bus.service.ts
203
+ slot: be.platform.event-bus
204
+ role: "publish (outbox write through tx), dead letters, requeue, pending retries."
205
+ template: event-bus/platform/event-bus.service.ts.tpl
206
+ - path: src/modules/platform/event-bus/event-envelope.policy.ts
207
+ slot: be.platform.event-bus
208
+ template: event-bus/platform/event-envelope.policy.ts.tpl
209
+ - path: src/modules/platform/event-bus/event-envelope.policy.spec.ts
210
+ slot: be.platform.event-bus
211
+ template: event-bus/platform/event-envelope.policy.spec.ts.tpl
212
+ - path: src/modules/platform/event-bus/event-relay.service.spec.ts
213
+ slot: be.platform.event-bus
214
+ template: event-bus/platform/event-relay.service.spec.ts.tpl
215
+ - path: src/modules/platform/event-bus/event-relay.service.ts
216
+ slot: be.platform.event-bus
217
+ role: "Relay of committed outbox rows to Kafka, per registered connection."
218
+ template: event-bus/platform/event-relay.service.ts.tpl
219
+ - path: src/modules/platform/event-bus/event-runner.service.spec.ts
220
+ slot: be.platform.event-bus
221
+ template: event-bus/platform/event-runner.service.spec.ts.tpl
222
+ - path: src/modules/platform/event-bus/event-runner.service.ts
223
+ slot: be.platform.event-bus
224
+ role: "Consumer registry and Kafka read loop with retry and dead letter."
225
+ template: event-bus/platform/event-runner.service.ts.tpl
226
+ - path: src/modules/platform/event-bus/event-transport.port.ts
227
+ slot: be.platform.event-bus
228
+ template: event-bus/platform/event-transport.port.ts.tpl
229
+ - path: src/modules/platform/event-bus/event.policy.ts
230
+ slot: be.platform.event-bus
231
+ role: "Topics, attempts and backoff."
232
+ template: event-bus/platform/event.policy.ts.tpl
233
+ - path: src/modules/platform/event-bus/event.policy.spec.ts
234
+ slot: be.platform.event-bus
235
+ template: event-bus/platform/event.policy.spec.ts.tpl
236
+ - path: src/modules/platform/event-bus/index.ts
237
+ slot: be.platform.event-bus
238
+ template: event-bus/platform/index.ts.tpl
239
+ - path: src/modules/platform/event-bus/kafka-event-transport.client.ts
240
+ slot: be.platform.event-bus
241
+ role: "The one kafkajs importer."
242
+ template: event-bus/platform/kafka-event-transport.client.ts.tpl
243
+ - path: src/modules/platform/event-bus/kafka-event-transport.client.spec.ts
244
+ slot: be.platform.event-bus
245
+ template: event-bus/platform/kafka-event-transport.client.spec.ts.tpl
246
+ - path: src/modules/platform/event-bus/messages/event-bus.messages.ts
247
+ slot: be.domain.messages
248
+ template: event-bus/platform/messages/event-bus.messages.ts.tpl
249
+ - path: src/modules/platform/event-bus/persistence/connection.ts
250
+ slot: be.persistence
251
+ template: event-bus/platform/persistence/connection.ts.tpl
252
+ - path: src/modules/platform/event-bus/persistence/entities/event-outbox.entity.ts
253
+ slot: be.persistence
254
+ template: event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl
255
+ - path: src/modules/platform/event-bus/persistence/event-bus.rows.ts
256
+ slot: be.persistence
257
+ template: event-bus/platform/persistence/event-bus.rows.ts.tpl
258
+ - path: src/modules/platform/event-bus/persistence/event-bus.sql.ts
259
+ slot: be.persistence
260
+ template: event-bus/platform/persistence/event-bus.sql.ts.tpl
261
+ - path: src/modules/platform/event-bus/persistence/migrations/<epochMs13>-create-event-outbox.ts
262
+ slot: be.persistence
263
+ template: event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl
264
+ - path: src/modules/events/<service>/index.ts
265
+ slot: be.events
266
+ - path: src/modules/events/<service>/<event>.event.ts
267
+ slot: be.events
268
+ role: One typed event class.
269
+ rules: [BE_EVENT_CLASS_CONTRACT]
270
+ - path: contracts/<service>/events.json
271
+ slot: be.contract.events
272
+ role: The emitted catalog of the service events (name, version, payload shape); the event classes equal it, so a consumer in another service reads one contract.
273
+ rules: [BE_EVENT_CLASS_CONTRACT]
274
+ - path: src/features/<kind>/<feature>/transport/message/<event>.consumer.ts
275
+ slot: be.transport.message
276
+ role: The consumer door of one event.
277
+ rules: [BE_EVENT_CONSUMER_SHAPE]
278
+ - path: src/features/<kind>/<feature>/transport/message/<feature>-message.module.ts
279
+ slot: be.transport.message
280
+ role: Registers the consumers with the registry.
281
+ - path: src/tests/e2e/<area>/<event>.e2e-spec.ts
282
+ slot: be.tests.e2e
283
+ role: "The consumed event's e2e flow; titles event-bus/<scenario>: ... prove the pattern."
284
+ spec: event-bus/atomic-with-transaction, event-bus/relay-only-after-commit, event-bus/redelivery-then-dead-letter, event-bus/duplicate-delivery-is-a-noop
285
+ rules: [BE_PATTERN_SPEC_MISSING]
@@ -0,0 +1,261 @@
1
+ schema: starci/knowledge-source@1
2
+ id: be.jobs
3
+ title: Fenced background jobs
4
+ purpose: |-
5
+ Given work that runs in the background and must survive a worker that stalls, restarts or is replaced, how is it claimed and
6
+ settled? A job is a row whose `fencingToken` the claim bumps. Every write on a claimed job carries the token the writer holds and
7
+ is refused when it is stale, so a zombie worker (one that lost its claim but kept running) changes nothing, and every external
8
+ effect carries a run key that includes the token, so the provider drops a repeat. `platform/jobs` is the one owner of the row.
9
+ appliesTo:
10
+ - backend
11
+ family: be
12
+ # The slot ids this topic teaches (knowledge/hfs/slots.yaml); scripts/gates/read-digest.mjs inverts these
13
+ # into the READ-step slot -> pattern-topic map (knowledge/op-gate.yaml kinds), each slot's topics in index.yaml order.
14
+ slots: [be.feature.jobs, be.feature.jobs.steps, be.platform.jobs]
15
+ pattern: fenced-job
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 decision 2026-10-02 (fenced jobs with a required fencing token)
23
+ - https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
24
+ limitations: |-
25
+ Static checks prove who writes the job row, that the token is required in the type and not cast around, that effects carry a
26
+ run key and that a processor has the fenced base. They do not prove that the provider honours the idempotency key or that a
27
+ step is safe to repeat; the test-world scenarios (`fenced-job/*`) prove the zombie and re-dispatch behaviour against real
28
+ Postgres and a double of the provider.
29
+ rules:
30
+ - id: BE-JOBS-1
31
+ title: The job row has one owner and one atomic claim
32
+ kind: mandatory
33
+ hfsRules: [R157, R86]
34
+ requirement: |-
35
+ `modules/platform/jobs` holds the job entity (`id`, `kind`, `status`, `currentStep`, `fencingToken`, `claimedBy`,
36
+ `leaseExpiresAt`, `payload`, a unique `jobKey`), `job-claim.service.ts` and the persistence of the table. `claim()` is one
37
+ atomic upsert keyed by the delivery (`INSERT ... ON CONFLICT (job_key) DO UPDATE SET fencing_token = jobs.fencing_token + 1 ... WHERE
38
+ status = 'failed' OR the lease expired ... RETURNING`, a `SqlText` constant): the job row is created by its first claim, the token is
39
+ bumped by the store and returned, and a done job or a live claim matches nothing. `JobRunnerService` calls it for every BullMQ
40
+ delivery (the job key is the BullMQ job id), then `process`, then the guarded `complete`. Nothing else writes the row: no
41
+ `manager.update`, `increment`, `save`, `insert`, `delete` or query-builder write on the job entity outside `platform/jobs`. The table
42
+ exists on every connection that runs jobs.
43
+ rationale: |-
44
+ A read-modify-write claim races, and a second writer defeats the fence. One owner and one atomic statement keep the token
45
+ monotonic.
46
+ cases:
47
+ - id: case-1
48
+ when: A worker picks up a job
49
+ write: 'const job = await this.claims.claim({ kind: "send-receipt", jobKey: delivery.id, payload, workerId, leaseMs }) // token = previous + 1'
50
+ verification:
51
+ automated:
52
+ - BE_JOB_WRITE_OUTSIDE_OWNER
53
+ - BE_SQL_TABLE_OWNER
54
+ manual:
55
+ - Confirm the claim SQL is a single statement and the lease length fits the longest step.
56
+ relatedExamples: [fenced-job]
57
+ - id: BE-JOBS-2
58
+ title: Every write to a claimed job carries expectedFencingToken, required in the type
59
+ kind: mandatory
60
+ hfsRules: [R158]
61
+ requirement: |-
62
+ `JobClaims.advance`, `complete` and `fail` take `GuardedWrite & {...}` where `GuardedWrite = { jobId: string; expectedFencingToken: number }`:
63
+ the token is required, never optional or nullable, and a call never passes it through `as`, `!` or `any`. Each write is
64
+ `UPDATE ... WHERE id = $1 AND fencing_token = $2`; zero rows updated throws `JobFencedOut` and the worker stops with no
65
+ further effect. A step or processor never catches around a guarded write: the `FencedProcessor` base owns the failure path.
66
+ rationale: |-
67
+ An optional guard is a guard some caller forgets. The token is checked atomically by the store that accepts the write, which
68
+ is the only place a stale worker can be refused.
69
+ cases:
70
+ - id: case-1
71
+ when: A step finishes
72
+ write: 'await this.claims.advance({ jobId: job.jobId, expectedFencingToken: job.fencingToken, step: "send" })'
73
+ verification:
74
+ automated:
75
+ - BE_JOB_FENCE_REQUIRED
76
+ manual: []
77
+ relatedExamples: [fenced-job]
78
+ - id: BE-JOBS-3
79
+ title: An external effect carries a run key that includes the token
80
+ kind: mandatory
81
+ hfsRules: [R159]
82
+ requirement: |-
83
+ `JobClaims.runKey(job, step)` returns a `RunKey` (`<jobId>:<step>:<fencingToken>`); it is the only way to make one. The step's
84
+ one delegating call hands the payload as delivered and the run key to the module service that owns the effect, and every
85
+ call that reaches an integration passes the key as the provider's idempotency key, so a repeated effect of a stale
86
+ worker is dropped by the provider. A read from an integration belongs in the application layer, not in a step. When the
87
+ provider cannot enforce idempotency, constrain overlap and reconcile before a retry.
88
+ rationale: |-
89
+ Fencing the local row does not fence the remote effect. The key ties the effect to one claim.
90
+ cases:
91
+ - id: case-1
92
+ when: A step sends a mail
93
+ write: 'await this.mail.send(message, this.claims.runKey(job, "send"))'
94
+ verification:
95
+ automated:
96
+ - BE_JOB_RUN_KEY
97
+ manual:
98
+ - Confirm the provider documents idempotent handling of its key.
99
+ relatedExamples: [fenced-job]
100
+ - id: BE-JOBS-4
101
+ title: A job is a processor plus steps, started by a producer and run by a worker or api app
102
+ kind: mandatory
103
+ hfsRules: [R160, R156, R46, R203]
104
+ requirement: |-
105
+ `features/jobs/<job>/transport/queue/<job>.processor.ts` declares a class extending `FencedProcessor` (it claims, calls `process(job)` and
106
+ settles through the guarded writes); `steps/<step>.step.ts` declares a class implementing `JobStep`; `application/` holds
107
+ plain CQRS. The processor and every step are thin feature files (R203 `BE_FEATURE_THIN`): they map the claimed job and
108
+ delegate into `modules/`; the claim, the fence, the settle and the retry live in `platform/jobs`. A domain service starts
109
+ the job with a typed queue producer `enqueueX(payload, tx)` inside its transaction
110
+ (queues.yaml). The jobs kind imports only `modules/*`: it never imports an api, saga or reactor feature, and it talks to
111
+ them through events. An app of kind `worker` or `api` composes the job queue module.
112
+ rationale: |-
113
+ One place and one base class mean a job cannot skip the fence, and the kind boundary keeps a long job from running inside a
114
+ request.
115
+ cases:
116
+ - id: case-1
117
+ when: A new background job send-receipt
118
+ write: 'features/jobs/send-receipt/{transport/queue/send-receipt.processor.ts, transport/queue/send-receipt-queue.module.ts, send-receipt.module.ts, steps/mail.step.ts, application/send-receipt.command.ts}'
119
+ verification:
120
+ automated:
121
+ - BE_JOB_SHAPE
122
+ - BE_KIND_ISOLATION
123
+ - BE_BACKGROUND_UNOWNED
124
+ - BE_FEATURE_THIN
125
+ manual:
126
+ - Confirm each step is one external effect that is safe to repeat under the same run key.
127
+ relatedExamples: [fenced-job]
128
+ - id: BE-JOBS-5
129
+ title: A declared fenced-job is proven on the test world
130
+ kind: mandatory
131
+ hfsRules: [R155]
132
+ requirement: |-
133
+ An app that declares `fenced-job` has tests titled `fenced-job/<scenario>: ...` for `claim-bumps-token`,
134
+ `zombie-fenced-out` (a worker with a stale token gets `JobFencedOut` and the external double sees zero calls) and
135
+ `redispatch-isolated` (a re-dispatch claims a higher token, the old worker's write is refused and the new run's effects carry
136
+ their own run key), run on the test world with real Postgres.
137
+ rationale: |-
138
+ The fence is a runtime property; only a paused old worker resuming after reassignment shows it holds.
139
+ cases:
140
+ - id: case-1
141
+ when: The send-receipt e2e suite
142
+ write: "it('fenced-job/zombie-fenced-out: the stale worker changes nothing', ...)"
143
+ verification:
144
+ automated:
145
+ - BE_PATTERN_SPEC_MISSING
146
+ manual: []
147
+ relatedExamples: [fenced-job]
148
+ files:
149
+ - path: src/modules/platform/jobs/index.ts
150
+ slot: be.platform.jobs
151
+ role: "Public surface of the capability."
152
+ template: jobs/platform/jobs.index.ts.tpl
153
+ - path: src/modules/platform/jobs/jobs.module.ts
154
+ slot: be.platform.jobs
155
+ role: "Dynamic module registered once per app that runs jobs, over the connection that holds the job table."
156
+ template: jobs/platform/jobs.module.ts.tpl
157
+ - path: src/modules/platform/jobs/jobs.module-definition.ts
158
+ slot: be.platform.jobs
159
+ template: jobs/platform/jobs.module-definition.ts.tpl
160
+ - path: src/modules/platform/jobs/jobs.options.ts
161
+ slot: be.platform.jobs
162
+ template: jobs/platform/jobs.options.ts.tpl
163
+ - path: src/modules/platform/jobs/jobs.config.ts
164
+ slot: be.platform.jobs
165
+ template: jobs/platform/jobs.config.ts.tpl
166
+ - path: src/modules/platform/jobs/jobs.decorators.ts
167
+ slot: be.platform.jobs
168
+ role: "InjectJobClaims, InjectJobProcessorRegistry and their tokens."
169
+ template: jobs/platform/jobs.decorators.ts.tpl
170
+ - path: src/modules/platform/jobs/jobs.contracts.ts
171
+ slot: be.platform.jobs
172
+ role: "ClaimedJob, GuardedWrite (the token is required), RunKey."
173
+ template: jobs/platform/jobs.contracts.ts.tpl
174
+ - path: src/modules/platform/jobs/jobs.port.ts
175
+ slot: be.platform.jobs
176
+ role: "JobClaims, JobStep and JobProcessorRegistry ports."
177
+ rules: [BE_JOB_FENCE_REQUIRED]
178
+ template: jobs/platform/jobs.port.ts.tpl
179
+ - path: src/modules/platform/jobs/job-claim.service.ts
180
+ slot: be.platform.jobs
181
+ role: "The claim and the guarded writes; the only writer of the job row."
182
+ rules: [BE_JOB_WRITE_OUTSIDE_OWNER]
183
+ template: jobs/platform/job-claim.service.ts.tpl
184
+ - path: src/modules/platform/jobs/job-claim.service.spec.ts
185
+ slot: be.platform.jobs
186
+ template: jobs/platform/job-claim.service.spec.ts.tpl
187
+ - path: src/modules/platform/jobs/job-runner.service.ts
188
+ slot: be.platform.jobs
189
+ role: "Subscribes processors to their queues; claim, process, complete or fail, fenced out means stop."
190
+ template: jobs/platform/job-runner.service.ts.tpl
191
+ - path: src/modules/platform/jobs/job-runner.service.spec.ts
192
+ slot: be.platform.jobs
193
+ template: jobs/platform/job-runner.service.spec.ts.tpl
194
+ - path: src/modules/platform/jobs/fenced.processor.ts
195
+ slot: be.platform.jobs
196
+ role: "The base class every processor extends."
197
+ template: jobs/platform/fenced.processor.ts.tpl
198
+ - path: src/modules/platform/jobs/fenced.processor.spec.ts
199
+ slot: be.platform.jobs
200
+ template: jobs/platform/fenced.processor.spec.ts.tpl
201
+ - path: src/modules/platform/jobs/jobs.log-events.ts
202
+ slot: be.platform.jobs
203
+ template: jobs/platform/jobs.log-events.ts.tpl
204
+ - path: src/modules/platform/jobs/errors/jobs.error.ts
205
+ slot: be.platform.jobs
206
+ role: "JobFencedOut."
207
+ template: jobs/platform/jobs.error.ts.tpl
208
+ - path: src/modules/platform/jobs/messages/jobs.messages.ts
209
+ slot: be.domain.messages
210
+ template: jobs/platform/jobs.messages.ts.tpl
211
+ - path: src/modules/platform/jobs/persistence/connection.ts
212
+ slot: be.persistence
213
+ role: "The job entity array registered on every connection that runs jobs."
214
+ template: jobs/platform/persistence.connection.ts.tpl
215
+ - path: src/modules/platform/jobs/persistence/entities/job.entity.ts
216
+ slot: be.persistence
217
+ template: jobs/platform/persistence.job.entity.ts.tpl
218
+ - path: src/modules/platform/jobs/persistence/jobs.sql.ts
219
+ slot: be.persistence
220
+ role: "The claim and guarded-write statements as SqlText constants."
221
+ template: jobs/platform/persistence.jobs.sql.ts.tpl
222
+ - path: src/modules/platform/jobs/persistence/jobs.rows.ts
223
+ slot: be.persistence
224
+ template: jobs/platform/persistence.jobs.rows.ts.tpl
225
+ - path: src/modules/platform/jobs/persistence/migrations/<epochMs13>-create-jobs.ts
226
+ slot: be.persistence
227
+ template: jobs/platform/persistence.migration.ts.tpl
228
+ - path: src/features/jobs/<job>/index.ts
229
+ slot: be.feature.jobs
230
+ template: jobs/job.index.ts.tpl
231
+ - path: src/features/jobs/<job>/<job>.module.ts
232
+ slot: be.feature.jobs
233
+ template: jobs/job.module.ts.tpl
234
+ - path: src/features/jobs/<job>/transport/queue/<job>-queue.module.ts
235
+ slot: be.feature.jobs.queue
236
+ template: jobs/job.queue.module.ts.tpl
237
+ role: The queue transport module of the job; composed by an api or worker app, registers the processor.
238
+ - path: src/features/jobs/<job>/transport/queue/<job>.processor.ts
239
+ slot: be.feature.jobs.queue
240
+ template: jobs/job.processor.ts.tpl
241
+ role: 'The processor of one job; extends FencedProcessor — thin (R203 `BE_FEATURE_THIN`): `process` maps the claimed job and delegates into modules/.'
242
+ rules: [BE_JOB_SHAPE]
243
+ - path: src/features/jobs/<job>/steps/<step>.step.ts
244
+ slot: be.feature.jobs.steps
245
+ template: jobs/job.step.ts.tpl
246
+ role: 'One external effect; implements JobStep — thin (R203 `BE_FEATURE_THIN`): the fence call first, then one delegating call that hands the payload and the run key to a service of the modules; the payload guards live there.'
247
+ rules: [BE_JOB_SHAPE, BE_JOB_RUN_KEY]
248
+ - path: src/features/jobs/<job>/application/<action>.command.ts
249
+ slot: be.feature.application
250
+ optional: true
251
+ - path: src/features/jobs/<job>/application/<action>.handler.ts
252
+ slot: be.feature.application
253
+ optional: true
254
+ - path: src/features/jobs/<job>/application/<action>.contracts.ts
255
+ slot: be.feature.application
256
+ optional: true
257
+ - path: src/tests/e2e/<area>/<job>.e2e-spec.ts
258
+ slot: be.tests.e2e
259
+ role: "Titles fenced-job/<scenario>: ... prove the pattern."
260
+ spec: fenced-job/claim-bumps-token, fenced-job/zombie-fenced-out, fenced-job/redispatch-isolated
261
+ rules: [BE_PATTERN_SPEC_MISSING]