@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,6 @@
1
+ import { Module } from "@nestjs/common"
2
+ import { @@Channel@@Subscription } from "./@@channel@@.subscription"
3
+
4
+ @Module({ providers: [@@Channel@@Subscription] })
5
+ /** The GraphQL transport of the @@channel@@ channel: its subscription door; the hub comes from the app's realtime capability. */
6
+ export class @@Channel@@GraphqlModule {}
@@ -0,0 +1 @@
1
+ export { @@Channel@@GraphqlModule } from "./transport/graphql/@@channel@@-graphql.module"
@@ -0,0 +1,12 @@
1
+ import { Field, ID, InputType } from "@nestjs/graphql"
2
+ import { IsUUID, MaxLength } from "class-validator"
3
+
4
+ @InputType()
5
+ /** The @@channelCamel@@Changed arguments: what the caller follows. */
6
+ export class @@Channel@@Input {
7
+ /** The id of the thing the caller follows. */
8
+ @Field(() => ID)
9
+ @IsUUID()
10
+ @MaxLength(36)
11
+ id!: string
12
+ }
@@ -0,0 +1,51 @@
1
+ import { mock } from "@starci/jest-preset"
2
+ import { Test } from "@nestjs/testing"
3
+ import { @@service@@ } from "@@serviceModule@@"
4
+ import { REALTIME_HUB } from "@modules/platform/realtime"
5
+ import type { RealtimeHub, RealtimeTopic } from "@modules/platform/realtime"
6
+ import { @@Channel@@Subscription } from "./@@channel@@.subscription"
7
+
8
+ const emptyStream = (): AsyncIterable<never> => (async function* () {})()
9
+
10
+ /** A hub whose subscribe records the names of the topics it was asked for and answers one stream. */
11
+ const hubAnswering = (stream: AsyncIterable<never>) => {
12
+ const topics: Array<string> = []
13
+ const hub = mock<RealtimeHub>({
14
+ subscribe: <T extends object>(topic: RealtimeTopic<T>): AsyncIterable<T> => {
15
+ topics.push(topic.name)
16
+ return stream
17
+ },
18
+ })
19
+ return { hub, topics }
20
+ }
21
+
22
+ const build = async (hub: RealtimeHub) => {
23
+ const moduleRef = await Test.createTestingModule({
24
+ providers: [@@Channel@@Subscription, { provide: REALTIME_HUB, useValue: hub }],
25
+ }).compile()
26
+ return moduleRef.get(@@Channel@@Subscription)
27
+ }
28
+
29
+ describe("@@Channel@@Subscription", () => {
30
+ describe("@@channelCamel@@Changed", () => {
31
+ it("subscribes the client to the topic of its own principal and returns the hub stream", async () => {
32
+ const frames = emptyStream()
33
+ const { hub, topics } = hubAnswering(frames)
34
+ const door = await build(hub)
35
+
36
+ const stream = door.@@channelCamel@@Changed({ id: "p-1", roles: ["member"] }, { id: "x-1" })
37
+
38
+ expect(stream).toBe(frames)
39
+ expect(topics).toEqual([@@service@@("p-1", "x-1").name])
40
+ })
41
+
42
+ it("subscribes another principal to another topic, so it cannot receive the first one's frames", async () => {
43
+ const { hub, topics } = hubAnswering(emptyStream())
44
+ const door = await build(hub)
45
+
46
+ door.@@channelCamel@@Changed({ id: "p-2", roles: ["member"] }, { id: "x-1" })
47
+
48
+ expect(topics).not.toContain(@@service@@("p-1", "x-1").name)
49
+ })
50
+ })
51
+ })
@@ -0,0 +1,32 @@
1
+ import { Args, Resolver, Subscription } from "@nestjs/graphql"
2
+ import { CurrentPrincipal, Roles } from "@modules/domain/identity"
3
+ import { @@service@@ } from "@@serviceModule@@"
4
+ import type { @@Channel@@Frame } from "@@serviceModule@@"
5
+ import type { Principal } from "@modules/platform/cqrs"
6
+ import { InjectRealtimeHub } from "@modules/platform/realtime"
7
+ import type { RealtimeHub } from "@modules/platform/realtime"
8
+ import { @@Channel@@ChangedType } from "./dto/@@channel@@-changed.type"
9
+ import { @@Channel@@Input } from "./dto/@@channel@@.input"
10
+
11
+ @Resolver()
12
+ /**
13
+ * The push door of the @@channel@@ channel: it subscribes the client to the topic of the hub and writes nothing. `@@service@@` of
14
+ * the domain module builds the topic from the principal, so a client can only listen to a channel its principal owns; the same
15
+ * module exports the `@@Channel@@Frame` type of what travels on it.
16
+ */
17
+ export class @@Channel@@Subscription {
18
+ constructor(@InjectRealtimeHub() private readonly hub: RealtimeHub) {}
19
+
20
+ /** The changes pushed as they happen; the topic is built from the principal, so another subscriber receives nothing. */
21
+ @Subscription(() => @@Channel@@ChangedType, {
22
+ name: "@@channelCamel@@Changed",
23
+ resolve: (frame: @@Channel@@Frame): @@Channel@@ChangedType => frame,
24
+ })
25
+ @Roles("member")
26
+ @@channelCamel@@Changed(
27
+ @CurrentPrincipal() principal: Principal,
28
+ @Args("input") input: @@Channel@@Input,
29
+ ): AsyncIterable<@@Channel@@Frame> {
30
+ return this.hub.subscribe(@@service@@(principal.id, input.id))
31
+ }
32
+ }
@@ -0,0 +1,9 @@
1
+ import { Field, ObjectType } from "@nestjs/graphql"
2
+
3
+ @ObjectType()
4
+ /** One push of the @@channel@@ channel: what the subscriber may see of a change, nothing else. */
5
+ export class @@Channel@@ChangedType {
6
+ /** The id of the thing that changed. */
7
+ @Field()
8
+ id!: string
9
+ }
@@ -0,0 +1,11 @@
1
+ import { Command } from "@nestjs/cqrs"
2
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
3
+ import type { SagaTransition } from "@modules/platform/saga"
4
+ import type { Compensate@@Saga@@Request } from "./compensate-@@saga@@.contracts"
5
+
6
+ /** Asks to compensate the @@saga@@ saga of an id whose answer failed; the @@failed@@ consumer sends it for every delivered event. */
7
+ export class Compensate@@Saga@@Command extends Command<SagaTransition> {
8
+ constructor(readonly params: PublicExecuteParams<Compensate@@Saga@@Request>) {
9
+ super()
10
+ }
11
+ }
@@ -0,0 +1,7 @@
1
+ /** What the event that reports the failure carries: the id it is about and the id of the delivery (the inbox dedupe key). */
2
+ export interface Compensate@@Saga@@Request {
3
+ /** The id the saga run is about. */
4
+ readonly id: string
5
+ /** The id of the delivered event. */
6
+ readonly eventId: string
7
+ }
@@ -0,0 +1,22 @@
1
+ import { CommandHandler } from "@nestjs/cqrs"
2
+ import { ICQRSHandler } from "@modules/platform/cqrs"
3
+ import { InjectLogger } from "@modules/platform/logging"
4
+ import type { Logger } from "@modules/platform/logging"
5
+ import type { SagaTransition } from "@modules/platform/saga"
6
+ import { @@Saga@@SagaService } from "../@@saga@@.saga.service"
7
+ import { Compensate@@Saga@@Command } from "./compensate-@@saga@@.command"
8
+
9
+ @CommandHandler(Compensate@@Saga@@Command)
10
+ /** Compensates the @@saga@@ saga of a failed run; the saga decides the fence and the no-op of a redelivery. */
11
+ export class Compensate@@Saga@@Handler extends ICQRSHandler<Compensate@@Saga@@Command, SagaTransition> {
12
+ constructor(
13
+ @InjectLogger() logger: Logger,
14
+ private readonly saga: @@Saga@@SagaService,
15
+ ) {
16
+ super(logger)
17
+ }
18
+
19
+ protected override process(command: Compensate@@Saga@@Command): Promise<SagaTransition> {
20
+ return this.saga.compensate(command.params.request.id, command.params.request.eventId)
21
+ }
22
+ }
@@ -0,0 +1,21 @@
1
+ import { Injectable } from "@nestjs/common"
2
+ import type { CommandBus } from "@nestjs/cqrs"
3
+ import { InjectCommandBus } from "@modules/platform/cqrs"
4
+ import { Undo@@Saga@@Command } from "../application/undo-@@saga@@.command"
5
+
6
+ @Injectable()
7
+ /** The compensation of the @@step@@ step: asks the domain to undo what the step did. */
8
+ export class @@Step@@Compensation {
9
+ /** The step this compensation undoes. */
10
+ readonly step = "@@step@@"
11
+
12
+ /** The event that tells the saga to run this compensation (the contract of `@@from@@` says it compensates `@@owner@@.@@step@@`). */
13
+ readonly event = "@@from@@.@@failed@@"
14
+
15
+ constructor(@InjectCommandBus() private readonly commandBus: CommandBus) {}
16
+
17
+ /** Dispatches the one command of the compensation. */
18
+ async run(id: string): Promise<void> {
19
+ await this.commandBus.execute(new Undo@@Saga@@Command({ request: { id } }))
20
+ }
21
+ }
@@ -0,0 +1,11 @@
1
+ import { Command } from "@nestjs/cqrs"
2
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
3
+ import type { SagaTransition } from "@modules/platform/saga"
4
+ import type { Complete@@Saga@@Request } from "./complete-@@saga@@.contracts"
5
+
6
+ /** Asks to complete the @@saga@@ saga of an id whose answer arrived; the @@done@@ consumer sends it for every delivered event. */
7
+ export class Complete@@Saga@@Command extends Command<SagaTransition> {
8
+ constructor(readonly params: PublicExecuteParams<Complete@@Saga@@Request>) {
9
+ super()
10
+ }
11
+ }
@@ -0,0 +1,7 @@
1
+ /** What the event that reports the last step done carries: the id it is about and the id of the delivery (the inbox dedupe key). */
2
+ export interface Complete@@Saga@@Request {
3
+ /** The id the saga run is about. */
4
+ readonly id: string
5
+ /** The id of the delivered event. */
6
+ readonly eventId: string
7
+ }
@@ -0,0 +1,22 @@
1
+ import { CommandHandler } from "@nestjs/cqrs"
2
+ import { ICQRSHandler } from "@modules/platform/cqrs"
3
+ import { InjectLogger } from "@modules/platform/logging"
4
+ import type { Logger } from "@modules/platform/logging"
5
+ import type { SagaTransition } from "@modules/platform/saga"
6
+ import { @@Saga@@SagaService } from "../@@saga@@.saga.service"
7
+ import { Complete@@Saga@@Command } from "./complete-@@saga@@.command"
8
+
9
+ @CommandHandler(Complete@@Saga@@Command)
10
+ /** Completes the @@saga@@ saga of a run whose last step answered; the saga decides the fence and the no-op of a redelivery. */
11
+ export class Complete@@Saga@@Handler extends ICQRSHandler<Complete@@Saga@@Command, SagaTransition> {
12
+ constructor(
13
+ @InjectLogger() logger: Logger,
14
+ private readonly saga: @@Saga@@SagaService,
15
+ ) {
16
+ super(logger)
17
+ }
18
+
19
+ protected override process(command: Complete@@Saga@@Command): Promise<SagaTransition> {
20
+ return this.saga.complete(command.params.request.id, command.params.request.eventId)
21
+ }
22
+ }
@@ -0,0 +1,21 @@
1
+ import { Injectable } from "@nestjs/common"
2
+ import type { CommandBus } from "@nestjs/cqrs"
3
+ import { @@Done@@Event } from "@modules/events/@@from@@"
4
+ import { InjectCommandBus } from "@modules/platform/cqrs"
5
+ import type { EventConsumer, EventDelivery } from "@modules/platform/event-bus"
6
+ import { Complete@@Saga@@Command } from "../../application/complete-@@saga@@.command"
7
+
8
+ @Injectable()
9
+ /** Consumer of `@@from@@.@@done@@`, the last event of the @@saga@@ saga: hands each delivery to the complete command. */
10
+ export class @@Done@@Consumer implements EventConsumer<@@Done@@Event> {
11
+ readonly event = @@Done@@Event
12
+
13
+ constructor(@InjectCommandBus() private readonly commandBus: CommandBus) {}
14
+
15
+ /** Dispatches one complete command; the saga takes the delivery through the inbox, so a redelivery is a no-op. */
16
+ async handle(delivery: EventDelivery<@@Done@@Event>): Promise<void> {
17
+ await this.commandBus.execute(
18
+ new Complete@@Saga@@Command({ request: { id: delivery.event.payload.id, eventId: delivery.eventId } }),
19
+ )
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ import { Injectable } from "@nestjs/common"
2
+ import type { CommandBus } from "@nestjs/cqrs"
3
+ import { @@Failed@@Event } from "@modules/events/@@from@@"
4
+ import { InjectCommandBus } from "@modules/platform/cqrs"
5
+ import type { EventConsumer, EventDelivery } from "@modules/platform/event-bus"
6
+ import { Compensate@@Saga@@Command } from "../../application/compensate-@@saga@@.command"
7
+
8
+ @Injectable()
9
+ /** Consumer of `@@from@@.@@failed@@`, the failure event of the @@saga@@ saga: hands each delivery to the compensate command. */
10
+ export class @@Failed@@Consumer implements EventConsumer<@@Failed@@Event> {
11
+ readonly event = @@Failed@@Event
12
+
13
+ constructor(@InjectCommandBus() private readonly commandBus: CommandBus) {}
14
+
15
+ /** Dispatches one compensate command; the saga takes the delivery through the inbox, so a redelivery is a no-op. */
16
+ async handle(delivery: EventDelivery<@@Failed@@Event>): Promise<void> {
17
+ await this.commandBus.execute(
18
+ new Compensate@@Saga@@Command({ request: { id: delivery.event.payload.id, eventId: delivery.eventId } }),
19
+ )
20
+ }
21
+ }
@@ -0,0 +1 @@
1
+ export { @@Saga@@MessageModule } from "./transport/message/@@saga@@-message.module"
@@ -0,0 +1,23 @@
1
+ import { Module } from "@nestjs/common"
2
+ import type { OnModuleInit } from "@nestjs/common"
3
+ import { InjectEventConsumerRegistry } from "@modules/platform/event-bus"
4
+ import type { EventConsumerRegistry } from "@modules/platform/event-bus"
5
+ import { @@Saga@@Module } from "../../@@saga@@.module"
6
+ import { @@Done@@Consumer } from "./@@done@@.consumer"
7
+ import { @@Failed@@Consumer } from "./@@failed@@.consumer"
8
+
9
+ @Module({ imports: [@@Saga@@Module], providers: [@@Done@@Consumer, @@Failed@@Consumer] })
10
+ /** The message transport of the @@saga@@ saga: registers its consumers with the event bus. */
11
+ export class @@Saga@@MessageModule implements OnModuleInit {
12
+ constructor(
13
+ @InjectEventConsumerRegistry() private readonly registry: EventConsumerRegistry,
14
+ private readonly @@doneCamel@@Consumer: @@Done@@Consumer,
15
+ private readonly @@failedCamel@@Consumer: @@Failed@@Consumer,
16
+ ) {}
17
+
18
+ /** Hands every consumer to the registry. */
19
+ onModuleInit(): void {
20
+ this.registry.add(this.@@doneCamel@@Consumer)
21
+ this.registry.add(this.@@failedCamel@@Consumer)
22
+ }
23
+ }
@@ -0,0 +1,20 @@
1
+ import { Module } from "@nestjs/common"
2
+ import { Compensate@@Saga@@Handler } from "./application/compensate-@@saga@@.handler"
3
+ import { Complete@@Saga@@Handler } from "./application/complete-@@saga@@.handler"
4
+ import { Undo@@Saga@@Handler } from "./application/undo-@@saga@@.handler"
5
+ import { @@Step@@Compensation } from "./compensations/@@step@@.compensation"
6
+ import { @@Saga@@SagaService } from "./@@saga@@.saga.service"
7
+ import { @@Step@@Step } from "./steps/@@step@@.saga-step"
8
+
9
+ @Module({
10
+ providers: [
11
+ Compensate@@Saga@@Handler,
12
+ Complete@@Saga@@Handler,
13
+ Undo@@Saga@@Handler,
14
+ @@Saga@@SagaService,
15
+ @@Step@@Step,
16
+ @@Step@@Compensation,
17
+ ],
18
+ })
19
+ /** The @@saga@@ saga: its orchestrator, its step and compensation, and the handlers of the commands the consumers dispatch. */
20
+ export class @@Saga@@Module {}
@@ -0,0 +1,70 @@
1
+ import { Test } from "@nestjs/testing"
2
+ import { mock } from "@starci/jest-preset"
3
+ import { @@sagaUpper@@_SAGA } from "@@serviceModule@@"
4
+ import { SAGA_SERVICE } from "@modules/platform/saga"
5
+ import type { SagaService } from "@modules/platform/saga"
6
+ import { @@Step@@Compensation } from "./compensations/@@step@@.compensation"
7
+ import { @@Saga@@SagaService } from "./@@saga@@.saga.service"
8
+ import { @@Step@@Step } from "./steps/@@step@@.saga-step"
9
+
10
+ const build = async () => {
11
+ const sagas = mock<SagaService>()
12
+ const compensation = mock<@@Step@@Compensation>()
13
+ const moduleRef = await Test.createTestingModule({
14
+ providers: [
15
+ @@Saga@@SagaService,
16
+ { provide: SAGA_SERVICE, useValue: sagas },
17
+ @@Step@@Step,
18
+ { provide: @@Step@@Compensation, useValue: compensation },
19
+ ],
20
+ }).compile()
21
+ return { saga: moduleRef.get(@@Saga@@SagaService), sagas, compensation }
22
+ }
23
+
24
+ describe("@@Saga@@SagaService", () => {
25
+ describe("compensate", () => {
26
+ it("hands the run and the compensation of its step to the saga state machine", async () => {
27
+ const { saga, sagas, compensation } = await build()
28
+ sagas.compensate.mockResolvedValue("applied")
29
+
30
+ expect(await saga.compensate("x-1", "e-1")).toBe("applied")
31
+
32
+ expect(sagas.compensate).toHaveBeenCalledWith({
33
+ saga: @@sagaUpper@@_SAGA,
34
+ correlationId: "x-1",
35
+ eventId: "e-1",
36
+ step: expect.any(@@Step@@Step),
37
+ compensation,
38
+ })
39
+ })
40
+ })
41
+
42
+ describe("complete", () => {
43
+ it("hands the run to the saga state machine to settle as completed", async () => {
44
+ const { saga, sagas } = await build()
45
+ sagas.complete.mockResolvedValue("applied")
46
+
47
+ expect(await saga.complete("x-1", "e-2")).toBe("applied")
48
+
49
+ expect(sagas.complete).toHaveBeenCalledWith({ saga: @@sagaUpper@@_SAGA, correlationId: "x-1", eventId: "e-2" })
50
+ })
51
+ })
52
+
53
+ describe("stateOf", () => {
54
+ it("answers the persisted state of the run", async () => {
55
+ const { saga, sagas } = await build()
56
+ sagas.state.mockResolvedValue({ correlationId: "x-1", status: "running", version: 1 })
57
+
58
+ expect(await saga.stateOf("x-1")).toEqual({ correlationId: "x-1", status: "running", version: 1 })
59
+
60
+ expect(sagas.state).toHaveBeenCalledWith({ saga: @@sagaUpper@@_SAGA, correlationId: "x-1" })
61
+ })
62
+
63
+ it("answers null when the id never started a run", async () => {
64
+ const { saga, sagas } = await build()
65
+ sagas.state.mockResolvedValue(null)
66
+
67
+ expect(await saga.stateOf("x-9")).toBeNull()
68
+ })
69
+ })
70
+ })
@@ -0,0 +1,44 @@
1
+ import { Injectable } from "@nestjs/common"
2
+ import { @@sagaUpper@@_SAGA } from "@@serviceModule@@"
3
+ import { InjectSagaService } from "@modules/platform/saga"
4
+ import type { SagaService, SagaTransition } from "@modules/platform/saga"
5
+ import type { Find@@Saga@@SagaResult } from "./@@saga@@.saga-state"
6
+ import { @@Step@@Compensation } from "./compensations/@@step@@.compensation"
7
+ import { @@Step@@Step } from "./steps/@@step@@.saga-step"
8
+
9
+ @Injectable()
10
+ /**
11
+ * The orchestrator of the @@saga@@ saga: the list of its steps and, for each, the compensation that undoes it. It decides nothing:
12
+ * the state machine, the version fence and the `Compensating` log are `platform/saga`'s, the work of each compensation is a
13
+ * command. The run starts in the transaction of the domain (`@@sagaUpper@@_SAGA`, written by @@service@@ together with the event the
14
+ * step puts on the wire) and ends when the answer arrives: the done event completes it, the failed event compensates every step
15
+ * in reverse order.
16
+ */
17
+ export class @@Saga@@SagaService {
18
+ constructor(
19
+ @InjectSagaService() private readonly sagas: SagaService,
20
+ private readonly @@stepCamel@@Step: @@Step@@Step,
21
+ private readonly @@stepCamel@@Compensation: @@Step@@Compensation,
22
+ ) {}
23
+
24
+ /** Compensates the run of an id after the event that reports its failure; the store takes the delivery through the inbox. */
25
+ compensate(id: string, eventId: string): Promise<SagaTransition> {
26
+ return this.sagas.compensate({
27
+ saga: @@sagaUpper@@_SAGA,
28
+ correlationId: id,
29
+ eventId,
30
+ step: this.@@stepCamel@@Step,
31
+ compensation: this.@@stepCamel@@Compensation,
32
+ })
33
+ }
34
+
35
+ /** Where the run of an id stands, or null when it never started one. */
36
+ stateOf(id: string): Promise<Find@@Saga@@SagaResult> {
37
+ return this.sagas.state({ saga: @@sagaUpper@@_SAGA, correlationId: id })
38
+ }
39
+
40
+ /** Completes the run of an id after the event that reports its last step done; the store takes the delivery through the inbox. */
41
+ complete(id: string, eventId: string): Promise<SagaTransition> {
42
+ return this.sagas.complete({ saga: @@sagaUpper@@_SAGA, correlationId: id, eventId })
43
+ }
44
+ }
@@ -0,0 +1,14 @@
1
+ import type { SagaStatus } from "@modules/platform/saga"
2
+
3
+ /** The persisted state of one run of the @@saga@@ saga: one row per run, moved only through its version (the fence of `platform/saga`). */
4
+ export interface @@Saga@@SagaState {
5
+ /** The id the run is about. */
6
+ readonly correlationId: string
7
+ /** Where the run stands: running its steps, compensating, compensated or completed. */
8
+ readonly status: SagaStatus
9
+ /** The fence: every transition names the version it read and moves it by one. */
10
+ readonly version: number
11
+ }
12
+
13
+ /** The state of the run of an id, or null when it never started one. */
14
+ export type Find@@Saga@@SagaResult = @@Saga@@SagaState | null
@@ -0,0 +1,11 @@
1
+ import { Injectable } from "@nestjs/common"
2
+
3
+ @Injectable()
4
+ /** The first step of the @@saga@@ saga: the domain does its work in the transaction that starts the run and announces `@@owner@@.@@step@@`. */
5
+ export class @@Step@@Step {
6
+ /** The name the compensation of this step carries. */
7
+ readonly name = "@@step@@"
8
+
9
+ /** The event this step puts on the wire (the contract `be/contracts/<service>/events.json`). */
10
+ readonly event = "@@owner@@.@@step@@"
11
+ }
@@ -0,0 +1,10 @@
1
+ import { Command } from "@nestjs/cqrs"
2
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
3
+ import type { Undo@@Saga@@Request } from "./undo-@@saga@@.contracts"
4
+
5
+ /** Asks to undo what the @@saga@@ saga did for an id; the compensation of its step sends it. */
6
+ export class Undo@@Saga@@Command extends Command<void> {
7
+ constructor(readonly params: PublicExecuteParams<Undo@@Saga@@Request>) {
8
+ super()
9
+ }
10
+ }
@@ -0,0 +1,5 @@
1
+ /** What undoing the @@saga@@ saga takes. */
2
+ export interface Undo@@Saga@@Request {
3
+ /** The id the saga run is about. */
4
+ readonly id: string
5
+ }
@@ -0,0 +1,21 @@
1
+ import { CommandHandler } from "@nestjs/cqrs"
2
+ import { @@service@@ } from "@@serviceModule@@"
3
+ import { ICQRSHandler } from "@modules/platform/cqrs"
4
+ import { InjectLogger } from "@modules/platform/logging"
5
+ import type { Logger } from "@modules/platform/logging"
6
+ import { Undo@@Saga@@Command } from "./undo-@@saga@@.command"
7
+
8
+ @CommandHandler(Undo@@Saga@@Command)
9
+ /** Undoes the @@saga@@ saga for an id; @@service@@ decides the transaction and the no-op of a redelivery (`undo@@Saga@@`). */
10
+ export class Undo@@Saga@@Handler extends ICQRSHandler<Undo@@Saga@@Command, void> {
11
+ constructor(
12
+ @InjectLogger() logger: Logger,
13
+ private readonly domain: @@service@@,
14
+ ) {
15
+ super(logger)
16
+ }
17
+
18
+ protected override process(command: Undo@@Saga@@Command): Promise<void> {
19
+ return this.domain.undo@@Saga@@({ id: command.params.request.id })
20
+ }
21
+ }
@@ -0,0 +1,72 @@
1
+ import { createHmac } from "node:crypto"
2
+ import { FakeClock, mock } from "@starci/jest-preset"
3
+ import type { RawBodyRequest } from "@nestjs/common"
4
+ import { Test } from "@nestjs/testing"
5
+ import type { Request } from "express"
6
+ import { @@service@@ } from "@@serviceModule@@"
7
+ import { CLOCK } from "@modules/platform/clock"
8
+ import { Secret } from "@modules/platform/config"
9
+ import { HttpSecurityModule } from "@modules/platform/http-security"
10
+ import { @@Event@@Request } from "./dto/@@event@@.request"
11
+ import { @@Provider@@Webhook } from "./@@provider@@.webhook"
12
+
13
+ const AT = "2026-01-02T03:04:05.000Z"
14
+ const SECRET = "spec-webhook-secret"
15
+ const SIGNED_AT = String(new Date(AT).getTime())
16
+
17
+ /** The delivery the notifier sends: the validated request class, as the pipe builds it. */
18
+ const deliveryOf = (): @@Event@@Request => {
19
+ const delivery = new @@Event@@Request()
20
+ delivery.id = "delivery-1"
21
+ return delivery
22
+ }
23
+
24
+ const DELIVERY = deliveryOf()
25
+ const RAW_BODY = Buffer.from(JSON.stringify(DELIVERY))
26
+ const REQUEST = mock<RawBodyRequest<Request>>({ rawBody: RAW_BODY })
27
+
28
+ const sign = (rawBody: Buffer, timestamp: string): string =>
29
+ `sha256=${createHmac("sha256", SECRET).update(`${timestamp}.`).update(rawBody).digest("hex")}`
30
+
31
+ const build = async (deliveries: @@service@@) => {
32
+ const moduleRef = await Test.createTestingModule({
33
+ imports: [
34
+ HttpSecurityModule.register({
35
+ allowedOrigins: [],
36
+ rateLimit: { windowMs: 60_000, defaultLimit: 100, strictLimit: 100 },
37
+ webhooks: { "@@provider@@": { secret: new Secret(SECRET), toleranceMs: 300_000 } },
38
+ }),
39
+ ],
40
+ controllers: [@@Provider@@Webhook],
41
+ providers: [
42
+ { provide: CLOCK, useValue: new FakeClock(AT) },
43
+ { provide: @@service@@, useValue: deliveries },
44
+ ],
45
+ }).compile()
46
+ return moduleRef.get(@@Provider@@Webhook)
47
+ }
48
+
49
+ describe("@@Provider@@Webhook", () => {
50
+ describe("receive", () => {
51
+ it("proves the delivery on its raw body and headers, then hands it to the intake once", async () => {
52
+ const deliveries = mock<@@service@@>()
53
+ const door = await build(deliveries)
54
+
55
+ await door.receive(REQUEST, sign(RAW_BODY, SIGNED_AT), SIGNED_AT, DELIVERY)
56
+
57
+ expect(deliveries.accept@@Provider@@Delivery).toHaveBeenCalledTimes(1)
58
+ expect(deliveries.accept@@Provider@@Delivery).toHaveBeenCalledWith(DELIVERY)
59
+ })
60
+
61
+ it("never reaches the intake when the signature is not the provider's", async () => {
62
+ const deliveries = mock<@@service@@>()
63
+ const door = await build(deliveries)
64
+
65
+ await expect(
66
+ door.receive(REQUEST, sign(Buffer.from("other body"), SIGNED_AT), SIGNED_AT, DELIVERY),
67
+ ).rejects.toMatchObject({ code: "HTTP_SECURITY_WEBHOOK_SIGNATURE_INVALID" })
68
+
69
+ expect(deliveries.accept@@Provider@@Delivery).not.toHaveBeenCalled()
70
+ })
71
+ })
72
+ })
@@ -0,0 +1,36 @@
1
+ import { Body, Controller, Headers, HttpCode, HttpStatus, Post, Req } from "@nestjs/common"
2
+ import type { RawBodyRequest } from "@nestjs/common"
3
+ import type { Request } from "express"
4
+ import { Public, PublicReason } from "@modules/domain/identity"
5
+ import { @@service@@ } from "@@serviceModule@@"
6
+ import { InjectWebhookSignature, RateLimit, RateTier } from "@modules/platform/http-security"
7
+ import type { WebhookSignatureService } from "@modules/platform/http-security"
8
+ import { @@Event@@Request } from "./dto/@@event@@.request"
9
+
10
+ @Controller("webhooks/@@provider@@")
11
+ /**
12
+ * POST /webhooks/@@provider@@, the signed intake door of the @@provider@@ notifier. The signature and the replay window are proven
13
+ * first; the verified delivery goes to exactly one intake method of @@service@@ (`accept@@Provider@@Delivery`), which records it
14
+ * once and publishes the event in its own transaction.
15
+ */
16
+ export class @@Provider@@Webhook {
17
+ constructor(
18
+ @InjectWebhookSignature() private readonly signature: WebhookSignatureService,
19
+ private readonly deliveries: @@service@@,
20
+ ) {}
21
+
22
+ /** Acknowledges with 204 and no body: the notifier learns only that the delivery was accepted. */
23
+ @Post()
24
+ @HttpCode(HttpStatus.NO_CONTENT)
25
+ @Public({ reason: PublicReason.SignedWebhook })
26
+ @RateLimit(RateTier.Strict)
27
+ async receive(
28
+ @Req() request: RawBodyRequest<Request>,
29
+ @Headers("x-@@provider@@-signature") signature: string | undefined,
30
+ @Headers("x-@@provider@@-timestamp") timestamp: string | undefined,
31
+ @Body() delivery: @@Event@@Request,
32
+ ): Promise<void> {
33
+ this.signature.verify({ provider: "@@provider@@", rawBody: request.rawBody, signature, timestamp })
34
+ await this.deliveries.accept@@Provider@@Delivery(delivery)
35
+ }
36
+ }
@@ -0,0 +1,6 @@
1
+ import { Module } from "@nestjs/common"
2
+ import { @@Provider@@Webhook } from "./@@provider@@.webhook"
3
+
4
+ @Module({ controllers: [@@Provider@@Webhook] })
5
+ /** The HTTP transport of the @@provider@@ notifier: its webhook door; the intake service and the signature service come from the app's capabilities. */
6
+ export class @@Provider@@HttpModule {}
@@ -0,0 +1 @@
1
+ export { @@Provider@@HttpModule } from "./transport/http/@@provider@@-http.module"
@@ -0,0 +1,9 @@
1
+ import { IsString, MaxLength } from "class-validator"
2
+
3
+ /** The delivery the @@provider@@ notifier sends, as the validation pipe builds it: the fields the intake reads, bounded; add the provider's other fields here. */
4
+ export class @@Event@@Request {
5
+ /** The provider's id of this delivery: the dedupe key of the intake. */
6
+ @IsString()
7
+ @MaxLength(64)
8
+ id!: string
9
+ }