@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,50 @@
1
+ import { Injectable } from "@nestjs/common"
2
+ import type { CanActivate, ExecutionContext } from "@nestjs/common"
3
+ import type { Reflector } from "@nestjs/core"
4
+ import { InjectSupabaseAccessTokenVerifier } from "@modules/integrations/supabase"
5
+ import type { VerifySupabaseAccessToken } from "@modules/integrations/supabase"
6
+ import { InjectReflector } from "@modules/platform/composition"
7
+ import type { Principal } from "@modules/platform/cqrs"
8
+ import { requestOf } from "@modules/platform/http-security"
9
+ import { unwrapOutcome } from "@modules/platform/primitives"
10
+ import { admit } from "./admission.policy"
11
+ import { IdentityError } from "./errors/identity.error"
12
+ import type { PublicMetadata } from "./identity.contracts"
13
+ import { PUBLIC_KEY } from "./identity.decorators"
14
+
15
+ const bearerToken = (authorization: string | undefined): string | undefined => {
16
+ const [scheme, token, extra] = authorization?.split(" ") ?? []
17
+ return scheme?.toLowerCase() === "bearer" && token && extra === undefined ? token : undefined
18
+ }
19
+
20
+ @Injectable()
21
+ /** The third app guard and default-deny gate: non-public doors require a verified Supabase principal. */
22
+ export class AuthGuard implements CanActivate {
23
+ constructor(
24
+ @InjectReflector() private readonly reflector: Reflector,
25
+ @InjectSupabaseAccessTokenVerifier()
26
+ private readonly verifyAccessToken: VerifySupabaseAccessToken,
27
+ ) {}
28
+
29
+ /** Verifies the bearer token when the door is not public, then applies the admission policy. */
30
+ async canActivate(context: ExecutionContext): Promise<boolean> {
31
+ const metadata = this.reflector.getAllAndOverride<PublicMetadata | undefined>(PUBLIC_KEY, [
32
+ context.getHandler(),
33
+ context.getClass(),
34
+ ])
35
+ let principal: Principal | undefined
36
+ if (metadata === undefined) {
37
+ const request = requestOf(context)
38
+ const token = bearerToken(request.headers.authorization)
39
+ if (token !== undefined) {
40
+ const verified = await this.verifyAccessToken(token)
41
+ if (verified.kind === "ok") {
42
+ principal = verified.value
43
+ request.principal = verified.value
44
+ }
45
+ }
46
+ }
47
+ unwrapOutcome(admit(metadata, principal), IdentityError)
48
+ return true
49
+ }
50
+ }
@@ -0,0 +1,26 @@
1
+ import type { Principal } from "@modules/platform/cqrs"
2
+
3
+ declare module "express-serve-static-core" {
4
+ /** The request carries the principal established by the default-deny auth guard. */
5
+ interface Request {
6
+ /** The authenticated Supabase caller, when the door is not public. */
7
+ principal?: Principal
8
+ }
9
+ }
10
+
11
+ /** Why a door is open to anonymous callers; the vocabulary is closed. */
12
+ export enum PublicReason {
13
+ /** Liveness and readiness probes. */
14
+ Health = "health",
15
+ /** A webhook whose signature is verified before its domain intake runs. */
16
+ SignedWebhook = "signed-webhook",
17
+ }
18
+
19
+ /** The metadata `@Public` attaches to a door. */
20
+ export interface PublicMetadata {
21
+ /** Why the door is anonymous. */
22
+ readonly reason: PublicReason
23
+ }
24
+
25
+ /** What identity admission returns to the guard. */
26
+ export type IdentityAdmission = Principal | PublicReason
@@ -0,0 +1,19 @@
1
+ import { SetMetadata, createParamDecorator } from "@nestjs/common"
2
+ import type { ExecutionContext } from "@nestjs/common"
3
+ import { requestOf } from "@modules/platform/http-security"
4
+ import type { Principal } from "@modules/platform/cqrs"
5
+ import { IdentityError, IdentityErrorCode } from "./errors/identity.error"
6
+ import type { PublicMetadata } from "./identity.contracts"
7
+
8
+ /** Metadata key of `@Public`. */
9
+ export const PUBLIC_KEY = "domain.identity.public"
10
+
11
+ /** Opens a door to anonymous callers, stating why; every other door needs a verified Supabase session. */
12
+ export const Public = (metadata: PublicMetadata): ReturnType<typeof SetMetadata> => SetMetadata(PUBLIC_KEY, metadata)
13
+
14
+ /** The authenticated Supabase caller established by the default-deny guard. */
15
+ export const CurrentPrincipal = createParamDecorator((_data: unknown, context: ExecutionContext): Principal => {
16
+ const principal = requestOf(context).principal
17
+ if (!principal) throw new IdentityError({ code: IdentityErrorCode.Unauthenticated })
18
+ return principal
19
+ })
@@ -0,0 +1,26 @@
1
+ import { Module } from "@nestjs/common"
2
+ import type { DynamicModule } from "@nestjs/common"
3
+ import { SUPABASE_ACCESS_TOKEN_VERIFIER } from "@modules/integrations/supabase"
4
+ import { AuthGuard } from "./auth.guard"
5
+ import { ConfigurableModuleClass, OPTIONS_TYPE } from "./identity.module-definition"
6
+
7
+ @Module({})
8
+ /** The identity capability: its default-deny guard admits only public doors or a verified Supabase principal. */
9
+ export class IdentityModule extends ConfigurableModuleClass {
10
+ /** Registers identity once per app with the verifier parsed at the composition root. */
11
+ static register(options: typeof OPTIONS_TYPE): DynamicModule {
12
+ const base = super.register(options)
13
+ return {
14
+ ...base,
15
+ providers: [
16
+ ...(base.providers ?? []),
17
+ {
18
+ provide: SUPABASE_ACCESS_TOKEN_VERIFIER,
19
+ useValue: options.verifyAccessToken,
20
+ },
21
+ AuthGuard,
22
+ ],
23
+ exports: [AuthGuard],
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,7 @@
1
+ import type { VerifySupabaseAccessToken } from "@modules/integrations/supabase"
2
+
3
+ /** The verified identity source used by the default-deny guard. */
4
+ export interface IdentityOptions {
5
+ /** Cryptographically verifies a Supabase access token before it can become a principal. */
6
+ readonly verifyAccessToken: VerifySupabaseAccessToken
7
+ }
@@ -0,0 +1,6 @@
1
+ export { AuthGuard } from "./auth.guard"
2
+ export { IDENTITY_ERROR_KINDS } from "./errors/identity.error"
3
+ export { IDENTITY_MESSAGES } from "./messages/identity.messages"
4
+ export { PublicReason } from "./identity.contracts"
5
+ export { Public } from "./identity.decorators"
6
+ export { IdentityModule } from "./identity.module"
@@ -0,0 +1,19 @@
1
+ import { DomainError } from "@modules/platform/errors"
2
+ import type { ErrorKind } from "@modules/platform/errors"
3
+
4
+ /** Codes of the Supabase integration. */
5
+ export enum SupabaseErrorCode {
6
+ /** The access token failed signature or required-claim verification. */
7
+ AccessTokenInvalid = "SUPABASE_ACCESS_TOKEN_INVALID",
8
+ /** A privileged Auth or Storage operation could not reach Supabase. */
9
+ ProviderUnavailable = "SUPABASE_PROVIDER_UNAVAILABLE",
10
+ }
11
+
12
+ /** How each Supabase integration code travels. */
13
+ export const SUPABASE_ERROR_KINDS: Record<SupabaseErrorCode, ErrorKind> = {
14
+ [SupabaseErrorCode.AccessTokenInvalid]: "unauthenticated",
15
+ [SupabaseErrorCode.ProviderUnavailable]: "unavailable",
16
+ }
17
+
18
+ /** The one error class of the Supabase integration. */
19
+ export class SupabaseError extends DomainError<SupabaseErrorCode> {}
@@ -0,0 +1,10 @@
1
+ export { createSupabaseAdminClient } from "./supabase.client"
2
+ export { parseSupabaseConfig } from "./supabase.config"
3
+ export {
4
+ InjectSupabaseAccessTokenVerifier,
5
+ SUPABASE_ACCESS_TOKEN_VERIFIER,
6
+ SUPABASE_ADMIN_CLIENT,
7
+ } from "./supabase.decorators"
8
+ export { SUPABASE_ERROR_KINDS } from "./errors/supabase.error"
9
+ export { createSupabaseAccessTokenVerifier } from "./supabase.jwks"
10
+ export type { SupabaseOptions, VerifySupabaseAccessToken } from "./supabase.options"
@@ -0,0 +1,24 @@
1
+ import { createClient } from "@supabase/supabase-js"
2
+ import type { SupabaseClient } from "@supabase/supabase-js"
3
+ import type { Database } from "../../../../../supabase/types/database.types"
4
+ import type { SupabaseOptions } from "./supabase.options"
5
+
6
+ /** The privileged surface exposed by this integration: Auth administration and Storage, never database queries. */
7
+ export interface SupabaseAdminClient {
8
+ /** Supabase Auth administration. */
9
+ readonly auth: SupabaseClient<Database>["auth"]["admin"]
10
+ /** Supabase Storage administration. */
11
+ readonly storage: SupabaseClient<Database>["storage"]
12
+ }
13
+
14
+ /** Creates the back-end-only client and narrows it to Auth and Storage administration. */
15
+ export const createSupabaseAdminClient = (options: SupabaseOptions): SupabaseAdminClient => {
16
+ const client: SupabaseClient<Database> = createClient<Database>(options.url, options.serviceRoleKey.reveal(), {
17
+ auth: {
18
+ autoRefreshToken: false,
19
+ detectSessionInUrl: false,
20
+ persistSession: false,
21
+ },
22
+ })
23
+ return { auth: client.auth.admin, storage: client.storage }
24
+ }
@@ -0,0 +1,8 @@
1
+ import type { EnvSource } from "@modules/platform/config"
2
+ import type { SupabaseOptions } from "./supabase.options"
3
+
4
+ /** Reads Supabase configuration with no credential or localhost defaults. */
5
+ export const parseSupabaseConfig = (env: EnvSource): SupabaseOptions => ({
6
+ url: env.url("SUPABASE_URL"),
7
+ serviceRoleKey: env.secret("SUPABASE_SERVICE_ROLE_KEY"),
8
+ })
@@ -0,0 +1,17 @@
1
+ import { injector } from "@modules/platform/composition"
2
+ import type { TypedParameterDecorator } from "@modules/platform/composition"
3
+ import type { SupabaseAdminClient } from "./supabase.client"
4
+ import type { VerifySupabaseAccessToken } from "./supabase.options"
5
+
6
+ /** Token of the integration's Auth and Storage administrator. */
7
+ export const SUPABASE_ADMIN_CLIENT: unique symbol = Symbol("integrations.supabase.admin-client")
8
+ /** Token of the cryptographic Supabase access-token verifier. */
9
+ export const SUPABASE_ACCESS_TOKEN_VERIFIER: unique symbol = Symbol("integrations.supabase.access-token-verifier")
10
+
11
+ /** Injects the Auth and Storage administrator. Parameter type: SupabaseAdminClient. */
12
+ export const InjectSupabaseAdminClient = (): TypedParameterDecorator<SupabaseAdminClient> =>
13
+ injector<SupabaseAdminClient>(SUPABASE_ADMIN_CLIENT)
14
+
15
+ /** Injects the access-token verifier. Parameter type: VerifySupabaseAccessToken. */
16
+ export const InjectSupabaseAccessTokenVerifier = (): TypedParameterDecorator<VerifySupabaseAccessToken> =>
17
+ injector<VerifySupabaseAccessToken>(SUPABASE_ACCESS_TOKEN_VERIFIER)
@@ -0,0 +1,32 @@
1
+ import { createRemoteJWKSet, jwtVerify } from "jose"
2
+ import { ok, refused } from "@modules/platform/primitives"
3
+ import { SupabaseErrorCode } from "./errors/supabase.error"
4
+ import type { SupabaseOptions, SupabasePrincipal, VerifySupabaseAccessToken } from "./supabase.options"
5
+
6
+ const AUDIENCE = "authenticated"
7
+
8
+ /** Creates the verifier for this project's remote JWKS, exact issuer, authenticated audience and pinned algorithm. */
9
+ export const createSupabaseAccessTokenVerifier = (options: SupabaseOptions): VerifySupabaseAccessToken => {
10
+ const issuer = `${options.url.replace(/\/$/, "")}/auth/v1`
11
+ const keys = createRemoteJWKSet(new URL(`${issuer}/.well-known/jwks.json`))
12
+ return async (token) => {
13
+ try {
14
+ const { payload } = await jwtVerify(token, keys, {
15
+ algorithms: ["ES256"],
16
+ audience: AUDIENCE,
17
+ issuer,
18
+ })
19
+ if (typeof payload.sub !== "string" || typeof payload.exp !== "number" || payload.role !== AUDIENCE) {
20
+ return refused(SupabaseErrorCode.AccessTokenInvalid)
21
+ }
22
+ const principal: SupabasePrincipal = {
23
+ id: payload.sub,
24
+ role: AUDIENCE,
25
+ ...(typeof payload.email === "string" ? { email: payload.email } : {}),
26
+ }
27
+ return ok(principal)
28
+ } catch (cause) {
29
+ return refused(SupabaseErrorCode.AccessTokenInvalid, { cause: String(cause) })
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,24 @@
1
+ import type { Secret } from "@modules/platform/config"
2
+ import type { Outcome } from "@modules/platform/primitives"
3
+ import type { SupabaseErrorCode } from "./errors/supabase.error"
4
+
5
+ /** The verified identity claims admitted from a Supabase access token. */
6
+ export interface SupabasePrincipal {
7
+ /** The Auth user identifier from the verified `sub` claim. */
8
+ readonly id: string
9
+ /** The verified email claim when the provider issued one. */
10
+ readonly email?: string
11
+ /** Supabase's authenticated role, required by the verifier. */
12
+ readonly role: "authenticated"
13
+ }
14
+
15
+ /** Verifies one bearer token and returns only claims that passed every cryptographic and semantic check. */
16
+ export type VerifySupabaseAccessToken = (token: string) => Promise<Outcome<SupabasePrincipal, SupabaseErrorCode>>
17
+
18
+ /** Supabase configuration parsed once at API boot. */
19
+ export interface SupabaseOptions {
20
+ /** The project origin, used for Auth, Storage, issuer and JWKS endpoints. */
21
+ readonly url: string
22
+ /** The service-role credential, held only by the back-end integration. */
23
+ readonly serviceRoleKey: Secret
24
+ }
@@ -0,0 +1,4 @@
1
+ export type { Principal, PublicExecuteParams } from "./cqrs.contracts"
2
+ export { InjectQueryBus } from "./cqrs.decorators"
3
+ export { ICQRSHandler } from "./cqrs.handler"
4
+ export { CqrsModule } from "./cqrs.module"
@@ -0,0 +1,54 @@
1
+ import { Module } from "@nestjs/common"
2
+ import type { DynamicModule } from "@nestjs/common"
3
+ import { TypeOrmModule, getDataSourceToken } from "@nestjs/typeorm"
4
+ import type { DataSource } from "typeorm"
5
+ import { DATABASE_OPTIONS } from "./database.port"
6
+ import { ConfigurableModuleClass, OPTIONS_TYPE } from "./database.module-definition"
7
+ import { PRIMARY_CONNECTION } from "./primary.connection"
8
+ import { PRIMARY_ENTITY_MANAGER } from "./primary.decorators"
9
+
10
+ /** The token each declared connection provides its shared EntityManager under. */
11
+ type EntityManagerToken = typeof PRIMARY_ENTITY_MANAGER
12
+
13
+ const ENTITY_MANAGER_TOKENS: ReadonlyMap<string, EntityManagerToken> = new Map<string, EntityManagerToken>([
14
+ [PRIMARY_CONNECTION, PRIMARY_ENTITY_MANAGER],
15
+ ])
16
+
17
+ @Module({})
18
+ /** Opens each named Supabase PostgreSQL connection and exposes its shared EntityManager without entities or migrations. */
19
+ export class DatabaseModule extends ConfigurableModuleClass {
20
+ /** Registers the capability once per app with the connections it opens. */
21
+ static register(options: typeof OPTIONS_TYPE): DynamicModule {
22
+ const base = super.register(options)
23
+ const managers = options.connections.flatMap((connection) => {
24
+ const token = ENTITY_MANAGER_TOKENS.get(connection.name)
25
+ return token === undefined
26
+ ? []
27
+ : [
28
+ {
29
+ provide: token,
30
+ inject: [getDataSourceToken(connection.name)],
31
+ useFactory: (source: DataSource) => source.manager,
32
+ },
33
+ ]
34
+ })
35
+ return {
36
+ ...base,
37
+ imports: [
38
+ ...(base.imports ?? []),
39
+ ...options.connections.map((connection) =>
40
+ TypeOrmModule.forRoot({
41
+ name: connection.name,
42
+ type: "postgres",
43
+ url: connection.url.reveal(),
44
+ schema: connection.schema,
45
+ synchronize: false,
46
+ retryAttempts: 2,
47
+ }),
48
+ ),
49
+ ],
50
+ providers: [...(base.providers ?? []), ...managers],
51
+ exports: [DATABASE_OPTIONS, ...managers.map((manager) => manager.provide)],
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,23 @@
1
+ import type { Secret } from "@modules/platform/config"
2
+
3
+ /** What the primary connection parser reads from the environment. */
4
+ export interface DatabaseConnectionConfig {
5
+ /** The logical connection name declared in `hfs.json`. */
6
+ readonly name: string
7
+ /** The Supabase PostgreSQL provider; schema changes remain under `supabase/migrations`. */
8
+ readonly provider: "supabase"
9
+ /** The dedicated least-privilege application role's connection URL. */
10
+ readonly url: Secret
11
+ }
12
+
13
+ /** Runtime connection options add only the isolated PostgreSQL schema; lite has no entity or migration registry. */
14
+ export interface DatabaseConnectionOptions extends DatabaseConnectionConfig {
15
+ /** The schema named by the `hfs.json` isolation contract. */
16
+ readonly schema?: string
17
+ }
18
+
19
+ /** Options of the database capability. */
20
+ export interface DatabaseOptions {
21
+ /** The connections this app opens; one per physical database. */
22
+ readonly connections: ReadonlyArray<DatabaseConnectionOptions>
23
+ }
@@ -0,0 +1,10 @@
1
+ import { injector } from "@modules/platform/composition"
2
+ import type { TypedParameterDecorator } from "@modules/platform/composition"
3
+ import type { DatabaseOptions } from "./database.options"
4
+
5
+ /** Token of the options of the database capability. */
6
+ export const DATABASE_OPTIONS: unique symbol = Symbol("platform.database.options")
7
+
8
+ /** Injects the options of the database capability. Parameter type: DatabaseOptions. */
9
+ export const InjectDatabaseOptions = (): TypedParameterDecorator<DatabaseOptions> =>
10
+ injector<DatabaseOptions>(DATABASE_OPTIONS)
@@ -0,0 +1,49 @@
1
+ import type { EntityManager } from "typeorm"
2
+ import { DatabaseError, DatabaseErrorCode } from "./errors/database.error"
3
+
4
+ declare const sqlTextBrand: unique symbol
5
+ declare const sqlIdentBrand: unique symbol
6
+
7
+ /** SQL text built by the `sql` tag: the only kind of string `EntityManager.query` accepts. */
8
+ export type SqlText = string & { readonly [sqlTextBrand]: true }
9
+
10
+ /** A checked identifier: the only value a `sql` tag substitution accepts. */
11
+ export type SqlIdent = string & { readonly [sqlIdentBrand]: true }
12
+
13
+ /** The biggest page a client can ask for. */
14
+ export const PAGE_SIZE_MAX = 100
15
+ /** The biggest number of rows a server-side list read returns. */
16
+ export const LIST_ROWS_MAX = 500
17
+ /** The size of the keyset batches a background scan reads. */
18
+ export const BATCH_ROWS = 500
19
+
20
+ const IDENT_PATTERN = /^[a-z_][a-z0-9_]*$/
21
+
22
+ function assertSqlText(_value: string): asserts _value is SqlText {
23
+ // The brand is compile-time only; the `sql` tag is its single caller.
24
+ }
25
+
26
+ function assertSqlIdent(_value: string): asserts _value is SqlIdent {
27
+ // The brand is compile-time only; `ident` is the single caller after checking the name.
28
+ }
29
+
30
+ /** Builds SQL text whose only substitutions are already checked identifiers. */
31
+ {{> be/common/sql-tag.ts.partial}}
32
+
33
+ /** Checks a dynamic identifier against the names the caller allows and brands it. */
34
+ {{> be/common/sql-ident.ts.partial}}
35
+
36
+ type NotFoundFactory = () => never
37
+
38
+ /** Requires the row selected by an id-and-owner statement and lets the capability own its typed not-found error. */
39
+ export const requireOwnedRow = async <T>(
40
+ entityManager: EntityManager,
41
+ statement: SqlText,
42
+ id: string,
43
+ ownerId: string,
44
+ notFound: NotFoundFactory,
45
+ ): Promise<T> => {
46
+ const row = (await entityManager.query<Array<T>>(statement, [id, ownerId]))[0]
47
+ if (row === undefined) return notFound()
48
+ return row
49
+ }
@@ -0,0 +1,4 @@
1
+ export { DatabaseModule } from "./database.module"
2
+ export type { DatabaseConnectionOptions } from "./database.options"
3
+ export { DATABASE_ERROR_KINDS } from "./errors/database.error"
4
+ export { parsePrimaryDatabaseConfig } from "./primary.config"
@@ -0,0 +1,21 @@
1
+ import { spawn } from "node:child_process"
2
+ import { Injectable } from "@nestjs/common"
3
+
4
+ const runDbPush = (): Promise<void> =>
5
+ new Promise((resolve, reject) => {
6
+ const command = process.platform === "win32" ? "npm.cmd" : "npm"
7
+ const child = spawn(command, ["run", "db:push"], { stdio: "inherit" })
8
+ child.once("error", reject)
9
+ child.once("exit", (code) =>
10
+ code === 0 ? resolve() : reject(new Error(`db:push exited ${code ?? "without a status"}`)),
11
+ )
12
+ })
13
+
14
+ @Injectable()
15
+ /** The lite migration runner delegates the Supabase schema authority to the managed db:push wrapper. */
16
+ export class MigrationRunnerService {
17
+ /** Runs the managed wrapper and propagates a CLI or migration failure. */
18
+ async run(): Promise<void> {
19
+ await runDbPush()
20
+ }
21
+ }
@@ -0,0 +1,11 @@
1
+ import type { EnvSource } from "@modules/platform/config"
2
+ import type { DatabaseConnectionOptions } from "./database.options"
3
+ import { PRIMARY_CONNECTION } from "./primary.connection"
4
+
5
+ /** Reads the least-privilege Supabase PostgreSQL connection with no default. */
6
+ export const parsePrimaryDatabaseConfig = (env: EnvSource): DatabaseConnectionOptions => ({
7
+ name: PRIMARY_CONNECTION,
8
+ provider: "supabase",
9
+ url: env.secret("PRIMARY_DB_URL"),
10
+ schema: env.string("PRIMARY_DB_SCHEMA"),
11
+ })
@@ -0,0 +1,28 @@
1
+ import { readFile } from "node:fs/promises"
2
+ import { Injectable } from "@nestjs/common"
3
+ import type { EntityManager } from "typeorm"
4
+ import type { SqlText } from "./database.sql"
5
+ import { InjectPrimaryEntityManager } from "./primary.decorators"
6
+
7
+ const SEED_FILE = "supabase/seed.sql"
8
+
9
+ function assertSeedText(_text: string): asserts _text is SqlText {
10
+ // The brand is compile-time only; this function's caller reads the tracked seed file verbatim.
11
+ }
12
+
13
+ const seedText = (text: string): SqlText => {
14
+ assertSeedText(text)
15
+ return text
16
+ }
17
+
18
+ @Injectable()
19
+ /** Executes the one tracked Supabase seed through the shared least-privilege EntityManager. */
20
+ export class SeedRunnerService {
21
+ constructor(@InjectPrimaryEntityManager() private readonly entityManager: EntityManager) {}
22
+
23
+ /** Reads and executes the tracked seed when it contains at least one statement. */
24
+ async run(): Promise<void> {
25
+ const text = seedText(await readFile(SEED_FILE, "utf8"))
26
+ if (text.trim() !== "") await this.entityManager.query(text)
27
+ }
28
+ }
@@ -0,0 +1,25 @@
1
+ import { Module } from "@nestjs/common"
2
+ import type { DynamicModule } from "@nestjs/common"
3
+ import { WEBHOOK_SIGNATURE } from "./http-security.decorators"
4
+ import { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE } from "./http-security.module-definition"
5
+ import { RequestValidationService } from "./request-validation.service"
6
+ import { WebhookSignatureService } from "./webhook-signature.service"
7
+
8
+ @Module({})
9
+ /** The http-security capability: the options its guards (registered by each api app) read. */
10
+ export class HttpSecurityModule extends ConfigurableModuleClass {
11
+ /** Registers the capability once per app. */
12
+ static register(options: typeof OPTIONS_TYPE): DynamicModule {
13
+ const base = super.register(options)
14
+ return {
15
+ ...base,
16
+ providers: [
17
+ ...(base.providers ?? []),
18
+ RequestValidationService,
19
+ WebhookSignatureService,
20
+ { provide: WEBHOOK_SIGNATURE, useExisting: WebhookSignatureService },
21
+ ],
22
+ exports: [MODULE_OPTIONS_TOKEN, RequestValidationService, WebhookSignatureService, WEBHOOK_SIGNATURE],
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,9 @@
1
+ export { HTTP_SECURITY_ERROR_KINDS } from "./errors/http-security.error"
2
+ export { requestOf } from "./execution-request.mapper"
3
+ export { parseHttpSecurityConfig } from "./http-security.config"
4
+ export { HttpSecurityModule } from "./http-security.module"
5
+ export type { HttpSecurityOptions } from "./http-security.options"
6
+ export { HTTP_SECURITY_MESSAGES } from "./messages/http-security.messages"
7
+ export { OriginGuard } from "./origin.guard"
8
+ export { RateLimitGuard } from "./rate-limit.guard"
9
+ export { RequestValidationService } from "./request-validation.service"
@@ -0,0 +1,4 @@
1
+ export type { Locale, MessageBundle } from "./i18n.contracts"
2
+ export { InjectMessageCatalog, InjectRequestLocale } from "./i18n.decorators"
3
+ export { I18nModule } from "./i18n.module"
4
+ export type { MessageCatalog, RequestLocale } from "./i18n.port"
@@ -0,0 +1,5 @@
1
+ export { createJsonLogger } from "./json-logger.service"
2
+ export { InjectLogger } from "./logging.decorators"
3
+ export { LoggingLogEvent } from "./logging.log-events"
4
+ export { LoggingModule } from "./logging.module"
5
+ export type { Logger } from "./logging.port"
@@ -0,0 +1,16 @@
1
+ import { DomainError } from "@modules/platform/errors"
2
+ import type { ErrorKind } from "@modules/platform/errors"
3
+
4
+ /** Codes of the {{name}} capability. */
5
+ export enum {{Name}}ErrorCode {
6
+ /** No row owned by the authenticated principal has the requested id. */
7
+ NotFound = "{{upper}}_NOT_FOUND",
8
+ }
9
+
10
+ /** How each {{name}} code travels. */
11
+ export const {{upper}}_ERROR_KINDS: Record<{{Name}}ErrorCode, ErrorKind> = {
12
+ [{{Name}}ErrorCode.NotFound]: "not-found",
13
+ }
14
+
15
+ /** The one error class of the {{name}} capability. */
16
+ export class {{Name}}Error extends DomainError<{{Name}}ErrorCode> {}
@@ -0,0 +1,2 @@
1
+ export { {{upper}}_ERROR_KINDS } from "./errors/{{name}}.error"
2
+ export { {{Name}}Module } from "./{{name}}.module"
@@ -0,0 +1,51 @@
1
+ create table public.{{tableSql}} (
2
+ id uuid primary key default gen_random_uuid(),
3
+ owner_id uuid not null references auth.users (id) on delete cascade,
4
+ created_at timestamptz not null default now(),
5
+ updated_at timestamptz not null default now()
6
+ );
7
+
8
+ create index {{table}}_owner_id_idx on public.{{tableSql}} (owner_id);
9
+
10
+ alter table public.{{tableSql}} enable row level security;
11
+
12
+ create trigger {{table}}_set_updated_at
13
+ before update on public.{{tableSql}}
14
+ for each row
15
+ execute function private.set_updated_at();
16
+
17
+ create policy "{{table}}_authenticated_select"
18
+ on public.{{tableSql}}
19
+ for select
20
+ to authenticated
21
+ using (owner_id = (select auth.uid()));
22
+
23
+ create policy "{{table}}_authenticated_insert"
24
+ on public.{{tableSql}}
25
+ for insert
26
+ to authenticated
27
+ with check (owner_id = (select auth.uid()));
28
+
29
+ create policy "{{table}}_authenticated_update"
30
+ on public.{{tableSql}}
31
+ for update
32
+ to authenticated
33
+ using (owner_id = (select auth.uid()))
34
+ with check (owner_id = (select auth.uid()));
35
+
36
+ create policy "{{table}}_authenticated_delete"
37
+ on public.{{tableSql}}
38
+ for delete
39
+ to authenticated
40
+ using (owner_id = (select auth.uid()));
41
+
42
+ create policy "{{table}}_app_be_select"
43
+ on public.{{tableSql}}
44
+ for select
45
+ to app_be
46
+ using (owner_id is not null);
47
+
48
+ revoke all on table public.{{tableSql}} from public, anon;
49
+ grant select, insert, delete on table public.{{tableSql}} to authenticated;
50
+ grant update (updated_at) on table public.{{tableSql}} to authenticated;
51
+ grant select on table public.{{tableSql}} to app_be;
@@ -0,0 +1,7 @@
1
+ import { ConfigurableModuleBuilder } from "@nestjs/common"
2
+ import type { {{Name}}Options } from "./{{name}}.options"
3
+
4
+ /** The configurable-module base of the {{name}} capability. */
5
+ export const { ConfigurableModuleClass, OPTIONS_TYPE } = new ConfigurableModuleBuilder<{{Name}}Options>()
6
+ .setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
7
+ .build()