@starci/hfs 5.1.0 → 5.3.0

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 (365) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +11 -2
  3. package/cache/hfs-family.d.mts +2 -3
  4. package/cache/hfs-family.mjs +4 -9
  5. package/cache/secret-records.d.mts +7 -0
  6. package/cache/secret-records.mjs +12 -0
  7. package/cache/session.mjs +4 -2
  8. package/codemod/account-doors.d.mts +36 -0
  9. package/codemod/account-doors.mjs +128 -0
  10. package/codemod/main.mjs +31 -1
  11. package/codemod/module-definition.d.mts +52 -0
  12. package/codemod/module-definition.mjs +242 -0
  13. package/codemod/quality-binding.d.mts +33 -0
  14. package/codemod/quality-binding.mjs +38 -0
  15. package/codemod/secrets-cache.d.mts +35 -0
  16. package/codemod/secrets-cache.mjs +70 -0
  17. package/codemod/sops-rules.d.mts +34 -0
  18. package/codemod/sops-rules.mjs +69 -0
  19. package/codemod/theme-tokens.mjs +48 -2
  20. package/db/postgres.mjs +9 -1
  21. package/lint/run.d.mts +6 -0
  22. package/lint/run.mjs +63 -10
  23. package/package.json +1 -1
  24. package/runtime/knowledge/hfs/canon-pins.yaml +65 -11
  25. package/runtime/knowledge/hfs/fe-stack.yaml +9 -9
  26. package/runtime/knowledge/hfs/fork-images.yaml +7 -0
  27. package/runtime/knowledge/hfs/rules.yaml +67 -15
  28. package/runtime/knowledge/hfs/slots.yaml +174 -30
  29. package/runtime/modules/kernel/failure-codes.yaml +67 -4
  30. package/runtime/modules/schemas/application-stacks.schema.yaml +6 -0
  31. package/runtime/scripts/hfs/architecture/config.d.mts +4 -0
  32. package/runtime/scripts/hfs/architecture/config.mjs +25 -1
  33. package/runtime/scripts/hfs/architecture/identity.mjs +18 -83
  34. package/runtime/scripts/hfs/architecture/module-per-transport.d.mts +1 -1
  35. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +6 -1
  36. package/runtime/scripts/hfs/architecture/root-packages.d.mts +26 -0
  37. package/runtime/scripts/hfs/architecture/root-packages.mjs +78 -0
  38. package/runtime/scripts/hfs/architecture/transport-owner.d.mts +1 -2
  39. package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -6
  40. package/runtime/scripts/hfs/architecture/type-context.d.mts +4 -1
  41. package/runtime/scripts/hfs/architecture/type-context.mjs +30 -0
  42. package/runtime/scripts/hfs/architecture/typescript.d.mts +7 -1
  43. package/runtime/scripts/hfs/architecture/typescript.mjs +21 -1
  44. package/runtime/scripts/hfs/explicit-surface-rule.mjs +1 -1
  45. package/runtime/scripts/hfs/manifest-shape.d.mts +1 -0
  46. package/runtime/scripts/hfs/manifest-shape.mjs +3 -1
  47. package/runtime/scripts/hfs/rules/a15-findings.mjs +5 -0
  48. package/runtime/scripts/hfs/rules/database-access.mjs +39 -1
  49. package/runtime/scripts/hfs/rules/monorepo.d.mts +4 -0
  50. package/runtime/scripts/hfs/rules/monorepo.mjs +10 -5
  51. package/runtime/scripts/hfs/rules/secrets.mjs +11 -0
  52. package/runtime/scripts/hfs/rules/stacks-local.d.mts +7 -0
  53. package/runtime/scripts/hfs/rules/stacks-local.mjs +71 -0
  54. package/runtime/scripts/hfs/rules/stacks-runtime.mjs +6 -1
  55. package/runtime/scripts/hfs/rules/stacks-service-images.d.mts +12 -0
  56. package/runtime/scripts/hfs/rules/stacks-service-images.mjs +120 -0
  57. package/runtime/scripts/hfs/secret-dev-init.d.mts +11 -0
  58. package/runtime/scripts/hfs/secret-dev-init.mjs +79 -0
  59. package/runtime/scripts/hfs/secret-errors.d.mts +3 -0
  60. package/runtime/scripts/hfs/secret-errors.mjs +8 -0
  61. package/runtime/scripts/hfs/secret-init.d.mts +6 -0
  62. package/runtime/scripts/hfs/secret-init.mjs +23 -0
  63. package/runtime/scripts/hfs/secret-sops.mjs +9 -1
  64. package/runtime/scripts/hfs/secret-standard.d.mts +20 -0
  65. package/runtime/scripts/hfs/secret-standard.mjs +106 -0
  66. package/runtime/scripts/hfs/secret.mjs +15 -8
  67. package/runtime/scripts/hfs/slots.mjs +1 -1
  68. package/runtime/scripts/hfs/stack-local-config.d.mts +101 -0
  69. package/runtime/scripts/hfs/stack-local-config.mjs +320 -0
  70. package/runtime/scripts/hfs/stacks-gate.mjs +1 -1
  71. package/runtime/scripts/hfs/view.d.mts +23 -0
  72. package/runtime/scripts/hfs/view.mjs +68 -0
  73. package/runtime/scripts/lib/declaration-surface.d.mts +4 -0
  74. package/runtime/scripts/lib/declaration-surface.mjs +55 -1
  75. package/runtime/scripts/lib/secret-records.d.mts +13 -0
  76. package/runtime/scripts/lib/secret-records.mjs +83 -0
  77. package/runtime/scripts/lib/swarm-stack.d.mts +2 -1
  78. package/runtime/scripts/lib/swarm-stack.mjs +2 -2
  79. package/scaffold/add-inbox-lite.mjs +2 -2
  80. package/scaffold/add-module.d.mts +16 -0
  81. package/scaffold/add-module.mjs +232 -0
  82. package/scaffold/add-queue-lite.mjs +50 -3
  83. package/scaffold/add.mjs +5 -0
  84. package/scaffold/app.d.mts +2 -2
  85. package/scaffold/app.mjs +16 -18
  86. package/scaffold/lite-exports.mjs +1 -15
  87. package/scaffold/module-registry.d.mts +19 -0
  88. package/scaffold/module-registry.mjs +84 -0
  89. package/scripts/sync-bundles.d.mts +8 -0
  90. package/src/main.mjs +23 -2
  91. package/stack/cli.mjs +6 -1
  92. package/stack/context.d.mts +15 -0
  93. package/stack/context.mjs +1 -0
  94. package/stack/images/openclaw-runtime/Dockerfile +18 -0
  95. package/stack/images/openclaw-runtime/entrypoint.sh +23 -0
  96. package/stack/images/openclaw-runtime/openclaw-init.mjs +101 -0
  97. package/stack/images/openclaw-runtime/openclaw-relay.mjs +25 -0
  98. package/stack/local-cli.d.mts +25 -0
  99. package/stack/local-cli.mjs +89 -0
  100. package/stack/local-config.d.mts +17 -0
  101. package/stack/local-config.mjs +37 -0
  102. package/stack/local-custody.d.mts +24 -0
  103. package/stack/local-custody.mjs +109 -0
  104. package/stack/local-doctor.d.mts +6 -0
  105. package/stack/local-doctor.mjs +49 -0
  106. package/stack/local-down.d.mts +8 -0
  107. package/stack/local-down.mjs +20 -0
  108. package/stack/local-host.d.mts +42 -0
  109. package/stack/local-host.mjs +163 -0
  110. package/stack/local-logs.d.mts +8 -0
  111. package/stack/local-logs.mjs +32 -0
  112. package/stack/local-ports.d.mts +7 -0
  113. package/stack/local-ports.mjs +39 -0
  114. package/stack/local-prepare.d.mts +22 -0
  115. package/stack/local-prepare.mjs +62 -0
  116. package/stack/local-run.d.mts +71 -0
  117. package/stack/local-run.mjs +200 -0
  118. package/stack/local-state.d.mts +35 -0
  119. package/stack/local-state.mjs +29 -0
  120. package/stack/local-status.d.mts +6 -0
  121. package/stack/local-status.mjs +36 -0
  122. package/stack/local-up.d.mts +11 -0
  123. package/stack/local-up.mjs +169 -0
  124. package/stack/local-verify.d.mts +6 -0
  125. package/stack/local-verify.mjs +21 -0
  126. package/stack/runtime-layers.d.mts +21 -0
  127. package/stack/runtime-layers.mjs +67 -0
  128. package/sync/hygiene.mjs +3 -1
  129. package/sync/index.d.mts +15 -12
  130. package/sync/index.mjs +114 -28
  131. package/templates/app/auth-doors.sql.partial +115 -0
  132. package/templates/app/ci-workflows/github/workflows/ci.yml +1 -1
  133. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +1 -1
  134. package/templates/app/module/jest.config.js.tpl +8 -0
  135. package/templates/app/module/src/index.ts.tpl +2 -0
  136. package/templates/app/module/src/manifest/declaration.spec.ts.tpl +11 -0
  137. package/templates/app/module/src/manifest/declaration.ts.tpl +13 -0
  138. package/templates/app/module/src/manifest/index.ts.tpl +1 -0
  139. package/templates/app/module/src/public/index.ts.tpl +2 -0
  140. package/templates/app/module/src/template/__name__.template.spec.ts.tpl +13 -0
  141. package/templates/app/module/src/template/__name__.template.ts.tpl +41 -0
  142. package/templates/app/module/src/template/index.ts.tpl +1 -0
  143. package/templates/app/module/src/workbench/components/__pascal__WorkbenchView.tsx.tpl +16 -0
  144. package/templates/app/module/src/workbench/index.ts.tpl +2 -0
  145. package/templates/app/package-scripts/core.json +1 -1
  146. package/templates/app/package-scripts/package.json +1 -1
  147. package/templates/app/package-scripts-lite/package.json +1 -1
  148. package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +85 -70
  149. package/templates/app/skeleton-lite/.starcistacks/dev/README.md +24 -12
  150. package/templates/app/skeleton-lite/.starcistacks/dev/infra/compose/compose.yaml +43 -0
  151. package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +21 -26
  152. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +6 -0
  153. package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +6 -1
  154. package/templates/app/skeleton-lite/db/migrations/__timestamp___baseline.sql +76 -13
  155. package/templates/be/auth-lite/src/features/api/auth/application/auth.contracts.ts +44 -0
  156. package/templates/be/auth-lite/src/features/api/auth/application/confirm.command.ts +11 -0
  157. package/templates/be/auth-lite/src/features/api/auth/application/confirm.handler.ts +22 -0
  158. package/templates/be/auth-lite/src/features/api/auth/application/forgot.command.ts +11 -0
  159. package/templates/be/auth-lite/src/features/api/auth/application/forgot.handler.ts +22 -0
  160. package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.handler.ts +23 -0
  161. package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.query.ts +12 -0
  162. package/templates/be/auth-lite/src/features/api/auth/application/google-begin.command.ts +11 -0
  163. package/templates/be/auth-lite/src/features/api/auth/application/google-begin.handler.ts +22 -0
  164. package/templates/be/auth-lite/src/features/api/auth/application/google-complete.command.ts +12 -0
  165. package/templates/be/auth-lite/src/features/api/auth/application/google-complete.handler.ts +23 -0
  166. package/templates/be/auth-lite/src/features/api/auth/application/login.command.ts +12 -0
  167. package/templates/be/auth-lite/src/features/api/auth/application/login.handler.ts +23 -0
  168. package/templates/be/auth-lite/src/features/api/auth/application/logout.command.ts +11 -0
  169. package/templates/be/auth-lite/src/features/api/auth/application/logout.handler.ts +22 -0
  170. package/templates/be/auth-lite/src/features/api/auth/application/refresh.command.ts +12 -0
  171. package/templates/be/auth-lite/src/features/api/auth/application/refresh.handler.ts +23 -0
  172. package/templates/be/auth-lite/src/features/api/auth/application/resend.command.ts +11 -0
  173. package/templates/be/auth-lite/src/features/api/auth/application/resend.handler.ts +22 -0
  174. package/templates/be/auth-lite/src/features/api/auth/application/reset.command.ts +11 -0
  175. package/templates/be/auth-lite/src/features/api/auth/application/reset.handler.ts +22 -0
  176. package/templates/be/auth-lite/src/features/api/auth/application/session.command.ts +12 -0
  177. package/templates/be/auth-lite/src/features/api/auth/application/session.handler.ts +23 -0
  178. package/templates/be/auth-lite/src/features/api/auth/application/signup.command.ts +11 -0
  179. package/templates/be/auth-lite/src/features/api/auth/application/signup.handler.ts +22 -0
  180. package/templates/be/auth-lite/src/features/api/auth/auth.module-definition.ts +6 -0
  181. package/templates/be/auth-lite/src/features/api/auth/auth.module.ts +21 -0
  182. package/templates/be/auth-lite/src/features/api/auth/auth.options.ts +3 -0
  183. package/templates/be/auth-lite/src/features/api/auth/index.ts +2 -0
  184. package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module-definition.ts +6 -0
  185. package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module.ts +9 -0
  186. package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.options.ts +3 -0
  187. package/templates/be/auth-lite/src/features/api/auth/transport/http/auth.controller.ts +145 -0
  188. package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/forgot.request.ts +16 -0
  189. package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/google-start.request.ts +17 -0
  190. package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/login.request.ts +16 -0
  191. package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/resend.request.ts +16 -0
  192. package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/reset.request.ts +18 -0
  193. package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/signup.request.ts +22 -0
  194. package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/token.request.ts +11 -0
  195. package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.spec.ts +46 -0
  196. package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.ts +19 -0
  197. package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.spec.ts +209 -0
  198. package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.ts +84 -0
  199. package/templates/be/auth-lite/src/modules/domain/identity/identity-email.log-events.ts +6 -0
  200. package/templates/be/auth-lite/src/modules/domain/identity/identity-email.service.spec.ts +233 -0
  201. package/templates/be/auth-lite/src/modules/domain/identity/identity-email.service.ts +176 -0
  202. package/templates/be/auth-lite/src/modules/domain/identity/identity-sign-in.log-events.ts +6 -0
  203. package/templates/be/auth-lite/src/modules/domain/identity/persistence/account-doors.port.ts +98 -0
  204. package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-attempt.entity.ts +18 -0
  205. package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-email-token.entity.ts +34 -0
  206. package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-refresh-token.entity.ts +34 -0
  207. package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-session.entity.ts +30 -0
  208. package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/index.ts +10 -0
  209. package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.spec.ts +86 -0
  210. package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.ts +45 -0
  211. package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.spec.ts +97 -0
  212. package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.ts +81 -0
  213. package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.spec.ts +95 -0
  214. package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.ts +93 -0
  215. package/templates/be/auth-lite/src/tests/fixtures/builders/identity.builder.ts +40 -0
  216. package/templates/be/inbox/module-definition.ts.tpl +2 -9
  217. package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +2 -11
  218. package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +2 -9
  219. package/templates/be/patterns/api/feature.module-definition.ts.tpl +3 -9
  220. package/templates/be/patterns/api/graphql.module-definition.ts.tpl +3 -9
  221. package/templates/be/patterns/cli/group.module-definition.ts.tpl +2 -9
  222. package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +3 -10
  223. package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +2 -9
  224. package/templates/be/patterns/jobs/job.module-definition.ts.tpl +2 -9
  225. package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +3 -9
  226. package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +3 -10
  227. package/templates/be/patterns/projections/projection.module-definition.ts.tpl +2 -9
  228. package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +3 -10
  229. package/templates/be/patterns/queues/queue.module-definition.ts.tpl +2 -9
  230. package/templates/be/patterns/reactors/message.module-definition.ts.tpl +3 -9
  231. package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +2 -9
  232. package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +3 -9
  233. package/templates/be/patterns/realtime/channel.sse.module-definition.ts.tpl +3 -9
  234. package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +3 -9
  235. package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +3 -9
  236. package/templates/be/patterns/saga/saga.module-definition.ts.tpl +2 -9
  237. package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +3 -9
  238. package/templates/be/platform/graphql/graphql.module-definition.ts +2 -9
  239. package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +3 -9
  240. package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +3 -9
  241. package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +2 -9
  242. package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +3 -9
  243. package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +2 -9
  244. package/templates/be/skeleton/src/modules/domain/identity/identity.module-definition.ts +2 -9
  245. package/templates/be/skeleton/src/modules/domain/liveness/liveness.module-definition.ts +2 -9
  246. package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +2 -9
  247. package/templates/be/skeleton/src/modules/platform/clock/clock.module-definition.ts +2 -9
  248. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +2 -0
  249. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module-definition.ts +2 -9
  250. package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +3 -13
  251. package/templates/be/skeleton/src/modules/platform/errors/errors.module-definition.ts +4 -11
  252. package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +4 -9
  253. package/templates/be/skeleton/src/modules/platform/http-security/http-security.module-definition.ts +5 -11
  254. package/templates/be/skeleton/src/modules/platform/i18n/i18n.module-definition.ts +4 -11
  255. package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +2 -9
  256. package/templates/be/skeleton/src/modules/platform/logging/logging.module-definition.ts +2 -9
  257. package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +2 -9
  258. package/templates/be/skeleton-lite/apps/api/src/api.env.ts +4 -0
  259. package/templates/be/skeleton-lite/apps/api/src/api.options.ts +3 -0
  260. package/templates/be/skeleton-lite/apps/api/src/app.module.ts +6 -5
  261. package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +3 -2
  262. package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +12 -5
  263. package/templates/be/skeleton-lite/src/modules/domain/identity/errors/identity.error.ts +40 -0
  264. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +22 -1
  265. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +50 -2
  266. package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +3 -0
  267. package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity-mail.messages.ts +65 -0
  268. package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity.messages.ts +28 -0
  269. package/templates/be/skeleton-lite/src/modules/integrations/mail/index.ts +6 -0
  270. package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.config.ts +33 -0
  271. package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.decorators.ts +14 -0
  272. package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.module-definition.ts +12 -0
  273. package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.module.ts +27 -0
  274. package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.options.ts +22 -0
  275. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/index.ts +3 -3
  276. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.config.ts +3 -1
  277. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.decorators.ts +8 -4
  278. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module-definition.ts +5 -0
  279. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module.ts +29 -0
  280. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.options.ts +6 -31
  281. package/templates/be/skeleton-lite/src/modules/integrations/passport/errors/passport.error.ts +18 -9
  282. package/templates/be/skeleton-lite/src/modules/integrations/passport/index.ts +35 -11
  283. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.spec.ts +44 -8
  284. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.ts +40 -18
  285. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.decorators.ts +18 -6
  286. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.spec.ts +73 -0
  287. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.ts +26 -0
  288. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.options.ts +24 -53
  289. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.registration.ts +31 -0
  290. package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +1 -1
  291. package/templates/be/skeleton-lite/src/modules/platform/database/entities.ts +2 -1
  292. package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +1 -0
  293. package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +1 -1
  294. package/templates/be/skeleton-lite/src/modules/platform/http-security/rate-limit.guard.spec.ts +11 -0
  295. package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +22 -4
  296. package/templates/be/table/api/feature.module-definition.ts.tpl +3 -9
  297. package/templates/be/table/api/graphql.module-definition.ts.tpl +3 -9
  298. package/templates/be/table/module-definition.ts.tpl +2 -9
  299. package/templates/be/test-world/e2e/serves-once-migrated.e2e-spec.ts.tpl +9 -9
  300. package/templates/be/test-world/fixtures/migrations.sql.ts.tpl +4 -4
  301. package/templates/fe/auth-lite/apps/web/src/modules/auth/client.ts +26 -0
  302. package/templates/fe/auth-lite/apps/web/src/modules/auth/email-doors.ts +16 -0
  303. package/templates/fe/auth-lite/apps/web/src/modules/auth/index.ts +5 -0
  304. package/templates/fe/common/site-shell.tsx.partial +3 -3
  305. package/templates/fe/skeleton/apps/__app__/src/components/providers/AppProviders/index.tsx +33 -10
  306. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/index.ts +1 -0
  307. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useViewerId.ts +11 -0
  308. package/templates/fe/skeleton/apps/__app__/src/modules/toast/toast.tsx +15 -12
  309. package/templates/fe/skeleton/apps/__app__/src/modules/viewer-cache/index.ts +5 -0
  310. package/templates/fe/skeleton/apps/__app__/src/redux/slices/session.ts +1 -1
  311. package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +30 -67
  312. package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +6 -21
  313. package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +10 -16
  314. package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +4 -7
  315. package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/index.tsx +2 -3
  316. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/confirm/page.tsx +9 -0
  317. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/forgot/page.tsx +9 -0
  318. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/resend/page.tsx +9 -0
  319. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/reset/page.tsx +9 -0
  320. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/signup/page.tsx +9 -0
  321. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +5 -9
  322. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/EmailDoorForm/component.tsx +51 -0
  323. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/EmailDoorForm/index.tsx +84 -0
  324. package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +4 -7
  325. package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/index.tsx +1 -2
  326. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppConfirmPage/index.tsx +25 -0
  327. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppForgotPage/index.tsx +21 -0
  328. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +11 -3
  329. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +4 -1
  330. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppResendPage/index.tsx +21 -0
  331. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppResetPage/index.tsx +25 -0
  332. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppSignupPage/index.tsx +21 -0
  333. package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/emailAuth.shared.ts +12 -0
  334. package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/index.ts +2 -0
  335. package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/useEmailDoor.ts +32 -0
  336. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +2 -2
  337. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/en.json +50 -1
  338. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +50 -1
  339. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +7 -30
  340. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +13 -14
  341. package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +5 -0
  342. package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +4 -17
  343. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +0 -16
  344. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +0 -11
  345. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +0 -7
  346. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +0 -6
  347. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +0 -8
  348. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +0 -6
  349. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +0 -186
  350. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.spec.ts +0 -22
  351. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.ts +0 -25
  352. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.spec.ts +0 -104
  353. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.ts +0 -34
  354. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.spec.ts +0 -28
  355. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.ts +0 -11
  356. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.spec.ts +0 -88
  357. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.ts +0 -69
  358. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/google.strategy.ts +0 -41
  359. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/local.strategy.ts +0 -40
  360. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/oauth-state.store.ts +0 -63
  361. package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/classNames.ts +0 -4
  362. package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/auth-door.ts +0 -30
  363. package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +0 -50
  364. package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/token-store.ts +0 -31
  365. package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/classNames.ts +0 -4
@@ -0,0 +1,12 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Query } from "@nestjs/cqrs"
3
+ import type { JwksDocument } from "@modules/integrations/passport"
4
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
5
+ import type { EmptyRequest } from "./auth.contracts"
6
+
7
+ /** Reads the public key document access tokens verify against. */
8
+ export class GetJwksQuery extends Query<JwksDocument> {
9
+ constructor(readonly params: PublicExecuteParams<EmptyRequest>) {
10
+ super()
11
+ }
12
+ }
@@ -0,0 +1,11 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
4
+ import type { ExchangeRequest } from "./auth.contracts"
5
+
6
+ /** Starts the Google round trip: the strategy writes the redirect and the signed state cookie. */
7
+ export class GoogleBeginCommand extends Command<void> {
8
+ constructor(readonly params: PublicExecuteParams<ExchangeRequest>) {
9
+ super()
10
+ }
11
+ }
@@ -0,0 +1,22 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentitySignInService } from "@modules/domain/identity"
4
+ import { ICQRSHandler } from "@modules/platform/cqrs"
5
+ import { InjectLogger } from "@modules/platform/logging"
6
+ import type { Logger } from "@modules/platform/logging"
7
+ import { GoogleBeginCommand } from "./google-begin.command"
8
+
9
+ @CommandHandler(GoogleBeginCommand)
10
+ /** Starts the Google round trip: the strategy writes the redirect and the signed state cookie. */
11
+ export class GoogleBeginHandler extends ICQRSHandler<GoogleBeginCommand, void> {
12
+ constructor(
13
+ @InjectLogger() logger: Logger,
14
+ private readonly signIn: IdentitySignInService,
15
+ ) {
16
+ super(logger)
17
+ }
18
+
19
+ protected override process(command: GoogleBeginCommand): Promise<void> {
20
+ return this.signIn.beginGoogle(command.params.request.exchange)
21
+ }
22
+ }
@@ -0,0 +1,12 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { GoogleRedirect } from "@modules/integrations/passport"
4
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
5
+ import type { ExchangeRequest } from "./auth.contracts"
6
+
7
+ /** Finishes the Google round trip. */
8
+ export class GoogleCompleteCommand extends Command<GoogleRedirect> {
9
+ constructor(readonly params: PublicExecuteParams<ExchangeRequest>) {
10
+ super()
11
+ }
12
+ }
@@ -0,0 +1,23 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentitySignInService } from "@modules/domain/identity"
4
+ import type { GoogleRedirect } from "@modules/integrations/passport"
5
+ import { ICQRSHandler } from "@modules/platform/cqrs"
6
+ import { InjectLogger } from "@modules/platform/logging"
7
+ import type { Logger } from "@modules/platform/logging"
8
+ import { GoogleCompleteCommand } from "./google-complete.command"
9
+
10
+ @CommandHandler(GoogleCompleteCommand)
11
+ /** Finishes the Google round trip. */
12
+ export class GoogleCompleteHandler extends ICQRSHandler<GoogleCompleteCommand, GoogleRedirect> {
13
+ constructor(
14
+ @InjectLogger() logger: Logger,
15
+ private readonly signIn: IdentitySignInService,
16
+ ) {
17
+ super(logger)
18
+ }
19
+
20
+ protected override process(command: GoogleCompleteCommand): Promise<GoogleRedirect> {
21
+ return this.signIn.completeGoogle(command.params.request.exchange)
22
+ }
23
+ }
@@ -0,0 +1,12 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { AccessGrant } from "@modules/integrations/passport"
4
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
5
+ import type { ExchangeRequest } from "./auth.contracts"
6
+
7
+ /** Signs in with email and password through the local strategy and opens a session. */
8
+ export class LoginCommand extends Command<AccessGrant> {
9
+ constructor(readonly params: PublicExecuteParams<ExchangeRequest>) {
10
+ super()
11
+ }
12
+ }
@@ -0,0 +1,23 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentitySignInService } from "@modules/domain/identity"
4
+ import type { AccessGrant } from "@modules/integrations/passport"
5
+ import { ICQRSHandler } from "@modules/platform/cqrs"
6
+ import { InjectLogger } from "@modules/platform/logging"
7
+ import type { Logger } from "@modules/platform/logging"
8
+ import { LoginCommand } from "./login.command"
9
+
10
+ @CommandHandler(LoginCommand)
11
+ /** Signs in with email and password through the local strategy and opens a session. */
12
+ export class LoginHandler extends ICQRSHandler<LoginCommand, AccessGrant> {
13
+ constructor(
14
+ @InjectLogger() logger: Logger,
15
+ private readonly signIn: IdentitySignInService,
16
+ ) {
17
+ super(logger)
18
+ }
19
+
20
+ protected override process(command: LoginCommand): Promise<AccessGrant> {
21
+ return this.signIn.login(command.params.request.exchange)
22
+ }
23
+ }
@@ -0,0 +1,11 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
4
+ import type { ExchangeRequest } from "./auth.contracts"
5
+
6
+ /** Ends the session of the refresh cookie and clears it. */
7
+ export class LogoutCommand extends Command<void> {
8
+ constructor(readonly params: PublicExecuteParams<ExchangeRequest>) {
9
+ super()
10
+ }
11
+ }
@@ -0,0 +1,22 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentitySignInService } from "@modules/domain/identity"
4
+ import { ICQRSHandler } from "@modules/platform/cqrs"
5
+ import { InjectLogger } from "@modules/platform/logging"
6
+ import type { Logger } from "@modules/platform/logging"
7
+ import { LogoutCommand } from "./logout.command"
8
+
9
+ @CommandHandler(LogoutCommand)
10
+ /** Ends the session of the refresh cookie and clears it. */
11
+ export class LogoutHandler extends ICQRSHandler<LogoutCommand, void> {
12
+ constructor(
13
+ @InjectLogger() logger: Logger,
14
+ private readonly signIn: IdentitySignInService,
15
+ ) {
16
+ super(logger)
17
+ }
18
+
19
+ protected override process(command: LogoutCommand): Promise<void> {
20
+ return this.signIn.logout(command.params.request.exchange)
21
+ }
22
+ }
@@ -0,0 +1,12 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { AccessGrant } from "@modules/integrations/passport"
4
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
5
+ import type { ExchangeRequest } from "./auth.contracts"
6
+
7
+ /** Rotates the refresh cookie and answers a new access token; a bad cookie is cleared. */
8
+ export class RefreshCommand extends Command<AccessGrant> {
9
+ constructor(readonly params: PublicExecuteParams<ExchangeRequest>) {
10
+ super()
11
+ }
12
+ }
@@ -0,0 +1,23 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentitySignInService } from "@modules/domain/identity"
4
+ import type { AccessGrant } from "@modules/integrations/passport"
5
+ import { ICQRSHandler } from "@modules/platform/cqrs"
6
+ import { InjectLogger } from "@modules/platform/logging"
7
+ import type { Logger } from "@modules/platform/logging"
8
+ import { RefreshCommand } from "./refresh.command"
9
+
10
+ @CommandHandler(RefreshCommand)
11
+ /** Rotates the refresh cookie and answers a new access token; a bad cookie is cleared. */
12
+ export class RefreshHandler extends ICQRSHandler<RefreshCommand, AccessGrant> {
13
+ constructor(
14
+ @InjectLogger() logger: Logger,
15
+ private readonly signIn: IdentitySignInService,
16
+ ) {
17
+ super(logger)
18
+ }
19
+
20
+ protected override process(command: RefreshCommand): Promise<AccessGrant> {
21
+ return this.signIn.refresh(command.params.request.exchange)
22
+ }
23
+ }
@@ -0,0 +1,11 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
4
+ import type { AddressData } from "./auth.contracts"
5
+
6
+ /** Mails a new confirmation link to an unconfirmed account; the answer never says whether the address has one. */
7
+ export class ResendCommand extends Command<void> {
8
+ constructor(readonly params: PublicExecuteParams<AddressData>) {
9
+ super()
10
+ }
11
+ }
@@ -0,0 +1,22 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentityEmailService } from "@modules/domain/identity"
4
+ import { ICQRSHandler } from "@modules/platform/cqrs"
5
+ import { InjectLogger } from "@modules/platform/logging"
6
+ import type { Logger } from "@modules/platform/logging"
7
+ import { ResendCommand } from "./resend.command"
8
+
9
+ @CommandHandler(ResendCommand)
10
+ /** Mails a new confirmation link to an unconfirmed account; the answer never says whether the address has one. */
11
+ export class ResendHandler extends ICQRSHandler<ResendCommand, void> {
12
+ constructor(
13
+ @InjectLogger() logger: Logger,
14
+ private readonly email: IdentityEmailService,
15
+ ) {
16
+ super(logger)
17
+ }
18
+
19
+ protected override process(command: ResendCommand): Promise<void> {
20
+ return this.email.resend(command.params.request.email, command.params.request.language)
21
+ }
22
+ }
@@ -0,0 +1,11 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
4
+ import type { ResetData } from "./auth.contracts"
5
+
6
+ /** Spends a reset link: sets the new password and ends every session of the person. */
7
+ export class ResetCommand extends Command<void> {
8
+ constructor(readonly params: PublicExecuteParams<ResetData>) {
9
+ super()
10
+ }
11
+ }
@@ -0,0 +1,22 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentityEmailService } from "@modules/domain/identity"
4
+ import { ICQRSHandler } from "@modules/platform/cqrs"
5
+ import { InjectLogger } from "@modules/platform/logging"
6
+ import type { Logger } from "@modules/platform/logging"
7
+ import { ResetCommand } from "./reset.command"
8
+
9
+ @CommandHandler(ResetCommand)
10
+ /** Spends a reset link: sets the new password and ends every session of the person. */
11
+ export class ResetHandler extends ICQRSHandler<ResetCommand, void> {
12
+ constructor(
13
+ @InjectLogger() logger: Logger,
14
+ private readonly email: IdentityEmailService,
15
+ ) {
16
+ super(logger)
17
+ }
18
+
19
+ protected override process(command: ResetCommand): Promise<void> {
20
+ return this.email.reset(command.params.request.token, command.params.request.password)
21
+ }
22
+ }
@@ -0,0 +1,12 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { AccessGrant } from "@modules/integrations/passport"
4
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
5
+ import type { ExchangeRequest } from "./auth.contracts"
6
+
7
+ /** Answers a fresh access token for the cookie session without rotating the cookie. */
8
+ export class SessionCommand extends Command<AccessGrant> {
9
+ constructor(readonly params: PublicExecuteParams<ExchangeRequest>) {
10
+ super()
11
+ }
12
+ }
@@ -0,0 +1,23 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentitySignInService } from "@modules/domain/identity"
4
+ import type { AccessGrant } from "@modules/integrations/passport"
5
+ import { ICQRSHandler } from "@modules/platform/cqrs"
6
+ import { InjectLogger } from "@modules/platform/logging"
7
+ import type { Logger } from "@modules/platform/logging"
8
+ import { SessionCommand } from "./session.command"
9
+
10
+ @CommandHandler(SessionCommand)
11
+ /** Answers a fresh access token for the cookie session without rotating the cookie. */
12
+ export class SessionHandler extends ICQRSHandler<SessionCommand, AccessGrant> {
13
+ constructor(
14
+ @InjectLogger() logger: Logger,
15
+ private readonly signIn: IdentitySignInService,
16
+ ) {
17
+ super(logger)
18
+ }
19
+
20
+ protected override process(command: SessionCommand): Promise<AccessGrant> {
21
+ return this.signIn.session(command.params.request.exchange)
22
+ }
23
+ }
@@ -0,0 +1,11 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
4
+ import type { SignupData } from "./auth.contracts"
5
+
6
+ /** Creates an unconfirmed password account and mails the confirmation link; the answer never says whether the address already had an account. */
7
+ export class SignupCommand extends Command<void> {
8
+ constructor(readonly params: PublicExecuteParams<SignupData>) {
9
+ super()
10
+ }
11
+ }
@@ -0,0 +1,22 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentityEmailService } from "@modules/domain/identity"
4
+ import { ICQRSHandler } from "@modules/platform/cqrs"
5
+ import { InjectLogger } from "@modules/platform/logging"
6
+ import type { Logger } from "@modules/platform/logging"
7
+ import { SignupCommand } from "./signup.command"
8
+
9
+ @CommandHandler(SignupCommand)
10
+ /** Creates an unconfirmed password account and mails the confirmation link; the answer never says whether the address already had an account. */
11
+ export class SignupHandler extends ICQRSHandler<SignupCommand, void> {
12
+ constructor(
13
+ @InjectLogger() logger: Logger,
14
+ private readonly email: IdentityEmailService,
15
+ ) {
16
+ super(logger)
17
+ }
18
+
19
+ protected override process(command: SignupCommand): Promise<void> {
20
+ return this.email.signup(command.params.request.email, command.params.request.password, command.params.request.language)
21
+ }
22
+ }
@@ -0,0 +1,6 @@
1
+ import { defineConfigurableModule } from "@starci/nest-kit"
2
+ import type { AuthApiOptions } from "./auth.options"
3
+
4
+ /** The configurable-module base of the auth feature module. */
5
+ export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE } =
6
+ defineConfigurableModule<AuthApiOptions>()
@@ -0,0 +1,21 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Module } from "@nestjs/common"
3
+ import { LoginHandler } from "./application/login.handler"
4
+ import { RefreshHandler } from "./application/refresh.handler"
5
+ import { SessionHandler } from "./application/session.handler"
6
+ import { LogoutHandler } from "./application/logout.handler"
7
+ import { GoogleBeginHandler } from "./application/google-begin.handler"
8
+ import { GoogleCompleteHandler } from "./application/google-complete.handler"
9
+ import { GetJwksHandler } from "./application/get-jwks.handler"
10
+ import { SignupHandler } from "./application/signup.handler"
11
+ import { ResendHandler } from "./application/resend.handler"
12
+ import { ConfirmHandler } from "./application/confirm.handler"
13
+ import { ForgotHandler } from "./application/forgot.handler"
14
+ import { ResetHandler } from "./application/reset.handler"
15
+ import { ConfigurableModuleClass } from "./auth.module-definition"
16
+
17
+ @Module({
18
+ providers: [LoginHandler, RefreshHandler, SessionHandler, LogoutHandler, GoogleBeginHandler, GoogleCompleteHandler, GetJwksHandler, SignupHandler, ResendHandler, ConfirmHandler, ForgotHandler, ResetHandler],
19
+ })
20
+ /** The auth feature: the handlers behind the sign-in doors. */
21
+ export class AuthApiModule extends ConfigurableModuleClass {}
@@ -0,0 +1,3 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ /** The auth feature module takes no options yet. */
3
+ export type AuthApiOptions = Record<string, never>
@@ -0,0 +1,2 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ export { AuthHttpModule } from "./transport/http/auth-http.module"
@@ -0,0 +1,6 @@
1
+ import { defineConfigurableModule } from "@starci/nest-kit"
2
+ import type { AuthHttpOptions } from "./auth-http.options"
3
+
4
+ /** The configurable-module base of the auth-http module. */
5
+ export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE } =
6
+ defineConfigurableModule<AuthHttpOptions>()
@@ -0,0 +1,9 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Module } from "@nestjs/common"
3
+ import { AuthApiModule } from "../../auth.module"
4
+ import { AuthController } from "./auth.controller"
5
+ import { ConfigurableModuleClass } from "./auth-http.module-definition"
6
+
7
+ @Module({ imports: [AuthApiModule.register({})], controllers: [AuthController] })
8
+ /** The HTTP transport of the auth feature: the REST doors under /auth. */
9
+ export class AuthHttpModule extends ConfigurableModuleClass {}
@@ -0,0 +1,3 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ /** The auth-http module takes no options yet. */
3
+ export type AuthHttpOptions = Record<string, never>
@@ -0,0 +1,145 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Body, Controller, Get, HttpCode, Post, Query, Redirect, Req, Res } from "@nestjs/common"
3
+ import type { CommandBus, QueryBus } from "@nestjs/cqrs"
4
+ import type { Request, Response } from "express"
5
+ import { Public, PublicReason } from "@modules/domain/identity"
6
+ import type { AccessGrant, GoogleRedirect, JwksDocument } from "@modules/integrations/passport"
7
+ import { InjectCommandBus, InjectQueryBus } from "@modules/platform/cqrs"
8
+ import { RateLimit, RateTier } from "@modules/platform/http-security"
9
+ import { SignupCommand } from "../../application/signup.command"
10
+ import { ResendCommand } from "../../application/resend.command"
11
+ import { ConfirmCommand } from "../../application/confirm.command"
12
+ import { ForgotCommand } from "../../application/forgot.command"
13
+ import { ResetCommand } from "../../application/reset.command"
14
+ import { GetJwksQuery } from "../../application/get-jwks.query"
15
+ import { GoogleBeginCommand } from "../../application/google-begin.command"
16
+ import { GoogleCompleteCommand } from "../../application/google-complete.command"
17
+ import { LoginCommand } from "../../application/login.command"
18
+ import { LogoutCommand } from "../../application/logout.command"
19
+ import { RefreshCommand } from "../../application/refresh.command"
20
+ import { SessionCommand } from "../../application/session.command"
21
+ import { ForgotRequest } from "./dto/forgot.request"
22
+ import { GoogleStartRequest } from "./dto/google-start.request"
23
+ import { LoginRequest } from "./dto/login.request"
24
+ import { ResendRequest } from "./dto/resend.request"
25
+ import { ResetRequest } from "./dto/reset.request"
26
+ import { SignupRequest } from "./dto/signup.request"
27
+ import { TokenRequest } from "./dto/token.request"
28
+
29
+ @Controller("auth")
30
+ /** The cookie doors of sign-in: email and password, the email doors (sign up, confirm, resend, forgot, reset), Google, refresh, session, sign-out and the public keys. */
31
+ export class AuthController {
32
+ constructor(
33
+ @InjectCommandBus() private readonly commandBus: CommandBus,
34
+ @InjectQueryBus() private readonly queryBus: QueryBus,
35
+ ) {}
36
+
37
+ /** Signs in with email and password; the refresh token is set as an httpOnly cookie. */
38
+ @Post("login")
39
+ @HttpCode(200)
40
+ @Public({ reason: PublicReason.AuthHandshake })
41
+ @RateLimit(RateTier.Strict)
42
+ login(
43
+ @Body() _body: LoginRequest,
44
+ @Req() req: Request,
45
+ @Res({ passthrough: true }) res: Response,
46
+ ): Promise<AccessGrant> {
47
+ return this.commandBus.execute(new LoginCommand({ request: { exchange: { req, res } } }))
48
+ }
49
+
50
+ /** Rotates the refresh cookie and answers a new access token; a bad cookie is cleared. */
51
+ @Post("refresh")
52
+ @HttpCode(200)
53
+ @Public({ reason: PublicReason.AuthHandshake })
54
+ @RateLimit(RateTier.Strict)
55
+ refresh(@Req() req: Request, @Res({ passthrough: true }) res: Response): Promise<AccessGrant> {
56
+ return this.commandBus.execute(new RefreshCommand({ request: { exchange: { req, res } } }))
57
+ }
58
+
59
+ /** Answers a fresh access token for the cookie session WITHOUT rotating the cookie, so parallel requests of one browser never race. */
60
+ @Get("session")
61
+ @Public({ reason: PublicReason.AuthHandshake })
62
+ @RateLimit(RateTier.Strict)
63
+ session(@Req() req: Request, @Res({ passthrough: true }) res: Response): Promise<AccessGrant> {
64
+ return this.commandBus.execute(new SessionCommand({ request: { exchange: { req, res } } }))
65
+ }
66
+
67
+ /** Ends the session of the refresh cookie and clears it. */
68
+ @Post("logout")
69
+ @HttpCode(204)
70
+ @Public({ reason: PublicReason.AuthHandshake })
71
+ @RateLimit(RateTier.Strict)
72
+ logout(@Req() req: Request, @Res({ passthrough: true }) res: Response): Promise<void> {
73
+ return this.commandBus.execute(new LogoutCommand({ request: { exchange: { req, res } } }))
74
+ }
75
+
76
+ /** Sends the browser to Google (authorization code with state and PKCE); `returnTo` is an absolute address on the web origin or a path. */
77
+ @Get("google")
78
+ @Public({ reason: PublicReason.AuthHandshake })
79
+ @RateLimit(RateTier.Strict)
80
+ google(@Query() _query: GoogleStartRequest, @Req() req: Request, @Res() res: Response): Promise<void> {
81
+ return this.commandBus.execute(new GoogleBeginCommand({ request: { exchange: { req, res } } }))
82
+ }
83
+
84
+ /** Google sends the browser back here; the session cookie is set and the browser lands on the web app (or its sign-in page on failure). */
85
+ @Get("google/callback")
86
+ @Redirect()
87
+ @Public({ reason: PublicReason.AuthHandshake })
88
+ @RateLimit(RateTier.Strict)
89
+ googleCallback(@Req() req: Request, @Res({ passthrough: true }) res: Response): Promise<GoogleRedirect> {
90
+ return this.commandBus.execute(new GoogleCompleteCommand({ request: { exchange: { req, res } } }))
91
+ }
92
+
93
+ /** Creates an unconfirmed account and mails the confirmation link; always answers 204. */
94
+ @Post("signup")
95
+ @HttpCode(204)
96
+ @Public({ reason: PublicReason.AuthHandshake })
97
+ @RateLimit(RateTier.Strict)
98
+ signup(@Body() body: SignupRequest): Promise<void> {
99
+ return this.commandBus.execute(new SignupCommand({ request: { email: body.email, password: body.password, language: body.language } }))
100
+ }
101
+
102
+ /** Mails a new confirmation link; always answers 204. */
103
+ @Post("resend")
104
+ @HttpCode(204)
105
+ @Public({ reason: PublicReason.AuthHandshake })
106
+ @RateLimit(RateTier.Strict)
107
+ resend(@Body() body: ResendRequest): Promise<void> {
108
+ return this.commandBus.execute(new ResendCommand({ request: { email: body.email, language: body.language } }))
109
+ }
110
+
111
+ /** Spends a confirmation link; a spent, expired or unknown link is refused with IDENTITY_RESET_TOKEN_INVALID. */
112
+ @Post("confirm")
113
+ @HttpCode(204)
114
+ @Public({ reason: PublicReason.AuthHandshake })
115
+ @RateLimit(RateTier.Strict)
116
+ confirm(@Body() body: TokenRequest): Promise<void> {
117
+ return this.commandBus.execute(new ConfirmCommand({ request: { token: body.token } }))
118
+ }
119
+
120
+ /** Mails a password reset link when the address has an account; always answers 204. */
121
+ @Post("forgot")
122
+ @HttpCode(204)
123
+ @Public({ reason: PublicReason.AuthHandshake })
124
+ @RateLimit(RateTier.Strict)
125
+ forgot(@Body() body: ForgotRequest): Promise<void> {
126
+ return this.commandBus.execute(new ForgotCommand({ request: { email: body.email, language: body.language } }))
127
+ }
128
+
129
+ /** Spends a reset link and sets the new password; every session of the person ends. */
130
+ @Post("reset")
131
+ @HttpCode(204)
132
+ @Public({ reason: PublicReason.AuthHandshake })
133
+ @RateLimit(RateTier.Strict)
134
+ reset(@Body() body: ResetRequest): Promise<void> {
135
+ return this.commandBus.execute(new ResetCommand({ request: { token: body.token, password: body.password } }))
136
+ }
137
+
138
+ /** The public keys access tokens verify against. */
139
+ @Get("jwks")
140
+ @Public({ reason: PublicReason.AuthHandshake })
141
+ @RateLimit(RateTier.Strict)
142
+ jwks(): Promise<JwksDocument> {
143
+ return this.queryBus.execute(new GetJwksQuery({ request: {} }))
144
+ }
145
+ }
@@ -0,0 +1,16 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { IsEmail, IsIn, IsOptional, MaxLength } from "class-validator"
3
+ import type { MailLanguage } from "@modules/integrations/mail"
4
+
5
+ /** The posted address of a password reset request. */
6
+ export class ForgotRequest {
7
+ /** The sign-in address. */
8
+ @IsEmail()
9
+ @MaxLength(254)
10
+ email!: string
11
+
12
+ /** The language of the mail; the app's default when left out. */
13
+ @IsOptional()
14
+ @IsIn(["vi", "en"])
15
+ language?: MailLanguage
16
+ }
@@ -0,0 +1,17 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { IsOptional, IsString, MaxLength } from "class-validator"
3
+
4
+ /** The query that starts a Google sign-in. */
5
+ export class GoogleStartRequest {
6
+ /** An absolute address on the web origin, or a path, to land on afterwards. */
7
+ @IsOptional()
8
+ @IsString()
9
+ @MaxLength(1024)
10
+ returnTo?: string
11
+
12
+ /** A path to land on afterwards (the older spelling of returnTo). */
13
+ @IsOptional()
14
+ @IsString()
15
+ @MaxLength(512)
16
+ next?: string
17
+ }
@@ -0,0 +1,16 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { IsEmail, IsString, MaxLength, MinLength } from "class-validator"
3
+
4
+ /** The posted sign-in pair. */
5
+ export class LoginRequest {
6
+ /** The sign-in address. */
7
+ @IsEmail()
8
+ @MaxLength(254)
9
+ email!: string
10
+
11
+ /** The password. */
12
+ @IsString()
13
+ @MinLength(1)
14
+ @MaxLength(128)
15
+ password!: string
16
+ }
@@ -0,0 +1,16 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { IsEmail, IsIn, IsOptional, MaxLength } from "class-validator"
3
+ import type { MailLanguage } from "@modules/integrations/mail"
4
+
5
+ /** The posted address of a resend. */
6
+ export class ResendRequest {
7
+ /** The sign-in address. */
8
+ @IsEmail()
9
+ @MaxLength(254)
10
+ email!: string
11
+
12
+ /** The language of the mail; the app's default when left out. */
13
+ @IsOptional()
14
+ @IsIn(["vi", "en"])
15
+ language?: MailLanguage
16
+ }