@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
@@ -21,23 +21,7 @@ export type {
21
21
  } from "./bootstrap.js";
22
22
  export { BootstrapPlanError, BootstrapWriteError, bootstrapTenants } from "./bootstrap.js";
23
23
  export { AUTH_EMAIL_PASSWORD_FEATURE, AuthErrors, AuthHandlers, AuthQueries } from "./constants.js";
24
- // Renderers for the auth mails. All four magic-link flows (reset, verify,
25
- // signup-activation, invite) emit structured AuthMailContent through delivery
26
- // (ctx.notify).
27
- export type {
28
- AuthMailContent,
29
- AuthMailLocale,
30
- AuthMailSection,
31
- RenderInviteEmailArgs,
32
- RenderTokenContentArgs,
33
- } from "./email-templates.js";
34
- export {
35
- renderActivationEmail,
36
- renderInviteEmail,
37
- renderResetPasswordEmail,
38
- renderUnlockAccountEmail,
39
- renderVerifyEmail,
40
- } from "./email-templates.js";
24
+ export type { AuthMailLocale } from "./email-templates.js";
41
25
  export type {
42
26
  AccountLockoutOptions,
43
27
  AccountUnlockOptions,
@@ -48,6 +32,7 @@ export type {
48
32
  SignupOptions,
49
33
  } from "./feature.js";
50
34
  export { authEmailPasswordEnvSchema, createAuthEmailPasswordFeature } from "./feature.js";
35
+ export { AuthMailNotificationTypes } from "./mail-notifications.js";
51
36
  export {
52
37
  AUTH_SELF_REGISTRATION_FEATURE,
53
38
  createAuthSelfRegistrationToggleFeature,
@@ -1,24 +1,24 @@
1
1
  // Shared dispatch for the magic-link mails (password-reset, email-verification,
2
- // signup-activation). All three follow the same delivery shape: append the
3
- // token to the app page, render structured content, and notify the recipient
4
- // email directly (route:{email} skips preferences — the recipient may not have
5
- // a user account yet — and critical priority keeps a security mail
6
- // non-unsubscribable). No r.notification is declared; buildMessage falls back
7
- // to the structured `data` for the renderer.
2
+ // account-unlock, signup-activation, invite). All follow the same delivery
3
+ // shape: append the token to the app page and notify the recipient email
4
+ // directly (route:{email} skips preferences — the recipient may not have a user
5
+ // account yet — and critical priority keeps a security mail non-unsubscribable).
6
+ // Each mail type is a trigger-less r.notification whose stored template renders
7
+ // from the variables passed here.
8
8
 
9
9
  import { requestContext } from "@cosmicdrift/kumiko-framework/api";
10
10
  import type { NotifyFn } from "@cosmicdrift/kumiko-framework/engine";
11
11
  import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
12
- import { resolveMailLocale } from "@cosmicdrift/kumiko-framework/i18n";
13
- import type { AuthMailContent, AuthMailLocale, RenderTokenContentArgs } from "./email-templates.js";
12
+ import { mailT, resolveMailLocale } from "@cosmicdrift/kumiko-framework/i18n";
13
+ import { type AuthMailLocale, tokenMailExpiry } from "./email-templates.js";
14
+ import { authMailTemplateLocale } from "./mail-notifications.js";
14
15
 
15
- // Per-flow constants: which notification type to dispatch and how to render the
16
- // body. renderContent is the flow's template (renderResetPasswordEmail / … /
17
- // renderActivationEmail), all unified on RenderTokenContentArgs → AuthMailContent.
16
+ // Per-flow constants: which notification type to dispatch. fallbackAppName
17
+ // replaces the translated default when the flow has no app name of its own.
18
18
  export type MagicLinkMailSpec = {
19
19
  readonly handlerName: string;
20
20
  readonly notificationType: string;
21
- readonly renderContent: (args: RenderTokenContentArgs) => AuthMailContent;
21
+ readonly fallbackAppName?: string;
22
22
  };
23
23
 
24
24
  // Per-request values: the recipient + the app page that receives the token, plus
@@ -36,6 +36,8 @@ export type MagicLinkMailParams = {
36
36
  readonly locale?: AuthMailLocale;
37
37
  readonly issuedAt?: string;
38
38
  readonly timeZone?: string;
39
+ // Invite mails name the role the recipient is invited as.
40
+ readonly role?: string;
39
41
  };
40
42
 
41
43
  function resolveAppUrl(appUrl: string | ((locale: string) => string), locale: string): string {
@@ -75,18 +77,24 @@ export async function dispatchMagicLinkMail(
75
77
  // mailT already root-falls-back; appUrl must use the same negotiated tag
76
78
  // or language-in-path apps 404 on e.g. /de-AT/activate.
77
79
  const urlLocale = resolveMailLocale(locale);
78
- const content = spec.renderContent({
79
- url: appendToken(resolveAppUrl(params.appUrl, urlLocale), params.token),
80
- expiresAt: params.expiresAt,
81
- ...(params.locale !== undefined && { locale: params.locale }),
82
- ...(params.appName !== undefined && { appName: params.appName }),
83
- ...(params.issuedAt !== undefined && { issuedAt: params.issuedAt }),
84
- ...(params.timeZone !== undefined && { timeZone: params.timeZone }),
85
- });
80
+ const { validFor, validUntil } = tokenMailExpiry(
81
+ {
82
+ expiresAt: params.expiresAt,
83
+ ...(params.issuedAt !== undefined && { issuedAt: params.issuedAt }),
84
+ ...(params.timeZone !== undefined && { timeZone: params.timeZone }),
85
+ },
86
+ locale,
87
+ );
86
88
  await notify(spec.notificationType, {
87
89
  route: { email: params.email },
88
- data: content,
89
- locale,
90
+ data: {
91
+ appName: params.appName ?? spec.fallbackAppName ?? mailT(locale, "auth.mail.appNameDefault"),
92
+ actionUrl: appendToken(resolveAppUrl(params.appUrl, urlLocale), params.token),
93
+ validFor,
94
+ validUntil,
95
+ ...(params.role !== undefined && { role: params.role }),
96
+ },
97
+ locale: authMailTemplateLocale(spec.notificationType, locale),
90
98
  priority: "critical",
91
99
  });
92
100
  }
@@ -0,0 +1,158 @@
1
+ import {
2
+ type NotificationLocalizedTemplate,
3
+ QnTypes,
4
+ qn,
5
+ type TemplateVariables,
6
+ templateVariable,
7
+ } from "@cosmicdrift/kumiko-framework/engine";
8
+ import {
9
+ hasMailTranslationKey,
10
+ listMailLocales,
11
+ mailT,
12
+ resolveMailLocale,
13
+ } from "@cosmicdrift/kumiko-framework/i18n";
14
+ import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
15
+ import { AUTH_EMAIL_PASSWORD_FEATURE } from "./constants.js";
16
+
17
+ export const AUTH_MAIL_KINDS = {
18
+ passwordReset: { name: "password-reset", translationKey: "reset" },
19
+ accountUnlock: { name: "account-unlock", translationKey: "unlock" },
20
+ emailVerification: { name: "email-verification", translationKey: "verify" },
21
+ signupActivation: { name: "signup-activation", translationKey: "activation" },
22
+ invite: { name: "invite", translationKey: "invite" },
23
+ } as const;
24
+
25
+ export type AuthMailKind = keyof typeof AUTH_MAIL_KINDS;
26
+
27
+ type MagicLinkFlowOptions = Partial<
28
+ Record<"passwordReset" | "emailVerification" | "signup" | "invite" | "accountUnlock", unknown>
29
+ >;
30
+
31
+ export function sendsMagicLinkMail(flows: MagicLinkFlowOptions): boolean {
32
+ return Boolean(
33
+ flows.passwordReset ||
34
+ flows.emailVerification ||
35
+ flows.signup ||
36
+ flows.invite ||
37
+ flows.accountUnlock,
38
+ );
39
+ }
40
+
41
+ function authMailType(kind: AuthMailKind): string {
42
+ return qn(AUTH_EMAIL_PASSWORD_FEATURE, QnTypes.notify, AUTH_MAIL_KINDS[kind].name);
43
+ }
44
+
45
+ export const AuthMailNotificationTypes: Readonly<Record<AuthMailKind, string>> = {
46
+ passwordReset: authMailType("passwordReset"),
47
+ accountUnlock: authMailType("accountUnlock"),
48
+ emailVerification: authMailType("emailVerification"),
49
+ signupActivation: authMailType("signupActivation"),
50
+ invite: authMailType("invite"),
51
+ };
52
+
53
+ function subjectKey(kind: AuthMailKind): string {
54
+ return `auth.mail.${AUTH_MAIL_KINDS[kind].translationKey}.subject`;
55
+ }
56
+
57
+ const ALL_AUTH_MAIL_KINDS: readonly AuthMailKind[] = [
58
+ "passwordReset",
59
+ "accountUnlock",
60
+ "emailVerification",
61
+ "signupActivation",
62
+ "invite",
63
+ ];
64
+
65
+ const SUBJECT_KEY_BY_TYPE: ReadonlyMap<string, string> = new Map(
66
+ ALL_AUTH_MAIL_KINDS.map((kind) => [AuthMailNotificationTypes[kind], subjectKey(kind)]),
67
+ );
68
+
69
+ // A registered locale whose bundle lacks the subject key has no template (see
70
+ // authMailLocalizedTemplate); the renderer has no locale fallback, so the
71
+ // sender must pick a locale that has one or the mail would not go out at all.
72
+ export function authMailTemplateLocale(notificationType: string, requestedLocale: string): string {
73
+ const locale = resolveMailLocale(requestedLocale);
74
+ const key = SUBJECT_KEY_BY_TYPE.get(notificationType);
75
+ return key !== undefined && hasMailTranslationKey(locale, key) ? locale : "en";
76
+ }
77
+
78
+ export const AUTH_MAIL_VARIABLES: TemplateVariables = {
79
+ appName: templateVariable.text({ required: true }),
80
+ actionUrl: templateVariable.link({ required: true }),
81
+ validFor: templateVariable.text({ required: true }),
82
+ validUntil: templateVariable.text({ required: true }),
83
+ };
84
+
85
+ export const INVITE_MAIL_VARIABLES: TemplateVariables = {
86
+ ...AUTH_MAIL_VARIABLES,
87
+ role: templateVariable.text({ required: true }),
88
+ };
89
+
90
+ // Translation placeholder -> template variable. The markers survive HTML
91
+ // escaping, so only the translated text is escaped, never the placeholders.
92
+ const PLACEHOLDER_VARIABLES: Readonly<Record<string, string>> = {
93
+ app: "appName",
94
+ duration: "validFor",
95
+ when: "validUntil",
96
+ role: "role",
97
+ };
98
+ const MARKER_OPEN = "\uE000";
99
+ const MARKER_CLOSE = "\uE001";
100
+ const MARKER_PATTERN = new RegExp(`${MARKER_OPEN}(\\w+)${MARKER_CLOSE}`, "g");
101
+
102
+ function markers(): Readonly<Record<string, string>> {
103
+ return Object.fromEntries(
104
+ Object.keys(PLACEHOLDER_VARIABLES).map((name) => [
105
+ name,
106
+ `${MARKER_OPEN}${name}${MARKER_CLOSE}`,
107
+ ]),
108
+ );
109
+ }
110
+
111
+ function translatedHtml(locale: string, key: string): string {
112
+ return escapeHtml(mailT(locale, key, markers())).replace(MARKER_PATTERN, (_, name: string) => {
113
+ const variable = PLACEHOLDER_VARIABLES[name];
114
+ return variable === undefined
115
+ ? ""
116
+ : `<span data-variable="${escapeHtmlAttr(variable)}"></span>`;
117
+ });
118
+ }
119
+
120
+ function mailBody(locale: string, translationKey: string): string {
121
+ const html = (part: string) => translatedHtml(locale, `auth.mail.${translationKey}.${part}`);
122
+ return [
123
+ `<p>${html("greeting")}</p>`,
124
+ `<p>${html("intro")}</p>`,
125
+ `<p><a data-button href="{{actionUrl}}">${html("button")}</a></p>`,
126
+ `<p>${html("expiry")}</p>`,
127
+ `<p>${html("ignore")}</p>`,
128
+ ].join("");
129
+ }
130
+
131
+ // Evaluated when the registry is built, after every locale feature has
132
+ // registered its mail translations. English always ships; another locale only
133
+ // gets a template when it has its own subject, so a missing translation never
134
+ // silently becomes an English mail in that locale's slot.
135
+ export function authMailLocalizedTemplate(kind: AuthMailKind): () => NotificationLocalizedTemplate {
136
+ const { translationKey } = AUTH_MAIL_KINDS[kind];
137
+ const kindSubjectKey = subjectKey(kind);
138
+ return () => {
139
+ const locales = [
140
+ "en",
141
+ ...listMailLocales().filter(
142
+ (locale) => locale !== "en" && hasMailTranslationKey(locale, kindSubjectKey),
143
+ ),
144
+ ];
145
+ return Object.fromEntries(
146
+ locales.map((locale) => [
147
+ locale,
148
+ {
149
+ body: mailBody(locale, translationKey),
150
+ meta: {
151
+ subject: mailT(locale, kindSubjectKey, { app: "{{appName}}" }),
152
+ category: "transactional" as const,
153
+ },
154
+ },
155
+ ]),
156
+ );
157
+ };
158
+ }
@@ -9,15 +9,15 @@ import type { NotifyFn } from "@cosmicdrift/kumiko-framework/engine";
9
9
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
10
10
  import type { Redis } from "ioredis";
11
11
  import { AUTH_SIGNUP_DEFAULT_TTL_MINUTES } from "./constants.js";
12
- import { renderActivationEmail } from "./email-templates.js";
13
12
  import { dispatchMagicLinkMail } from "./magic-link-mail.js";
13
+ import { AuthMailNotificationTypes } from "./mail-notifications.js";
14
14
  import {
15
15
  invalidateExistingSignupToken,
16
16
  normalizeEmail,
17
17
  storeSignupToken,
18
18
  } from "./signup-token-store.js";
19
19
 
20
- export const SIGNUP_ACTIVATION_NOTIFICATION_TYPE = "auth-email-password:signup-activation";
20
+ export const SIGNUP_ACTIVATION_NOTIFICATION_TYPE = AuthMailNotificationTypes.signupActivation;
21
21
 
22
22
  export type IssueSignupActivationArgs = {
23
23
  readonly redis: Redis;
@@ -65,7 +65,6 @@ export async function issueSignupActivation(
65
65
  {
66
66
  handlerName: "signup-request",
67
67
  notificationType: SIGNUP_ACTIVATION_NOTIFICATION_TYPE,
68
- renderContent: renderActivationEmail,
69
68
  },
70
69
  {
71
70
  email,
@@ -48,10 +48,10 @@ export type AuthProviderPlugin = {
48
48
 
49
49
  export function isValidTokenShape(shape: unknown): shape is TokenShape {
50
50
  if (typeof shape !== "object" || shape === null || !("kind" in shape)) return false;
51
- const kind = (shape as { kind: unknown }).kind;
51
+ const kind = shape.kind;
52
52
  if (kind === "jwt") return true;
53
53
  if (kind === "prefix") {
54
- const prefix = "prefix" in shape ? (shape as { prefix: unknown }).prefix : undefined;
54
+ const prefix = "prefix" in shape ? shape.prefix : undefined;
55
55
  return typeof prefix === "string" && prefix.length > 0;
56
56
  }
57
57
  return false;
@@ -64,9 +64,9 @@ export function isAuthProviderPlugin(o: unknown): o is AuthProviderPlugin {
64
64
  typeof o === "object" &&
65
65
  o !== null &&
66
66
  "build" in o &&
67
- typeof (o as { build: unknown }).build === "function" &&
67
+ typeof o.build === "function" &&
68
68
  "shape" in o &&
69
- isValidTokenShape((o as { shape: unknown }).shape)
69
+ isValidTokenShape(o.shape)
70
70
  );
71
71
  }
72
72
 
@@ -105,12 +105,7 @@ export type SessionStoreProvider = {
105
105
  };
106
106
 
107
107
  export function isSessionStoreProvider(o: unknown): o is SessionStoreProvider {
108
- return (
109
- typeof o === "object" &&
110
- o !== null &&
111
- "build" in o &&
112
- typeof (o as { build: unknown }).build === "function"
113
- );
108
+ return typeof o === "object" && o !== null && "build" in o && typeof o.build === "function";
114
109
  }
115
110
 
116
111
  // Tenant-resolution extension points (#1373). Both are single-provider and
@@ -154,19 +149,14 @@ export function isTenantResolverProvider(o: unknown): o is TenantResolverProvide
154
149
  typeof o === "object" &&
155
150
  o !== null &&
156
151
  "build" in o &&
157
- typeof (o as { build: unknown }).build === "function" &&
152
+ typeof o.build === "function" &&
158
153
  "trust" in o &&
159
- isTenantResolverTrust((o as { trust: unknown }).trust)
154
+ isTenantResolverTrust(o.trust)
160
155
  );
161
156
  }
162
157
 
163
158
  export function isTenantExistenceProvider(o: unknown): o is TenantExistenceProvider {
164
- return (
165
- typeof o === "object" &&
166
- o !== null &&
167
- "build" in o &&
168
- typeof (o as { build: unknown }).build === "function"
169
- );
159
+ return typeof o === "object" && o !== null && "build" in o && typeof o.build === "function";
170
160
  }
171
161
 
172
162
  // r.useExtension options-shape for auth-foundation's four extension points.
@@ -53,11 +53,9 @@ export function bindMfaRevokeAllOtherSessionsFromFeature(
53
53
  ): BindMfaRevokeAllOtherSessions | undefined {
54
54
  const exports = feature.exports;
55
55
  if (exports && typeof exports === "object" && "bindRevokeAllOtherSessions" in exports) {
56
- const { bindRevokeAllOtherSessions } = exports as {
57
- bindRevokeAllOtherSessions: unknown;
58
- };
56
+ const { bindRevokeAllOtherSessions } = exports;
59
57
  if (typeof bindRevokeAllOtherSessions === "function") {
60
- // @cast-boundary exports-walk — feature.exports is untyped by design
58
+ // @cast-boundary feature-exports — feature.exports is untyped by design
61
59
  return bindRevokeAllOtherSessions as BindMfaRevokeAllOtherSessions;
62
60
  }
63
61
  }
@@ -71,9 +69,9 @@ export function mfaStatusCheckerFromFeature(
71
69
  ): MfaStatusChecker | undefined {
72
70
  const exports = feature.exports;
73
71
  if (exports && typeof exports === "object" && "checkMfaStatus" in exports) {
74
- const { checkMfaStatus } = exports as { checkMfaStatus: unknown };
72
+ const { checkMfaStatus } = exports;
75
73
  if (typeof checkMfaStatus === "function") {
76
- // @cast-boundary exports-walk — feature.exports is untyped by design
74
+ // @cast-boundary feature-exports — feature.exports is untyped by design
77
75
  return checkMfaStatus as MfaStatusChecker;
78
76
  }
79
77
  }
@@ -87,9 +85,9 @@ export function mfaStatusCheckerFromFeature(
87
85
  export function mfaVerifierFromFeature(feature: FeatureDefinition): MfaCodeVerifier | undefined {
88
86
  const exports = feature.exports;
89
87
  if (exports && typeof exports === "object" && "verifyMfaCode" in exports) {
90
- const { verifyMfaCode } = exports as { verifyMfaCode: unknown };
88
+ const { verifyMfaCode } = exports;
91
89
  if (typeof verifyMfaCode === "function") {
92
- // @cast-boundary exports-walk — feature.exports is untyped by design
90
+ // @cast-boundary feature-exports — feature.exports is untyped by design
93
91
  return verifyMfaCode as MfaCodeVerifier;
94
92
  }
95
93
  }
@@ -107,9 +105,9 @@ export function bindRevokeAllPatTokensFromFeature(
107
105
  ): BindRevokeAllPatTokens | undefined {
108
106
  const exports = feature.exports;
109
107
  if (exports && typeof exports === "object" && "bindRevokeAllPatTokens" in exports) {
110
- const { bindRevokeAllPatTokens } = exports as { bindRevokeAllPatTokens: unknown };
108
+ const { bindRevokeAllPatTokens } = exports;
111
109
  if (typeof bindRevokeAllPatTokens === "function") {
112
- // @cast-boundary exports-walk — feature.exports is untyped by design
110
+ // @cast-boundary feature-exports — feature.exports is untyped by design
113
111
  return bindRevokeAllPatTokens as BindRevokeAllPatTokens;
114
112
  }
115
113
  }
@@ -1,5 +1,6 @@
1
1
  import { createHmac, timingSafeEqual } from "node:crypto";
2
2
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
3
+ import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
3
4
 
4
5
  function hmacSign(input: string, secret: string): string {
5
6
  return createHmac("sha256", secret).update(input).digest("base64url");
@@ -20,11 +21,10 @@ export function createHmacTokenCodec<
20
21
  function splitEncodedBody(
21
22
  value: unknown,
22
23
  ): { readonly payload: TPayload; readonly expiresAtMs: number } | null {
23
- if (typeof value !== "object" || value === null) return null;
24
- const v = value as Record<string, unknown>;
25
- const expiresAtMs = v["expiresAtMs"];
24
+ if (!isPlainObject(value)) return null;
25
+ const expiresAtMs = value["expiresAtMs"];
26
26
  if (typeof expiresAtMs !== "number") return null;
27
- const { expiresAtMs: _drop, ...rest } = v;
27
+ const { expiresAtMs: _drop, ...rest } = value;
28
28
  return isPayload(rest) ? { payload: rest, expiresAtMs } : null;
29
29
  }
30
30
 
@@ -1,5 +1,6 @@
1
1
  import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
3
+ import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
3
4
  import { createHmacTokenCodec, type HmacTokenVerifyResult } from "./hmac-token-codec.js";
4
5
 
5
6
  export type MfaChallengePayload = {
@@ -8,9 +9,8 @@ export type MfaChallengePayload = {
8
9
  };
9
10
 
10
11
  function isPayload(value: unknown): value is MfaChallengePayload {
11
- if (typeof value !== "object" || value === null) return false;
12
- const v = value as Record<string, unknown>;
13
- return typeof v["userId"] === "string" && typeof v["tenantId"] === "string";
12
+ if (!isPlainObject(value)) return false;
13
+ return typeof value["userId"] === "string" && typeof value["tenantId"] === "string";
14
14
  }
15
15
 
16
16
  // Domain-separated from mfa-preauth-setup (see hmac-token-codec). Single-use is
@@ -1,5 +1,6 @@
1
1
  import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
3
+ import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
3
4
  import { createHmacTokenCodec, type HmacTokenVerifyResult } from "./hmac-token-codec.js";
4
5
 
5
6
  export type MfaPreauthSetupPayload = {
@@ -8,9 +9,8 @@ export type MfaPreauthSetupPayload = {
8
9
  };
9
10
 
10
11
  function isPayload(value: unknown): value is MfaPreauthSetupPayload {
11
- if (typeof value !== "object" || value === null) return false;
12
- const v = value as Record<string, unknown>;
13
- return typeof v["userId"] === "string" && typeof v["tenantId"] === "string";
12
+ if (!isPlainObject(value)) return false;
13
+ return typeof value["userId"] === "string" && typeof value["tenantId"] === "string";
14
14
  }
15
15
 
16
16
  // Domain "mfa-preauth-setup" must never equal the challenge domain: a setup
@@ -11,6 +11,7 @@
11
11
  // generated secret material, not just re-derive it from a lookup.
12
12
  import { createHmac, timingSafeEqual } from "node:crypto";
13
13
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
14
+ import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
14
15
 
15
16
  export type MfaSetupPayload = {
16
17
  readonly userId: string;
@@ -47,15 +48,14 @@ export type VerifyMfaSetupResult =
47
48
  | { readonly ok: false; readonly reason: "malformed" | "bad_signature" | "expired" };
48
49
 
49
50
  function isEncodedBody(value: unknown): value is EncodedBody {
50
- if (typeof value !== "object" || value === null) return false;
51
- const v = value as Record<string, unknown>;
51
+ if (!isPlainObject(value)) return false;
52
52
  return (
53
- typeof v["userId"] === "string" &&
54
- typeof v["totpSecretBase32"] === "string" &&
55
- Array.isArray(v["recoveryCodeHashes"]) &&
56
- v["recoveryCodeHashes"].every((h) => typeof h === "string") &&
57
- typeof v["expiresAtMs"] === "number" &&
58
- (v["tenantId"] === undefined || typeof v["tenantId"] === "string")
53
+ typeof value["userId"] === "string" &&
54
+ typeof value["totpSecretBase32"] === "string" &&
55
+ Array.isArray(value["recoveryCodeHashes"]) &&
56
+ value["recoveryCodeHashes"].every((h) => typeof h === "string") &&
57
+ typeof value["expiresAtMs"] === "number" &&
58
+ (value["tenantId"] === undefined || typeof value["tenantId"] === "string")
59
59
  );
60
60
  }
61
61
 
@@ -1,4 +1,11 @@
1
1
  [
2
+ {
3
+ "version": "0.363.0",
4
+ "type": "breaking",
5
+ "title": "Contract confirmation and termination mails use template variables",
6
+ "detail": "contract-confirmation, contract-termination-receipt and contract-termination-operator-notice are r.notification declarations with variables and localized templates (de, en). The notification types are qualified names now (billing-foundation:notify:<name>). The notify data is the template variable object (subject, details, contractLines, termsParagraphs, operatorEmail), not sections.",
7
+ "migration": "Update delivery preferences and tests that referenced the old type strings, and anything that listens to the notification data. An app that mounts it without includeBundled needs template-resolver, renderer-foundation and @cosmicdrift/kumiko-renderer-mail-html at boot (composeRendererStack() from dev-server mounts all of them), plus a delivery with the channel-email feature; apps without the template tables need a schema migration (`kumiko schema generate`)."
8
+ },
2
9
  {
3
10
  "version": "0.354.0",
4
11
  "type": "breaking",
@@ -1,6 +1,7 @@
1
1
  /// <reference types="temporal-polyfill/global" preserve="true" />
2
2
  // @runtime client
3
3
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
4
+ import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
4
5
 
5
6
  // Feature name
6
7
  export const BILLING_FOUNDATION_FEATURE = "billing-foundation" as const;
@@ -11,14 +12,14 @@ export const SUBSCRIPTION_PROVIDER_EXTENSION = "subscriptionProvider" as const;
11
12
 
12
13
  /** delivery notification type of the § 312f contract confirmation mail. */
13
14
  export const CONTRACT_CONFIRMATION_NOTIFICATION_TYPE =
14
- "billing-foundation:contract-confirmation" as const;
15
+ "billing-foundation:notify:contract-confirmation" as const;
15
16
 
16
17
  /** delivery notification types of the § 312k termination receipt and the
17
18
  * operator notice. */
18
19
  export const CONTRACT_TERMINATION_RECEIPT_NOTIFICATION_TYPE =
19
- "billing-foundation:contract-termination-receipt" as const;
20
+ "billing-foundation:notify:contract-termination-receipt" as const;
20
21
  export const CONTRACT_TERMINATION_OPERATOR_NOTIFICATION_TYPE =
21
- "billing-foundation:contract-termination-operator-notice" as const;
22
+ "billing-foundation:notify:contract-termination-operator-notice" as const;
22
23
 
23
24
  // Qualified write handler names (QN format: scope:type:name).
24
25
  export const SubscriptionFoundationHandlers = {
@@ -230,5 +231,5 @@ export const TERMINATION_SCOPES = ["tenant-host", "platform"] as const;
230
231
  export type TerminationScope = (typeof TERMINATION_SCOPES)[number];
231
232
 
232
233
  export function isTerminationScope(value: unknown): value is TerminationScope {
233
- return (TERMINATION_SCOPES as readonly unknown[]).includes(value);
234
+ return isOneOf(TERMINATION_SCOPES, value);
234
235
  }