@cosmicdrift/kumiko-bundled-features 0.363.0 → 0.365.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 (361) hide show
  1. package/dist/audit/feature.js +1 -1
  2. package/dist/audit/handlers/list.query.js +9 -5
  3. package/dist/audit/run-escape-hatch-retention.d.ts +1 -0
  4. package/dist/audit/run-escape-hatch-retention.js +39 -12
  5. package/dist/auth-email-password/bootstrap.d.ts +9 -8
  6. package/dist/auth-email-password/bootstrap.js +12 -4
  7. package/dist/auth-email-password/email-rate-limits.d.ts +4 -0
  8. package/dist/auth-email-password/email-rate-limits.js +13 -0
  9. package/dist/auth-email-password/feature.js +2 -0
  10. package/dist/auth-email-password/handlers/change-password.write.d.ts +1 -1
  11. package/dist/auth-email-password/handlers/change-password.write.js +6 -2
  12. package/dist/auth-email-password/handlers/confirm-account-unlock.write.d.ts +1 -1
  13. package/dist/auth-email-password/handlers/invite-accept-with-login.write.d.ts +1 -1
  14. package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +2 -3
  15. package/dist/auth-email-password/handlers/invite-accept.write.d.ts +1 -1
  16. package/dist/auth-email-password/handlers/invite-create.write.d.ts +4 -5
  17. package/dist/auth-email-password/handlers/invite-create.write.js +4 -1
  18. package/dist/auth-email-password/handlers/invite-signup-complete.write.d.ts +1 -1
  19. package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
  20. package/dist/auth-email-password/handlers/logout.write.d.ts +1 -1
  21. package/dist/auth-email-password/handlers/logout.write.js +6 -2
  22. package/dist/auth-email-password/handlers/request-account-unlock.write.d.ts +1 -1
  23. package/dist/auth-email-password/handlers/request-email-verification.write.d.ts +1 -1
  24. package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -1
  25. package/dist/auth-email-password/handlers/reset-password.write.d.ts +1 -1
  26. package/dist/auth-email-password/handlers/signup-confirm.write.d.ts +1 -1
  27. package/dist/auth-email-password/handlers/signup-request.write.d.ts +1 -1
  28. package/dist/auth-email-password/handlers/signup-request.write.js +2 -1
  29. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +2 -1
  30. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +1 -1
  31. package/dist/auth-email-password/handlers/token-request-handler.d.ts +1 -1
  32. package/dist/auth-email-password/handlers/token-request-handler.js +2 -1
  33. package/dist/auth-email-password/handlers/verify-email.write.d.ts +1 -1
  34. package/dist/auth-email-password/magic-link-mail.js +6 -3
  35. package/dist/auth-email-password/mail-i18n.d.ts +3 -0
  36. package/dist/auth-email-password/mail-i18n.js +10 -0
  37. package/dist/auth-email-password/web/auth-gate.js +16 -4
  38. package/dist/auth-mfa/handlers/disable.write.d.ts +1 -1
  39. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.d.ts +1 -1
  40. package/dist/auth-mfa/handlers/enable-confirm.write.d.ts +1 -1
  41. package/dist/auth-mfa/handlers/enable-start-preauth.write.d.ts +1 -1
  42. package/dist/auth-mfa/handlers/enable-start.write.d.ts +1 -1
  43. package/dist/auth-mfa/handlers/regenerate-recovery.write.d.ts +1 -1
  44. package/dist/auth-mfa/handlers/verify.write.d.ts +1 -1
  45. package/dist/billing-foundation/consumer-protection/confirmation-mail.js +52 -5
  46. package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +3 -0
  47. package/dist/billing-foundation/consumer-protection/consent-gate.js +21 -2
  48. package/dist/billing-foundation/consumer-protection/mail-notifications.js +1 -1
  49. package/dist/billing-foundation/consumer-protection/register-termination.js +11 -1
  50. package/dist/billing-foundation/consumer-protection/termination-mail.d.ts +5 -3
  51. package/dist/billing-foundation/consumer-protection/termination-mail.js +1 -2
  52. package/dist/billing-foundation/consumer-protection/termination-receipt-limit.d.ts +6 -0
  53. package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +34 -0
  54. package/dist/billing-foundation/consumer-protection/termination-record.d.ts +2 -0
  55. package/dist/billing-foundation/consumer-protection/termination-record.js +15 -2
  56. package/dist/billing-foundation/consumer-protection/termination-request.js +30 -20
  57. package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +1 -1
  58. package/dist/billing-foundation/consumer-protection/termination-texts.js +6 -0
  59. package/dist/billing-foundation/consumer-protection/withdrawal-period.d.ts +10 -0
  60. package/dist/billing-foundation/consumer-protection/withdrawal-period.js +26 -0
  61. package/dist/billing-foundation/handlers/create-checkout-session.write.js +11 -5
  62. package/dist/billing-foundation/handlers/process-event.write.js +10 -6
  63. package/dist/billing-foundation/i18n.js +9 -0
  64. package/dist/billing-foundation/subscription-tier-sync.d.ts +7 -1
  65. package/dist/billing-foundation/subscription-tier-sync.js +21 -7
  66. package/dist/billing-foundation/types.d.ts +7 -2
  67. package/dist/cap-counter/book-cap-usage.js +8 -0
  68. package/dist/cap-counter/calendar-cap-usage-reservation.js +2 -0
  69. package/dist/cap-counter/cap-reservation.js +7 -3
  70. package/dist/cap-counter/enforce-cap.d.ts +18 -1
  71. package/dist/cap-counter/enforce-cap.js +49 -6
  72. package/dist/cap-counter/handlers/increment-rolling.write.d.ts +1 -0
  73. package/dist/cap-counter/handlers/increment-rolling.write.js +2 -0
  74. package/dist/cap-counter/index.d.ts +1 -1
  75. package/dist/cap-counter/index.js +1 -1
  76. package/dist/cap-counter/with-cap-enforcement.d.ts +2 -0
  77. package/dist/cap-overview/handlers/tenant-caps-list.query.js +3 -1
  78. package/dist/cap-overview/handlers/tenant-options.query.js +3 -1
  79. package/dist/channel-discord/discord-channel.js +3 -2
  80. package/dist/channel-email/email-channel.js +40 -23
  81. package/dist/channel-email/smtp-transport.js +6 -1
  82. package/dist/channel-in-app/handlers/mark-all-read.write.d.ts +1 -1
  83. package/dist/channel-in-app/handlers/mark-read.write.d.ts +1 -1
  84. package/dist/channel-slack/slack-channel.js +3 -2
  85. package/dist/channel-teams/teams-channel.js +3 -2
  86. package/dist/compliance-profiles/handlers/set-profile.write.d.ts +1 -1
  87. package/dist/config/handlers/reset.write.d.ts +1 -1
  88. package/dist/config/handlers/set.write.d.ts +1 -1
  89. package/dist/config/handlers/set.write.js +2 -2
  90. package/dist/config/resolver.d.ts +1 -0
  91. package/dist/config/resolver.js +11 -14
  92. package/dist/config/write-helpers.d.ts +2 -0
  93. package/dist/config/write-helpers.js +19 -0
  94. package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -1
  95. package/dist/crypto-shredding/handlers/forget-subject.write.js +30 -20
  96. package/dist/custom-fields/db/queries/retention.d.ts +1 -1
  97. package/dist/custom-fields/db/queries/retention.js +2 -1
  98. package/dist/custom-fields/handlers/update-tenant-field.write.js +0 -1
  99. package/dist/data-retention/run-retention-cleanup.js +4 -5
  100. package/dist/delivery/attachment-ref.d.ts +2 -0
  101. package/dist/delivery/attachment-ref.js +30 -1
  102. package/dist/delivery/channel-context.js +19 -1
  103. package/dist/delivery/delivery-service.js +71 -82
  104. package/dist/delivery/handlers/resubscribe-address.write.d.ts +1 -1
  105. package/dist/delivery/handlers/resubscribe-user.write.d.ts +1 -1
  106. package/dist/delivery/handlers/set-preference.write.d.ts +1 -1
  107. package/dist/delivery/handlers/unsubscribe-address.write.d.ts +1 -1
  108. package/dist/delivery/handlers/unsubscribe-user.write.d.ts +1 -1
  109. package/dist/delivery/index.d.ts +1 -1
  110. package/dist/delivery/index.js +1 -1
  111. package/dist/delivery/jobs.js +43 -11
  112. package/dist/delivery/types.d.ts +7 -1
  113. package/dist/feature-toggles/handlers/set.write.d.ts +1 -1
  114. package/dist/feature-toggles/toggle-runtime.d.ts +5 -3
  115. package/dist/feature-toggles/toggle-runtime.js +25 -12
  116. package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
  117. package/dist/file-foundation/read-file-ref-content.d.ts +1 -1
  118. package/dist/file-foundation/read-file-ref-content.js +6 -4
  119. package/dist/files-tenant-data/hooks.js +4 -0
  120. package/dist/files-tenant-data/index.d.ts +1 -0
  121. package/dist/files-tenant-data/index.js +2 -1
  122. package/dist/form-draft/db/queries/owned-file-refs.d.ts +1 -1
  123. package/dist/form-draft/db/queries/owned-file-refs.js +9 -2
  124. package/dist/form-draft/handlers/discard.write.d.ts +1 -1
  125. package/dist/form-draft/handlers/discard.write.js +1 -1
  126. package/dist/form-draft/handlers/save.write.d.ts +1 -1
  127. package/dist/inbound-mail-foundation/oauth-access-token.js +8 -3
  128. package/dist/inbound-mail-foundation/watch-supervisor.js +3 -1
  129. package/dist/jobs/db/queries/stale-run-sweep.d.ts +1 -1
  130. package/dist/jobs/db/queries/stale-run-sweep.js +12 -5
  131. package/dist/jobs/feature.js +5 -3
  132. package/dist/jobs/handlers/list.query.js +3 -1
  133. package/dist/jobs/handlers/retry.write.d.ts +1 -1
  134. package/dist/jobs/handlers/stale-run-sweep.job.d.ts +1 -1
  135. package/dist/jobs/handlers/stale-run-sweep.job.js +2 -2
  136. package/dist/jobs/handlers/trigger.write.d.ts +1 -1
  137. package/dist/jobs/job-run-logger.js +35 -10
  138. package/dist/jobs/tenant-job-run-table.js +3 -2
  139. package/dist/ledger/entity.js +3 -1
  140. package/dist/managed-pages/handlers/set.write.d.ts +1 -1
  141. package/dist/metrics/filters.d.ts +16 -1
  142. package/dist/metrics/filters.js +27 -11
  143. package/dist/personal-access-tokens/handlers/create.write.d.ts +1 -1
  144. package/dist/personal-access-tokens/handlers/revoke.write.d.ts +1 -1
  145. package/dist/personal-access-tokens/revoke-for-user.js +3 -7
  146. package/dist/renderer-foundation/types.d.ts +2 -0
  147. package/dist/renderer-pdf/render-table-pdf.d.ts +27 -33
  148. package/dist/renderer-pdf/render-table-pdf.js +95 -34
  149. package/dist/secrets/feature.js +4 -3
  150. package/dist/secrets/handlers/delete.write.d.ts +1 -1
  151. package/dist/secrets/handlers/delete.write.js +3 -1
  152. package/dist/secrets/handlers/set.write.d.ts +1 -1
  153. package/dist/secrets/secrets-context.js +15 -3
  154. package/dist/secrets/write-gate.d.ts +3 -1
  155. package/dist/secrets/write-gate.js +6 -3
  156. package/dist/sessions/handlers/revoke-all-for-user.write.d.ts +1 -1
  157. package/dist/sessions/handlers/revoke-all-others.write.d.ts +1 -1
  158. package/dist/sessions/handlers/revoke.write.d.ts +1 -1
  159. package/dist/shared/chunked-entity-migration.d.ts +1 -0
  160. package/dist/shared/chunked-entity-migration.js +1 -1
  161. package/dist/shared/index.d.ts +1 -0
  162. package/dist/shared/index.js +1 -0
  163. package/dist/shared/mail-template-body.d.ts +3 -0
  164. package/dist/shared/mail-template-body.js +10 -0
  165. package/dist/shared/resource-image-formats.d.ts +23 -0
  166. package/dist/shared/resource-image-formats.js +21 -0
  167. package/dist/shared/sharp-input-limits.js +7 -8
  168. package/dist/step-dispatcher/webhook-runner.js +2 -2
  169. package/dist/subscription-stripe/plugin-methods.js +18 -8
  170. package/dist/subscription-stripe/verify-webhook.d.ts +1 -1
  171. package/dist/template-resolver/api.js +3 -2
  172. package/dist/template-resolver/compile-document-body.d.ts +7 -0
  173. package/dist/template-resolver/compile-document-body.js +114 -21
  174. package/dist/template-resolver/compile-steps-block.d.ts +10 -0
  175. package/dist/template-resolver/compile-steps-block.js +45 -0
  176. package/dist/template-resolver/constants.d.ts +6 -3
  177. package/dist/template-resolver/constants.js +13 -2
  178. package/dist/template-resolver/document-sanitizer.js +3 -0
  179. package/dist/template-resolver/document-schema.d.ts +3 -2
  180. package/dist/template-resolver/document-schema.js +29 -4
  181. package/dist/template-resolver/document-steps.d.ts +17 -0
  182. package/dist/template-resolver/document-steps.js +93 -0
  183. package/dist/template-resolver/feature.d.ts +2 -0
  184. package/dist/template-resolver/feature.js +13 -1
  185. package/dist/template-resolver/handlers/bundle-import.d.ts +7 -1
  186. package/dist/template-resolver/handlers/bundle-import.js +63 -38
  187. package/dist/template-resolver/handlers/collection-set.write.d.ts +1 -1
  188. package/dist/template-resolver/handlers/collection-set.write.js +4 -3
  189. package/dist/template-resolver/handlers/copy-locale.write.d.ts +1 -1
  190. package/dist/template-resolver/handlers/copy-locale.write.js +95 -49
  191. package/dist/template-resolver/handlers/diff-to-system.query.js +5 -38
  192. package/dist/template-resolver/handlers/discard-draft.write.d.ts +13 -0
  193. package/dist/template-resolver/handlers/discard-draft.write.js +37 -0
  194. package/dist/template-resolver/handlers/export-bundle.query.js +51 -1
  195. package/dist/template-resolver/handlers/find-by-id.query.d.ts +1 -0
  196. package/dist/template-resolver/handlers/find-by-id.query.js +6 -3
  197. package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +1 -1
  198. package/dist/template-resolver/handlers/import-bundle-system.write.js +16 -5
  199. package/dist/template-resolver/handlers/import-bundle.write.d.ts +1 -1
  200. package/dist/template-resolver/handlers/import-bundle.write.js +11 -1
  201. package/dist/template-resolver/handlers/layout-source.query.d.ts +5 -0
  202. package/dist/template-resolver/handlers/layout-source.query.js +46 -23
  203. package/dist/template-resolver/handlers/list.query.js +4 -1
  204. package/dist/template-resolver/handlers/override-diff.d.ts +20 -0
  205. package/dist/template-resolver/handlers/override-diff.js +41 -0
  206. package/dist/template-resolver/handlers/overview.query.d.ts +9 -0
  207. package/dist/template-resolver/handlers/overview.query.js +60 -12
  208. package/dist/template-resolver/handlers/reset-to-system.write.d.ts +1 -1
  209. package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +1 -1
  210. package/dist/template-resolver/handlers/resource-remove.write.d.ts +1 -1
  211. package/dist/template-resolver/handlers/resource-upload.write.d.ts +2 -2
  212. package/dist/template-resolver/handlers/resource-upload.write.js +36 -6
  213. package/dist/template-resolver/handlers/send-test.write.d.ts +3 -1
  214. package/dist/template-resolver/handlers/send-test.write.js +37 -10
  215. package/dist/template-resolver/handlers/set.write.d.ts +1 -1
  216. package/dist/template-resolver/handlers/template-gates.js +13 -2
  217. package/dist/template-resolver/handlers/template-write.js +36 -4
  218. package/dist/template-resolver/handlers/tenant-overrides.query.d.ts +32 -0
  219. package/dist/template-resolver/handlers/tenant-overrides.query.js +80 -16
  220. package/dist/template-resolver/handlers/toggle-status.write.d.ts +17 -4
  221. package/dist/template-resolver/handlers/toggle-status.write.js +35 -19
  222. package/dist/template-resolver/handlers/upsert-system.write.d.ts +1 -1
  223. package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +1 -1
  224. package/dist/template-resolver/handlers/upsert-tenant.write.js +1 -1
  225. package/dist/template-resolver/handlers/usage.query.js +11 -2
  226. package/dist/template-resolver/handlers/validate.query.js +4 -1
  227. package/dist/template-resolver/index.d.ts +5 -4
  228. package/dist/template-resolver/index.js +5 -4
  229. package/dist/template-resolver/layout-appearance.d.ts +10 -0
  230. package/dist/template-resolver/layout-appearance.js +36 -0
  231. package/dist/template-resolver/mail-constants.d.ts +2 -0
  232. package/dist/template-resolver/mail-constants.js +3 -0
  233. package/dist/template-resolver/pdf-constants.d.ts +7 -0
  234. package/dist/template-resolver/pdf-constants.js +8 -0
  235. package/dist/template-resolver/qualified-names.d.ts +4 -0
  236. package/dist/template-resolver/qualified-names.js +4 -0
  237. package/dist/template-resolver/resource-image.d.ts +2 -9
  238. package/dist/template-resolver/resource-image.js +2 -11
  239. package/dist/template-resolver/seeding.js +23 -4
  240. package/dist/template-resolver/store-template-resource.d.ts +53 -9
  241. package/dist/template-resolver/store-template-resource.js +250 -97
  242. package/dist/template-resolver/table.d.ts +103 -0
  243. package/dist/template-resolver/table.js +9 -2
  244. package/dist/template-resolver/template-bundle.js +4 -0
  245. package/dist/template-resolver/template-draft.d.ts +54 -0
  246. package/dist/template-resolver/template-draft.js +72 -0
  247. package/dist/template-resolver/template-meta.d.ts +35 -6
  248. package/dist/template-resolver/template-meta.js +67 -16
  249. package/dist/template-resolver/template-preview.d.ts +19 -2
  250. package/dist/template-resolver/template-preview.js +98 -55
  251. package/dist/template-resolver/template-resources.js +3 -4
  252. package/dist/template-resolver/template-usage.d.ts +5 -1
  253. package/dist/template-resolver/template-usage.js +14 -9
  254. package/dist/template-resolver/template-validation.js +13 -9
  255. package/dist/template-resolver/web/client-plugin.js +4 -2
  256. package/dist/template-resolver/web/document-editor-extensions.d.ts +5 -1
  257. package/dist/template-resolver/web/document-editor-extensions.js +76 -13
  258. package/dist/template-resolver/web/document-editor.d.ts +2 -1
  259. package/dist/template-resolver/web/document-editor.js +12 -2
  260. package/dist/template-resolver/web/document-tiptap-editor.d.ts +1 -1
  261. package/dist/template-resolver/web/document-tiptap-editor.js +173 -34
  262. package/dist/template-resolver/web/document-toolbar-blocks.d.ts +13 -5
  263. package/dist/template-resolver/web/document-toolbar-blocks.js +6 -0
  264. package/dist/template-resolver/web/i18n.js +24 -4
  265. package/dist/template-resolver/web/index.d.ts +2 -0
  266. package/dist/template-resolver/web/index.js +2 -0
  267. package/dist/template-resolver/web/placeholder-line-editor.d.ts +6 -2
  268. package/dist/template-resolver/web/placeholder-line-editor.js +9 -7
  269. package/dist/tenant/handlers/add-member.write.d.ts +1 -1
  270. package/dist/tenant/handlers/cancel-invitation.write.d.ts +1 -1
  271. package/dist/tenant/handlers/create.write.d.ts +1 -1
  272. package/dist/tenant/handlers/remove-member.write.d.ts +1 -1
  273. package/dist/tenant/handlers/toggle-enabled.write.d.ts +2 -2
  274. package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
  275. package/dist/tenant/handlers/update-member-roles.write.js +3 -6
  276. package/dist/tenant/handlers/update.write.d.ts +1 -1
  277. package/dist/tenant/seeding.js +23 -24
  278. package/dist/tenant-handover/handlers/claim.write.d.ts +1 -1
  279. package/dist/tenant-handover/move-entity-graph.js +14 -4
  280. package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +1 -1
  281. package/dist/tenant-lifecycle/handlers/request-destruction.write.d.ts +1 -1
  282. package/dist/tenant-lifecycle/run-tenant-destroy.d.ts +1 -0
  283. package/dist/tenant-lifecycle/run-tenant-destroy.js +8 -1
  284. package/dist/tenant-lifecycle/stages.js +3 -2
  285. package/dist/tier-engine/constants.d.ts +2 -0
  286. package/dist/tier-engine/constants.js +4 -0
  287. package/dist/tier-engine/entity.d.ts +15 -14
  288. package/dist/tier-engine/entity.js +4 -3
  289. package/dist/tier-engine/feature.js +9 -3
  290. package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +6 -1
  291. package/dist/tier-engine/handlers/set-tenant-tier.write.js +7 -4
  292. package/dist/tier-engine/index.d.ts +1 -1
  293. package/dist/tier-engine/index.js +1 -1
  294. package/dist/tier-engine/tier-resolver.d.ts +13 -13
  295. package/dist/user/constants.d.ts +1 -0
  296. package/dist/user/handlers/create.write.d.ts +1 -1
  297. package/dist/user/handlers/update.write.d.ts +1 -1
  298. package/dist/user/invited-global-roles.d.ts +2 -4
  299. package/dist/user/resolve-display-names.js +5 -9
  300. package/dist/user/seeding.js +20 -1
  301. package/dist/user-data-rights/email-templates.js +0 -1
  302. package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +1 -1
  303. package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.d.ts +1 -1
  304. package/dist/user-data-rights/handlers/lift-restriction.write.d.ts +1 -1
  305. package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +1 -1
  306. package/dist/user-data-rights/handlers/request-deletion.write.d.ts +1 -1
  307. package/dist/user-data-rights/handlers/request-export.write.d.ts +1 -1
  308. package/dist/user-data-rights/handlers/restrict-account.write.d.ts +1 -1
  309. package/dist/user-data-rights/handlers/run-forget-cleanup.write.d.ts +1 -1
  310. package/dist/user-data-rights/i18n.js +5 -0
  311. package/dist/user-data-rights/lib/update-user-lifecycle.d.ts +2 -0
  312. package/dist/user-data-rights/lib/update-user-lifecycle.js +6 -2
  313. package/dist/user-data-rights/run-forget-cleanup.js +2 -2
  314. package/dist/user-profile/handlers/change-email.write.d.ts +1 -1
  315. package/dist/waitlist/feature.js +1 -1
  316. package/dist/waitlist/handlers/invite.write.d.ts +1 -1
  317. package/dist/waitlist/handlers/invite.write.js +21 -3
  318. package/dist/waitlist/handlers/reject.write.d.ts +1 -1
  319. package/dist/waitlist/handlers/reject.write.js +3 -16
  320. package/dist/waitlist/handlers/submit.write.d.ts +1 -1
  321. package/dist/waitlist/handlers/submit.write.js +29 -7
  322. package/dist/waitlist/i18n.js +9 -0
  323. package/dist/waitlist/lib.d.ts +2 -1
  324. package/dist/waitlist/lib.js +15 -0
  325. package/dist/waitlist/options.js +1 -1
  326. package/dist/workflow-runner/event-trigger.js +2 -2
  327. package/dist/workflow-runner/feature.js +1 -1
  328. package/dist/workflow-runner/handlers/resume-run.write.js +7 -2
  329. package/package.json +19 -10
  330. package/src/audit/changes.json +18 -0
  331. package/src/auth-email-password/changes.json +55 -0
  332. package/src/billing-foundation/changes.json +48 -0
  333. package/src/cap-counter/changes.json +19 -0
  334. package/src/cap-overview/changes.json +6 -0
  335. package/src/channel-email/changes.json +18 -0
  336. package/src/config/changes.json +6 -0
  337. package/src/crypto-shredding/changes.json +6 -0
  338. package/src/custom-fields/changes.json +6 -0
  339. package/src/delivery/changes.json +30 -0
  340. package/src/document-ingest-foundation/changes.json +6 -0
  341. package/src/feature-toggles/changes.json +7 -0
  342. package/src/file-foundation/changes.json +6 -0
  343. package/src/files-tenant-data/changes.json +6 -0
  344. package/src/inbound-mail-foundation/changes.json +6 -0
  345. package/src/jobs/changes.json +31 -0
  346. package/src/ledger/changes.json +7 -0
  347. package/src/metrics/changes.json +6 -0
  348. package/src/personal-access-tokens/changes.json +6 -0
  349. package/src/renderer-pdf/changes.json +12 -0
  350. package/src/secrets/changes.json +12 -0
  351. package/src/shared/changes.json +6 -0
  352. package/src/subscription-stripe/changes.json +6 -0
  353. package/src/template-resolver/changes.json +149 -0
  354. package/src/tenant/changes.json +6 -0
  355. package/src/tenant-lifecycle/changes.json +6 -0
  356. package/src/tier-engine/changes.json +18 -0
  357. package/src/user/changes.json +6 -0
  358. package/src/user-data-rights/changes.json +12 -0
  359. package/src/user-profile/changes.json +1 -1
  360. package/src/waitlist/changes.json +18 -0
  361. package/src/workflow-runner/changes.json +6 -0
@@ -51,7 +51,7 @@ export function createAuditFeature() {
51
51
  db: ctx.db.unsafeRaw(),
52
52
  registry: ctx.registry,
53
53
  configResolver: ctx.configResolver,
54
- userId: ctx._userId ?? SYSTEM_USER_ID,
54
+ userId: SYSTEM_USER_ID,
55
55
  });
56
56
  },
57
57
  });
@@ -88,11 +88,15 @@ export const listQuery = defineQueryHandler({
88
88
  aggregateType: scopeFilter.aggregateType ?? p.aggregateType,
89
89
  }, direction);
90
90
  const rows = await selectMany(ctx.db, eventsTable, where, {
91
- // `id` as tie-breaker keeps the order total, so pages never overlap.
92
- orderBy: [
93
- { col: p.sort === "type" ? "type" : "createdAt", direction },
94
- { col: "id", direction },
95
- ],
91
+ // The id cursor only resumes an id-ordered listing. created_at comes from the
92
+ // transaction start and can run against insert order, so ordering by it would let
93
+ // a page boundary skip rows; only the type sort leaves id order (and is one page).
94
+ orderBy: p.sort === "type"
95
+ ? [
96
+ { col: "type", direction },
97
+ { col: "id", direction },
98
+ ]
99
+ : [{ col: "id", direction }],
96
100
  limit: p.limit,
97
101
  });
98
102
  const serialised = rows.map((r) => ({
@@ -6,6 +6,7 @@ export declare function runEscapeHatchRetention(args: {
6
6
  readonly registry: Registry;
7
7
  readonly configResolver: ConfigResolver | undefined;
8
8
  readonly userId: string;
9
+ readonly batchSize?: number;
9
10
  }): Promise<{
10
11
  readonly deletedCount: number;
11
12
  }>;
@@ -1,4 +1,4 @@
1
- import { aggregateWhere, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
1
+ import { aggregateWhere, runnerCanBeginTransaction, selectMany, } from "@cosmicdrift/kumiko-framework/bun-db";
2
2
  import { COMPLIANCE_PROFILES, subtractRetentionSpec, } from "@cosmicdrift/kumiko-framework/compliance";
3
3
  import { parseTenantId, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
4
4
  import { eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
@@ -10,7 +10,7 @@ import { DEFAULT_ESCAPE_HATCH_RETENTION_DAYS, ESCAPE_HATCH_RETENTION_DAYS_KEY, }
10
10
  import { ESCAPE_HATCH_USE_AGGREGATE_TYPE, escapeHatchUsedSchema, } from "./escape-hatch-audit-sink.js";
11
11
  // pruneEvents opens its own transaction, so a runner that cannot begin one (a tx handle) is rejected instead of nested.
12
12
  export function isDbConnection(runner) {
13
- return "begin" in runner && typeof runner.begin === "function";
13
+ return runnerCanBeginTransaction(runner);
14
14
  }
15
15
  async function resolveRetentionDays(args) {
16
16
  if (!args.configResolver)
@@ -19,18 +19,29 @@ async function resolveRetentionDays(args) {
19
19
  const raw = await config(ESCAPE_HATCH_RETENTION_DAYS_KEY);
20
20
  return typeof raw === "number" && raw >= 1 ? raw : DEFAULT_ESCAPE_HATCH_RETENTION_DAYS;
21
21
  }
22
+ // Only an operator-set value counts: the key's declared default must not override the tenant profiles.
23
+ async function resolveExplicitRetentionDays(args) {
24
+ const keyDef = args.registry.getConfigKey(ESCAPE_HATCH_RETENTION_DAYS_KEY);
25
+ if (!args.configResolver || !keyDef)
26
+ return undefined;
27
+ const { value, source } = await args.configResolver.getWithSource(ESCAPE_HATCH_RETENTION_DAYS_KEY, keyDef, SYSTEM_TENANT_ID, args.userId, args.db);
28
+ const isOperatorSet = source !== "default" && source !== "missing";
29
+ return isOperatorSet && typeof value === "number" && value >= 1 ? value : undefined;
30
+ }
22
31
  // Soft dependency: audit must stay mountable without compliance-profiles, so we probe its entity.
23
32
  const COMPLIANCE_PROFILE_ENTITY = "tenant-compliance-profile";
24
33
  const PRUNE_CHUNK_SIZE = 1000;
34
+ const PRUNE_BATCH_SIZE = 5000;
25
35
  // Overrides may only lengthen retention: the earlier cutoff (same `now`) is the longer period.
26
36
  function earlierInstant(a, b) {
27
37
  return Temporal.Instant.compare(a, b) <= 0 ? a : b;
28
38
  }
29
- async function resolveTenantCutoff(db, tenantId, now) {
39
+ async function resolveTenantCutoff(db, tenantId, now, operatorFloor) {
30
40
  const { profile } = await resolveProfileForTenant({ db, tenantId });
31
41
  const effective = subtractRetentionSpec(now, profile.auditLog.retention);
32
42
  const base = subtractRetentionSpec(now, COMPLIANCE_PROFILES[profile.key].auditLog.retention);
33
- return earlierInstant(effective, base);
43
+ const profileCutoff = earlierInstant(effective, base);
44
+ return operatorFloor ? earlierInstant(profileCutoff, operatorFloor) : profileCutoff;
34
45
  }
35
46
  // A cross-tenant audit (e.g. identity-switch) lives under the actor's tenant but must also
36
47
  // satisfy the target tenant's retention. A malformed payload falls back to the storing tenant.
@@ -45,14 +56,17 @@ async function storingTenantIds(db) {
45
56
  return tenantId ? [tenantId] : [];
46
57
  });
47
58
  }
48
- async function pruneByTenantProfiles(db) {
59
+ async function pruneByTenantProfiles(db, operatorRetentionDays) {
49
60
  const now = getTemporal().Now.instant();
61
+ const operatorFloor = operatorRetentionDays === undefined
62
+ ? undefined
63
+ : now.subtract({ hours: operatorRetentionDays * 24 });
50
64
  const cutoffByTenant = new Map();
51
65
  const cutoffOf = async (tenantId) => {
52
66
  const cached = cutoffByTenant.get(tenantId);
53
67
  if (cached)
54
68
  return cached;
55
- const cutoff = await resolveTenantCutoff(db, tenantId, now);
69
+ const cutoff = await resolveTenantCutoff(db, tenantId, now, operatorFloor);
56
70
  cutoffByTenant.set(tenantId, cutoff);
57
71
  return cutoff;
58
72
  };
@@ -87,6 +101,22 @@ async function pruneByTenantProfiles(db) {
87
101
  }
88
102
  return deletedCount;
89
103
  }
104
+ async function pruneInBatches(db, olderThanDays, batchSize = PRUNE_BATCH_SIZE) {
105
+ const olderThan = getTemporal()
106
+ .Now.instant()
107
+ .subtract({ hours: olderThanDays * 24 });
108
+ let deletedCount = 0;
109
+ for (;;) {
110
+ const batch = await pruneEvents(db, {
111
+ aggregateTypes: [ESCAPE_HATCH_USE_AGGREGATE_TYPE],
112
+ olderThan,
113
+ batchSize,
114
+ });
115
+ deletedCount += batch.deletedCount;
116
+ if (batch.deletedCount < batchSize)
117
+ return deletedCount;
118
+ }
119
+ }
90
120
  export async function runEscapeHatchRetention(args) {
91
121
  const { db } = args;
92
122
  if (!isDbConnection(db)) {
@@ -94,11 +124,8 @@ export async function runEscapeHatchRetention(args) {
94
124
  }
95
125
  if (!args.registry.getEntity(COMPLIANCE_PROFILE_ENTITY)) {
96
126
  const olderThanDays = await resolveRetentionDays({ ...args, db });
97
- const { deletedCount } = await pruneEvents(db, {
98
- aggregateTypes: [ESCAPE_HATCH_USE_AGGREGATE_TYPE],
99
- olderThanDays,
100
- });
101
- return { deletedCount };
127
+ return { deletedCount: await pruneInBatches(db, olderThanDays, args.batchSize) };
102
128
  }
103
- return { deletedCount: await pruneByTenantProfiles(db) };
129
+ const operatorRetentionDays = await resolveExplicitRetentionDays({ ...args, db });
130
+ return { deletedCount: await pruneByTenantProfiles(db, operatorRetentionDays) };
104
131
  }
@@ -1,10 +1,10 @@
1
- import { makeDispatchSystemWrite, type SystemDispatchArgs } from "@cosmicdrift/kumiko-framework/api";
1
+ import { makeDispatchSystemWrite, type RoleDispatchArgs } from "@cosmicdrift/kumiko-framework/api";
2
2
  import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
3
- import { type TenantId, type WriteResult } from "@cosmicdrift/kumiko-framework/engine";
3
+ import { type Registry, type TenantId, type WriteResult } from "@cosmicdrift/kumiko-framework/engine";
4
4
  import type { Dispatcher } from "@cosmicdrift/kumiko-framework/pipeline";
5
+ import type { EnvelopeCipher } from "@cosmicdrift/kumiko-framework/secrets";
5
6
  import type { Redis } from "ioredis";
6
- import type { GLOBAL_ROLE_OPTIONS } from "../user/constants.js";
7
- type GlobalRole = (typeof GLOBAL_ROLE_OPTIONS)[number];
7
+ import type { GlobalRole } from "../user/constants.js";
8
8
  export type BootstrapInvite = {
9
9
  readonly email: string;
10
10
  /** Membership role in the tenant, validated like a tenant-admin invite
@@ -18,9 +18,7 @@ export type BootstrapSeedDeps = {
18
18
  /** Writes as the system user with the given tenant roles in the seeded
19
19
  * tenant; use it when the app gates tenant data on its own roles rather
20
20
  * than SystemAdmin. */
21
- readonly dispatchWriteAs: (args: Pick<SystemDispatchArgs, "handlerQn" | "payload"> & {
22
- readonly roles: readonly string[];
23
- }) => Promise<WriteResult>;
21
+ readonly dispatchWriteAs: (args: Omit<RoleDispatchArgs, "tenantId">) => Promise<WriteResult>;
24
22
  };
25
23
  export type BootstrapConfigValue = string | number | boolean;
26
24
  export type BootstrapTenant = {
@@ -54,6 +52,10 @@ export type BootstrapDeps = {
54
52
  readonly db: DbConnection;
55
53
  readonly redis: Redis;
56
54
  readonly dispatcher: Dispatcher;
55
+ /** With these, a rerun can compare `encrypted`/`piiEncrypted` config keys by
56
+ * their decrypted value; without them such keys are rewritten on every run. */
57
+ readonly registry?: Registry;
58
+ readonly configCipher?: EnvelopeCipher;
57
59
  };
58
60
  export type BootstrapTenantOutcome = "created" | "exists";
59
61
  export type BootstrapInviteOutcome = "invited" | "resent" | "pending" | "active" | "cancelled" | "role-mismatch";
@@ -77,4 +79,3 @@ export declare class BootstrapPlanError extends Error {
77
79
  export declare class BootstrapWriteError extends Error {
78
80
  }
79
81
  export declare function bootstrapTenants(deps: BootstrapDeps, plan: BootstrapPlan): Promise<BootstrapReport>;
80
- export {};
@@ -4,14 +4,14 @@
4
4
  // exactly as for any other writer. Nobody gets a role before accepting: the
5
5
  // invitation row holds membership role + global roles until the invitee
6
6
  // accepts through the regular invite-accept routes.
7
- import { makeDispatchSystemWrite, } from "@cosmicdrift/kumiko-framework/api";
7
+ import { makeDispatchSystemWrite, makeDispatchWriteAs, } from "@cosmicdrift/kumiko-framework/api";
8
8
  import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
9
9
  import { ConfigScopes, createSystemUser, } from "@cosmicdrift/kumiko-framework/engine";
10
10
  import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
11
11
  // kumiko-lint-ignore cross-feature-import bootstrap sets per-tenant config through the config feature's write handler
12
12
  import { ConfigHandlers } from "../config/constants.js";
13
13
  // kumiko-lint-ignore cross-feature-import bootstrap compares against the stored config row to stay idempotent
14
- import { findConfigRow } from "../config/write-helpers.js";
14
+ import { configRowHoldsValue, findConfigRow } from "../config/write-helpers.js";
15
15
  // kumiko-lint-ignore cross-feature-import bootstrap provisions tenant + invitation rows owned by the tenant feature
16
16
  import { TenantHandlers } from "../tenant/constants.js";
17
17
  // kumiko-lint-ignore cross-feature-import bootstrap provisions tenant + invitation rows owned by the tenant feature
@@ -78,8 +78,10 @@ async function applyTenantConfig(deps, tenant) {
78
78
  const applied = [];
79
79
  for (const [key, value] of Object.entries(tenant.config ?? {})) {
80
80
  const existing = await findConfigRow(deps.db, key, tenant.id, null);
81
- if (existing?.value === JSON.stringify(value))
81
+ const serialized = JSON.stringify(value);
82
+ if (await configRowHoldsValue(existing, deps.registry?.getConfigKey(key), deps.configCipher, key, serialized)) {
82
83
  continue;
84
+ }
83
85
  const result = await deps.dispatcher.write(ConfigHandlers.set, { key, value, scope: ConfigScopes.tenant },
84
86
  // SystemAdmin on top of the system role: keys with the default tenant
85
87
  // write access (admin roles) reject a bare system user, system-only keys
@@ -125,6 +127,11 @@ async function decideInvite(deps, invite) {
125
127
  }
126
128
  if (invitation.status === INVITATION_STATUS.accepted)
127
129
  return "active";
130
+ // The plan dropped a global role (e.g. an admin moved to tenants[].invites):
131
+ // the stored invitation would still grant it on accept, so it is re-issued.
132
+ if (storedGlobalRoles.some((role) => !invite.globalRoles.some((planned) => planned === role))) {
133
+ return "resent";
134
+ }
128
135
  if (await hasLiveInviteToken(deps.redis, invitation.id))
129
136
  return "pending";
130
137
  return "resent";
@@ -135,6 +142,7 @@ function isMailingOutcome(outcome) {
135
142
  export async function bootstrapTenants(deps, plan) {
136
143
  const plannedInvites = planInvites(plan);
137
144
  const dispatchSystemWrite = makeDispatchSystemWrite(deps.dispatcher);
145
+ const dispatchWriteAsRoles = makeDispatchWriteAs(deps.dispatcher);
138
146
  const tenants = [];
139
147
  for (const tenant of plan.tenants) {
140
148
  const outcome = await ensureTenant(deps, tenant);
@@ -144,7 +152,7 @@ export async function bootstrapTenants(deps, plan) {
144
152
  tenantId: tenant.id,
145
153
  db: deps.db,
146
154
  dispatchSystemWrite,
147
- dispatchWriteAs: ({ handlerQn, payload, roles }) => deps.dispatcher.write(handlerQn, payload, createSystemUser(tenant.id, roles)),
155
+ dispatchWriteAs: (args) => dispatchWriteAsRoles({ ...args, tenantId: tenant.id }),
148
156
  });
149
157
  }
150
158
  const configApplied = await applyTenantConfig(deps, tenant);
@@ -0,0 +1,4 @@
1
+ import type { PayloadRateLimitOption } from "@cosmicdrift/kumiko-types/handlers";
2
+ export declare const AUTH_EMAIL_RATE_LIMIT_WINDOW_SECONDS: number;
3
+ export declare const AUTH_EMAIL_RATE_LIMIT_MAX_REQUESTS = 3;
4
+ export declare const AUTH_EMAIL_PAYLOAD_RATE_LIMITS: readonly PayloadRateLimitOption[];
@@ -0,0 +1,13 @@
1
+ // The bucket counts across all IPs, so a long window would let an attacker who
2
+ // knows an address lock its owner out of recovery/signup for the whole window.
3
+ // A short one still throttles mail bombing but clears within minutes.
4
+ export const AUTH_EMAIL_RATE_LIMIT_WINDOW_SECONDS = 15 * 60;
5
+ export const AUTH_EMAIL_RATE_LIMIT_MAX_REQUESTS = 3;
6
+ export const AUTH_EMAIL_PAYLOAD_RATE_LIMITS = [
7
+ {
8
+ per: { payloadField: "email" },
9
+ limit: AUTH_EMAIL_RATE_LIMIT_MAX_REQUESTS,
10
+ windowSeconds: AUTH_EMAIL_RATE_LIMIT_WINDOW_SECONDS,
11
+ normalize: "email",
12
+ },
13
+ ];
@@ -19,6 +19,7 @@ import { createSignupConfirmHandler } from "./handlers/signup-confirm.write.js";
19
19
  import { createSignupRequestHandler, } from "./handlers/signup-request.write.js";
20
20
  import { createSwitchTenantMfaGateHandler } from "./handlers/switch-tenant-mfa-gate.write.js";
21
21
  import { createVerifyEmailHandler } from "./handlers/verify-email.write.js";
22
+ import { AUTH_EMAIL_PASSWORD_MAIL_I18N } from "./mail-i18n.js";
22
23
  import { AUTH_MAIL_VARIABLES, authMailLocalizedTemplate, INVITE_MAIL_VARIABLES, sendsMagicLinkMail, } from "./mail-notifications.js";
23
24
  /**
24
25
  * Env-vars contract for the `auth-email-password` feature.
@@ -89,6 +90,7 @@ export function createAuthEmailPasswordFeature(opts = {}) {
89
90
  if (sendsMagicLinkMail(opts))
90
91
  r.requires("delivery");
91
92
  r.envSchema(authEmailPasswordEnvSchema);
93
+ r.translations({ keys: AUTH_EMAIL_PASSWORD_MAIL_I18N });
92
94
  // Declared unconditionally: apps also issue the activation link from their
93
95
  // own flows (issueSignupActivation) without enabling the signup option.
94
96
  r.notification("password-reset", {
@@ -4,4 +4,4 @@ export declare const changePasswordWrite: import("@cosmicdrift/kumiko-types/defi
4
4
  newPassword: z.ZodString;
5
5
  }, z.core.$strip>, {
6
6
  kind: string;
7
- }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
7
+ }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -1,4 +1,4 @@
1
- import { access, createSystemUser, defineWriteHandler, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
1
+ import { createSystemUser, defineWriteHandler, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import * as z from "zod";
3
3
  import { hashPassword, verifyPassword } from "../../shared/index.js";
4
4
  import { UserHandlers, UserQueries } from "../../user/index.js";
@@ -14,7 +14,11 @@ export const changePasswordWrite = defineWriteHandler({
14
14
  oldPassword: z.string().min(1),
15
15
  newPassword: passwordSchema,
16
16
  }),
17
- access: { roles: access.authenticated },
17
+ access: {
18
+ openToAll: {
19
+ reason: "Self-service: acts only on event.user.id and re-checks the caller's current password; invited members hold only an app role, so a fixed role list would lock them out.",
20
+ },
21
+ },
18
22
  escapeHatch: {
19
23
  grants: ["systemIdentity"],
20
24
  reason: "Reads the caller's own passwordHash (privileged-only field) via ctx.queryAs(SYSTEM, ...) " +
@@ -6,4 +6,4 @@ export declare function createConfirmAccountUnlockHandler(opts: ConfirmAccountUn
6
6
  token: z.ZodString;
7
7
  }, z.core.$strip>, {
8
8
  kind: "account-unlocked";
9
- }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
9
+ }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -18,4 +18,4 @@ export declare function createInviteAcceptWithLoginHandler(opts?: InviteAcceptWi
18
18
  token: z.ZodString;
19
19
  email: z.ZodEmail;
20
20
  password: z.ZodString;
21
- }, z.core.$strip>, InviteAcceptWithLoginData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
21
+ }, z.core.$strip>, InviteAcceptWithLoginData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -56,9 +56,8 @@ export function createInviteAcceptWithLoginHandler(opts = {}) {
56
56
  rateLimit: { per: "ip+handler", limit: 20, windowSeconds: 60 },
57
57
  escapeHatch: {
58
58
  grants: ["unsafeRaw"],
59
- reason: "Anonymous invite-accept has no session in the invited tenant yet — checks existing " +
60
- "membership via ctx.queryAs(SYSTEM, tenant:query:memberships) of the invitation's tenant. " +
61
- "Also reads the pending invitation by id (the invitee is not yet a member of the " +
59
+ reason: "Anonymous invite-accept has no session in the invited tenant yet — reads the pending " +
60
+ "invitation by id (the invitee is not yet a member of the " +
62
61
  "invitation's tenant), adds the membership and accepts the invitation in the " +
63
62
  "invitation's tenant, which differs from the caller's tenant, and reads the MFA " +
64
63
  "enrollment of that tenant via the mfaStatusChecker callback.",
@@ -8,4 +8,4 @@ export type InviteAcceptData = {
8
8
  };
9
9
  export declare function createInviteAcceptHandler(): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"invite-accept", z.ZodObject<{
10
10
  token: z.ZodString;
11
- }, z.core.$strip>, InviteAcceptData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
11
+ }, z.core.$strip>, InviteAcceptData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -1,7 +1,6 @@
1
1
  import * as z from "zod";
2
- import { GLOBAL_ROLE_OPTIONS } from "../../user/constants.js";
2
+ import { type GlobalRole } from "../../user/constants.js";
3
3
  import type { AuthMailLocale } from "../email-templates.js";
4
- type GlobalRole = (typeof GLOBAL_ROLE_OPTIONS)[number];
5
4
  export type InviteCreateData = {
6
5
  readonly kind: "invite-created";
7
6
  readonly invitationId: string;
@@ -33,12 +32,12 @@ export type InviteCreateOptions = {
33
32
  export declare function createInviteCreateHandler(opts: InviteCreateOptions): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"invite-create", z.ZodObject<{
34
33
  email: z.ZodEmail;
35
34
  role: z.ZodString;
36
- }, z.core.$strip>, InviteCreateData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
35
+ }, z.core.$strip>, InviteCreateData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
37
36
  export declare function createSystemInviteCreateHandler(opts: InviteCreateOptions): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"system-invite-create", z.ZodObject<{
38
37
  email: z.ZodEmail;
39
38
  role: z.ZodString;
40
39
  globalRoles: z.ZodDefault<z.ZodArray<z.ZodEnum<{
41
40
  SystemAdmin: "SystemAdmin";
42
41
  }>>>;
43
- }, z.core.$strip>, InviteCreateData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
44
- export {};
42
+ locale: z.ZodOptional<z.ZodString>;
43
+ }, z.core.$strip>, InviteCreateData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -20,6 +20,7 @@ import { generateToken } from "@cosmicdrift/kumiko-framework/api";
20
20
  import { createEventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
21
21
  import { access, assignableAppRolesOf, defineWriteHandler, } from "@cosmicdrift/kumiko-framework/engine";
22
22
  import { InternalError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
23
+ import { isValidLocaleTag } from "@cosmicdrift/kumiko-framework/i18n";
23
24
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
24
25
  import * as z from "zod";
25
26
  // kumiko-lint-ignore cross-feature-import invite-flow lebt in auth-email-password (Magic-Link-Pattern), DB-row-owner ist tenant-feature
@@ -133,7 +134,7 @@ async function issueInvitation(opts, ttlSeconds, request, inviter, ctx, refuseAc
133
134
  }
134
135
  const token = generateToken();
135
136
  await storeInviteToken(ctx.redis, { invitationId, token, ttlSeconds });
136
- const locale = resolveHandlerMailLocale(ctx, opts.locale);
137
+ const locale = request.locale ?? resolveHandlerMailLocale(ctx, opts.locale);
137
138
  await dispatchMagicLinkMail(ctx.notify, {
138
139
  handlerName: "invite-create",
139
140
  notificationType: AuthMailNotificationTypes.invite,
@@ -188,6 +189,8 @@ const SystemInviteCreateSchema = z.object({
188
189
  email: z.email(),
189
190
  role: z.string().min(1).max(50),
190
191
  globalRoles: z.array(z.enum(GLOBAL_ROLE_OPTIONS)).max(GLOBAL_ROLE_OPTIONS.length).default([]),
192
+ // Preferred over the request locale: provisioning callers know the invitee's language.
193
+ locale: z.string().max(35).refine(isValidLocaleTag, "invalid locale tag").optional(),
191
194
  });
192
195
  // System-only sibling of invite-create: the only path that can put global
193
196
  // roles (SystemAdmin) on an invitation. No session mint path yields the
@@ -13,4 +13,4 @@ export type InviteSignupCompleteOptions = Pick<LoginHandlerOptions, "mfaStatusCh
13
13
  export declare function createInviteSignupCompleteHandler(opts?: InviteSignupCompleteOptions): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"invite-signup-complete", z.ZodObject<{
14
14
  token: z.ZodString;
15
15
  password: z.ZodString;
16
- }, z.core.$strip>, InviteSignupCompleteData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
16
+ }, z.core.$strip>, InviteSignupCompleteData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -74,5 +74,5 @@ export declare function gateBuildSession(ctx: HandlerContext, userId: string, te
74
74
  export declare function createLoginHandler(opts?: LoginHandlerOptions): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"login", z.ZodObject<{
75
75
  email: z.ZodEmail;
76
76
  password: z.ZodString;
77
- }, z.core.$strip>, LoginResult, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
77
+ }, z.core.$strip>, LoginResult, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
78
78
  export {};
@@ -1,2 +1,2 @@
1
1
  import * as z from "zod";
2
- export declare const logoutWrite: import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"logout", z.ZodObject<{}, z.core.$strip>, unknown, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
2
+ export declare const logoutWrite: import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"logout", z.ZodObject<{}, z.core.$strip>, unknown, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -1,4 +1,4 @@
1
- import { access, defineWriteHandler, stepsPipeline } from "@cosmicdrift/kumiko-framework/engine";
1
+ import { defineWriteHandler, stepsPipeline } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import * as z from "zod";
3
3
  // Logout — JWT is stateless, so server-side we only return OK. A future
4
4
  // revocation list / session table can land here without changing the
@@ -6,7 +6,11 @@ import * as z from "zod";
6
6
  export const logoutWrite = defineWriteHandler({
7
7
  name: "logout",
8
8
  schema: z.object({}),
9
- access: { roles: access.authenticated },
9
+ access: {
10
+ openToAll: {
11
+ reason: "Self-service: touches no data, any signed-in member must be able to sign out regardless of which role they hold.",
12
+ },
13
+ },
10
14
  description: "Acknowledges a sign-out for the authenticated caller so the client can discard its token; the JWT is stateless, so no server-side session state changes here.",
11
15
  perform: stepsPipeline(({ r }) => [
12
16
  r.step.return({ isSuccess: true, data: { kind: "logged-out" } }),
@@ -3,4 +3,4 @@ export type RequestAccountUnlockOptions = TokenRequestOptions;
3
3
  export type RequestUnlockData = TokenRequestData<"unlock-requested">;
4
4
  export declare function createRequestAccountUnlockHandler(opts: RequestAccountUnlockOptions): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"request-account-unlock", import("zod").ZodObject<{
5
5
  email: import("zod").ZodEmail;
6
- }, import("zod/v4/core").$strip>, TokenRequestData<"unlock-requested">, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
6
+ }, import("zod/v4/core").$strip>, TokenRequestData<"unlock-requested">, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -3,4 +3,4 @@ export type RequestEmailVerificationOptions = TokenRequestOptions;
3
3
  export type RequestVerificationData = TokenRequestData<"verification-requested">;
4
4
  export declare function createRequestEmailVerificationHandler(opts: RequestEmailVerificationOptions): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"request-email-verification", import("zod").ZodObject<{
5
5
  email: import("zod").ZodEmail;
6
- }, import("zod/v4/core").$strip>, TokenRequestData<"verification-requested">, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
6
+ }, import("zod/v4/core").$strip>, TokenRequestData<"verification-requested">, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -3,4 +3,4 @@ export type RequestPasswordResetOptions = TokenRequestOptions;
3
3
  export type RequestResetData = TokenRequestData<"reset-requested">;
4
4
  export declare function createRequestPasswordResetHandler(opts: RequestPasswordResetOptions): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"request-password-reset", import("zod").ZodObject<{
5
5
  email: import("zod").ZodEmail;
6
- }, import("zod/v4/core").$strip>, TokenRequestData<"reset-requested">, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
6
+ }, import("zod/v4/core").$strip>, TokenRequestData<"reset-requested">, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -7,4 +7,4 @@ export declare function createResetPasswordHandler(opts: ResetPasswordOptions):
7
7
  newPassword: z.ZodString;
8
8
  }, z.core.$strip>, {
9
9
  kind: "password-reset";
10
- }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
10
+ }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -27,5 +27,5 @@ type ClaimedHandover = {
27
27
  export declare function createSignupConfirmHandler(opts?: SignupConfirmOptions): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"signup-confirm", z.ZodObject<{
28
28
  token: z.ZodString;
29
29
  password: z.ZodString;
30
- }, z.core.$strip>, SignupConfirmData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
30
+ }, z.core.$strip>, SignupConfirmData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
31
31
  export {};
@@ -29,4 +29,4 @@ export declare function createSignupRequestHandler(opts: SignupRequestOptions):
29
29
  entityType: z.ZodString;
30
30
  token: z.ZodString;
31
31
  }, z.core.$strip>>;
32
- }, z.core.$strip>, SignupRequestData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
32
+ }, z.core.$strip>, SignupRequestData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -24,6 +24,7 @@ import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
24
24
  import { InternalError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
25
25
  import * as z from "zod";
26
26
  import { findSignupHandoverProvider } from "../../shared/index.js";
27
+ import { AUTH_EMAIL_PAYLOAD_RATE_LIMITS } from "../email-rate-limits.js";
27
28
  import { resolveHandlerMailLocale } from "../magic-link-mail.js";
28
29
  import { AUTH_SELF_REGISTRATION_FEATURE } from "../self-registration-toggle.js";
29
30
  import { issueSignupActivation } from "../signup-activation.js";
@@ -48,7 +49,7 @@ export function createSignupRequestHandler(opts) {
48
49
  schema: SignupRequestSchema,
49
50
  access: { roles: ["anonymous"] },
50
51
  rateLimit: { per: "ip+handler", limit: 10, windowSeconds: 60 },
51
- additionalRateLimits: [{ per: { payloadField: "email" }, limit: 3, windowSeconds: 86400 }],
52
+ additionalRateLimits: AUTH_EMAIL_PAYLOAD_RATE_LIMITS,
52
53
  description: "Starts magic-link self-registration by mailing a fresh activation link to an address and invalidating any link still outstanding for it; the answer looks the same whether or not the address is already registered.",
53
54
  escapeHatch: { reason: HANDOVER_GRANT_VERIFY_REASON, grants: ["unsafeRaw"] },
54
55
  handler: async (event, ctx) => {
@@ -2,8 +2,9 @@ import * as z from "zod";
2
2
  import { gateEnforceMfa, type LoginHandlerOptions } from "./login.write.js";
3
3
  export type SwitchTenantMfaGateData = {
4
4
  readonly kind: "mfa-gate-clear";
5
+ readonly roles: readonly string[];
5
6
  } | NonNullable<Awaited<ReturnType<typeof gateEnforceMfa>>>;
6
7
  export declare function createSwitchTenantMfaGateHandler(opts: Pick<LoginHandlerOptions, "mfaStatusChecker">): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"switch-tenant-mfa-gate", z.ZodObject<{
7
8
  userId: z.ZodString;
8
9
  tenantId: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
9
- }, z.core.$strip>, SwitchTenantMfaGateData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
10
+ }, z.core.$strip>, SwitchTenantMfaGateData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -33,7 +33,7 @@ export function createSwitchTenantMfaGateHandler(opts) {
33
33
  const userRow = parseAuthUserRow(await ctx.queryAs(createSystemUser(tenantId), UserQueries.findForAuth, { id: userId }));
34
34
  const roles = buildSessionRoles(parseRoles(userRow?.roles ?? null), active.membership.roles);
35
35
  const mfaGate = await gateEnforceMfa(ctx, opts, userId, tenantId, roles);
36
- return { isSuccess: true, data: mfaGate ?? { kind: "mfa-gate-clear" } };
36
+ return { isSuccess: true, data: mfaGate ?? { kind: "mfa-gate-clear", roles } };
37
37
  },
38
38
  });
39
39
  }
@@ -41,4 +41,4 @@ export type TokenRequestOptions = {
41
41
  };
42
42
  export declare function createTokenRequestHandler<TName extends string, TSuccessKind extends string>(spec: TokenRequestSpec<TName, TSuccessKind>, opts: TokenRequestOptions): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<TName, z.ZodObject<{
43
43
  email: z.ZodEmail;
44
- }, z.core.$strip>, TokenRequestData<TSuccessKind>, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
44
+ }, z.core.$strip>, TokenRequestData<TSuccessKind>, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -17,6 +17,7 @@ import { isValidIanaTimeZone } from "@cosmicdrift/kumiko-framework/time";
17
17
  import * as z from "zod";
18
18
  import { UserQueries } from "../../user/index.js";
19
19
  import { parseAuthUserRow } from "../auth-user-row.js";
20
+ import { AUTH_EMAIL_PAYLOAD_RATE_LIMITS } from "../email-rate-limits.js";
20
21
  import { dispatchMagicLinkMail, resolveHandlerMailLocale } from "../magic-link-mail.js";
21
22
  const RequestTokenSchema = z.object({
22
23
  email: z.email(),
@@ -33,7 +34,7 @@ export function createTokenRequestHandler(spec, opts) {
33
34
  schema: RequestTokenSchema,
34
35
  access: { roles: ["anonymous"] },
35
36
  rateLimit: { per: "ip+handler", limit: 10, windowSeconds: 60 },
36
- additionalRateLimits: [{ per: { payloadField: "email" }, limit: 5, windowSeconds: 86400 }],
37
+ additionalRateLimits: AUTH_EMAIL_PAYLOAD_RATE_LIMITS,
37
38
  escapeHatch: {
38
39
  grants: ["systemIdentity"],
39
40
  reason: "Anonymous requester has no session — looks up the target user by email via " +
@@ -9,4 +9,4 @@ export type VerifyEmailData = {
9
9
  };
10
10
  export declare function createVerifyEmailHandler(opts: VerifyEmailOptions): import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"verify-email", z.ZodObject<{
11
11
  token: z.ZodString;
12
- }, z.core.$strip>, VerifyEmailData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
12
+ }, z.core.$strip>, VerifyEmailData, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
@@ -37,21 +37,24 @@ export async function dispatchMagicLinkMail(notify, spec, params) {
37
37
  // mailT already root-falls-back; appUrl must use the same negotiated tag
38
38
  // or language-in-path apps 404 on e.g. /de-AT/activate.
39
39
  const urlLocale = resolveMailLocale(locale);
40
+ // Template, expiry formatting and app-name fallback must share one locale,
41
+ // otherwise an English template shows e.g. French "3 heures".
42
+ const templateLocale = authMailTemplateLocale(spec.notificationType, locale);
40
43
  const { validFor, validUntil } = tokenMailExpiry({
41
44
  expiresAt: params.expiresAt,
42
45
  ...(params.issuedAt !== undefined && { issuedAt: params.issuedAt }),
43
46
  ...(params.timeZone !== undefined && { timeZone: params.timeZone }),
44
- }, locale);
47
+ }, templateLocale);
45
48
  await notify(spec.notificationType, {
46
49
  route: { email: params.email },
47
50
  data: {
48
- appName: params.appName ?? spec.fallbackAppName ?? mailT(locale, "auth.mail.appNameDefault"),
51
+ appName: params.appName ?? spec.fallbackAppName ?? mailT(templateLocale, "auth.mail.appNameDefault"),
49
52
  actionUrl: appendToken(resolveAppUrl(params.appUrl, urlLocale), params.token),
50
53
  validFor,
51
54
  validUntil,
52
55
  ...(params.role !== undefined && { role: params.role }),
53
56
  },
54
- locale: authMailTemplateLocale(spec.notificationType, locale),
57
+ locale: templateLocale,
55
58
  priority: "critical",
56
59
  });
57
60
  }
@@ -0,0 +1,3 @@
1
+ export declare const AUTH_EMAIL_PASSWORD_MAIL_I18N: Readonly<Record<string, {
2
+ readonly en: string;
3
+ }>>;
@@ -0,0 +1,10 @@
1
+ // @runtime client
2
+ // Server-registered so the document designer names the seeded auth mails.
3
+ export const AUTH_EMAIL_PASSWORD_MAIL_I18N = {
4
+ "auth-email-password:feature.label": { en: "Sign-in" },
5
+ "auth-email-password:notify.password-reset.label": { en: "Password reset" },
6
+ "auth-email-password:notify.account-unlock.label": { en: "Account unlock" },
7
+ "auth-email-password:notify.email-verification.label": { en: "Email verification" },
8
+ "auth-email-password:notify.signup-activation.label": { en: "Sign-up activation" },
9
+ "auth-email-password:notify.invite.label": { en: "Invitation" },
10
+ };