@cosmicdrift/kumiko-samples 0.362.0 → 0.363.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 (252) hide show
  1. package/package.json +1 -1
  2. package/packages/bundled-features/package.json +17 -6
  3. package/packages/bundled-features/src/admin-shell/changes.json +6 -0
  4. package/packages/bundled-features/src/admin-shell/feature.ts +8 -1
  5. package/packages/bundled-features/src/agent-tools/agent-manifest.ts +3 -4
  6. package/packages/bundled-features/src/agent-tools/filter-ops.ts +1 -3
  7. package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -3
  8. package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +2 -1
  9. package/packages/bundled-features/src/auth-email-password/changes.json +14 -0
  10. package/packages/bundled-features/src/auth-email-password/email-templates.ts +12 -138
  11. package/packages/bundled-features/src/auth-email-password/feature.ts +30 -9
  12. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -5
  13. package/packages/bundled-features/src/auth-email-password/handlers/request-account-unlock.write.ts +2 -5
  14. package/packages/bundled-features/src/auth-email-password/handlers/request-email-verification.write.ts +2 -5
  15. package/packages/bundled-features/src/auth-email-password/handlers/request-password-reset.write.ts +2 -5
  16. package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +3 -4
  17. package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +3 -12
  18. package/packages/bundled-features/src/auth-email-password/index.ts +2 -17
  19. package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +30 -22
  20. package/packages/bundled-features/src/auth-email-password/mail-notifications.ts +158 -0
  21. package/packages/bundled-features/src/auth-email-password/signup-activation.ts +2 -3
  22. package/packages/bundled-features/src/auth-foundation/types.ts +8 -18
  23. package/packages/bundled-features/src/auth-mfa/feature.ts +8 -10
  24. package/packages/bundled-features/src/auth-mfa/hmac-token-codec.ts +4 -4
  25. package/packages/bundled-features/src/auth-mfa/mfa-challenge-token.ts +3 -3
  26. package/packages/bundled-features/src/auth-mfa/mfa-preauth-setup-token.ts +3 -3
  27. package/packages/bundled-features/src/auth-mfa/mfa-setup-token.ts +8 -8
  28. package/packages/bundled-features/src/billing-foundation/changes.json +7 -0
  29. package/packages/bundled-features/src/billing-foundation/constants.ts +5 -4
  30. package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +38 -44
  31. package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +3 -3
  32. package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +115 -0
  33. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +2 -0
  34. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +30 -30
  35. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-notify.ts +2 -2
  36. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-pages.ts +1 -4
  37. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +2 -2
  38. package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +2 -2
  39. package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +4 -9
  40. package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +1 -1
  41. package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +2 -2
  42. package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +1 -1
  43. package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -6
  44. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +2 -1
  45. package/packages/bundled-features/src/cap-overview/web/cap-usage-cell.tsx +6 -4
  46. package/packages/bundled-features/src/channel-email/changes.json +19 -0
  47. package/packages/bundled-features/src/channel-email/email-channel.ts +122 -29
  48. package/packages/bundled-features/src/channel-email/feature.ts +6 -3
  49. package/packages/bundled-features/src/channel-email/index.ts +7 -1
  50. package/packages/bundled-features/src/channel-email/pii-guard.ts +13 -0
  51. package/packages/bundled-features/src/channel-email/smtp-transport.ts +15 -1
  52. package/packages/bundled-features/src/channel-email/types.ts +23 -0
  53. package/packages/bundled-features/src/channel-in-app/handlers/mark-read.write.ts +2 -2
  54. package/packages/bundled-features/src/config/handlers/set.write.ts +1 -5
  55. package/packages/bundled-features/src/custom-fields/db/queries/user-data-rights.ts +2 -2
  56. package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -11
  57. package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +7 -6
  58. package/packages/bundled-features/src/data-retention/changes.json +6 -0
  59. package/packages/bundled-features/src/data-retention/index.ts +1 -0
  60. package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +8 -6
  61. package/packages/bundled-features/src/delivery/attachment-ref.ts +84 -0
  62. package/packages/bundled-features/src/delivery/changes.json +48 -0
  63. package/packages/bundled-features/src/delivery/channel-context.ts +129 -4
  64. package/packages/bundled-features/src/delivery/constants.ts +2 -0
  65. package/packages/bundled-features/src/delivery/delivery-service.ts +104 -26
  66. package/packages/bundled-features/src/delivery/feature.ts +1 -1
  67. package/packages/bundled-features/src/delivery/index.ts +17 -2
  68. package/packages/bundled-features/src/delivery/jobs.ts +48 -5
  69. package/packages/bundled-features/src/delivery/notify-factory.ts +14 -9
  70. package/packages/bundled-features/src/delivery/testing.ts +9 -1
  71. package/packages/bundled-features/src/delivery/types.ts +28 -18
  72. package/packages/bundled-features/src/delivery/unsubscribe.ts +5 -2
  73. package/packages/bundled-features/src/derivatives-sharp/render.ts +1 -22
  74. package/packages/bundled-features/src/document-ingest-foundation/pages.ts +6 -3
  75. package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +4 -4
  76. package/packages/bundled-features/src/file-foundation/index.ts +7 -0
  77. package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +86 -0
  78. package/packages/bundled-features/src/folders/web/folder-manager.tsx +5 -2
  79. package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +11 -11
  80. package/packages/bundled-features/src/form-draft/db/queries/draft-count.ts +2 -2
  81. package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +2 -2
  82. package/packages/bundled-features/src/form-draft/release-file-refs.ts +3 -2
  83. package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
  84. package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +6 -6
  85. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +1 -1
  86. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +1 -1
  87. package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +2 -2
  88. package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +7 -7
  89. package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +2 -6
  90. package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +5 -13
  91. package/packages/bundled-features/src/inbound-mail-foundation/types.ts +4 -6
  92. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +40 -25
  93. package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +4 -3
  94. package/packages/bundled-features/src/jobs/handlers/catalog.query.ts +1 -1
  95. package/packages/bundled-features/src/ledger/recurring.ts +1 -3
  96. package/packages/bundled-features/src/managed-pages/branding.ts +33 -9
  97. package/packages/bundled-features/src/managed-pages/changes.json +6 -0
  98. package/packages/bundled-features/src/managed-pages/feature.ts +1 -0
  99. package/packages/bundled-features/src/managed-pages/i18n.ts +1 -0
  100. package/packages/bundled-features/src/managed-pages/screens/branding-screen.ts +8 -0
  101. package/packages/bundled-features/src/page-render/branding.ts +3 -0
  102. package/packages/bundled-features/src/page-render/markdown.ts +1 -1
  103. package/packages/bundled-features/src/personal-access-tokens/feature.ts +2 -4
  104. package/packages/bundled-features/src/personal-access-tokens/handlers/list.query.ts +2 -1
  105. package/packages/bundled-features/src/personal-access-tokens/index.ts +1 -1
  106. package/packages/bundled-features/src/personal-access-tokens/revoke-for-user.ts +2 -1
  107. package/packages/bundled-features/src/renderer-foundation/README.md +11 -10
  108. package/packages/bundled-features/src/renderer-foundation/api.ts +14 -14
  109. package/packages/bundled-features/src/renderer-foundation/changes.json +39 -0
  110. package/packages/bundled-features/src/renderer-foundation/constants.ts +6 -8
  111. package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
  112. package/packages/bundled-features/src/renderer-foundation/index.ts +13 -2
  113. package/packages/bundled-features/src/renderer-foundation/types.ts +132 -35
  114. package/packages/bundled-features/src/renderer-pdf/feature.ts +7 -1
  115. package/packages/bundled-features/src/sessions/db/queries/cleanup.ts +2 -2
  116. package/packages/bundled-features/src/sessions/feature.ts +2 -4
  117. package/packages/bundled-features/src/sessions/handlers/list.query.ts +2 -1
  118. package/packages/bundled-features/src/shared/attachment-file-name.ts +39 -0
  119. package/packages/bundled-features/src/shared/index.ts +6 -0
  120. package/packages/bundled-features/src/shared/is-tenant-db.ts +1 -1
  121. package/packages/bundled-features/src/shared/mail-template-body.ts +25 -0
  122. package/packages/bundled-features/src/shared/sharp-input-limits.ts +26 -0
  123. package/packages/bundled-features/src/shared/signup-handover.ts +4 -4
  124. package/packages/bundled-features/src/tags/handlers/assign-tag.write.ts +5 -1
  125. package/packages/bundled-features/src/tags/web/tag-chip.tsx +2 -1
  126. package/packages/bundled-features/src/template-resolver/README.md +63 -7
  127. package/packages/bundled-features/src/template-resolver/api.ts +83 -8
  128. package/packages/bundled-features/src/template-resolver/changes.json +118 -0
  129. package/packages/bundled-features/src/template-resolver/compile-document-body.ts +214 -0
  130. package/packages/bundled-features/src/template-resolver/constants.ts +39 -11
  131. package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +335 -0
  132. package/packages/bundled-features/src/template-resolver/document-schema.ts +323 -0
  133. package/packages/bundled-features/src/template-resolver/feature.ts +56 -6
  134. package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +188 -0
  135. package/packages/bundled-features/src/template-resolver/handlers/collection-set.write.ts +30 -2
  136. package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +142 -0
  137. package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +72 -0
  138. package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +83 -0
  139. package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +3 -3
  140. package/packages/bundled-features/src/template-resolver/handlers/import-bundle-system.write.ts +42 -0
  141. package/packages/bundled-features/src/template-resolver/handlers/import-bundle.write.ts +22 -0
  142. package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +108 -0
  143. package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +5 -7
  144. package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +250 -0
  145. package/packages/bundled-features/src/template-resolver/handlers/preview.query.ts +25 -0
  146. package/packages/bundled-features/src/template-resolver/handlers/reset-to-system.write.ts +58 -0
  147. package/packages/bundled-features/src/template-resolver/handlers/resource-link-pdf.write.ts +34 -0
  148. package/packages/bundled-features/src/template-resolver/handlers/resource-remove.write.ts +50 -0
  149. package/packages/bundled-features/src/template-resolver/handlers/resource-shared.ts +24 -0
  150. package/packages/bundled-features/src/template-resolver/handlers/resource-upload.write.ts +135 -0
  151. package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +106 -0
  152. package/packages/bundled-features/src/template-resolver/handlers/set.write.ts +32 -3
  153. package/packages/bundled-features/src/template-resolver/handlers/shared.ts +7 -2
  154. package/packages/bundled-features/src/template-resolver/handlers/template-gates.ts +184 -0
  155. package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +183 -0
  156. package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +55 -0
  157. package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +57 -10
  158. package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +19 -71
  159. package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +15 -76
  160. package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +17 -0
  161. package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +116 -0
  162. package/packages/bundled-features/src/template-resolver/index.ts +100 -0
  163. package/packages/bundled-features/src/template-resolver/layout-regions.ts +206 -0
  164. package/packages/bundled-features/src/template-resolver/linked-resources.ts +102 -0
  165. package/packages/bundled-features/src/template-resolver/preview-display-html.ts +36 -0
  166. package/packages/bundled-features/src/template-resolver/print-document.ts +58 -0
  167. package/packages/bundled-features/src/template-resolver/qualified-names.ts +30 -0
  168. package/packages/bundled-features/src/template-resolver/record-mapping.ts +26 -0
  169. package/packages/bundled-features/src/template-resolver/resource-image.ts +47 -0
  170. package/packages/bundled-features/src/template-resolver/seeding.ts +147 -17
  171. package/packages/bundled-features/src/template-resolver/store-template-resource.ts +416 -0
  172. package/packages/bundled-features/src/template-resolver/table.ts +14 -0
  173. package/packages/bundled-features/src/template-resolver/template-bundle.ts +79 -0
  174. package/packages/bundled-features/src/template-resolver/template-diff.ts +78 -0
  175. package/packages/bundled-features/src/template-resolver/template-meta.ts +129 -0
  176. package/packages/bundled-features/src/template-resolver/template-preview.ts +573 -0
  177. package/packages/bundled-features/src/template-resolver/template-resources.ts +74 -0
  178. package/packages/bundled-features/src/template-resolver/template-usage.ts +48 -0
  179. package/packages/bundled-features/src/template-resolver/template-validation.ts +229 -0
  180. package/packages/bundled-features/src/template-resolver/testing.ts +5 -5
  181. package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +9 -6
  182. package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +162 -0
  183. package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +28 -0
  184. package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +850 -0
  185. package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +61 -0
  186. package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +26 -0
  187. package/packages/bundled-features/src/template-resolver/web/i18n.ts +100 -0
  188. package/packages/bundled-features/src/template-resolver/web/index.ts +19 -0
  189. package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor-lazy.tsx +36 -0
  190. package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +169 -0
  191. package/packages/bundled-features/src/template-resolver/web/placeholder-text.ts +37 -0
  192. package/packages/bundled-features/src/template-resolver/web/use-stable-by-key.ts +14 -0
  193. package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +3 -2
  194. package/packages/bundled-features/src/tenant/seeding.ts +41 -7
  195. package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
  196. package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +2 -1
  197. package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +4 -4
  198. package/packages/bundled-features/src/user/handlers/detail.query.ts +3 -5
  199. package/packages/bundled-features/src/user/roles-input-schema.ts +2 -2
  200. package/packages/bundled-features/src/user-data-rights/changes.json +7 -0
  201. package/packages/bundled-features/src/user-data-rights/db/queries/forget-cleanup.ts +2 -2
  202. package/packages/bundled-features/src/user-data-rights/email-templates.ts +5 -149
  203. package/packages/bundled-features/src/user-data-rights/feature.ts +72 -35
  204. package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +2 -1
  205. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +5 -11
  206. package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +134 -53
  207. package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +2 -2
  208. package/packages/bundled-features/src/user-data-rights/mail-notifications.ts +157 -0
  209. package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +1 -1
  210. package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +2 -1
  211. package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +2 -1
  212. package/packages/bundled-features/src/waitlist/changes.json +7 -0
  213. package/packages/bundled-features/src/waitlist/feature.ts +17 -0
  214. package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +4 -5
  215. package/packages/bundled-features/src/waitlist/mail.ts +69 -18
  216. package/packages/bundled-features/src/workflow-runner/event-trigger.ts +1 -2
  217. package/samples/apps/ui-walkthrough/package.json +2 -0
  218. package/samples/apps/ui-walkthrough/src/app/auth-constants.ts +9 -0
  219. package/samples/apps/ui-walkthrough/src/app/client.tsx +6 -0
  220. package/samples/apps/ui-walkthrough/src/app/seed-templates.ts +154 -0
  221. package/samples/apps/ui-walkthrough/src/app/server.ts +5 -1
  222. package/samples/apps/ui-walkthrough/src/app/shell.tsx +12 -1
  223. package/samples/apps/ui-walkthrough/src/app/template-seed-data.ts +354 -0
  224. package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +68 -0
  225. package/samples/apps/ui-walkthrough/src/features/billing/i18n.ts +18 -0
  226. package/samples/apps/ui-walkthrough/src/features/billing/index.ts +2 -0
  227. package/samples/apps/ui-walkthrough/src/features/billing/web/index.ts +7 -0
  228. package/samples/apps/ui-walkthrough/src/run-config.ts +37 -1
  229. package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +0 -2
  230. package/samples/apps/use-all-bundled/feature-manifest.json +115 -20
  231. package/samples/apps/use-all-bundled/kumiko/migrations/0045_template-resource-meta.rebuild.json +4 -0
  232. package/samples/apps/use-all-bundled/kumiko/migrations/0046_pdf-rendered.rebuild.json +4 -0
  233. package/samples/apps/use-all-bundled/package.json +2 -0
  234. package/samples/apps/use-all-bundled/playwright.config.ts +38 -3
  235. package/samples/apps/use-all-bundled/schema/generate.ts +13 -3
  236. package/samples/apps/use-all-bundled/schema/schema.generated.ts +3 -0
  237. package/samples/apps/use-all-bundled/scripts/check-coverage.ts +3 -0
  238. package/samples/apps/use-all-bundled/src/app/auth-constants.ts +3 -0
  239. package/samples/apps/use-all-bundled/src/app/client.tsx +4 -0
  240. package/samples/apps/use-all-bundled/src/app/seed-documents.ts +128 -0
  241. package/samples/apps/use-all-bundled/src/app/seed.ts +4 -0
  242. package/samples/apps/use-all-bundled/src/run-config.ts +11 -11
  243. package/samples/recipes/delivery-notifications/README.md +26 -25
  244. package/samples/recipes/delivery-notifications/package.json +2 -1
  245. package/samples/recipes/delivery-notifications/src/feature.ts +64 -58
  246. package/samples/recipes/seed-migration/README.md +1 -1
  247. package/packages/bundled-features/src/renderer-simple/changes.json +0 -27
  248. package/packages/bundled-features/src/renderer-simple/feature.ts +0 -66
  249. package/packages/bundled-features/src/renderer-simple/index.ts +0 -8
  250. package/packages/bundled-features/src/renderer-simple/mail-blocks.ts +0 -138
  251. package/packages/bundled-features/src/renderer-simple/resolve-variables.ts +0 -57
  252. package/packages/bundled-features/src/renderer-simple/simple-renderer.ts +0 -304
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-samples",
3
- "version": "0.362.0",
3
+ "version": "0.363.0",
4
4
  "description": "Source trees of the Kumiko sample recipes, sample apps and bundled features in repo layout, for tooling such as few-shot corpus builds.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-bundled-features",
3
- "version": "0.362.0",
3
+ "version": "0.363.0",
4
4
  "description": "Built-in features — tenant, user, auth, delivery. The stuff you'd rewrite anyway, already typed.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -115,7 +115,6 @@
115
115
  "./channel-discord": "./src/channel-discord/index.ts",
116
116
  "./channel-teams": "./src/channel-teams/index.ts",
117
117
  "./channel-telegram": "./src/channel-telegram/index.ts",
118
- "./renderer-simple": "./src/renderer-simple/index.ts",
119
118
  "./renderer-pdf": "./src/renderer-pdf/index.ts",
120
119
  "./files-provider-s3": "./src/files-provider-s3/index.ts",
121
120
  "./rate-limiting": "./src/rate-limiting/index.ts",
@@ -131,6 +130,7 @@
131
130
  "./feature-toggles": "./src/feature-toggles/index.ts",
132
131
  "./feature-toggles/web": "./src/feature-toggles/web/index.ts",
133
132
  "./template-resolver": "./src/template-resolver/index.ts",
133
+ "./template-resolver/document-schema": "./src/template-resolver/document-schema.ts",
134
134
  "./template-resolver/seeding": "./src/template-resolver/seeding.ts",
135
135
  "./template-resolver/testing": "./src/template-resolver/testing.ts",
136
136
  "./template-resolver/web": "./src/template-resolver/web/index.ts",
@@ -156,9 +156,19 @@
156
156
  "@cosmicdrift/kumiko-types": "workspace:*",
157
157
  "@mollie/api-client": "^4.5.0",
158
158
  "@node-rs/argon2": "^2.0.2",
159
+ "@tiptap/core": "3.31.3",
160
+ "@tiptap/extension-link": "3.31.3",
161
+ "@tiptap/extension-list": "3.31.3",
162
+ "@tiptap/extension-table": "3.31.3",
163
+ "@tiptap/html": "3.31.3",
164
+ "@tiptap/extensions": "3.31.3",
165
+ "@tiptap/pm": "3.31.3",
166
+ "@tiptap/react": "3.31.3",
167
+ "@tiptap/starter-kit": "3.31.3",
159
168
  "@types/mailparser": "^3.4.6",
160
169
  "@types/qrcode": "^1.5.5",
161
170
  "clsx": "^2.1.1",
171
+ "happy-dom": "^20.11.2",
162
172
  "hono": "^4.13.1",
163
173
  "imapflow": "^1.7.8",
164
174
  "ioredis": "^5.10.1",
@@ -553,10 +563,6 @@
553
563
  "types": "./dist/channel-telegram/index.d.ts",
554
564
  "default": "./dist/channel-telegram/index.js"
555
565
  },
556
- "./renderer-simple": {
557
- "types": "./dist/renderer-simple/index.d.ts",
558
- "default": "./dist/renderer-simple/index.js"
559
- },
560
566
  "./renderer-pdf": {
561
567
  "types": "./dist/renderer-pdf/index.d.ts",
562
568
  "default": "./dist/renderer-pdf/index.js"
@@ -617,6 +623,10 @@
617
623
  "types": "./dist/template-resolver/index.d.ts",
618
624
  "default": "./dist/template-resolver/index.js"
619
625
  },
626
+ "./template-resolver/document-schema": {
627
+ "types": "./dist/template-resolver/document-schema.d.ts",
628
+ "default": "./dist/template-resolver/document-schema.js"
629
+ },
620
630
  "./template-resolver/seeding": {
621
631
  "types": "./dist/template-resolver/seeding.d.ts",
622
632
  "default": "./dist/template-resolver/seeding.js"
@@ -689,6 +699,7 @@
689
699
  "@testing-library/user-event": "^14.6.1",
690
700
  "@cosmicdrift/kumiko-locale-de": "workspace:*",
691
701
  "@cosmicdrift/kumiko-locale-es": "workspace:*",
702
+ "@cosmicdrift/kumiko-renderer-mail-html": "workspace:*",
692
703
  "jsqr": "^1.4.0",
693
704
  "unpdf": "^1.8.1"
694
705
  }
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.363.0",
4
+ "type": "improvement",
5
+ "title": "includeDocumentDesigner lists the template navs in the admin workspaces",
6
+ "detail": "With includeDocumentDesigner, the tenant workspace shows document-designer:nav:templates and the platform workspace document-designer:nav:platform-templates. The option requires the document-designer feature and defaults to false."
7
+ },
2
8
  {
3
9
  "version": "0.330.0",
4
10
  "type": "breaking",
@@ -36,6 +36,9 @@ export type CreateAdminShellOptions = {
36
36
  * Requires cap-overview mounted. Default false — apps that don't mount cap-overview
37
37
  * must not get a nav entry pointing at a screen that doesn't exist. */
38
38
  readonly includeCapOverview?: boolean;
39
+ /** Tenant nav → document-designer:nav:templates, platform nav → document-designer:nav:platform-templates.
40
+ * Requires document-designer mounted (a separate package, so no import here). Default false. */
41
+ readonly includeDocumentDesigner?: boolean;
39
42
  /** When false, only overview screens + nav are registered — app owns r.workspace(). Default true. */
40
43
  readonly registerWorkspaces?: boolean;
41
44
  /** Metric list the overview panels are built from — pass the SAME list to
@@ -49,12 +52,13 @@ export function createAdminShellFeature(options: CreateAdminShellOptions = {}):
49
52
  const platformWsId = options.workspaceIds?.platform ?? DEFAULT_PLATFORM_WORKSPACE_ID;
50
53
  const includeTierAdmin = options.includeTierAdmin ?? true;
51
54
  const includeCapOverview = options.includeCapOverview ?? false;
55
+ const includeDocumentDesigner = options.includeDocumentDesigner ?? false;
52
56
  const registerWorkspaces = options.registerWorkspaces ?? true;
53
57
  const metrics = options.metrics ?? DEFAULT_METRICS;
54
58
 
55
59
  return defineFeature(ADMIN_SHELL_FEATURE, (r) => {
56
60
  r.describe(
57
- "Registers tenant-admin and platform-admin workspaces with provider nav into owner-feature screens (`tenant:screen:members`, `audit:screen:audit-log`, `tenant:screen:tenant-list`, `jobs:screen:job-runs`, optional `tier-engine:screen:tier-admin`, optional `cap-overview:screen:my-caps`/`cap-overview:screen:tenant-cap-list`) and the two overview dashboards, built from the metrics list (`metrics` option, default DEFAULT_METRICS). Mount after user, tenant, audit, jobs, metrics and metrics-system; pass `workspaceIds` to match app URL conventions (e.g. Studio `d`/`s`, PublicStatus `admin`/`sysadmin`). Client: `adminShellClient()`, `tenantClient()`, `auditClient()`, `jobsClient()`, optional `tierEngineClient()`.",
61
+ "Registers tenant-admin and platform-admin workspaces with provider nav into owner-feature screens (`tenant:screen:members`, `audit:screen:audit-log`, `tenant:screen:tenant-list`, `jobs:screen:job-runs`, optional `tier-engine:screen:tier-admin`, optional `cap-overview:screen:my-caps`/`cap-overview:screen:tenant-cap-list`, optional `document-designer` template navs) and the two overview dashboards, built from the metrics list (`metrics` option, default DEFAULT_METRICS). Mount after user, tenant, audit, jobs, metrics and metrics-system; pass `workspaceIds` to match app URL conventions (e.g. Studio `d`/`s`, PublicStatus `admin`/`sysadmin`). Client: `adminShellClient()`, `tenantClient()`, `auditClient()`, `jobsClient()`, optional `tierEngineClient()`.",
58
62
  );
59
63
  r.uiHints({
60
64
  displayLabel: "Admin Shell",
@@ -69,6 +73,7 @@ export function createAdminShellFeature(options: CreateAdminShellOptions = {}):
69
73
  r.requires("metrics-system");
70
74
  if (includeTierAdmin) r.requires("tier-engine");
71
75
  if (includeCapOverview) r.requires("cap-overview");
76
+ if (includeDocumentDesigner) r.requires("document-designer");
72
77
 
73
78
  r.translations({ keys: ADMIN_SHELL_I18N });
74
79
 
@@ -77,12 +82,14 @@ export function createAdminShellFeature(options: CreateAdminShellOptions = {}):
77
82
  "tenant:nav:members",
78
83
  "audit:nav:audit-log",
79
84
  ...(includeCapOverview ? (["admin-shell:nav:my-caps"] as const) : []),
85
+ ...(includeDocumentDesigner ? (["document-designer:nav:templates"] as const) : []),
80
86
  ];
81
87
  const platformNav: string[] = [
82
88
  "admin-shell:nav:platform-overview",
83
89
  "admin-shell:nav:tenants",
84
90
  ...(includeCapOverview ? (["admin-shell:nav:tenant-caps"] as const) : []),
85
91
  "jobs:nav:job-runs",
92
+ ...(includeDocumentDesigner ? (["document-designer:nav:platform-templates"] as const) : []),
86
93
  ...(includeTierAdmin ? (["admin-shell:nav:tier-admin"] as const) : []),
87
94
  ];
88
95
 
@@ -18,6 +18,7 @@ import {
18
18
  SYSTEM_ONLY_JSON_SCHEMA_KEY,
19
19
  } from "@cosmicdrift/kumiko-framework/engine";
20
20
  import { translationValueOtherText } from "@cosmicdrift/kumiko-framework/ui-types";
21
+ import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
21
22
  import * as z from "zod";
22
23
  import { recordManifestDenyQns } from "./manifest-deny-qns.js";
23
24
  import type {
@@ -199,7 +200,7 @@ export function systemOnlyPropertyNames(jsonSchema: Record<string, unknown>): re
199
200
  if (typeof properties !== "object" || properties === null) return [];
200
201
  return Object.entries(properties)
201
202
  .filter(
202
- ([, property]) => (property as Record<string, unknown>)[SYSTEM_ONLY_JSON_SCHEMA_KEY] === true,
203
+ ([, property]) => isPlainObject(property) && property[SYSTEM_ONLY_JSON_SCHEMA_KEY] === true,
203
204
  )
204
205
  .map(([name]) => name);
205
206
  }
@@ -243,9 +244,7 @@ function buildHandlerEntry(
243
244
  // A handler whose schema can't be expressed as JSON Schema (transforms,
244
245
  // z.instanceof, ...) is unusable for an agent tool call — leave it out
245
246
  // rather than ship a broken tool.
246
- inputSchema = withoutSystemOnlyProperties(
247
- z.toJSONSchema(def.schema, { io: "input" }) as Record<string, unknown>,
248
- );
247
+ inputSchema = withoutSystemOnlyProperties(z.toJSONSchema(def.schema, { io: "input" }));
249
248
  } catch {
250
249
  return undefined;
251
250
  }
@@ -1,5 +1,3 @@
1
1
  export const FILTER_OPS = ["eq", "ne", "lt", "gt", "lte", "gte", "in"] as const;
2
2
 
3
- export function isRecord(value: unknown): value is Record<string, unknown> {
4
- return typeof value === "object" && value !== null && !Array.isArray(value);
5
- }
3
+ export { isPlainObject as isRecord } from "@cosmicdrift/kumiko-types/type-guards";
@@ -137,9 +137,7 @@ function addToolsForListHandler(
137
137
  addTool(sink, tool, { kind: "search", entityName, qn });
138
138
  }
139
139
 
140
- for (const [fieldName, field] of Object.entries(
141
- entity.fields as Record<string, FieldDefinition>,
142
- )) {
140
+ for (const [fieldName, field] of Object.entries(entity.fields)) {
143
141
  if (!isFilterable(field) || !isReadableWithoutRowCondition(field, roles)) continue;
144
142
  const fieldSchema = jsonSchemaTypeForField(field);
145
143
  if (!fieldSchema) continue;
@@ -1,5 +1,6 @@
1
1
  import type { SessionUser, WriteResult } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import { runAsDirectCallEntry } from "@cosmicdrift/kumiko-framework/internal/request-context";
3
+ import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
3
4
  import { FILTER_OPS, isRecord } from "./filter-ops.js";
4
5
  import type { ToolDispatchDescriptor } from "./types.js";
5
6
 
@@ -48,7 +49,7 @@ type PayloadResult =
48
49
  | { readonly ok: false; readonly error: string };
49
50
 
50
51
  function isFilterOp(value: unknown): value is FilterOp {
51
- return typeof value === "string" && (FILTER_OPS as readonly string[]).includes(value);
52
+ return isOneOf(FILTER_OPS, value);
52
53
  }
53
54
 
54
55
  function buildSearchPayload(
@@ -1,4 +1,18 @@
1
1
  [
2
+ {
3
+ "version": "0.363.0",
4
+ "type": "breaking",
5
+ "title": "AuthMailContent is removed",
6
+ "detail": "The sections-based content type only fed the retired renderer-simple. Auth mails are template notifications with variables, so nothing builds or consumes AuthMailContent anymore.",
7
+ "migration": "Drop AuthMailContent imports; the auth mail text lives in the seeded templates and is overridden per tenant in the template editor."
8
+ },
9
+ {
10
+ "version": "0.363.0",
11
+ "type": "breaking",
12
+ "title": "Auth mails are notifications with seeded templates, render*Email is gone",
13
+ "detail": "password-reset, account-unlock, email-verification, signup-activation and invite are declared as r.notification with a localized template and variables, seeded as system templates at boot and rendered by renderer-mail-html, so a tenant can override the text in the template editor. A locale without its own subject key falls back to en, because the renderer has no locale fallback. AuthMailNotificationTypes is exported for the qualified names.",
14
+ "migration": "The notification types are now `auth-email-password:notify:<name>` (before: the plain names). Update delivery preferences, preference rows and tests that reference the old names. renderResetPasswordEmail, renderUnlockAccountEmail, renderVerifyEmail, renderActivationEmail, renderInviteEmail, AuthMailSection, RenderTokenContentArgs and RenderInviteEmailArgs are removed; AuthMailContent stays. An app that mounts auth-email-password now needs template-resolver, renderer-foundation and @cosmicdrift/kumiko-renderer-mail-html (composeRendererStack() from dev-server mounts all of them; with includeBundled, composeFeatures adds whichever is missing). Boot fails with a clear error otherwise, and apps without the template tables need a schema migration (`kumiko schema generate`). kumiko-schema-check counts the three as implicit features of every HAS_AUTH app, so its check fails until that migration exists. Tests that read the token from a mail use the text part (`mail.text`); the html part encodes `=` as `&#x3D;`."
15
+ },
2
16
  {
3
17
  "version": "0.354.0",
4
18
  "type": "breaking",
@@ -1,14 +1,11 @@
1
- // Default renderers for the transactional auth mails. All four magic-link flows
2
- // (password-reset, email-verification, signup-activation, invite) emit
3
- // structured AuthMailContent that the handler hands to delivery (ctx.notify) —
4
- // renderer-simple turns it into HTML. Apps wanting their own branding swap the
5
- // renderer; these templates are deliberately plain so the renderer (and the
6
- // operator reading the mailer log) can rely on them.
1
+ // English texts and expiry formatting for the transactional auth mails. The
2
+ // mail bodies are built from these keys into stored templates (see
3
+ // mail-notifications.ts).
7
4
  //
8
5
  // English ships here. Other languages register via @cosmicdrift/kumiko-locale-*
9
6
  // (localeDe() / localeEs() call registerMailTranslations).
10
7
 
11
- import { mailT, registerMailTranslations } from "@cosmicdrift/kumiko-framework/i18n";
8
+ import { registerMailTranslations } from "@cosmicdrift/kumiko-framework/i18n";
12
9
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
13
10
 
14
11
  export type AuthMailLocale = string;
@@ -56,138 +53,14 @@ export const AUTH_MAIL_EN: Readonly<Record<string, string>> = {
56
53
 
57
54
  registerMailTranslations("en", AUTH_MAIL_EN);
58
55
 
59
- export type RenderTokenContentArgs = {
60
- readonly url: string;
56
+ export type TokenMailTiming = {
61
57
  readonly expiresAt: string;
62
- readonly locale?: AuthMailLocale;
63
- /** Optional app name for subject + intro. Default from auth.mail.appNameDefault. */
64
- readonly appName?: string;
65
58
  /** ISO instant the token was issued; the validity duration is measured from it. Default: now. */
66
59
  readonly issuedAt?: string;
67
60
  /** IANA zone the expiry time is shown in. Default "UTC"; an invalid zone falls back to "UTC". */
68
61
  readonly timeZone?: string;
69
62
  };
70
63
 
71
- export type RenderInviteEmailArgs = RenderTokenContentArgs & { readonly role: string };
72
-
73
- export type AuthMailSection =
74
- | { readonly text: string }
75
- | { readonly button: { readonly label: string; readonly url: string } };
76
-
77
- export type AuthMailContent = {
78
- readonly subject: string;
79
- readonly header: string;
80
- readonly sections: readonly AuthMailSection[];
81
- readonly footer: string;
82
- };
83
-
84
- function tokenMailContent(spec: {
85
- readonly subject: string;
86
- readonly header: string;
87
- readonly greeting: string;
88
- readonly intro: string;
89
- readonly buttonLabel: string;
90
- readonly buttonUrl: string;
91
- readonly expiry: string;
92
- readonly ignore: string;
93
- }): AuthMailContent {
94
- return {
95
- subject: spec.subject,
96
- header: spec.header,
97
- sections: [
98
- { text: spec.greeting },
99
- { text: spec.intro },
100
- { button: { label: spec.buttonLabel, url: spec.buttonUrl } },
101
- { text: spec.expiry },
102
- ],
103
- footer: spec.ignore,
104
- };
105
- }
106
-
107
- function t(locale: string, key: string, params?: Readonly<Record<string, string>>): string {
108
- return mailT(locale, key, params);
109
- }
110
-
111
- function appNameFor(args: RenderTokenContentArgs): string {
112
- const locale = args.locale ?? "en";
113
- return args.appName ?? t(locale, "auth.mail.appNameDefault");
114
- }
115
-
116
- export function renderResetPasswordEmail(args: RenderTokenContentArgs): AuthMailContent {
117
- const locale = args.locale ?? "en";
118
- const app = appNameFor(args);
119
- return tokenMailContent({
120
- subject: t(locale, "auth.mail.reset.subject", { app }),
121
- header: t(locale, "auth.mail.reset.button"),
122
- greeting: t(locale, "auth.mail.reset.greeting"),
123
- intro: t(locale, "auth.mail.reset.intro", { app }),
124
- buttonLabel: t(locale, "auth.mail.reset.button"),
125
- buttonUrl: args.url,
126
- expiry: t(locale, "auth.mail.reset.expiry", expiryParams(args, locale)),
127
- ignore: t(locale, "auth.mail.reset.ignore"),
128
- });
129
- }
130
-
131
- export function renderUnlockAccountEmail(args: RenderTokenContentArgs): AuthMailContent {
132
- const locale = args.locale ?? "en";
133
- const app = appNameFor(args);
134
- return tokenMailContent({
135
- subject: t(locale, "auth.mail.unlock.subject", { app }),
136
- header: t(locale, "auth.mail.unlock.button"),
137
- greeting: t(locale, "auth.mail.unlock.greeting"),
138
- intro: t(locale, "auth.mail.unlock.intro", { app }),
139
- buttonLabel: t(locale, "auth.mail.unlock.button"),
140
- buttonUrl: args.url,
141
- expiry: t(locale, "auth.mail.unlock.expiry", expiryParams(args, locale)),
142
- ignore: t(locale, "auth.mail.unlock.ignore"),
143
- });
144
- }
145
-
146
- export function renderVerifyEmail(args: RenderTokenContentArgs): AuthMailContent {
147
- const locale = args.locale ?? "en";
148
- const app = appNameFor(args);
149
- return tokenMailContent({
150
- subject: t(locale, "auth.mail.verify.subject", { app }),
151
- header: t(locale, "auth.mail.verify.button"),
152
- greeting: t(locale, "auth.mail.verify.greeting"),
153
- intro: t(locale, "auth.mail.verify.intro", { app }),
154
- buttonLabel: t(locale, "auth.mail.verify.button"),
155
- buttonUrl: args.url,
156
- expiry: t(locale, "auth.mail.verify.expiry", expiryParams(args, locale)),
157
- ignore: t(locale, "auth.mail.verify.ignore"),
158
- });
159
- }
160
-
161
- export function renderActivationEmail(args: RenderTokenContentArgs): AuthMailContent {
162
- const locale = args.locale ?? "en";
163
- const app = appNameFor(args);
164
- return tokenMailContent({
165
- subject: t(locale, "auth.mail.activation.subject", { app }),
166
- header: t(locale, "auth.mail.activation.button"),
167
- greeting: t(locale, "auth.mail.activation.greeting"),
168
- intro: t(locale, "auth.mail.activation.intro", { app }),
169
- buttonLabel: t(locale, "auth.mail.activation.button"),
170
- buttonUrl: args.url,
171
- expiry: t(locale, "auth.mail.activation.expiry", expiryParams(args, locale)),
172
- ignore: t(locale, "auth.mail.activation.ignore"),
173
- });
174
- }
175
-
176
- export function renderInviteEmail(args: RenderInviteEmailArgs): AuthMailContent {
177
- const locale = args.locale ?? "en";
178
- const app = args.appName ?? "Workspace";
179
- return tokenMailContent({
180
- subject: t(locale, "auth.mail.invite.subject", { app }),
181
- header: t(locale, "auth.mail.invite.button"),
182
- greeting: t(locale, "auth.mail.invite.greeting"),
183
- intro: t(locale, "auth.mail.invite.intro", { app, role: args.role }),
184
- buttonLabel: t(locale, "auth.mail.invite.button"),
185
- buttonUrl: args.url,
186
- expiry: t(locale, "auth.mail.invite.expiry", expiryParams(args, locale)),
187
- ignore: t(locale, "auth.mail.invite.ignore"),
188
- });
189
- }
190
-
191
64
  const MS_PER_MINUTE = 60_000;
192
65
  const MS_PER_HOUR = 60 * MS_PER_MINUTE;
193
66
  const MS_PER_DAY = 24 * MS_PER_HOUR;
@@ -241,18 +114,19 @@ function formatWhen(expiresAt: Temporal.Instant, locale: string, timeZone: strin
241
114
  }
242
115
  }
243
116
 
244
- function expiryParams(
245
- args: RenderTokenContentArgs,
117
+ // Formatted as text here because the stored template only knows plain variables.
118
+ export function tokenMailExpiry(
119
+ args: TokenMailTiming,
246
120
  locale: string,
247
- ): { readonly duration: string; readonly when: string } {
121
+ ): { readonly validFor: string; readonly validUntil: string } {
248
122
  try {
249
123
  const expiresAt = Temporal.Instant.from(args.expiresAt);
250
124
  const issuedAt = args.issuedAt ? Temporal.Instant.from(args.issuedAt) : Temporal.Now.instant();
251
125
  return {
252
- duration: formatDuration(expiresAt.epochMilliseconds - issuedAt.epochMilliseconds, locale),
253
- when: formatWhen(expiresAt, locale, args.timeZone ?? "UTC"),
126
+ validFor: formatDuration(expiresAt.epochMilliseconds - issuedAt.epochMilliseconds, locale),
127
+ validUntil: formatWhen(expiresAt, locale, args.timeZone ?? "UTC"),
254
128
  };
255
129
  } catch {
256
- return { duration: args.expiresAt, when: args.expiresAt };
130
+ return { validFor: args.expiresAt, validUntil: args.expiresAt };
257
131
  }
258
132
  }
@@ -30,6 +30,12 @@ import {
30
30
  } from "./handlers/signup-request.write.js";
31
31
  import { createSwitchTenantMfaGateHandler } from "./handlers/switch-tenant-mfa-gate.write.js";
32
32
  import { createVerifyEmailHandler } from "./handlers/verify-email.write.js";
33
+ import {
34
+ AUTH_MAIL_VARIABLES,
35
+ authMailLocalizedTemplate,
36
+ INVITE_MAIL_VARIABLES,
37
+ sendsMagicLinkMail,
38
+ } from "./mail-notifications.js";
33
39
 
34
40
  /**
35
41
  * Env-vars contract for the `auth-email-password` feature.
@@ -199,17 +205,32 @@ export function createAuthEmailPasswordFeature(
199
205
  // All five magic-link flows (reset/verify/signup/invite/unlock) dispatch
200
206
  // via ctx.notify → delivery must be mounted. Fail closed at boot instead
201
207
  // of silently dropping the mail.
202
- if (
203
- opts.passwordReset ||
204
- opts.emailVerification ||
205
- opts.signup ||
206
- opts.invite ||
207
- opts.accountUnlock
208
- ) {
209
- r.requires("delivery");
210
- }
208
+ if (sendsMagicLinkMail(opts)) r.requires("delivery");
211
209
  r.envSchema(authEmailPasswordEnvSchema);
212
210
 
211
+ // Declared unconditionally: apps also issue the activation link from their
212
+ // own flows (issueSignupActivation) without enabling the signup option.
213
+ r.notification("password-reset", {
214
+ variables: AUTH_MAIL_VARIABLES,
215
+ localizedTemplate: authMailLocalizedTemplate("passwordReset"),
216
+ });
217
+ r.notification("account-unlock", {
218
+ variables: AUTH_MAIL_VARIABLES,
219
+ localizedTemplate: authMailLocalizedTemplate("accountUnlock"),
220
+ });
221
+ r.notification("email-verification", {
222
+ variables: AUTH_MAIL_VARIABLES,
223
+ localizedTemplate: authMailLocalizedTemplate("emailVerification"),
224
+ });
225
+ r.notification("signup-activation", {
226
+ variables: AUTH_MAIL_VARIABLES,
227
+ localizedTemplate: authMailLocalizedTemplate("signupActivation"),
228
+ });
229
+ r.notification("invite", {
230
+ variables: INVITE_MAIL_VARIABLES,
231
+ localizedTemplate: authMailLocalizedTemplate("invite"),
232
+ });
233
+
213
234
  const handlers = {
214
235
  login: r.writeHandler(
215
236
  createLoginHandler({
@@ -55,12 +55,10 @@ import { GLOBAL_ROLE_OPTIONS } from "../../user/constants.js";
55
55
  import { userTable } from "../../user/schema/user.js";
56
56
  import { AUTH_INVITE_DEFAULT_TTL_MINUTES } from "../constants.js";
57
57
  import type { AuthMailLocale } from "../email-templates.js";
58
- import { renderInviteEmail } from "../email-templates.js";
59
58
  import { inviteAlreadyAccepted } from "../errors.js";
60
59
  import { invalidateExistingInviteToken, storeInviteToken } from "../invite-token-store.js";
61
60
  import { dispatchMagicLinkMail, resolveHandlerMailLocale } from "../magic-link-mail.js";
62
-
63
- const INVITE_NOTIFICATION_TYPE = "auth-email-password:invite";
61
+ import { AuthMailNotificationTypes } from "../mail-notifications.js";
64
62
 
65
63
  type GlobalRole = (typeof GLOBAL_ROLE_OPTIONS)[number];
66
64
 
@@ -247,8 +245,8 @@ async function issueInvitation(
247
245
  ctx.notify,
248
246
  {
249
247
  handlerName: "invite-create",
250
- notificationType: INVITE_NOTIFICATION_TYPE,
251
- renderContent: (renderArgs) => renderInviteEmail({ ...renderArgs, role: request.role }),
248
+ notificationType: AuthMailNotificationTypes.invite,
249
+ fallbackAppName: "Workspace",
252
250
  },
253
251
  {
254
252
  email,
@@ -257,6 +255,7 @@ async function issueInvitation(
257
255
  expiresAt: expiresAt.toString(),
258
256
  issuedAt: issuedAt.toString(),
259
257
  timeZone: ctx.tz.user,
258
+ role: request.role,
260
259
  ...(opts.appName !== undefined && { appName: opts.appName }),
261
260
  locale,
262
261
  },
@@ -1,5 +1,5 @@
1
1
  import { AUTH_UNLOCK_DEFAULT_TTL_MINUTES, AuthErrors } from "../constants.js";
2
- import { renderUnlockAccountEmail } from "../email-templates.js";
2
+ import { AuthMailNotificationTypes } from "../mail-notifications.js";
3
3
  import { signUnlockToken } from "../unlock-token.js";
4
4
  import {
5
5
  createTokenRequestHandler,
@@ -7,8 +7,6 @@ import {
7
7
  type TokenRequestOptions,
8
8
  } from "./token-request-handler.js";
9
9
 
10
- const UNLOCK_NOTIFICATION_TYPE = "auth-email-password:account-unlock";
11
-
12
10
  export type RequestAccountUnlockOptions = TokenRequestOptions;
13
11
 
14
12
  // Public shape re-exported for callers that build custom routes on top of
@@ -32,8 +30,7 @@ export function createRequestAccountUnlockHandler(opts: RequestAccountUnlockOpti
32
30
  // locked right now. Confirming just clears the (possibly already
33
31
  // empty) lockout state — harmless no-op if they weren't locked.
34
32
  extraSilentSkip: () => false,
35
- notificationType: UNLOCK_NOTIFICATION_TYPE,
36
- renderContent: renderUnlockAccountEmail,
33
+ notificationType: AuthMailNotificationTypes.accountUnlock,
37
34
  },
38
35
  opts,
39
36
  );
@@ -1,5 +1,5 @@
1
1
  import { AUTH_VERIFY_DEFAULT_TTL_MINUTES, AuthErrors } from "../constants.js";
2
- import { renderVerifyEmail } from "../email-templates.js";
2
+ import { AuthMailNotificationTypes } from "../mail-notifications.js";
3
3
  import { signVerificationToken } from "../verification-token.js";
4
4
  import {
5
5
  createTokenRequestHandler,
@@ -7,8 +7,6 @@ import {
7
7
  type TokenRequestOptions,
8
8
  } from "./token-request-handler.js";
9
9
 
10
- const VERIFY_NOTIFICATION_TYPE = "auth-email-password:email-verification";
11
-
12
10
  export type RequestEmailVerificationOptions = TokenRequestOptions;
13
11
 
14
12
  export type RequestVerificationData = TokenRequestData<"verification-requested">;
@@ -29,8 +27,7 @@ export function createRequestEmailVerificationHandler(opts: RequestEmailVerifica
29
27
  // unknown/deleted to keep the enumeration surface symmetric — the
30
28
  // caller sees the same 200 regardless of whether a token was minted.
31
29
  extraSilentSkip: (user) => user.emailVerified === true,
32
- notificationType: VERIFY_NOTIFICATION_TYPE,
33
- renderContent: renderVerifyEmail,
30
+ notificationType: AuthMailNotificationTypes.emailVerification,
34
31
  },
35
32
  opts,
36
33
  );
@@ -1,5 +1,5 @@
1
1
  import { AUTH_RESET_DEFAULT_TTL_MINUTES, AuthErrors } from "../constants.js";
2
- import { renderResetPasswordEmail } from "../email-templates.js";
2
+ import { AuthMailNotificationTypes } from "../mail-notifications.js";
3
3
  import { signResetToken } from "../reset-token.js";
4
4
  import {
5
5
  createTokenRequestHandler,
@@ -7,8 +7,6 @@ import {
7
7
  type TokenRequestOptions,
8
8
  } from "./token-request-handler.js";
9
9
 
10
- const RESET_NOTIFICATION_TYPE = "auth-email-password:password-reset";
11
-
12
10
  export type RequestPasswordResetOptions = TokenRequestOptions;
13
11
 
14
12
  // Public shape re-exported for callers that build custom routes on top of
@@ -31,8 +29,7 @@ export function createRequestPasswordResetHandler(opts: RequestPasswordResetOpti
31
29
  // non-deleted user can initiate a reset regardless of verification
32
30
  // state. The sessions feature handles the post-change revocation.
33
31
  extraSilentSkip: () => false,
34
- notificationType: RESET_NOTIFICATION_TYPE,
35
- renderContent: renderResetPasswordEmail,
32
+ notificationType: AuthMailNotificationTypes.passwordReset,
36
33
  },
37
34
  opts,
38
35
  );
@@ -36,6 +36,7 @@ import {
36
36
  } from "@cosmicdrift/kumiko-framework/errors";
37
37
  import { generateUniqueName } from "@cosmicdrift/kumiko-framework/random";
38
38
  import { generateId } from "@cosmicdrift/kumiko-framework/utils";
39
+ import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
39
40
  import * as z from "zod";
40
41
  import {
41
42
  findSignupHandoverProvider,
@@ -100,10 +101,8 @@ const SIGNUP_CONFIRM_PROVISION_REASON =
100
101
  // match; this reads the same field the framework itself uses.
101
102
  function isBenignClaimRejection(error: WriteErrorInfo): boolean {
102
103
  return (
103
- typeof error.details === "object" &&
104
- error.details !== null &&
105
- (error.details as Record<string, unknown>)["reason"] ===
106
- SIGNUP_HANDOVER_BENIGN_CLAIM_REJECTION_CODE
104
+ isPlainObject(error.details) &&
105
+ error.details["reason"] === SIGNUP_HANDOVER_BENIGN_CLAIM_REJECTION_CODE
107
106
  );
108
107
  }
109
108
 
@@ -19,11 +19,7 @@ import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
19
19
  import * as z from "zod";
20
20
  import { UserQueries } from "../../user/index.js";
21
21
  import { type AuthUserRow, parseAuthUserRow } from "../auth-user-row.js";
22
- import type {
23
- AuthMailContent,
24
- AuthMailLocale,
25
- RenderTokenContentArgs,
26
- } from "../email-templates.js";
22
+ import type { AuthMailLocale } from "../email-templates.js";
27
23
  import { dispatchMagicLinkMail, resolveHandlerMailLocale } from "../magic-link-mail.js";
28
24
 
29
25
  const RequestTokenSchema = z.object({
@@ -67,13 +63,9 @@ export type TokenRequestSpec<TName extends string, TSuccessKind extends string>
67
63
  // soft-deleted". Verification skips when emailVerified is already true;
68
64
  // password-reset has no extra condition (returns false).
69
65
  readonly extraSilentSkip: (user: AuthUserRow) => boolean;
70
- // Notification type dispatched via ctx.notify on success. No r.notification
71
- // is declared (the recipient is an anonymous email, not a userId) — the
72
- // route:{email} path delivers directly and buildMessage falls back to data.
66
+ // Trigger-less r.notification dispatched via ctx.notify on success; the
67
+ // recipient is an anonymous email, so route:{email} delivers directly.
73
68
  readonly notificationType: string;
74
- // Builds the structured mail body from the magic-link + expiry. Per-flow
75
- // (renderResetPasswordEmail / renderVerifyEmail).
76
- readonly renderContent: (args: RenderTokenContentArgs) => AuthMailContent;
77
69
  };
78
70
 
79
71
  export type TokenRequestOptions = {
@@ -154,7 +146,6 @@ export function createTokenRequestHandler<TName extends string, TSuccessKind ext
154
146
  {
155
147
  handlerName: spec.handlerName,
156
148
  notificationType: spec.notificationType,
157
- renderContent: spec.renderContent,
158
149
  },
159
150
  {
160
151
  // Row value is ciphertext with an active KMS; the byte-exact