@starci/hfs 4.0.8 → 4.0.10

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 (703) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +81 -39
  4. package/emit/contracts.mjs +56 -9
  5. package/emit/db-types.mjs +169 -0
  6. package/emit/events.mjs +41 -0
  7. package/emit/operations-worker.mjs +1 -1
  8. package/emit/operations.mjs +2 -2
  9. package/emit/schema-worker.mjs +1 -1
  10. package/lint/bound-sys.cjs +50 -0
  11. package/lint/run.mjs +131 -24
  12. package/package.json +16 -10
  13. package/report/sonar.mjs +8 -8
  14. package/runtime/engine/admission.mjs +308 -0
  15. package/runtime/engine/canonical-json.mjs +10 -0
  16. package/runtime/engine/config.mjs +486 -0
  17. package/runtime/engine/db/blob.mjs +315 -0
  18. package/runtime/engine/db/ledger-paths.mjs +83 -0
  19. package/runtime/engine/db/ledger.mjs +1118 -0
  20. package/runtime/engine/db/machine-connection.mjs +135 -0
  21. package/runtime/engine/db/machine-schema.mjs +84 -0
  22. package/runtime/engine/db/machine.mjs +1367 -0
  23. package/runtime/engine/db/migrations/machine/0001-init.sql +924 -0
  24. package/runtime/engine/db/migrations/runtime/0001-init.sql +1108 -0
  25. package/runtime/engine/db/provider-reservations.mjs +101 -0
  26. package/runtime/engine/digest.mjs +16 -0
  27. package/runtime/engine/invalid-config.mjs +15 -0
  28. package/runtime/engine/orca-config.mjs +27 -0
  29. package/runtime/engine/refuse.mjs +11 -0
  30. package/runtime/engine/secrets.mjs +130 -0
  31. package/runtime/knowledge/hfs/canon-pins.yaml +162 -31
  32. package/runtime/knowledge/hfs/peer-integrations.yaml +2 -2
  33. package/runtime/knowledge/hfs/rules.yaml +2059 -0
  34. package/runtime/knowledge/hfs/slots.yaml +912 -120
  35. package/runtime/knowledge/patterns/be/api.yaml +369 -0
  36. package/runtime/knowledge/patterns/be/cli.yaml +297 -0
  37. package/runtime/knowledge/patterns/be/domain.yaml +113 -0
  38. package/runtime/knowledge/patterns/be/event-bus.yaml +285 -0
  39. package/runtime/knowledge/patterns/be/jobs.yaml +261 -0
  40. package/runtime/knowledge/patterns/be/projections.yaml +178 -0
  41. package/runtime/knowledge/patterns/be/queues.yaml +236 -0
  42. package/runtime/knowledge/patterns/be/reactors.yaml +140 -0
  43. package/runtime/knowledge/patterns/be/realtime.yaml +222 -0
  44. package/runtime/knowledge/patterns/be/saga.yaml +248 -0
  45. package/runtime/knowledge/patterns/be/webhooks.yaml +221 -0
  46. package/runtime/knowledge/patterns/fe/folder.yaml +131 -25
  47. package/runtime/knowledge/sonar-gate.yaml +52 -18
  48. package/runtime/modules/kernel/failure-codes.yaml +611 -51
  49. package/runtime/modules/models/registry.yaml +553 -0
  50. package/runtime/modules/models/runtimes.yaml +695 -0
  51. package/runtime/modules/ops/_labels.yaml +53 -0
  52. package/runtime/scripts/api/fs/forbidden-root.mjs +51 -0
  53. package/runtime/scripts/api/fs/is-link-like.mjs +25 -0
  54. package/runtime/scripts/api/fs/lib.mjs +16 -2
  55. package/runtime/scripts/api/fs/safe-remove.mjs +12 -132
  56. package/runtime/scripts/api/git/config-get.mjs +10 -0
  57. package/runtime/scripts/api/git/diff.mjs +5 -0
  58. package/runtime/scripts/api/git/lib.mjs +18 -26
  59. package/runtime/scripts/api/git/log.mjs +5 -0
  60. package/runtime/scripts/api/git/ls-files.mjs +6 -0
  61. package/runtime/scripts/api/git/ls-tree.mjs +5 -0
  62. package/runtime/scripts/api/git/merge-base.mjs +9 -0
  63. package/runtime/scripts/api/git/remote.mjs +5 -0
  64. package/runtime/scripts/api/git/rev-parse-query.mjs +5 -0
  65. package/runtime/scripts/api/git/show.mjs +6 -0
  66. package/runtime/scripts/api/node/lib.mjs +14 -0
  67. package/runtime/scripts/api/node/spawn-node.mjs +6 -0
  68. package/runtime/scripts/api/process/lib.mjs +111 -0
  69. package/runtime/scripts/api/process/owned-process.mjs +78 -0
  70. package/runtime/scripts/api/process/resolve-real-tool.mjs +46 -0
  71. package/runtime/scripts/api/process/run-program.mjs +7 -0
  72. package/runtime/scripts/api/process/stop-owned-process.mjs +9 -0
  73. package/runtime/scripts/api/sops/decrypt.mjs +9 -12
  74. package/runtime/scripts/api/sops/lib.mjs +255 -0
  75. package/runtime/scripts/api/sops/seal.mjs +22 -0
  76. package/runtime/scripts/connectors/lib.mjs +488 -0
  77. package/runtime/scripts/hfs/architecture/ast-walks.mjs +220 -0
  78. package/runtime/scripts/hfs/architecture/backend.mjs +16 -23
  79. package/runtime/scripts/hfs/architecture/background-unowned.mjs +16 -34
  80. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +3 -1
  81. package/runtime/scripts/hfs/architecture/clones.mjs +4 -4
  82. package/runtime/scripts/hfs/architecture/config.mjs +9 -6
  83. package/runtime/scripts/hfs/architecture/connection-map.mjs +20 -8
  84. package/runtime/scripts/hfs/architecture/context-coupling.mjs +88 -0
  85. package/runtime/scripts/hfs/architecture/context-map.mjs +237 -0
  86. package/runtime/scripts/hfs/architecture/context-owner.mjs +82 -0
  87. package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -0
  88. package/runtime/scripts/hfs/architecture/context-transaction.mjs +60 -0
  89. package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +4 -10
  90. package/runtime/scripts/hfs/architecture/contracts.mjs +33 -162
  91. package/runtime/scripts/hfs/architecture/dead-exports.mjs +58 -29
  92. package/runtime/scripts/hfs/architecture/doc-language.mjs +1 -1
  93. package/runtime/scripts/hfs/architecture/entrypoint.mjs +2 -1
  94. package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +2 -2
  95. package/runtime/scripts/hfs/architecture/feature-shape.mjs +5 -2
  96. package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
  97. package/runtime/scripts/hfs/architecture/frontend.mjs +17 -43
  98. package/runtime/scripts/hfs/architecture/hfs.mjs +24 -39
  99. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +1 -1
  100. package/runtime/scripts/hfs/architecture/index.mjs +17 -4
  101. package/runtime/scripts/hfs/architecture/machine-ast.mjs +44 -11
  102. package/runtime/scripts/hfs/architecture/managed-scripts.mjs +27 -0
  103. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +30 -6
  104. package/runtime/scripts/hfs/architecture/next-data.mjs +22 -63
  105. package/runtime/scripts/hfs/architecture/owners.mjs +35 -15
  106. package/runtime/scripts/hfs/architecture/register-once.mjs +2 -1
  107. package/runtime/scripts/hfs/architecture/registration.mjs +23 -81
  108. package/runtime/scripts/hfs/architecture/required-files.mjs +20 -12
  109. package/runtime/scripts/hfs/architecture/schema-owner.mjs +17 -114
  110. package/runtime/scripts/hfs/architecture/server-action.mjs +24 -0
  111. package/runtime/scripts/hfs/architecture/side-boundary.mjs +16 -0
  112. package/runtime/scripts/hfs/architecture/source-names.mjs +27 -147
  113. package/runtime/scripts/hfs/architecture/sql-owner.mjs +59 -18
  114. package/runtime/scripts/hfs/architecture/sql-returning.mjs +64 -0
  115. package/runtime/scripts/hfs/architecture/supabase-ast.mjs +111 -0
  116. package/runtime/scripts/hfs/architecture/supabase-be.mjs +131 -0
  117. package/runtime/scripts/hfs/architecture/supabase-tables.mjs +42 -0
  118. package/runtime/scripts/hfs/architecture/supabase.mjs +434 -0
  119. package/runtime/scripts/hfs/architecture/surface.mjs +9 -3
  120. package/runtime/scripts/hfs/architecture/test-world-files.mjs +9 -19
  121. package/runtime/scripts/hfs/architecture/tiers.mjs +47 -5
  122. package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -3
  123. package/runtime/scripts/hfs/architecture/typescript.mjs +12 -26
  124. package/runtime/scripts/hfs/architecture.mjs +4 -4
  125. package/runtime/scripts/hfs/check.mjs +75 -72
  126. package/runtime/scripts/hfs/coverage-scope.mjs +162 -0
  127. package/runtime/scripts/hfs/declaration-shape.mjs +62 -0
  128. package/runtime/scripts/hfs/declaration-slots.mjs +75 -0
  129. package/runtime/scripts/hfs/edition-slots.mjs +66 -0
  130. package/runtime/scripts/hfs/manifest-shape.mjs +109 -32
  131. package/runtime/scripts/hfs/param-names.mjs +18 -0
  132. package/runtime/scripts/hfs/path-findings.mjs +1 -1
  133. package/runtime/scripts/hfs/repo-identity.mjs +9 -6
  134. package/runtime/scripts/hfs/rule-params-shape.mjs +50 -0
  135. package/runtime/scripts/hfs/rules/app-root.mjs +51 -0
  136. package/runtime/scripts/hfs/rules/cli.mjs +37 -0
  137. package/runtime/scripts/hfs/rules/contract-compat.mjs +71 -0
  138. package/runtime/scripts/hfs/rules/contract.mjs +10 -9
  139. package/runtime/scripts/hfs/rules/database-config.mjs +102 -0
  140. package/runtime/scripts/hfs/rules/database-constants.mjs +13 -0
  141. package/runtime/scripts/hfs/rules/database-migrations.mjs +111 -0
  142. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +233 -0
  143. package/runtime/scripts/hfs/rules/database-sql.mjs +336 -0
  144. package/runtime/scripts/hfs/rules/database.mjs +68 -0
  145. package/runtime/scripts/hfs/rules/deps.mjs +1 -1
  146. package/runtime/scripts/hfs/rules/docker.mjs +259 -0
  147. package/runtime/scripts/hfs/rules/edition.mjs +111 -0
  148. package/runtime/scripts/hfs/rules/event-bus.mjs +153 -0
  149. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -1
  150. package/runtime/scripts/hfs/rules/fe-no-tests.mjs +6 -5
  151. package/runtime/scripts/hfs/rules/frontend-tree.mjs +9 -9
  152. package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -6
  153. package/runtime/scripts/hfs/rules/kinds.mjs +84 -0
  154. package/runtime/scripts/hfs/rules/lint-suppression.mjs +1 -1
  155. package/runtime/scripts/hfs/rules/monorepo.mjs +179 -0
  156. package/runtime/scripts/hfs/rules/peer-integrations.mjs +3 -3
  157. package/runtime/scripts/hfs/rules/pipeline.mjs +11 -21
  158. package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -2
  159. package/runtime/scripts/hfs/rules/read.mjs +1 -1
  160. package/runtime/scripts/hfs/rules/repo-local-checks.mjs +2 -2
  161. package/runtime/scripts/hfs/rules/saga.mjs +170 -0
  162. package/runtime/scripts/hfs/rules/secrets.mjs +3 -3
  163. package/runtime/scripts/hfs/rules/services.mjs +220 -0
  164. package/runtime/scripts/hfs/rules/spec-placement.mjs +1 -1
  165. package/runtime/scripts/hfs/rules/stacks.mjs +3 -3
  166. package/runtime/scripts/hfs/rules/supabase-secrets.mjs +89 -0
  167. package/runtime/scripts/hfs/rules/test-topology.mjs +2 -2
  168. package/runtime/scripts/hfs/secret.mjs +209 -0
  169. package/runtime/scripts/hfs/slots.mjs +78 -108
  170. package/runtime/scripts/hfs/sql/pg-parse.mjs +46 -0
  171. package/runtime/scripts/hfs/test-world-slot.mjs +6 -0
  172. package/runtime/scripts/hfs/tree.mjs +5 -4
  173. package/runtime/scripts/hfs/typescript-programs.mjs +0 -5
  174. package/runtime/scripts/hfs/view.mjs +3 -2
  175. package/runtime/scripts/lib/clip.mjs +19 -0
  176. package/runtime/scripts/lib/display-names.mjs +259 -0
  177. package/runtime/scripts/lib/dockerfile.mjs +124 -0
  178. package/runtime/scripts/lib/env.mjs +10 -0
  179. package/runtime/scripts/lib/event-contract.mjs +103 -0
  180. package/runtime/scripts/lib/example-refs.mjs +158 -0
  181. package/runtime/scripts/lib/fs-kind.mjs +14 -4
  182. package/runtime/scripts/lib/git.mjs +36 -0
  183. package/runtime/scripts/lib/graphql-contract.mjs +1 -1
  184. package/runtime/scripts/lib/i18n.mjs +86 -0
  185. package/runtime/scripts/lib/json-schema.mjs +52 -0
  186. package/runtime/scripts/lib/json.mjs +13 -3
  187. package/runtime/scripts/lib/language.mjs +24 -5
  188. package/runtime/scripts/lib/list.mjs +20 -0
  189. package/runtime/scripts/lib/mutation-fence.mjs +15 -0
  190. package/runtime/scripts/lib/package-at.mjs +37 -0
  191. package/runtime/scripts/lib/path-key.mjs +57 -2
  192. package/runtime/scripts/lib/process-identity.mjs +6 -0
  193. package/runtime/scripts/lib/read-text.mjs +34 -0
  194. package/runtime/scripts/lib/read-yaml.mjs +18 -0
  195. package/runtime/scripts/lib/redact.mjs +161 -0
  196. package/runtime/scripts/lib/regex.mjs +4 -0
  197. package/runtime/scripts/lib/same-text.mjs +5 -0
  198. package/runtime/scripts/lib/secret-patterns.mjs +4 -3
  199. package/runtime/scripts/lib/sleep-sync.mjs +2 -1
  200. package/runtime/scripts/lib/sleep.mjs +7 -0
  201. package/runtime/scripts/lib/sops-envelope.mjs +112 -0
  202. package/runtime/scripts/lib/source-phrases.mjs +34 -0
  203. package/runtime/scripts/lib/sqlite.mjs +21 -0
  204. package/runtime/scripts/lib/stack-services.mjs +7 -31
  205. package/runtime/scripts/lib/ts-ast.mjs +35 -0
  206. package/runtime/scripts/lib/walk.mjs +4 -3
  207. package/scaffold/add-app.mjs +174 -0
  208. package/scaffold/add-cli-lite.mjs +257 -0
  209. package/scaffold/add-inbox-lite.mjs +314 -0
  210. package/scaffold/add-table.mjs +344 -0
  211. package/scaffold/add.mjs +299 -0
  212. package/scaffold/app.mjs +358 -69
  213. package/scaffold/edition-gate.mjs +25 -0
  214. package/scaffold/image.mjs +19 -0
  215. package/scaffold/lite-exports.mjs +186 -0
  216. package/scaffold/service.mjs +32 -26
  217. package/scaffold/supabase-ports.mjs +26 -0
  218. package/src/main.mjs +327 -0
  219. package/sync/cli.mjs +6 -9
  220. package/sync/format.mjs +1 -1
  221. package/sync/hygiene.mjs +10 -10
  222. package/sync/index.mjs +167 -104
  223. package/sync/managed.mjs +19 -11
  224. package/sync/sonar-key.mjs +5 -0
  225. package/templates/app/ci-workflows/github/workflows/ci.yml +14 -14
  226. package/templates/app/ci-workflows/github/workflows/images.yml +37 -0
  227. package/templates/app/ci-workflows/sonar-steps.yml +9 -0
  228. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +66 -0
  229. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +31 -0
  230. package/templates/app/ci-workflows-lite/github/workflows/images.yml +37 -0
  231. package/templates/app/codegen-lite/scripts/codegen.mjs +32 -0
  232. package/templates/app/docker-ignore/dockerignore +50 -0
  233. package/templates/app/gitignore-lite +35 -0
  234. package/templates/app/hooks/husky/pre-commit +3 -8
  235. package/templates/app/hooks/husky/pre-push +45 -6
  236. package/templates/app/package-scripts/core.json +8 -0
  237. package/templates/app/package-scripts/package.json +3 -10
  238. package/templates/app/package-scripts-lite/package.json +15 -0
  239. package/templates/app/quality-config/codecov.yml +15 -3
  240. package/templates/app/quality-config/sonar-base.properties +4 -0
  241. package/templates/app/quality-config/sonar-project.properties +1 -4
  242. package/templates/app/quality-config-lite/sonar-project.properties +5 -0
  243. package/templates/app/skeleton/.starcistacks/dev/seeds/primary-notes.sql +5 -0
  244. package/templates/app/skeleton/.starciwork/features/system-health/index.yaml +4 -0
  245. package/templates/app/skeleton/.starciwork/{features/index.yaml → index.yaml} +2 -2
  246. package/templates/app/skeleton/README.md +7 -5
  247. package/templates/app/skeleton-lite/.sops.yaml +3 -0
  248. package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +5 -0
  249. package/templates/app/skeleton-lite/.starcistacks/dev/secrets/.gitkeep +1 -0
  250. package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +55 -0
  251. package/templates/app/skeleton-lite/README.md +40 -0
  252. package/templates/app/skeleton-lite/supabase/config.toml +41 -0
  253. package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +56 -0
  254. package/templates/app/skeleton-lite/supabase/seed.sql +0 -0
  255. package/templates/app/task-graph/turbo.json +23 -0
  256. package/templates/app/tool-config-lite/prettierignore +2 -0
  257. package/templates/app/tool-config-lite/prettierrc +1 -0
  258. package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +38 -0
  259. package/templates/app/upgrade-full/.starcistacks/dev/README.md +10 -0
  260. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +13 -0
  261. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +3 -0
  262. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +12 -0
  263. package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +7 -0
  264. package/templates/be/common/main-listen.ts.partial +14 -0
  265. package/templates/be/common/sql-ident.ts.partial +7 -0
  266. package/templates/be/common/sql-tag.ts.partial +5 -0
  267. package/templates/be/image/api/Dockerfile +38 -0
  268. package/templates/be/image/cli/Dockerfile +38 -0
  269. package/templates/be/image/worker/Dockerfile +36 -0
  270. package/templates/be/inbox/database-client.ts.tpl +50 -0
  271. package/templates/be/inbox/index.ts.tpl +2 -0
  272. package/templates/be/inbox/migration.sql.tpl +29 -0
  273. package/templates/be/inbox/module-definition.ts.tpl +6 -0
  274. package/templates/be/inbox/module.ts.tpl +14 -0
  275. package/templates/be/inbox/options.ts.tpl +2 -0
  276. package/templates/be/inbox/service.ts.tpl +33 -0
  277. package/templates/be/inbox/sql.ts.tpl +16 -0
  278. package/templates/be/inbox/webhook-door.ts.tpl +36 -0
  279. package/templates/be/patterns/api/application.command.http.ts.tpl +10 -0
  280. package/templates/be/patterns/api/application.command.ts.tpl +10 -0
  281. package/templates/be/patterns/api/application.contracts.ts.tpl +11 -0
  282. package/templates/be/patterns/api/application.handler.http.ts.tpl +22 -0
  283. package/templates/be/patterns/api/application.handler.ts.tpl +22 -0
  284. package/templates/be/patterns/api/feature.index.http.ts.tpl +1 -0
  285. package/templates/be/patterns/api/feature.index.ts.tpl +1 -0
  286. package/templates/be/patterns/api/feature.module.ts.tpl +6 -0
  287. package/templates/be/patterns/api/graphql.input.ts.tpl +13 -0
  288. package/templates/be/patterns/api/graphql.mapper.ts.tpl +9 -0
  289. package/templates/be/patterns/api/graphql.module.ts.tpl +7 -0
  290. package/templates/be/patterns/api/graphql.resolver.ts.tpl +27 -0
  291. package/templates/be/patterns/api/graphql.type.ts.tpl +9 -0
  292. package/templates/be/patterns/api/http.controller.ts.tpl +27 -0
  293. package/templates/be/patterns/api/http.mapper.ts.tpl +9 -0
  294. package/templates/be/patterns/api/http.module.ts.tpl +7 -0
  295. package/templates/be/patterns/api/http.request.ts.tpl +10 -0
  296. package/templates/be/patterns/api/http.response.ts.tpl +5 -0
  297. package/templates/be/patterns/cli/group.cli.spec.ts.tpl +15 -0
  298. package/templates/be/patterns/cli/group.cli.ts.tpl +11 -0
  299. package/templates/be/patterns/cli/group.module.ts.tpl +7 -0
  300. package/templates/be/patterns/cli/sub.run.cli.spec.ts.tpl +17 -0
  301. package/templates/be/patterns/cli/sub.run.cli.ts.tpl +15 -0
  302. package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +22 -0
  303. package/templates/be/patterns/event-bus/platform/event-bus.config.ts.tpl +19 -0
  304. package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +58 -0
  305. package/templates/be/patterns/event-bus/platform/event-bus.decorators.ts.tpl +46 -0
  306. package/templates/be/patterns/event-bus/platform/event-bus.log-events.ts.tpl +12 -0
  307. package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +10 -0
  308. package/templates/be/patterns/event-bus/platform/event-bus.module.ts.tpl +52 -0
  309. package/templates/be/patterns/event-bus/platform/event-bus.options.ts.tpl +19 -0
  310. package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +28 -0
  311. package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +165 -0
  312. package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +93 -0
  313. package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +90 -0
  314. package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +58 -0
  315. package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +136 -0
  316. package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +56 -0
  317. package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +272 -0
  318. package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +128 -0
  319. package/templates/be/patterns/event-bus/platform/event-transport.port.ts.tpl +180 -0
  320. package/templates/be/patterns/event-bus/platform/event.policy.spec.ts.tpl +67 -0
  321. package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +38 -0
  322. package/templates/be/patterns/event-bus/platform/index.ts.tpl +11 -0
  323. package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.spec.ts.tpl +348 -0
  324. package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +241 -0
  325. package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +15 -0
  326. package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +8 -0
  327. package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +37 -0
  328. package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +11 -0
  329. package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +12 -0
  330. package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +26 -0
  331. package/templates/be/patterns/jobs/job.index.ts.tpl +1 -0
  332. package/templates/be/patterns/jobs/job.module.ts.tpl +6 -0
  333. package/templates/be/patterns/jobs/job.processor.ts.tpl +20 -0
  334. package/templates/be/patterns/jobs/job.queue.module.ts.tpl +20 -0
  335. package/templates/be/patterns/jobs/job.step.ts.tpl +19 -0
  336. package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +31 -0
  337. package/templates/be/patterns/jobs/platform/fenced.processor.ts.tpl +13 -0
  338. package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +150 -0
  339. package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +100 -0
  340. package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +138 -0
  341. package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +69 -0
  342. package/templates/be/patterns/jobs/platform/jobs.config.ts.tpl +11 -0
  343. package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +62 -0
  344. package/templates/be/patterns/jobs/platform/jobs.decorators.ts.tpl +31 -0
  345. package/templates/be/patterns/jobs/platform/jobs.error.ts.tpl +22 -0
  346. package/templates/be/patterns/jobs/platform/jobs.index.ts.tpl +11 -0
  347. package/templates/be/patterns/jobs/platform/jobs.log-events.ts.tpl +9 -0
  348. package/templates/be/patterns/jobs/platform/jobs.messages.ts.tpl +15 -0
  349. package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +10 -0
  350. package/templates/be/patterns/jobs/platform/jobs.module.ts.tpl +32 -0
  351. package/templates/be/patterns/jobs/platform/jobs.options.ts.tpl +11 -0
  352. package/templates/be/patterns/jobs/platform/jobs.port.ts.tpl +36 -0
  353. package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +8 -0
  354. package/templates/be/patterns/jobs/platform/persistence.job.entity.ts.tpl +53 -0
  355. package/templates/be/patterns/jobs/platform/persistence.jobs.rows.ts.tpl +13 -0
  356. package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +33 -0
  357. package/templates/be/patterns/jobs/platform/persistence.migration.ts.tpl +29 -0
  358. package/templates/be/patterns/outbox/platform/index.ts.tpl +2 -0
  359. package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +96 -0
  360. package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +78 -0
  361. package/templates/be/patterns/outbox/platform/outbox.log-events.ts.tpl +5 -0
  362. package/templates/be/patterns/projections/migration.ts.tpl +18 -0
  363. package/templates/be/patterns/projections/persistence.connection.ts.tpl +8 -0
  364. package/templates/be/patterns/projections/projection.entity.ts.tpl +9 -0
  365. package/templates/be/patterns/projections/projection.index.ts.tpl +3 -0
  366. package/templates/be/patterns/projections/projection.module-definition.ts.tpl +7 -0
  367. package/templates/be/patterns/projections/projection.module.ts.tpl +18 -0
  368. package/templates/be/patterns/projections/projection.options.ts.tpl +2 -0
  369. package/templates/be/patterns/projections/projection.projection.spec.ts.tpl +55 -0
  370. package/templates/be/patterns/projections/projection.projection.ts.tpl +21 -0
  371. package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +142 -0
  372. package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +81 -0
  373. package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +8 -0
  374. package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +25 -0
  375. package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +25 -0
  376. package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +9 -0
  377. package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +12 -0
  378. package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +48 -0
  379. package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +17 -0
  380. package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +128 -0
  381. package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +48 -0
  382. package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +115 -0
  383. package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +91 -0
  384. package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +45 -0
  385. package/templates/be/patterns/queues/platform/queue.config.ts.tpl +15 -0
  386. package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +31 -0
  387. package/templates/be/patterns/queues/platform/queue.decorators.ts.tpl +45 -0
  388. package/templates/be/patterns/queues/platform/queue.index.ts.tpl +7 -0
  389. package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +5 -0
  390. package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +10 -0
  391. package/templates/be/patterns/queues/platform/queue.module.ts.tpl +62 -0
  392. package/templates/be/patterns/queues/platform/queue.options.ts.tpl +22 -0
  393. package/templates/be/patterns/queues/platform/queue.policy.spec.ts.tpl +19 -0
  394. package/templates/be/patterns/queues/platform/queue.policy.ts.tpl +11 -0
  395. package/templates/be/patterns/queues/platform/queue.port.ts.tpl +14 -0
  396. package/templates/be/patterns/queues/queue.index.ts.tpl +3 -0
  397. package/templates/be/patterns/queues/queue.module.ts.tpl +6 -0
  398. package/templates/be/patterns/queues/queue.queue.ts.tpl +23 -0
  399. package/templates/be/patterns/reactors/application.command.ts.tpl +10 -0
  400. package/templates/be/patterns/reactors/application.contracts.ts.tpl +12 -0
  401. package/templates/be/patterns/reactors/application.handler.ts.tpl +22 -0
  402. package/templates/be/patterns/reactors/consumer.ts.tpl +19 -0
  403. package/templates/be/patterns/reactors/message.module.ts.tpl +20 -0
  404. package/templates/be/patterns/reactors/reactor.index.ts.tpl +1 -0
  405. package/templates/be/patterns/reactors/reactor.module.ts.tpl +6 -0
  406. package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +6 -0
  407. package/templates/be/patterns/realtime/channel.index.ts.tpl +1 -0
  408. package/templates/be/patterns/realtime/channel.input.ts.tpl +12 -0
  409. package/templates/be/patterns/realtime/channel.subscription.spec.ts.tpl +51 -0
  410. package/templates/be/patterns/realtime/channel.subscription.ts.tpl +32 -0
  411. package/templates/be/patterns/realtime/channel.type.ts.tpl +9 -0
  412. package/templates/be/patterns/saga/saga.compensate.command.ts.tpl +11 -0
  413. package/templates/be/patterns/saga/saga.compensate.contracts.ts.tpl +7 -0
  414. package/templates/be/patterns/saga/saga.compensate.handler.ts.tpl +22 -0
  415. package/templates/be/patterns/saga/saga.compensation.ts.tpl +21 -0
  416. package/templates/be/patterns/saga/saga.complete.command.ts.tpl +11 -0
  417. package/templates/be/patterns/saga/saga.complete.contracts.ts.tpl +7 -0
  418. package/templates/be/patterns/saga/saga.complete.handler.ts.tpl +22 -0
  419. package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +21 -0
  420. package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +21 -0
  421. package/templates/be/patterns/saga/saga.index.ts.tpl +1 -0
  422. package/templates/be/patterns/saga/saga.message.module.ts.tpl +23 -0
  423. package/templates/be/patterns/saga/saga.module.ts.tpl +20 -0
  424. package/templates/be/patterns/saga/saga.service.spec.ts.tpl +70 -0
  425. package/templates/be/patterns/saga/saga.service.ts.tpl +44 -0
  426. package/templates/be/patterns/saga/saga.state.ts.tpl +14 -0
  427. package/templates/be/patterns/saga/saga.step.ts.tpl +11 -0
  428. package/templates/be/patterns/saga/saga.undo.command.ts.tpl +10 -0
  429. package/templates/be/patterns/saga/saga.undo.contracts.ts.tpl +5 -0
  430. package/templates/be/patterns/saga/saga.undo.handler.ts.tpl +21 -0
  431. package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +72 -0
  432. package/templates/be/patterns/webhooks/webhook.door.ts.tpl +36 -0
  433. package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +6 -0
  434. package/templates/be/patterns/webhooks/webhook.index.ts.tpl +1 -0
  435. package/templates/be/patterns/webhooks/webhook.request.ts.tpl +9 -0
  436. package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +3 -0
  437. package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -2
  438. package/templates/be/skeleton/apps/__app__/src/main.ts +7 -15
  439. package/templates/be/skeleton/apps/cli/src/app.module.ts +30 -0
  440. package/templates/be/skeleton/apps/cli/src/cli.options.ts +22 -0
  441. package/templates/be/skeleton/apps/cli/src/main.ts +24 -0
  442. package/templates/be/skeleton/src/features/cli/cli.module.ts +7 -0
  443. package/templates/be/skeleton/src/features/cli/index.ts +1 -0
  444. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +17 -0
  445. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +15 -0
  446. package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +7 -0
  447. package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.spec.ts +17 -0
  448. package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.ts +18 -0
  449. package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +17 -0
  450. package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +15 -0
  451. package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +7 -0
  452. package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +33 -0
  453. package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.ts +34 -0
  454. package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +13 -0
  455. package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +40 -0
  456. package/templates/be/skeleton/src/modules/domain/identity/messages/identity.messages.ts +1 -1
  457. package/templates/be/skeleton/src/modules/domain/note/errors/note.error.ts +16 -0
  458. package/templates/be/skeleton/src/modules/domain/note/index.ts +3 -0
  459. package/templates/be/skeleton/src/modules/domain/note/note.contracts.ts +19 -0
  460. package/templates/be/skeleton/src/modules/domain/note/note.decorators.ts +9 -0
  461. package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +7 -0
  462. package/templates/be/skeleton/src/modules/domain/note/note.module.ts +19 -0
  463. package/templates/be/skeleton/src/modules/domain/note/note.options.ts +2 -0
  464. package/templates/be/skeleton/src/modules/domain/note/note.service.spec.ts +76 -0
  465. package/templates/be/skeleton/src/modules/domain/note/note.service.ts +37 -0
  466. package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +8 -0
  467. package/templates/be/skeleton/src/modules/domain/note/persistence/entities/note.entity.ts +17 -0
  468. package/templates/be/skeleton/src/modules/domain/note/persistence/migrations/1790000000000-init-note.ts +22 -0
  469. package/templates/be/skeleton/src/modules/domain/note/persistence/note.rows.ts +17 -0
  470. package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +4 -0
  471. package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +20 -0
  472. package/templates/be/skeleton/src/modules/platform/config/index.ts +1 -1
  473. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +18 -0
  474. package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +41 -0
  475. package/templates/be/skeleton/src/modules/platform/database/connection-source.client.ts +17 -0
  476. package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +13 -0
  477. package/templates/be/skeleton/src/modules/platform/database/database.module.ts +77 -0
  478. package/templates/be/skeleton/src/modules/platform/database/database.options.ts +30 -0
  479. package/templates/be/skeleton/src/modules/platform/database/database.port.ts +65 -0
  480. package/templates/be/skeleton/src/modules/platform/database/database.sql.ts +47 -0
  481. package/templates/be/skeleton/src/modules/platform/database/errors/database.error.ts +16 -0
  482. package/templates/be/skeleton/src/modules/platform/database/index.ts +9 -0
  483. package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +30 -0
  484. package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.spec.ts +84 -0
  485. package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.ts +23 -0
  486. package/templates/be/skeleton/src/modules/platform/database/primary.config.ts +9 -0
  487. package/templates/be/skeleton/src/modules/platform/database/primary.connection.ts +2 -0
  488. package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +10 -0
  489. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +108 -0
  490. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +75 -0
  491. package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.spec.ts +88 -0
  492. package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.ts +29 -0
  493. package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +48 -0
  494. package/templates/be/skeleton/src/modules/platform/errors/messages/errors.messages.ts +1 -1
  495. package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +12 -0
  496. package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -0
  497. package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +8 -1
  498. package/templates/be/skeleton/src/modules/platform/http-security/http-security.decorators.ts +8 -0
  499. package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +14 -1
  500. package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +12 -0
  501. package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +10 -2
  502. package/templates/be/skeleton/src/modules/platform/http-security/origin.guard.spec.ts +63 -0
  503. package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +108 -0
  504. package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.spec.ts +39 -0
  505. package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.ts +25 -0
  506. package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +172 -0
  507. package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.ts +66 -0
  508. package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +3 -1
  509. package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +52 -0
  510. package/templates/be/skeleton-lite/apps/api/Dockerfile +37 -0
  511. package/templates/be/skeleton-lite/apps/api/src/api.options.ts +16 -0
  512. package/templates/be/skeleton-lite/apps/api/src/app.module.ts +81 -0
  513. package/templates/be/skeleton-lite/apps/api/src/main.ts +34 -0
  514. package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +14 -0
  515. package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +50 -0
  516. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +26 -0
  517. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +19 -0
  518. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +26 -0
  519. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +7 -0
  520. package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +6 -0
  521. package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +19 -0
  522. package/templates/be/skeleton-lite/src/modules/integrations/supabase/index.ts +10 -0
  523. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.ts +24 -0
  524. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.config.ts +8 -0
  525. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.decorators.ts +17 -0
  526. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.jwks.ts +32 -0
  527. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.options.ts +24 -0
  528. package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +4 -0
  529. package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +54 -0
  530. package/templates/be/skeleton-lite/src/modules/platform/database/database.options.ts +23 -0
  531. package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +10 -0
  532. package/templates/be/skeleton-lite/src/modules/platform/database/database.sql.ts +49 -0
  533. package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +4 -0
  534. package/templates/be/skeleton-lite/src/modules/platform/database/migration-runner.service.ts +21 -0
  535. package/templates/be/skeleton-lite/src/modules/platform/database/primary.config.ts +11 -0
  536. package/templates/be/skeleton-lite/src/modules/platform/database/seed-runner.service.ts +28 -0
  537. package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +25 -0
  538. package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +9 -0
  539. package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +4 -0
  540. package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +5 -0
  541. package/templates/be/table/error.ts.tpl +16 -0
  542. package/templates/be/table/index.ts.tpl +2 -0
  543. package/templates/be/table/migration.sql.tpl +51 -0
  544. package/templates/be/table/module-definition.ts.tpl +7 -0
  545. package/templates/be/table/module.ts.tpl +14 -0
  546. package/templates/be/table/options.ts.tpl +2 -0
  547. package/templates/be/table/rows.ts.tpl +4 -0
  548. package/templates/be/table/service.ts.tpl +23 -0
  549. package/templates/be/table/sql.ts.tpl +4 -0
  550. package/templates/be/tool-config/jest.config.js +3 -1
  551. package/templates/be/upgrade-full/apps/cli/src/app.module.ts +14 -0
  552. package/templates/be/upgrade-full/apps/cli/src/main.ts +26 -0
  553. package/templates/be/upgrade-full/src/features/cli/cli.module.ts +6 -0
  554. package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +23 -0
  555. package/templates/common/gitignore.base +1 -2
  556. package/templates/fe/app/package.json.tpl +1 -0
  557. package/templates/fe/app/tsconfig.json +12 -0
  558. package/templates/fe/common/app-layout.tsx.partial +8 -0
  559. package/templates/fe/common/site-shell.tsx.partial +9 -0
  560. package/templates/fe/image/next/Dockerfile +42 -0
  561. package/templates/fe/skeleton/apps/app/next.config.ts +23 -0
  562. package/templates/fe/skeleton/apps/app/postcss.config.mjs +7 -0
  563. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/error.tsx +2 -2
  564. package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +22 -0
  565. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/loading.tsx +2 -2
  566. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/not-found.tsx +2 -2
  567. package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +9 -0
  568. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/global-error.tsx +2 -2
  569. package/templates/fe/skeleton/apps/app/src/app/globals.css +10 -0
  570. package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +18 -0
  571. package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/component.tsx +19 -0
  572. package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/index.tsx +11 -0
  573. package/templates/fe/skeleton/apps/app/src/features/pages/AppGlobalErrorPage/index.tsx +16 -0
  574. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/component.tsx +10 -0
  575. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +15 -0
  576. package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +8 -0
  577. package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +9 -0
  578. package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +4 -0
  579. package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +28 -0
  580. package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -0
  581. package/templates/fe/skeleton/apps/app/src/modules/routes/index.ts +4 -0
  582. package/templates/fe/skeleton/apps/app/src/proxy.ts +1 -0
  583. package/templates/fe/skeleton/apps/{__app__ → landing}/next.config.ts +4 -2
  584. package/templates/fe/skeleton/apps/landing/postcss.config.mjs +7 -0
  585. package/templates/fe/skeleton/apps/landing/src/app/[locale]/error.tsx +10 -0
  586. package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +22 -0
  587. package/templates/fe/skeleton/apps/landing/src/app/[locale]/loading.tsx +6 -0
  588. package/templates/fe/skeleton/apps/landing/src/app/[locale]/not-found.tsx +6 -0
  589. package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +9 -0
  590. package/templates/fe/skeleton/apps/landing/src/app/global-error.tsx +10 -0
  591. package/templates/fe/skeleton/apps/landing/src/app/globals.css +10 -0
  592. package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +24 -0
  593. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/component.tsx +18 -0
  594. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/index.tsx +11 -0
  595. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingGlobalErrorPage/index.tsx +16 -0
  596. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +8 -0
  597. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +9 -0
  598. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/component.tsx +24 -0
  599. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +16 -0
  600. package/templates/fe/skeleton/apps/landing/src/modules/config/index.ts +12 -0
  601. package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +7 -0
  602. package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/vi.json +31 -0
  603. package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -0
  604. package/templates/fe/skeleton/apps/landing/src/modules/routes/index.ts +4 -0
  605. package/templates/fe/skeleton/apps/landing/src/proxy.ts +1 -0
  606. package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +49 -0
  607. package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +24 -0
  608. package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +20 -0
  609. package/templates/fe/skeleton/packages/__project__-ui/src/branches/Providers/index.tsx +23 -0
  610. package/templates/fe/skeleton/packages/__project__-ui/src/composites/ErrorNotice/index.tsx +14 -0
  611. package/templates/fe/skeleton/packages/__project__-ui/src/composites/GlobalErrorNotice/index.tsx +25 -0
  612. package/templates/fe/skeleton/packages/__project__-ui/src/composites/LoadingNotice/index.tsx +9 -0
  613. package/templates/fe/skeleton/packages/__project__-ui/src/composites/LocaleShell/index.tsx +21 -0
  614. package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +19 -0
  615. package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/classNames.ts +4 -0
  616. package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/index.tsx +19 -0
  617. package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +6 -0
  618. package/templates/fe/skeleton-lite/apps/web/next.config.ts +22 -0
  619. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/error.tsx +10 -0
  620. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +37 -0
  621. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/loading.tsx +6 -0
  622. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/not-found.tsx +6 -0
  623. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +9 -0
  624. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/providers.tsx +26 -0
  625. package/templates/fe/skeleton-lite/apps/web/src/app/auth/callback/route.ts +24 -0
  626. package/templates/fe/skeleton-lite/apps/web/src/app/global-error.tsx +10 -0
  627. package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/src/app/globals.css +1 -1
  628. package/templates/fe/skeleton-lite/apps/web/src/app/health/live/route.ts +5 -0
  629. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/component.tsx +29 -0
  630. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +41 -0
  631. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/component.tsx +17 -0
  632. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/index.tsx +38 -0
  633. package/templates/fe/skeleton-lite/apps/web/src/components/composites/ErrorNotice/index.tsx +13 -0
  634. package/templates/fe/skeleton-lite/apps/web/src/components/composites/GlobalErrorNotice/index.tsx +24 -0
  635. package/templates/fe/skeleton-lite/apps/web/src/components/composites/LoadingNotice/index.tsx +7 -0
  636. package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +18 -0
  637. package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/classNames.ts +4 -0
  638. package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/index.tsx +13 -0
  639. package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +31 -0
  640. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/component.tsx +12 -0
  641. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/index.tsx +10 -0
  642. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppGlobalErrorPage/index.tsx +15 -0
  643. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +14 -0
  644. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +30 -0
  645. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +8 -0
  646. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +9 -0
  647. package/templates/fe/skeleton-lite/apps/web/src/modules/brand/brand.css +20 -0
  648. package/templates/fe/skeleton-lite/apps/web/src/modules/brand/index.ts +2 -0
  649. package/templates/fe/skeleton-lite/apps/web/src/modules/config/index.ts +20 -0
  650. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/read-session.ts +25 -0
  651. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-in.ts +25 -0
  652. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +17 -0
  653. package/templates/fe/skeleton-lite/apps/web/src/modules/db/browser.ts +14 -0
  654. package/templates/fe/skeleton-lite/apps/web/src/modules/db/index.ts +3 -0
  655. package/templates/fe/skeleton-lite/apps/web/src/modules/db/outcome.ts +59 -0
  656. package/templates/fe/skeleton-lite/apps/web/src/modules/db/principal.ts +29 -0
  657. package/templates/fe/skeleton-lite/apps/web/src/modules/db/server.ts +70 -0
  658. package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +50 -0
  659. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +8 -0
  660. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +48 -0
  661. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +16 -0
  662. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +14 -0
  663. package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +7 -0
  664. package/templates/fe/skeleton-lite/apps/web/src/modules/routes/safe-next-path.ts +26 -0
  665. package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +43 -0
  666. package/templates/fe/table/read.ts.tpl +16 -0
  667. package/templates/fe/table/write.ts.tpl +16 -0
  668. package/upgrade/check.mjs +137 -0
  669. package/upgrade/index.mjs +524 -0
  670. package/bin/hfs.mjs +0 -233
  671. package/runtime/scripts/api/fs/rmdir-link.mjs +0 -12
  672. package/runtime/scripts/lib/test-secrets.mjs +0 -116
  673. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +0 -20
  674. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/page.tsx +0 -9
  675. package/templates/fe/skeleton/apps/__app__/src/components/composites/FailureScreen/index.tsx +0 -28
  676. package/templates/fe/skeleton/apps/__app__/src/features/layouts/LocaleShell/index.tsx +0 -35
  677. package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/component.tsx +0 -19
  678. package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/index.tsx +0 -11
  679. package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/component.tsx +0 -24
  680. package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/index.tsx +0 -14
  681. package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/component.tsx +0 -21
  682. package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/index.tsx +0 -15
  683. package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/component.tsx +0 -23
  684. package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/index.tsx +0 -8
  685. package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/component.tsx +0 -28
  686. package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/index.tsx +0 -8
  687. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/config.ts +0 -8
  688. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/index.ts +0 -2
  689. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages/vi.json +0 -25
  690. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/request.ts +0 -19
  691. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.ts +0 -9
  692. package/templates/fe/skeleton/apps/__app__/src/modules/routes/index.ts +0 -4
  693. package/templates/fe/skeleton/apps/__app__/src/proxy.ts +0 -10
  694. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.contracts.ts +0 -0
  695. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.handler.ts +0 -0
  696. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.query.ts +0 -0
  697. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/index.ts +0 -0
  698. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/system-health.module.ts +0 -0
  699. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/live.controller.ts +0 -0
  700. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/system-health-http.module.ts +0 -0
  701. /package/templates/fe/skeleton/apps/{__app__ → app}/src/app/health/live/route.ts +0 -0
  702. /package/templates/fe/skeleton/apps/{__app__ → app}/src/modules/config/index.ts +0 -0
  703. /package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/postcss.config.mjs +0 -0
@@ -0,0 +1,115 @@
1
+ import type { QueueHandler, QueueSchedulerDefinition } from "./queue.contracts"
2
+
3
+ /** BullMQ as the queue capability sees it: the one port the BullMQ client implements. */
4
+ export interface QueueTransport {
5
+ /** Adds the job to its queue with `jobId`; adding an id BullMQ already holds changes nothing, so a repeated relay pass is harmless. */
6
+ add(queue: string, jobId: string, payload: object): Promise<void>
7
+ /** Creates or updates a job scheduler by its id. */
8
+ upsertScheduler(scheduler: QueueSchedulerDefinition): Promise<void>
9
+ /** Starts a worker of the queue the handler names that runs it for every job, `concurrency` at a time. */
10
+ work(handler: QueueHandler, concurrency: number): Promise<void>
11
+ /** Resolves after `ms` milliseconds: the only clock of the relay loop. */
12
+ wait(ms: number): Promise<void>
13
+ /** Closes every queue and worker. */
14
+ close(): Promise<void>
15
+ }
16
+
17
+ /** The Redis a driver object connects to. */
18
+ export interface BullmqRedis {
19
+ /** The host. */
20
+ readonly host: string
21
+ /** The port. */
22
+ readonly port: number
23
+ /** BullMQ answers to commands only through a client that does not retry on its own. */
24
+ readonly maxRetriesPerRequest: number | null
25
+ }
26
+
27
+ /** The Redis options every queue and worker of the app shares. */
28
+ export interface BullmqConnection {
29
+ /** The key prefix of the app. */
30
+ readonly prefix: string
31
+ /** The Redis the driver connects to. */
32
+ readonly connection: BullmqRedis
33
+ }
34
+
35
+ /** The options a worker is built with: the shared connection plus its concurrency. */
36
+ export interface BullmqWorkerOptions extends BullmqConnection {
37
+ /** How many jobs the worker runs at a time. */
38
+ readonly concurrency: number
39
+ }
40
+
41
+ /** The wait between two tries of a job. */
42
+ export interface BullmqBackoff {
43
+ /** The backoff shape. */
44
+ readonly type: string
45
+ /** The first wait, in milliseconds. */
46
+ readonly delay: number
47
+ }
48
+
49
+ /** How long a finished job is kept. */
50
+ export interface BullmqRetention {
51
+ /** The seconds a finished job is kept. */
52
+ readonly age: number
53
+ }
54
+
55
+ /** The options the transport fixes on every job it adds. */
56
+ export interface BullmqJobOptions {
57
+ /** The id BullMQ dedupes on: the outbox row id. */
58
+ readonly jobId: string
59
+ /** How many times BullMQ tries the job. */
60
+ readonly attempts: number
61
+ /** The wait between two tries. */
62
+ readonly backoff: BullmqBackoff
63
+ /** How long a finished job is kept. */
64
+ readonly removeOnComplete: BullmqRetention
65
+ }
66
+
67
+ /** The options a scheduler fixes on the jobs it fires. */
68
+ export interface BullmqSchedulerJobOptions {
69
+ /** How many times BullMQ tries the tick. */
70
+ readonly attempts: number
71
+ }
72
+
73
+ /** The job a scheduler fires. */
74
+ export interface BullmqJobTemplate {
75
+ /** The job name, which is the queue name. */
76
+ readonly name: string
77
+ /** The payload of every tick. */
78
+ readonly data: object
79
+ /** The options of every tick. */
80
+ readonly opts: BullmqSchedulerJobOptions
81
+ }
82
+
83
+ /** A queue as the transport uses it. */
84
+ export interface BullmqQueue {
85
+ /** Adds the job; an id BullMQ already holds makes the add a no-op. */
86
+ add(name: string, payload: object, options: BullmqJobOptions): Promise<unknown>
87
+ /** Creates or updates the scheduler by its id. */
88
+ upsertJobScheduler(id: string, repeat: { readonly every: number }, template: BullmqJobTemplate): Promise<unknown>
89
+ /** Closes the queue. */
90
+ close(): Promise<void>
91
+ }
92
+
93
+ /** A job as a worker receives it: the fields the transport translates into a QueueDelivery. */
94
+ export interface BullmqJob {
95
+ /** The job id; a tick the scheduler fires can have none. */
96
+ readonly id?: string
97
+ /** The payload the producer wrote. */
98
+ readonly data: object
99
+ /** How many deliveries were started before this one. */
100
+ readonly attemptsMade: number
101
+ }
102
+
103
+ /** A worker as the transport uses it. */
104
+ export interface BullmqWorker {
105
+ /** Stops the worker; `force` drops the jobs it is still running. */
106
+ close(force?: boolean): Promise<void>
107
+ }
108
+
109
+ /** Builds the BullMQ objects; the module provides it so a spec can double it through the injection token. */
110
+ export interface QueueFactory {
111
+ /** The queue of the name over the shared connection. */
112
+ queue(name: string, options: BullmqConnection): BullmqQueue
113
+ /** A worker that runs the processor for every job of the queue. */
114
+ worker(name: string, processor: (job: BullmqJob) => Promise<void>, options: BullmqWorkerOptions): BullmqWorker
115
+ }
@@ -0,0 +1,91 @@
1
+ import { Test } from "@nestjs/testing"
2
+ import { mock } from "@starci/jest-preset"
3
+ import { LOGGER } from "@modules/platform/logging"
4
+ import type { Logger } from "@modules/platform/logging"
5
+ import type { QueueHandler } from "./queue.contracts"
6
+ import { QUEUE_OPTIONS, QUEUE_TRANSPORT } from "./queue.decorators"
7
+ import { QueueLogEvent } from "./queue.log-events"
8
+ import type { QueueOptions } from "./queue.options"
9
+ import type { QueueTransport } from "./queue-transport.port"
10
+ import { QueueWorkerService } from "./queue-worker.service"
11
+
12
+ const options: QueueOptions = {
13
+ redisHost: "localhost",
14
+ redisPort: 6379,
15
+ prefix: "test",
16
+ relayIntervalMs: 100,
17
+ relayBatch: 2,
18
+ concurrency: 3,
19
+ connections: [],
20
+ schedulers: [{ queue: "sweep", id: "sweep-every-minute", everyMs: 60000 }],
21
+ }
22
+
23
+ const handlerOf = (queue: string): QueueHandler => ({ queue, handle: () => Promise.resolve() })
24
+
25
+ const build = async () => {
26
+ const transport = mock<QueueTransport>()
27
+ transport.upsertScheduler.mockResolvedValue(undefined)
28
+ const logger = mock<Logger>()
29
+ const moduleRef = await Test.createTestingModule({
30
+ providers: [
31
+ QueueWorkerService,
32
+ { provide: QUEUE_OPTIONS, useValue: options },
33
+ { provide: QUEUE_TRANSPORT, useValue: transport },
34
+ { provide: LOGGER, useValue: logger },
35
+ ],
36
+ }).compile()
37
+ return { workers: moduleRef.get(QueueWorkerService), transport, logger }
38
+ }
39
+
40
+ describe("QueueWorkerService", () => {
41
+ describe("onApplicationBootstrap", () => {
42
+ it("starts one worker per registered queue with the configured concurrency and registers the schedulers", async () => {
43
+ const { workers, transport } = await build()
44
+ const mailHandler = handlerOf("mail")
45
+ const sweepHandler = handlerOf("sweep")
46
+ workers.add(mailHandler)
47
+ workers.add(sweepHandler)
48
+
49
+ await workers.onApplicationBootstrap()
50
+
51
+ expect(transport.work).toHaveBeenCalledWith(mailHandler, 3)
52
+ expect(transport.work).toHaveBeenCalledWith(sweepHandler, 3)
53
+ expect(transport.upsertScheduler).toHaveBeenCalledWith(options.schedulers[0])
54
+ })
55
+
56
+ it("keeps the last handler registered for a queue", async () => {
57
+ const { workers, transport } = await build()
58
+ const first = handlerOf("mail")
59
+ const second = handlerOf("mail")
60
+ workers.add(first)
61
+ workers.add(second)
62
+
63
+ await workers.onApplicationBootstrap()
64
+
65
+ expect(transport.work).toHaveBeenCalledTimes(1)
66
+ expect(transport.work).toHaveBeenCalledWith(second, 3)
67
+ })
68
+
69
+ it("logs a scheduler that cannot be registered and still starts the app", async () => {
70
+ const { workers, transport, logger } = await build()
71
+ const failure = new Error("redis down")
72
+ transport.upsertScheduler.mockRejectedValue(failure)
73
+
74
+ await expect(workers.onApplicationBootstrap()).resolves.toBeUndefined()
75
+
76
+ expect(logger.error).toHaveBeenCalledWith(QueueLogEvent.SchedulerFailed, failure, {
77
+ scheduler: "sweep-every-minute",
78
+ })
79
+ })
80
+ })
81
+
82
+ describe("onApplicationShutdown", () => {
83
+ it("closes the workers and the queues", async () => {
84
+ const { workers, transport } = await build()
85
+
86
+ await workers.onApplicationShutdown()
87
+
88
+ expect(transport.close).toHaveBeenCalledTimes(1)
89
+ })
90
+ })
91
+ })
@@ -0,0 +1,45 @@
1
+ import { Injectable } from "@nestjs/common"
2
+ import type { OnApplicationBootstrap, OnApplicationShutdown } from "@nestjs/common"
3
+ import { InjectLogger } from "@modules/platform/logging"
4
+ import type { Logger } from "@modules/platform/logging"
5
+ import type { QueueHandler } from "./queue.contracts"
6
+ import { InjectQueueOptions, InjectQueueTransport } from "./queue.decorators"
7
+ import { QueueLogEvent } from "./queue.log-events"
8
+ import type { QueueOptions } from "./queue.options"
9
+ import type { QueueWorkerRegistry } from "./queue.port"
10
+ import type { QueueTransport } from "./queue-transport.port"
11
+
12
+ @Injectable()
13
+ /**
14
+ * The consuming side of the queues: the registry the processors fill, and the workers and schedulers started when the app has
15
+ * registered every handler. A scheduler that cannot be registered is logged and does not stop the app: the next boot upserts it again.
16
+ */
17
+ export class QueueWorkerService implements QueueWorkerRegistry, OnApplicationBootstrap, OnApplicationShutdown {
18
+ private readonly handlers = new Map<string, QueueHandler>()
19
+
20
+ constructor(
21
+ @InjectQueueOptions() private readonly options: QueueOptions,
22
+ @InjectQueueTransport() private readonly transport: QueueTransport,
23
+ @InjectLogger() private readonly logger: Logger,
24
+ ) {}
25
+
26
+ /** Registers the handler under the queue it names; a second handler for the same queue replaces the first. */
27
+ add(handler: QueueHandler): void {
28
+ this.handlers.set(handler.queue, handler)
29
+ }
30
+
31
+ /** Starts one worker per registered queue, then registers the schedulers of the options. */
32
+ async onApplicationBootstrap(): Promise<void> {
33
+ for (const handler of this.handlers.values()) await this.transport.work(handler, this.options.concurrency)
34
+ for (const scheduler of this.options.schedulers) {
35
+ await this.transport.upsertScheduler(scheduler).catch((cause: unknown) => {
36
+ this.logger.error(QueueLogEvent.SchedulerFailed, cause, { scheduler: scheduler.id })
37
+ })
38
+ }
39
+ }
40
+
41
+ /** Closes the workers and the queues. */
42
+ async onApplicationShutdown(): Promise<void> {
43
+ await this.transport.close()
44
+ }
45
+ }
@@ -0,0 +1,15 @@
1
+ import type { EnvSource } from "@modules/platform/config"
2
+ import type { QueueOptions } from "./queue.options"
3
+
4
+ /** What the app's `main.ts` reads for the queues; the connections and the schedulers are the app root's choice. */
5
+ export type QueueConfig = Omit<QueueOptions, "connections" | "schedulers">
6
+
7
+ /** Reads the queue options: the Redis address is required, the rest are tunables with literal defaults. */
8
+ export const parseQueueConfig = (env: EnvSource): QueueConfig => ({
9
+ redisHost: env.string("QUEUE_REDIS_HOST"),
10
+ redisPort: env.int("QUEUE_REDIS_PORT", 6379),
11
+ prefix: env.optional("QUEUE_PREFIX") ?? "queue",
12
+ relayIntervalMs: env.duration("QUEUE_RELAY_INTERVAL", 200),
13
+ relayBatch: env.int("QUEUE_RELAY_BATCH", 50),
14
+ concurrency: env.int("QUEUE_CONCURRENCY", 5),
15
+ })
@@ -0,0 +1,31 @@
1
+ /** A recurring task: BullMQ upserts it by `id`, so every replica of an app may register it and the deployment still fires it once per interval. */
2
+ export interface QueueSchedulerDefinition {
3
+ /** The queue the ticks go to; its processor handles every tick like any other job. */
4
+ readonly queue: string
5
+ /** The stable id of the scheduler. */
6
+ readonly id: string
7
+ /** The interval between two ticks, in milliseconds. */
8
+ readonly everyMs: number
9
+ /** The payload of every tick. */
10
+ readonly payload?: object
11
+ }
12
+
13
+ /** One job as a handler receives it. */
14
+ export interface QueueDelivery {
15
+ /** The BullMQ job id: the outbox row id of an enqueued job, the tick id of a scheduled one. */
16
+ readonly id: string
17
+ /** The queue. */
18
+ readonly queue: string
19
+ /** The payload the producer wrote. */
20
+ readonly payload: object
21
+ /** How many deliveries were started, this one included. */
22
+ readonly attempt: number
23
+ }
24
+
25
+ /** The handler of one queue: it receives every delivery of the queue; a return completes the BullMQ job and a throw lets BullMQ retry it. */
26
+ export interface QueueHandler {
27
+ /** The queue this handler consumes. */
28
+ readonly queue: string
29
+ /** Handles one delivery. */
30
+ handle(delivery: QueueDelivery): Promise<void>
31
+ }
@@ -0,0 +1,45 @@
1
+ import type { EntityManager } from "typeorm"
2
+ import { injector } from "@modules/platform/composition"
3
+ import type { TypedParameterDecorator } from "@modules/platform/composition"
4
+ import type { QueueOptions } from "./queue.options"
5
+ import type { QueueOutbox, QueueWorkerRegistry } from "./queue.port"
6
+ import type { QueueFactory, QueueTransport } from "./queue-transport.port"
7
+
8
+ /** Token of the options of the queues, exported so a spec can provide it. */
9
+ export const QUEUE_OPTIONS: unique symbol = Symbol("platform.queue.options")
10
+
11
+ /** Token of the QueueOutbox port. */
12
+ export const QUEUE_OUTBOX: unique symbol = Symbol("platform.queue.outbox")
13
+
14
+ /** Token of the registry a processor registers its queue handler with. */
15
+ export const QUEUE_WORKER_REGISTRY: unique symbol = Symbol("platform.queue.worker-registry")
16
+
17
+ /** Token of the BullMQ port; it is also the health probe token of the queues. */
18
+ export const QUEUE_TRANSPORT: unique symbol = Symbol("platform.queue.transport")
19
+
20
+ /** Token of the entity managers of the connections whose outbox the relay reads, in the order the options name them. */
21
+ export const QUEUE_RELAY_MANAGERS: unique symbol = Symbol("platform.queue.relay-managers")
22
+
23
+ /** Token of the factory that builds the BullMQ objects, exported so a spec can provide it. */
24
+ export const QUEUE_FACTORY: unique symbol = Symbol("platform.queue.factory")
25
+
26
+ /** Injects the options of the queues. Parameter type: QueueOptions. */
27
+ export const InjectQueueOptions = (): TypedParameterDecorator<QueueOptions> => injector<QueueOptions>(QUEUE_OPTIONS)
28
+
29
+ /** Injects the QueueOutbox port. Parameter type: QueueOutbox. */
30
+ export const InjectQueueOutbox = (): TypedParameterDecorator<QueueOutbox> => injector<QueueOutbox>(QUEUE_OUTBOX)
31
+
32
+ /** Injects the worker registry. Parameter type: QueueWorkerRegistry. */
33
+ export const InjectQueueWorkerRegistry = (): TypedParameterDecorator<QueueWorkerRegistry> =>
34
+ injector<QueueWorkerRegistry>(QUEUE_WORKER_REGISTRY)
35
+
36
+ /** Injects the BullMQ port. Parameter type: QueueTransport. */
37
+ export const InjectQueueTransport = (): TypedParameterDecorator<QueueTransport> =>
38
+ injector<QueueTransport>(QUEUE_TRANSPORT)
39
+
40
+ /** Injects the entity managers the relay reads the outbox of. Parameter type: ReadonlyArray<EntityManager>. */
41
+ export const InjectQueueRelayManagers = (): TypedParameterDecorator<ReadonlyArray<EntityManager>> =>
42
+ injector<ReadonlyArray<EntityManager>>(QUEUE_RELAY_MANAGERS)
43
+
44
+ /** Injects the factory that builds the BullMQ objects. Parameter type: QueueFactory. */
45
+ export const InjectQueueFactory = (): TypedParameterDecorator<QueueFactory> => injector<QueueFactory>(QUEUE_FACTORY)
@@ -0,0 +1,7 @@
1
+ export { parseQueueConfig } from "./queue.config"
2
+ export type { QueueConfig } from "./queue.config"
3
+ export type { QueueDelivery, QueueSchedulerDefinition } from "./queue.contracts"
4
+ export { InjectQueueOutbox, InjectQueueWorkerRegistry, QUEUE_WORKER_REGISTRY } from "./queue.decorators"
5
+ export { QueueModule } from "./queue.module"
6
+ export type { QueueOutbox, QueueWorkerRegistry } from "./queue.port"
7
+ export { queueEntities, queueMigrations } from "./persistence/connection"
@@ -0,0 +1,5 @@
1
+ /** Log events of the queues. */
2
+ export enum QueueLogEvent {
3
+ /** A scheduler could not be registered at boot. */
4
+ SchedulerFailed = "queue.scheduler.failed",
5
+ }
@@ -0,0 +1,10 @@
1
+ import { ConfigurableModuleBuilder } from "@nestjs/common"
2
+ import { QUEUE_OPTIONS } from "./queue.decorators"
3
+ import type { QueueOptions } from "./queue.options"
4
+
5
+ /** The configurable-module base of the queues; `isGlobal` is decided by the app root. */
6
+ export const { ConfigurableModuleClass, OPTIONS_TYPE } = new ConfigurableModuleBuilder<QueueOptions>({
7
+ optionsInjectionToken: QUEUE_OPTIONS,
8
+ })
9
+ .setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
10
+ .build()
@@ -0,0 +1,62 @@
1
+ import { Module } from "@nestjs/common"
2
+ import type { DynamicModule } from "@nestjs/common"
3
+ import { Queue, Worker } from "bullmq"
4
+ import type { EntityManager } from "typeorm"
5
+ import {
6
+ QUEUE_FACTORY,
7
+ QUEUE_OUTBOX,
8
+ QUEUE_RELAY_MANAGERS,
9
+ QUEUE_TRANSPORT,
10
+ QUEUE_WORKER_REGISTRY,
11
+ } from "./queue.decorators"
12
+ import { ConfigurableModuleClass, OPTIONS_TYPE } from "./queue.module-definition"
13
+ import { BullmqQueueTransportClient } from "./bullmq-queue-transport.client"
14
+ import type {
15
+ BullmqConnection,
16
+ BullmqJob,
17
+ BullmqQueue,
18
+ BullmqWorker,
19
+ BullmqWorkerOptions,
20
+ } from "./queue-transport.port"
21
+ import { QueueOutboxService } from "./queue-outbox.service"
22
+ import { QueueRelayService } from "./queue-relay.service"
23
+ import { QueueWorkerService } from "./queue-worker.service"
24
+
25
+ @Module({})
26
+ /** The queues of a service: the outbox writer, its relay into BullMQ, the workers and the schedulers, registered once per app over the connections that hold an outbox. */
27
+ export class QueueModule extends ConfigurableModuleClass {
28
+ /** Registers the capability once per app that enqueues or processes jobs. */
29
+ static register(options: typeof OPTIONS_TYPE): DynamicModule {
30
+ const base = super.register(options)
31
+ return {
32
+ ...base,
33
+ providers: [
34
+ ...(base.providers ?? []),
35
+ {
36
+ provide: QUEUE_RELAY_MANAGERS,
37
+ useFactory: (...managers: Array<EntityManager>) => managers,
38
+ inject: [...options.connections],
39
+ },
40
+ {
41
+ provide: QUEUE_FACTORY,
42
+ useValue: {
43
+ queue: (name: string, options: BullmqConnection): BullmqQueue => new Queue(name, options),
44
+ worker: (
45
+ name: string,
46
+ processor: (job: BullmqJob) => Promise<void>,
47
+ options: BullmqWorkerOptions,
48
+ ): BullmqWorker => new Worker<object>(name, processor, options),
49
+ },
50
+ },
51
+ BullmqQueueTransportClient,
52
+ { provide: QUEUE_TRANSPORT, useExisting: BullmqQueueTransportClient },
53
+ QueueOutboxService,
54
+ QueueRelayService,
55
+ QueueWorkerService,
56
+ { provide: QUEUE_OUTBOX, useExisting: QueueOutboxService },
57
+ { provide: QUEUE_WORKER_REGISTRY, useExisting: QueueWorkerService },
58
+ ],
59
+ exports: [QUEUE_OUTBOX, QUEUE_WORKER_REGISTRY, QUEUE_TRANSPORT],
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,22 @@
1
+ import type { InjectionToken } from "@nestjs/common"
2
+ import type { QueueSchedulerDefinition } from "./queue.contracts"
3
+
4
+ /** What the queues of one app need: where Redis is, how the relay and the workers run, which connections hold an outbox, and the schedulers it registers. */
5
+ export interface QueueOptions {
6
+ /** The Redis host BullMQ talks to. */
7
+ readonly redisHost: string
8
+ /** The Redis port. */
9
+ readonly redisPort: number
10
+ /** What every BullMQ key starts with: the shared default in a deployment, the run prefix of a test world. */
11
+ readonly prefix: string
12
+ /** The pause between two relay passes over an empty outbox. */
13
+ readonly relayIntervalMs: number
14
+ /** The most outbox rows one relay pass hands to BullMQ. */
15
+ readonly relayBatch: number
16
+ /** How many jobs one worker of the app runs at once. */
17
+ readonly concurrency: number
18
+ /** The tokens of the shared entity managers of the connections that hold a queue outbox of the app: the relay reads each of them. */
19
+ readonly connections: ReadonlyArray<InjectionToken>
20
+ /** The job schedulers the app registers at boot; BullMQ upserts them by id, so a pool of replicas fires each one once per interval. */
21
+ readonly schedulers: ReadonlyArray<QueueSchedulerDefinition>
22
+ }
@@ -0,0 +1,19 @@
1
+ import { ATTEMPTS, BACKOFF_MS, KEEP_COMPLETED_SECONDS, jobNameOf } from "./queue.policy"
2
+
3
+ describe("queue policy", () => {
4
+ it("declares the retry and retention budget of every job", () => {
5
+ expect({
6
+ attempts: ATTEMPTS,
7
+ backoffMs: BACKOFF_MS,
8
+ keepCompletedSeconds: KEEP_COMPLETED_SECONDS,
9
+ }).toEqual({
10
+ attempts: 5,
11
+ backoffMs: 1000,
12
+ keepCompletedSeconds: 3600,
13
+ })
14
+ })
15
+
16
+ it("uses the queue name as the BullMQ job name", () => {
17
+ expect(jobNameOf("orders")).toBe("orders")
18
+ })
19
+ })
@@ -0,0 +1,11 @@
1
+ /** How many deliveries BullMQ gives a job before it keeps it as failed. */
2
+ export const ATTEMPTS = 5
3
+
4
+ /** The pause after the first failed delivery; BullMQ doubles it with every further failure. */
5
+ export const BACKOFF_MS = 1000
6
+
7
+ /** How long BullMQ keeps a completed job (seconds): long enough that a repeated relay pass finds its job id and adds nothing. */
8
+ export const KEEP_COMPLETED_SECONDS = 3600
9
+
10
+ /** The name the jobs of every queue carry inside their queue. */
11
+ export const jobNameOf = (queue: string): string => queue
@@ -0,0 +1,14 @@
1
+ import type { EntityManager } from "typeorm"
2
+ import type { QueueHandler } from "./queue.contracts"
3
+
4
+ /** The queue outbox: a typed producer writes a job row in the transaction of the caller, the relay hands it to BullMQ after commit. */
5
+ export interface QueueOutbox {
6
+ /** Writes the job in the outbox of the transaction `tx`, so it exists exactly when the change that needs it commits. */
7
+ write(tx: EntityManager, queue: string, payload: object): Promise<void>
8
+ }
9
+
10
+ /** Where a processor registers the handler of its queue; the workers start when the app has registered every handler. */
11
+ export interface QueueWorkerRegistry {
12
+ /** Registers the handler under the queue it names; a second handler for the same queue replaces the first. */
13
+ add(handler: QueueHandler): void
14
+ }
@@ -0,0 +1,3 @@
1
+ export { @@Queue@@Module } from "./@@queue@@.module"
2
+ export { @@Queue@@Queue, @@queueUpper@@_QUEUE } from "./@@queue@@.queue"
3
+ export type { @@Queue@@Payload } from "./@@queue@@.queue"
@@ -0,0 +1,6 @@
1
+ import { Module } from "@nestjs/common"
2
+ import { @@Queue@@Queue } from "./@@queue@@.queue"
3
+
4
+ @Module({ providers: [@@Queue@@Queue], exports: [@@Queue@@Queue] })
5
+ /** The @@queue@@ queue: its typed producer, imported by the domain module whose service enqueues. */
6
+ export class @@Queue@@Module {}
@@ -0,0 +1,23 @@
1
+ import { Injectable } from "@nestjs/common"
2
+ import type { EntityManager } from "typeorm"
3
+ import { InjectQueueOutbox } from "@modules/platform/queue"
4
+ import type { QueueOutbox } from "@modules/platform/queue"
5
+
6
+ /** The queue name: the contract between this producer and the processor of the job. */
7
+ export const @@queueUpper@@_QUEUE = "@@queue@@"
8
+
9
+ /** The payload of one @@queue@@ job: ids only. */
10
+ export interface @@Queue@@Payload {
11
+ readonly id: string
12
+ }
13
+
14
+ @Injectable()
15
+ /** The typed producer of the @@queue@@ queue. */
16
+ export class @@Queue@@Queue {
17
+ constructor(@InjectQueueOutbox() private readonly outbox: QueueOutbox) {}
18
+
19
+ /** Enqueues one @@queue@@ job in the transaction of the caller: the job exists exactly when the change that needs it commits. */
20
+ enqueue@@Queue@@(payload: @@Queue@@Payload, tx: EntityManager): Promise<void> {
21
+ return this.outbox.write(tx, @@queueUpper@@_QUEUE, payload)
22
+ }
23
+ }
@@ -0,0 +1,10 @@
1
+ import { Command } from "@nestjs/cqrs"
2
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
3
+ import type { @@Action@@Request, @@Action@@Result } from "./@@action@@.contracts"
4
+
5
+ /** Asks to apply one delivered @@event@@ event; the @@event@@ consumer sends it for every delivery. */
6
+ export class @@Action@@Command extends Command<@@Action@@Result> {
7
+ constructor(readonly params: PublicExecuteParams<@@Action@@Request>) {
8
+ super()
9
+ }
10
+ }
@@ -0,0 +1,12 @@
1
+ import type { @@Event@@Event } from "@modules/events/@@from@@"
2
+
3
+ /** What applying one delivered @@event@@ event takes. */
4
+ export interface @@Action@@Request {
5
+ /** The id of the delivered event; the domain service claims it in the inbox inside its own transaction. */
6
+ readonly eventId: string
7
+ /** The delivered event. */
8
+ readonly event: @@Event@@Event
9
+ }
10
+
11
+ /** Applying an event answers nothing: a redelivery is a no-op. */
12
+ export type @@Action@@Result = void
@@ -0,0 +1,22 @@
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 type { @@Action@@Result } from "./@@action@@.contracts"
7
+ import { @@Action@@Command } from "./@@action@@.command"
8
+
9
+ @CommandHandler(@@Action@@Command)
10
+ /** Hands the delivered @@event@@ event to the domain service, which claims the event id and applies the change in one transaction. */
11
+ export class @@Action@@Handler extends ICQRSHandler<@@Action@@Command, @@Action@@Result> {
12
+ constructor(
13
+ @InjectLogger() logger: Logger,
14
+ private readonly @@serviceCamel@@: @@service@@,
15
+ ) {
16
+ super(logger)
17
+ }
18
+
19
+ protected override process(command: @@Action@@Command): Promise<@@Action@@Result> {
20
+ return this.@@serviceCamel@@.apply(command.params.request)
21
+ }
22
+ }
@@ -0,0 +1,19 @@
1
+ import { Injectable } from "@nestjs/common"
2
+ import type { CommandBus } from "@nestjs/cqrs"
3
+ import { @@Event@@Event } from "@modules/events/@@from@@"
4
+ import { InjectCommandBus } from "@modules/platform/cqrs"
5
+ import type { EventConsumer, EventDelivery } from "@modules/platform/event-bus"
6
+ import { @@Action@@Command } from "../../application/@@action@@.command"
7
+
8
+ @Injectable()
9
+ /** The consumer door of the @@event@@ event in the @@reactor@@ reactor: it dispatches one command and forwards the event id. */
10
+ export class @@Event@@Consumer implements EventConsumer<@@Event@@Event> {
11
+ readonly event = @@Event@@Event
12
+
13
+ constructor(@InjectCommandBus() private readonly commandBus: CommandBus) {}
14
+
15
+ /** Delivery is at least once: the domain service the command reaches claims `eventId` in the inbox inside its own transaction. */
16
+ async handle(delivery: EventDelivery<@@Event@@Event>): Promise<void> {
17
+ await this.commandBus.execute(new @@Action@@Command({ request: { eventId: delivery.eventId, event: delivery.event } }))
18
+ }
19
+ }
@@ -0,0 +1,20 @@
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 { @@Reactor@@Module } from "../../@@reactor@@.module"
6
+ import { @@Event@@Consumer } from "./@@event@@.consumer"
7
+
8
+ @Module({ imports: [@@Reactor@@Module], providers: [@@Event@@Consumer] })
9
+ /** The message transport of the @@reactor@@ reactor: registers its consumers with the event bus; a worker or api app composes it. */
10
+ export class @@Reactor@@MessageModule implements OnModuleInit {
11
+ constructor(
12
+ @InjectEventConsumerRegistry() private readonly registry: EventConsumerRegistry,
13
+ private readonly @@eventCamel@@Consumer: @@Event@@Consumer,
14
+ ) {}
15
+
16
+ /** Hands every consumer to the registry. */
17
+ onModuleInit(): void {
18
+ this.registry.add(this.@@eventCamel@@Consumer)
19
+ }
20
+ }
@@ -0,0 +1 @@
1
+ export { @@Reactor@@MessageModule } from "./transport/message/@@reactor@@-message.module"
@@ -0,0 +1,6 @@
1
+ import { Module } from "@nestjs/common"
2
+ import { @@Action@@Handler } from "./application/@@action@@.handler"
3
+
4
+ @Module({ providers: [@@Action@@Handler] })
5
+ /** The @@reactor@@ reactor: the handler its consumers dispatch to. */
6
+ export class @@Reactor@@Module {}