@cosmicdrift/kumiko-bundled-features 0.386.0 → 0.388.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 (643) hide show
  1. package/LICENSE +21 -57
  2. package/dist/agent-tools/tool-catalog.js +1 -1
  3. package/dist/agent-tools/tool-dispatch.d.ts +7 -7
  4. package/dist/agent-tools/tool-dispatch.js +9 -9
  5. package/dist/audit/handlers/audit-events.js +1 -1
  6. package/dist/audit/screens.js +1 -1
  7. package/dist/auth-email-password/feature.js +1 -1
  8. package/dist/auth-email-password/handlers/change-password.write.js +3 -3
  9. package/dist/auth-email-password/handlers/confirm-token-flow.js +5 -5
  10. package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +1 -1
  11. package/dist/auth-email-password/handlers/invite-accept.write.js +18 -18
  12. package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
  13. package/dist/auth-email-password/handlers/invite-create.write.js +10 -10
  14. package/dist/auth-email-password/handlers/invite-info.query.js +1 -1
  15. package/dist/auth-email-password/handlers/invite-signup-complete.write.js +24 -26
  16. package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
  17. package/dist/auth-email-password/handlers/login.write.js +1 -1
  18. package/dist/auth-email-password/handlers/self-registration-status.query.js +1 -1
  19. package/dist/auth-email-password/handlers/signup-confirm.write.js +34 -37
  20. package/dist/auth-email-password/handlers/signup-request.write.js +6 -7
  21. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
  22. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +1 -4
  23. package/dist/auth-email-password/index.js +5 -5
  24. package/dist/auth-email-password/lockout-store.js +1 -1
  25. package/dist/auth-email-password/seeding.d.ts +42 -45
  26. package/dist/auth-email-password/seeding.js +51 -53
  27. package/dist/auth-email-password/signup-token-store.js +8 -8
  28. package/dist/auth-email-password/testing.js +3 -3
  29. package/dist/auth-email-password/web/auth-client.d.ts +2 -2
  30. package/dist/auth-email-password/web/auth-client.js +36 -37
  31. package/dist/auth-email-password/web/auth-form-primitives.js +10 -10
  32. package/dist/auth-email-password/web/auth-gate.js +15 -16
  33. package/dist/auth-email-password/web/client-plugin.d.ts +7 -7
  34. package/dist/auth-email-password/web/client-plugin.js +4 -5
  35. package/dist/auth-email-password/web/confirm-account-unlock-screen.js +1 -1
  36. package/dist/auth-email-password/web/constants.js +3 -3
  37. package/dist/auth-email-password/web/default-topbar-actions.d.ts +8 -8
  38. package/dist/auth-email-password/web/default-topbar-actions.js +6 -6
  39. package/dist/auth-email-password/web/forgot-password-screen.d.ts +2 -2
  40. package/dist/auth-email-password/web/forgot-password-screen.js +9 -9
  41. package/dist/auth-email-password/web/index.js +5 -5
  42. package/dist/auth-email-password/web/invite-accept-screen.d.ts +1 -1
  43. package/dist/auth-email-password/web/invite-accept-screen.js +16 -16
  44. package/dist/auth-email-password/web/login-screen.d.ts +1 -1
  45. package/dist/auth-email-password/web/login-screen.js +2 -3
  46. package/dist/auth-email-password/web/reset-password-screen.d.ts +4 -4
  47. package/dist/auth-email-password/web/reset-password-screen.js +12 -13
  48. package/dist/auth-email-password/web/session.d.ts +10 -9
  49. package/dist/auth-email-password/web/session.js +28 -29
  50. package/dist/auth-email-password/web/signup-complete-screen.js +3 -3
  51. package/dist/auth-email-password/web/signup-screen.d.ts +1 -1
  52. package/dist/auth-email-password/web/signup-screen.js +13 -13
  53. package/dist/auth-email-password/web/tenant-switcher.d.ts +4 -4
  54. package/dist/auth-email-password/web/tenant-switcher.js +21 -22
  55. package/dist/auth-email-password/web/use-shell-user.js +12 -13
  56. package/dist/auth-email-password/web/user-menu.d.ts +4 -4
  57. package/dist/auth-email-password/web/user-menu.js +12 -13
  58. package/dist/auth-email-password/web/verify-email-screen.d.ts +2 -2
  59. package/dist/auth-email-password/web/verify-email-screen.js +9 -10
  60. package/dist/auth-foundation/boot-checks.js +4 -4
  61. package/dist/auth-foundation/resolve-anonymous-access.d.ts +1 -1
  62. package/dist/auth-foundation/resolve-anonymous-access.js +3 -3
  63. package/dist/auth-foundation/types.js +2 -2
  64. package/dist/auth-mfa/feature-exports.d.ts +2 -2
  65. package/dist/auth-mfa/feature.js +8 -0
  66. package/dist/auth-mfa/handlers/disable.write.js +3 -3
  67. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +4 -4
  68. package/dist/auth-mfa/handlers/enable-confirm.write.js +2 -2
  69. package/dist/auth-mfa/handlers/enable-start-preauth.write.js +1 -1
  70. package/dist/auth-mfa/handlers/reencrypt.job.js +1 -1
  71. package/dist/auth-mfa/handlers/regenerate-recovery.write.js +5 -5
  72. package/dist/auth-mfa/handlers/verify.write.js +1 -1
  73. package/dist/auth-mfa/token-secrets.js +1 -1
  74. package/dist/auth-mfa/web/client-plugin.d.ts +1 -1
  75. package/dist/auth-mfa/web/client-plugin.js +1 -1
  76. package/dist/auth-mfa/web/i18n.js +8 -4
  77. package/dist/auth-mfa/web/index.js +4 -4
  78. package/dist/auth-mfa/web/mfa-client.js +17 -16
  79. package/dist/auth-mfa/web/mfa-code-or-recovery-field.d.ts +11 -0
  80. package/dist/auth-mfa/web/mfa-code-or-recovery-field.js +12 -0
  81. package/dist/auth-mfa/web/mfa-disable-dialog.js +14 -3
  82. package/dist/auth-mfa/web/mfa-regenerate-recovery-dialog.js +14 -3
  83. package/dist/auth-mfa/web/mfa-setup-preauth-screen.js +10 -5
  84. package/dist/auth-mfa/web/mfa-verify-screen.js +32 -11
  85. package/dist/billing-foundation/aggregate-id.d.ts +5 -5
  86. package/dist/billing-foundation/aggregate-id.js +10 -11
  87. package/dist/billing-foundation/checkout-core.d.ts +9 -14
  88. package/dist/billing-foundation/checkout-core.js +24 -39
  89. package/dist/billing-foundation/consumer-protection/confirmation-mail.js +8 -2
  90. package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +1 -0
  91. package/dist/billing-foundation/consumer-protection/consent-gate.js +1 -0
  92. package/dist/billing-foundation/consumer-protection/issue-confirmation.js +9 -3
  93. package/dist/billing-foundation/consumer-protection/mail-notifications.js +3 -1
  94. package/dist/billing-foundation/consumer-protection/termination-receipt-limit.d.ts +4 -2
  95. package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +6 -5
  96. package/dist/billing-foundation/consumer-protection/termination-record.d.ts +5 -1
  97. package/dist/billing-foundation/consumer-protection/termination-record.js +27 -17
  98. package/dist/billing-foundation/consumer-protection/termination-request.js +12 -7
  99. package/dist/billing-foundation/consumer-protection/termination-revoke.js +12 -7
  100. package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +1 -1
  101. package/dist/billing-foundation/consumer-protection/termination-texts.js +3 -0
  102. package/dist/billing-foundation/consumer-protection/withdrawal-period.d.ts +5 -2
  103. package/dist/billing-foundation/consumer-protection/withdrawal-period.js +8 -9
  104. package/dist/billing-foundation/entities.js +27 -27
  105. package/dist/billing-foundation/events.d.ts +1 -0
  106. package/dist/billing-foundation/events.js +19 -17
  107. package/dist/billing-foundation/feature.js +45 -44
  108. package/dist/billing-foundation/get-subscription-for-tenant.d.ts +3 -3
  109. package/dist/billing-foundation/get-subscription-for-tenant.js +3 -5
  110. package/dist/billing-foundation/handlers/billing-plans.query.js +35 -5
  111. package/dist/billing-foundation/handlers/create-checkout-session.write.js +19 -17
  112. package/dist/billing-foundation/handlers/create-portal-session.write.js +11 -12
  113. package/dist/billing-foundation/handlers/list-subscriptions.query.js +4 -4
  114. package/dist/billing-foundation/handlers/plan-switch-preview.query.d.ts +13 -0
  115. package/dist/billing-foundation/handlers/plan-switch-preview.query.js +48 -0
  116. package/dist/billing-foundation/handlers/process-event.write.d.ts +6 -0
  117. package/dist/billing-foundation/handlers/process-event.write.js +28 -22
  118. package/dist/billing-foundation/handlers/start-plan-checkout.write.js +32 -5
  119. package/dist/billing-foundation/handlers/switch-plan.write.d.ts +2 -2
  120. package/dist/billing-foundation/handlers/switch-plan.write.js +56 -64
  121. package/dist/billing-foundation/handlers/sync-subscription.write.js +12 -1
  122. package/dist/billing-foundation/i18n.js +22 -0
  123. package/dist/billing-foundation/index.d.ts +1 -1
  124. package/dist/billing-foundation/plan-catalog.d.ts +21 -5
  125. package/dist/billing-foundation/plan-catalog.js +25 -16
  126. package/dist/billing-foundation/plan-switch.d.ts +12 -0
  127. package/dist/billing-foundation/plan-switch.js +60 -0
  128. package/dist/billing-foundation/projection.d.ts +15 -11
  129. package/dist/billing-foundation/projection.js +38 -37
  130. package/dist/billing-foundation/subscription-tier-sync.js +3 -3
  131. package/dist/billing-foundation/tenant-destroy-hook.d.ts +2 -2
  132. package/dist/billing-foundation/tenant-destroy-hook.js +7 -7
  133. package/dist/billing-foundation/types.d.ts +97 -42
  134. package/dist/billing-foundation/types.js +29 -20
  135. package/dist/billing-foundation/validate-options.js +17 -0
  136. package/dist/billing-foundation/web/billing-plans-panel.js +82 -36
  137. package/dist/billing-foundation/web/checkout-consent-dialog.d.ts +17 -3
  138. package/dist/billing-foundation/web/checkout-consent-dialog.js +14 -10
  139. package/dist/billing-foundation/web/constants.d.ts +4 -0
  140. package/dist/billing-foundation/web/constants.js +4 -0
  141. package/dist/billing-foundation/web/plan-price.d.ts +15 -0
  142. package/dist/billing-foundation/web/plan-price.js +32 -0
  143. package/dist/billing-foundation/web/plan-switch-confirm-dialog.d.ts +15 -0
  144. package/dist/billing-foundation/web/plan-switch-confirm-dialog.js +66 -0
  145. package/dist/cap-counter/aggregate-id.js +8 -9
  146. package/dist/cap-counter/cap-reservation.js +5 -1
  147. package/dist/cap-counter/constants.js +11 -12
  148. package/dist/cap-counter/enforce-cap.d.ts +59 -62
  149. package/dist/cap-counter/enforce-cap.js +49 -49
  150. package/dist/cap-counter/entity.js +29 -29
  151. package/dist/cap-counter/feature.js +34 -35
  152. package/dist/cap-counter/handlers/increment-rolling.write.d.ts +2 -2
  153. package/dist/cap-counter/handlers/increment-rolling.write.js +31 -31
  154. package/dist/cap-counter/handlers/mark-soft-warned.write.js +4 -5
  155. package/dist/cap-counter/stock-cap-guard.js +8 -2
  156. package/dist/cap-counter/with-cap-enforcement.d.ts +3 -5
  157. package/dist/channel-email/feature.js +1 -1
  158. package/dist/channel-email/smtp-transport.d.ts +18 -17
  159. package/dist/channel-email/smtp-transport.js +24 -21
  160. package/dist/channel-in-app/handlers/unread-count.query.js +3 -4
  161. package/dist/compliance-profiles/feature.js +4 -4
  162. package/dist/compliance-profiles/handlers/for-tenant.query.js +3 -6
  163. package/dist/compliance-profiles/handlers/list-profiles.query.js +5 -5
  164. package/dist/compliance-profiles/handlers/needs-profile.query.js +4 -4
  165. package/dist/compliance-profiles/handlers/set-profile.write.js +13 -14
  166. package/dist/compliance-profiles/handlers/sub-processors.query.js +9 -11
  167. package/dist/compliance-profiles/resolve-for-tenant.js +7 -7
  168. package/dist/compliance-profiles/schema/profile-selection.js +18 -23
  169. package/dist/compliance-profiles/seeding.js +14 -15
  170. package/dist/compliance-profiles-ops/handlers/tenants-missing-profile.query.js +3 -3
  171. package/dist/compliance-profiles-ops/index.js +2 -2
  172. package/dist/config/handlers/cascade.query.d.ts +5 -0
  173. package/dist/config/handlers/cascade.query.js +4 -3
  174. package/dist/config/handlers/readiness.query.d.ts +4 -4
  175. package/dist/config/handlers/readiness.query.js +1 -1
  176. package/dist/config/handlers/reencrypt.job.js +4 -5
  177. package/dist/config/handlers/set.write.js +4 -4
  178. package/dist/config/handlers/values.query.d.ts +5 -0
  179. package/dist/config/handlers/values.query.js +4 -3
  180. package/dist/config/resolver.js +4 -4
  181. package/dist/config/table.js +10 -10
  182. package/dist/config/write-helpers.d.ts +1 -0
  183. package/dist/config/write-helpers.js +19 -10
  184. package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +2 -2
  185. package/dist/crypto-shredding/handlers/forget-subject.write.js +25 -21
  186. package/dist/custom-fields/aggregate-id.d.ts +12 -13
  187. package/dist/custom-fields/aggregate-id.js +16 -17
  188. package/dist/custom-fields/entity.js +49 -47
  189. package/dist/custom-fields/events.js +7 -8
  190. package/dist/custom-fields/feature.d.ts +14 -14
  191. package/dist/custom-fields/feature.js +53 -54
  192. package/dist/custom-fields/handlers/clear-custom-field.write.d.ts +3 -2
  193. package/dist/custom-fields/handlers/clear-custom-field.write.js +9 -8
  194. package/dist/custom-fields/handlers/define-tenant-field.write.d.ts +1 -1
  195. package/dist/custom-fields/handlers/define-tenant-field.write.js +2 -2
  196. package/dist/custom-fields/handlers/delete-system-field.write.js +6 -7
  197. package/dist/custom-fields/handlers/delete-tenant-field.write.d.ts +1 -1
  198. package/dist/custom-fields/handlers/delete-tenant-field.write.js +3 -3
  199. package/dist/custom-fields/handlers/set-custom-field.write.d.ts +3 -3
  200. package/dist/custom-fields/handlers/set-custom-field.write.js +25 -28
  201. package/dist/custom-fields/handlers/update-tenant-field.write.d.ts +1 -1
  202. package/dist/custom-fields/handlers/update-tenant-field.write.js +1 -1
  203. package/dist/custom-fields/lib/field-access.js +2 -2
  204. package/dist/custom-fields/lib/parse-serialized-field.js +1 -1
  205. package/dist/custom-fields/lib/quota.js +1 -1
  206. package/dist/custom-fields/lib/value-schema.js +10 -12
  207. package/dist/custom-fields/run-retention.js +3 -3
  208. package/dist/custom-fields/schemas.js +14 -14
  209. package/dist/custom-fields/web/client-plugin.js +2 -5
  210. package/dist/custom-fields/web/constants.js +2 -2
  211. package/dist/custom-fields/web/custom-fields-form-section.d.ts +4 -4
  212. package/dist/custom-fields/web/custom-fields-form-section.js +22 -22
  213. package/dist/custom-fields/wire-for-entity.js +65 -67
  214. package/dist/custom-fields/wire-user-data-rights.js +3 -3
  215. package/dist/data-retention/_internal/parse-override.js +2 -3
  216. package/dist/data-retention/feature.js +11 -18
  217. package/dist/data-retention/handlers/policy-for.query.js +6 -6
  218. package/dist/data-retention/keep-for.d.ts +7 -7
  219. package/dist/data-retention/keep-for.js +15 -15
  220. package/dist/data-retention/override-schema.d.ts +3 -4
  221. package/dist/data-retention/override-schema.js +13 -15
  222. package/dist/data-retention/presets.d.ts +4 -5
  223. package/dist/data-retention/presets.js +18 -23
  224. package/dist/data-retention/resolve-tenant-preset.js +17 -16
  225. package/dist/data-retention/resolver.d.ts +12 -12
  226. package/dist/data-retention/resolver.js +24 -23
  227. package/dist/data-retention/run-retention-cleanup.d.ts +8 -3
  228. package/dist/data-retention/run-retention-cleanup.js +61 -51
  229. package/dist/data-retention/schema/tenant-retention-override.js +13 -11
  230. package/dist/delivery/channel-context.js +6 -3
  231. package/dist/delivery/db/queries/preferences.js +2 -2
  232. package/dist/delivery/delivery-service.js +22 -7
  233. package/dist/delivery/feature.js +2 -2
  234. package/dist/delivery/handlers/log.query.js +1 -1
  235. package/dist/delivery/handlers/preferences.query.d.ts +1 -1
  236. package/dist/delivery/handlers/preferences.query.js +20 -1
  237. package/dist/delivery/handlers/unsubscribe-page-chrome.query.js +1 -1
  238. package/dist/delivery/tables.js +9 -8
  239. package/dist/delivery/unsubscribe-link.d.ts +3 -1
  240. package/dist/delivery/unsubscribe-page-strings.d.ts +1 -0
  241. package/dist/delivery/unsubscribe-page-strings.js +1 -0
  242. package/dist/delivery/unsubscribe-page.d.ts +2 -0
  243. package/dist/delivery/unsubscribe-page.js +5 -0
  244. package/dist/delivery/unsubscribe.js +29 -11
  245. package/dist/document-ingest-foundation/entity.js +4 -4
  246. package/dist/document-ingest-foundation/events.js +2 -2
  247. package/dist/document-ingest-foundation/feature.js +2 -2
  248. package/dist/document-ingest-foundation/pages.js +1 -1
  249. package/dist/document-ingest-foundation/tenant-destroy-hook.js +4 -4
  250. package/dist/feature-toggles/compose-tier-resolver.js +1 -1
  251. package/dist/feature-toggles/feature.js +1 -1
  252. package/dist/feature-toggles/global-feature-state-table.js +1 -1
  253. package/dist/feature-toggles/handlers/registered.query.js +5 -5
  254. package/dist/feature-toggles/toggle-sync-signal.js +1 -1
  255. package/dist/feature-toggles/web/constants.js +1 -1
  256. package/dist/file-derivatives/feature.d.ts +1 -1
  257. package/dist/file-derivatives/feature.js +1 -1
  258. package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
  259. package/dist/file-derivatives/presets.js +1 -1
  260. package/dist/file-foundation/feature.js +6 -6
  261. package/dist/file-foundation/index.d.ts +1 -1
  262. package/dist/file-foundation/index.js +1 -1
  263. package/dist/file-foundation/read-file-ref-content.d.ts +3 -0
  264. package/dist/file-foundation/read-file-ref-content.js +6 -1
  265. package/dist/file-provider-inmemory/feature.d.ts +1 -1
  266. package/dist/file-provider-inmemory/feature.js +16 -16
  267. package/dist/file-provider-s3/env-named-feature.d.ts +0 -2
  268. package/dist/file-provider-s3/feature.js +16 -16
  269. package/dist/file-provider-s3/lib/s3-provider.js +6 -6
  270. package/dist/files/feature.js +6 -6
  271. package/dist/files-tenant-data/handlers/sweep-orphaned-derivatives.job.js +4 -4
  272. package/dist/files-tenant-data/hooks.js +24 -26
  273. package/dist/files-tenant-data/index.js +1 -1
  274. package/dist/folders/feature.d.ts +1 -1
  275. package/dist/folders/web/folder-manager.js +2 -2
  276. package/dist/folders/web/tree.js +5 -5
  277. package/dist/form-draft/db/queries/cleanup.js +3 -3
  278. package/dist/form-draft/db/queries/owned-file-refs.js +1 -1
  279. package/dist/form-draft/decrypt-draft.js +5 -1
  280. package/dist/form-draft/entity.js +3 -3
  281. package/dist/form-draft/feature.js +4 -5
  282. package/dist/form-draft/handlers/discard.write.js +1 -1
  283. package/dist/form-draft/release-file-refs.js +6 -6
  284. package/dist/form-draft/schemas.js +3 -3
  285. package/dist/form-draft/web/constants.js +0 -2
  286. package/dist/host-routing/dispatch.js +11 -20
  287. package/dist/inbound-mail-foundation/aggregate-id.d.ts +13 -13
  288. package/dist/inbound-mail-foundation/aggregate-id.js +19 -19
  289. package/dist/inbound-mail-foundation/connect-routes.js +4 -3
  290. package/dist/inbound-mail-foundation/constants.d.ts +22 -24
  291. package/dist/inbound-mail-foundation/constants.js +30 -33
  292. package/dist/inbound-mail-foundation/entities.d.ts +12 -12
  293. package/dist/inbound-mail-foundation/entities.js +47 -48
  294. package/dist/inbound-mail-foundation/events.d.ts +4 -3
  295. package/dist/inbound-mail-foundation/events.js +50 -47
  296. package/dist/inbound-mail-foundation/feature.js +39 -39
  297. package/dist/inbound-mail-foundation/handlers/account-state.js +8 -8
  298. package/dist/inbound-mail-foundation/handlers/connect-account.write.js +25 -26
  299. package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +7 -7
  300. package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +50 -51
  301. package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +8 -8
  302. package/dist/inbound-mail-foundation/handlers/list-messages.query.js +10 -10
  303. package/dist/inbound-mail-foundation/handlers/scope-visibility.js +3 -4
  304. package/dist/inbound-mail-foundation/handlers/update-account.write.d.ts +2 -2
  305. package/dist/inbound-mail-foundation/handlers/update-account.write.js +22 -14
  306. package/dist/inbound-mail-foundation/i18n.js +0 -1
  307. package/dist/inbound-mail-foundation/oauth-state.d.ts +5 -5
  308. package/dist/inbound-mail-foundation/oauth-state.js +11 -10
  309. package/dist/inbound-mail-foundation/projection.d.ts +4 -4
  310. package/dist/inbound-mail-foundation/projection.js +31 -29
  311. package/dist/inbound-mail-foundation/provider-factory.d.ts +4 -4
  312. package/dist/inbound-mail-foundation/provider-factory.js +9 -9
  313. package/dist/inbound-mail-foundation/retention-sweep.d.ts +5 -3
  314. package/dist/inbound-mail-foundation/retention-sweep.js +38 -36
  315. package/dist/inbound-mail-foundation/screens.js +0 -3
  316. package/dist/inbound-mail-foundation/tenant-destroy-hook.js +14 -16
  317. package/dist/inbound-mail-foundation/types.d.ts +49 -49
  318. package/dist/inbound-mail-foundation/types.js +21 -21
  319. package/dist/inbound-mail-foundation/watch-supervisor.d.ts +10 -10
  320. package/dist/inbound-mail-foundation/watch-supervisor.js +85 -67
  321. package/dist/inbound-provider-imap/credential-document.js +13 -13
  322. package/dist/inbound-provider-imap/feature.d.ts +2 -2
  323. package/dist/inbound-provider-imap/feature.js +42 -40
  324. package/dist/inbound-provider-imap/imap-client.d.ts +12 -11
  325. package/dist/inbound-provider-imap/imap-client.js +20 -19
  326. package/dist/inbound-provider-inmemory/feature.d.ts +9 -9
  327. package/dist/inbound-provider-inmemory/feature.js +19 -19
  328. package/dist/jobs/db/queries/retention.js +2 -2
  329. package/dist/jobs/db/queries/stale-run-sweep.js +3 -3
  330. package/dist/jobs/events.js +1 -1
  331. package/dist/jobs/feature.d.ts +1 -0
  332. package/dist/jobs/feature.js +13 -10
  333. package/dist/jobs/handlers/detail.query.js +4 -4
  334. package/dist/jobs/handlers/list.query.js +1 -1
  335. package/dist/jobs/handlers/projection-rebuild.job.js +1 -1
  336. package/dist/jobs/handlers/reindex-entity.job.js +1 -1
  337. package/dist/jobs/handlers/retry.write.js +1 -1
  338. package/dist/jobs/handlers/tenant-failures.query.d.ts +1 -1
  339. package/dist/jobs/handlers/tenant-failures.query.js +45 -41
  340. package/dist/jobs/handlers/tenant-runs.query.d.ts +1 -1
  341. package/dist/jobs/handlers/tenant-runs.query.js +54 -50
  342. package/dist/jobs/job-instant.js +1 -1
  343. package/dist/jobs/job-run-logger.js +15 -15
  344. package/dist/jobs/job-run-table.js +3 -3
  345. package/dist/jobs/screens.js +1 -1
  346. package/dist/jobs/tenant-job-failure-table.js +1 -1
  347. package/dist/jobs/tenant-job-run-table.js +1 -1
  348. package/dist/ledger/entity.js +5 -5
  349. package/dist/ledger/feature.js +3 -3
  350. package/dist/ledger/handlers/confirm-schedule-period.write.js +3 -3
  351. package/dist/ledger/recurring.js +12 -12
  352. package/dist/ledger/schemas.js +1 -1
  353. package/dist/ledger/web/constants.js +2 -2
  354. package/dist/ledger/web/index.js +7 -7
  355. package/dist/legal-pages/feature.d.ts +4 -4
  356. package/dist/legal-pages/feature.js +25 -26
  357. package/dist/legal-pages/markdown.js +3 -3
  358. package/dist/legal-pages/web/client-plugin.js +23 -24
  359. package/dist/legal-pages/web/index.js +0 -1
  360. package/dist/mail-foundation/feature.d.ts +5 -5
  361. package/dist/mail-foundation/feature.js +27 -25
  362. package/dist/mail-transport-inmemory/feature.d.ts +4 -4
  363. package/dist/mail-transport-inmemory/feature.js +27 -28
  364. package/dist/mail-transport-smtp/feature.js +20 -20
  365. package/dist/managed-pages/branding.js +6 -6
  366. package/dist/managed-pages/feature.d.ts +26 -26
  367. package/dist/managed-pages/feature.js +34 -34
  368. package/dist/managed-pages/handlers/by-slug.query.js +5 -6
  369. package/dist/managed-pages/handlers/by-tenant-published.query.js +2 -2
  370. package/dist/managed-pages/handlers/set.write.js +5 -7
  371. package/dist/managed-pages/screens/page-screens.js +11 -11
  372. package/dist/managed-pages/seeding.js +8 -8
  373. package/dist/managed-pages/table.js +13 -12
  374. package/dist/managed-pages/web/client-plugin.d.ts +1 -1
  375. package/dist/managed-pages/web/client-plugin.js +5 -5
  376. package/dist/managed-pages/web/i18n.js +1 -1
  377. package/dist/notes-history/entity.js +4 -4
  378. package/dist/notes-history/feature.d.ts +2 -2
  379. package/dist/notes-history-user-data/hooks.js +1 -1
  380. package/dist/page-render/layout.js +9 -9
  381. package/dist/page-render/markdown.js +11 -11
  382. package/dist/page-render/security-headers.js +7 -6
  383. package/dist/personal-access-tokens/feature.d.ts +1 -1
  384. package/dist/personal-access-tokens/feature.js +9 -5
  385. package/dist/personal-access-tokens/roles.js +2 -2
  386. package/dist/readiness/handlers/status.query.js +3 -3
  387. package/dist/renderer-foundation/api.js +12 -13
  388. package/dist/renderer-foundation/constants.js +2 -2
  389. package/dist/renderer-foundation/feature.js +7 -7
  390. package/dist/renderer-pdf/render-table-pdf.d.ts +1 -1
  391. package/dist/renderer-pdf/render-table-pdf.js +22 -10
  392. package/dist/secrets/feature.js +9 -9
  393. package/dist/secrets/web/constants.js +2 -2
  394. package/dist/seo/feature.js +3 -3
  395. package/dist/sessions/constants.d.ts +1 -1
  396. package/dist/sessions/constants.js +1 -1
  397. package/dist/sessions/feature.d.ts +2 -2
  398. package/dist/sessions/feature.js +12 -8
  399. package/dist/sessions/handlers/revoke-all-for-user.write.js +2 -2
  400. package/dist/sessions/handlers/revoke-all-others.write.js +2 -2
  401. package/dist/sessions/handlers/revoke.write.js +1 -1
  402. package/dist/sessions/schema/user-session.js +4 -4
  403. package/dist/sessions/screens.js +2 -2
  404. package/dist/sessions/session-callbacks.js +2 -2
  405. package/dist/sessions/session-revoked-event.js +2 -2
  406. package/dist/shared/assert-no-where-rule-in-ownership-write.js +1 -1
  407. package/dist/shared/decrypt-stored-pii.d.ts +1 -0
  408. package/dist/shared/decrypt-stored-pii.js +7 -2
  409. package/dist/shared/encrypt-for-direct-write.js +1 -1
  410. package/dist/shared/index.d.ts +1 -1
  411. package/dist/shared/index.js +1 -1
  412. package/dist/shared/mail-template-body.d.ts +6 -2
  413. package/dist/shared/mail-template-body.js +7 -4
  414. package/dist/shared/map-with-concurrency.js +5 -4
  415. package/dist/shared/parent-visibility.js +2 -2
  416. package/dist/shared/password-hashing.js +1 -1
  417. package/dist/shared/signup-handover.js +1 -2
  418. package/dist/shared/single-use-token-store.js +1 -1
  419. package/dist/step-dispatcher/feature.js +1 -0
  420. package/dist/subscription-dodo/client.d.ts +17 -0
  421. package/dist/subscription-dodo/client.js +31 -0
  422. package/dist/subscription-dodo/constants.d.ts +12 -0
  423. package/dist/subscription-dodo/constants.js +19 -0
  424. package/dist/subscription-dodo/feature.d.ts +37 -0
  425. package/dist/subscription-dodo/feature.js +144 -0
  426. package/dist/subscription-dodo/index.d.ts +2 -0
  427. package/dist/subscription-dodo/index.js +4 -0
  428. package/dist/subscription-dodo/plugin-methods.d.ts +23 -0
  429. package/dist/subscription-dodo/plugin-methods.js +260 -0
  430. package/dist/subscription-dodo/runtime.d.ts +27 -0
  431. package/dist/subscription-dodo/runtime.js +89 -0
  432. package/dist/subscription-dodo/verify-webhook.d.ts +37 -0
  433. package/dist/subscription-dodo/verify-webhook.js +189 -0
  434. package/dist/subscription-mollie/constants.js +15 -17
  435. package/dist/subscription-mollie/feature.d.ts +17 -18
  436. package/dist/subscription-mollie/feature.js +24 -24
  437. package/dist/subscription-mollie/index.js +3 -3
  438. package/dist/subscription-mollie/plugin-methods.d.ts +2 -2
  439. package/dist/subscription-mollie/plugin-methods.js +15 -14
  440. package/dist/subscription-mollie/verify-webhook.d.ts +10 -10
  441. package/dist/subscription-mollie/verify-webhook.js +27 -27
  442. package/dist/subscription-stripe/constants.js +4 -6
  443. package/dist/subscription-stripe/feature.d.ts +14 -14
  444. package/dist/subscription-stripe/feature.js +36 -34
  445. package/dist/subscription-stripe/index.js +6 -6
  446. package/dist/subscription-stripe/plugin-methods.js +18 -18
  447. package/dist/subscription-stripe/runtime.d.ts +1 -1
  448. package/dist/subscription-stripe/runtime.js +8 -7
  449. package/dist/subscription-stripe/verify-webhook.d.ts +11 -11
  450. package/dist/subscription-stripe/verify-webhook.js +59 -61
  451. package/dist/tags/feature.d.ts +3 -3
  452. package/dist/tags/screens.js +1 -1
  453. package/dist/template-resolver/api.d.ts +3 -2
  454. package/dist/template-resolver/api.js +34 -12
  455. package/dist/template-resolver/compile-document-body.js +1 -0
  456. package/dist/template-resolver/feature.js +2 -2
  457. package/dist/template-resolver/handlers/bundle-import.js +3 -3
  458. package/dist/template-resolver/handlers/copy-locale.write.d.ts +5 -1
  459. package/dist/template-resolver/handlers/copy-locale.write.js +22 -7
  460. package/dist/template-resolver/handlers/diff-to-system.query.js +3 -2
  461. package/dist/template-resolver/handlers/document-base.js +3 -2
  462. package/dist/template-resolver/handlers/find-by-id.query.js +8 -7
  463. package/dist/template-resolver/handlers/list.query.js +6 -7
  464. package/dist/template-resolver/handlers/overview.query.d.ts +3 -0
  465. package/dist/template-resolver/handlers/overview.query.js +6 -3
  466. package/dist/template-resolver/handlers/shared.js +2 -2
  467. package/dist/template-resolver/handlers/template-write.js +3 -3
  468. package/dist/template-resolver/handlers/tenant-override-counts.query.js +1 -3
  469. package/dist/template-resolver/handlers/tenant-override-selection.d.ts +2 -1
  470. package/dist/template-resolver/handlers/tenant-overrides.query.js +28 -5
  471. package/dist/template-resolver/handlers/upsert-system.write.js +3 -4
  472. package/dist/template-resolver/handlers/upsert-tenant.write.js +4 -5
  473. package/dist/template-resolver/handlers/usage.query.d.ts +2 -1
  474. package/dist/template-resolver/handlers/usage.query.js +26 -16
  475. package/dist/template-resolver/handlers/validate.query.js +7 -2
  476. package/dist/template-resolver/seeding.js +30 -14
  477. package/dist/template-resolver/store-template-resource.d.ts +1 -0
  478. package/dist/template-resolver/store-template-resource.js +19 -4
  479. package/dist/template-resolver/table.js +10 -11
  480. package/dist/template-resolver/template-delivery-policy.d.ts +0 -5
  481. package/dist/template-resolver/template-delivery-policy.js +0 -6
  482. package/dist/template-resolver/template-meta.js +7 -4
  483. package/dist/template-resolver/template-preview.js +4 -3
  484. package/dist/template-resolver/web/client-plugin.js +2 -2
  485. package/dist/template-resolver/web/constants.d.ts +2 -2
  486. package/dist/template-resolver/web/constants.js +3 -3
  487. package/dist/template-resolver/web/index.d.ts +1 -0
  488. package/dist/template-resolver/web/index.js +1 -0
  489. package/dist/template-resolver/web/mail-layout-constants.d.ts +5 -0
  490. package/dist/template-resolver/web/mail-layout-constants.js +7 -0
  491. package/dist/tenant/feature.js +7 -7
  492. package/dist/tenant/handlers/member-directory.query.d.ts +1 -0
  493. package/dist/tenant/handlers/member-directory.query.js +11 -4
  494. package/dist/tenant/handlers/members.query.js +1 -1
  495. package/dist/tenant/handlers/team-list.query.js +1 -1
  496. package/dist/tenant/handlers/tenant-directory.query.d.ts +1 -0
  497. package/dist/tenant/handlers/tenant-directory.query.js +9 -5
  498. package/dist/tenant/invitation-table.js +37 -40
  499. package/dist/tenant/membership-table.js +3 -3
  500. package/dist/tenant/schema/tenant.js +7 -9
  501. package/dist/tenant/screens.js +1 -1
  502. package/dist/tenant/seeding.d.ts +6 -5
  503. package/dist/tenant/seeding.js +12 -12
  504. package/dist/tenant/testing.js +5 -6
  505. package/dist/tenant/web/constants.js +2 -2
  506. package/dist/tenant-handover/events.js +5 -7
  507. package/dist/tenant-handover/feature.js +1 -1
  508. package/dist/tenant-handover/grant.js +1 -1
  509. package/dist/tenant-handover/handlers/claim.write.js +1 -1
  510. package/dist/tenant-handover/move-entity-graph.js +8 -9
  511. package/dist/tenant-handover/signup-handover-provider.js +1 -2
  512. package/dist/tenant-handover/transfer-graph.js +4 -5
  513. package/dist/tenant-lifecycle/boot-checks.js +1 -1
  514. package/dist/tenant-lifecycle/constants.d.ts +1 -0
  515. package/dist/tenant-lifecycle/constants.js +4 -0
  516. package/dist/tenant-lifecycle/feature.js +1 -1
  517. package/dist/tenant-lifecycle/run-tenant-destroy.js +2 -2
  518. package/dist/tenant-settings/constants.js +1 -1
  519. package/dist/tenant-settings/tenant-defaults.d.ts +1 -1
  520. package/dist/tenant-settings/tenant-defaults.js +9 -2
  521. package/dist/tier-engine/aggregate-id.d.ts +11 -11
  522. package/dist/tier-engine/aggregate-id.js +15 -16
  523. package/dist/tier-engine/compose-app.d.ts +14 -14
  524. package/dist/tier-engine/compose-app.js +1 -1
  525. package/dist/tier-engine/entity.js +18 -19
  526. package/dist/tier-engine/feature.d.ts +19 -17
  527. package/dist/tier-engine/feature.js +68 -69
  528. package/dist/tier-engine/handlers/active-tier.query.js +6 -7
  529. package/dist/tier-engine/handlers/get-tenant-tier.query.js +4 -4
  530. package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +7 -8
  531. package/dist/tier-engine/handlers/set-tenant-tier.write.js +7 -4
  532. package/dist/tier-engine/tier-resolver.js +2 -2
  533. package/dist/tier-engine/trial.js +8 -8
  534. package/dist/tier-engine/web/constants.js +2 -2
  535. package/dist/tier-engine/web/index.js +4 -4
  536. package/dist/user/db/queries/stream-tenant-backfill.js +8 -8
  537. package/dist/user/handlers/create.write.js +4 -4
  538. package/dist/user/schema/user.d.ts +11 -11
  539. package/dist/user/schema/user.js +45 -44
  540. package/dist/user/seeding.d.ts +15 -15
  541. package/dist/user/seeding.js +28 -28
  542. package/dist/user/testing.js +3 -3
  543. package/dist/user-data-rights/audit-download.d.ts +8 -8
  544. package/dist/user-data-rights/audit-download.js +19 -20
  545. package/dist/user-data-rights/boot-checks.js +1 -1
  546. package/dist/user-data-rights/deletion-token.js +1 -1
  547. package/dist/user-data-rights/feature.d.ts +49 -50
  548. package/dist/user-data-rights/feature.js +54 -53
  549. package/dist/user-data-rights/handlers/cancel-deletion.write.js +9 -9
  550. package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +8 -8
  551. package/dist/user-data-rights/handlers/download-by-job.query.js +29 -30
  552. package/dist/user-data-rights/handlers/download-by-token.query.js +40 -42
  553. package/dist/user-data-rights/handlers/export-status.query.js +8 -9
  554. package/dist/user-data-rights/handlers/lift-restriction.write.js +7 -8
  555. package/dist/user-data-rights/handlers/list-download-attempts.query.js +2 -2
  556. package/dist/user-data-rights/handlers/my-audit-log.query.js +4 -4
  557. package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +5 -5
  558. package/dist/user-data-rights/handlers/request-deletion-by-email.write.js +16 -15
  559. package/dist/user-data-rights/handlers/request-deletion.write.d.ts +2 -2
  560. package/dist/user-data-rights/handlers/request-deletion.write.js +7 -9
  561. package/dist/user-data-rights/handlers/request-export.write.js +42 -41
  562. package/dist/user-data-rights/handlers/restrict-account.write.js +22 -19
  563. package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +8 -8
  564. package/dist/user-data-rights/index.js +8 -10
  565. package/dist/user-data-rights/lib/default-mailers.js +3 -3
  566. package/dist/user-data-rights/lib/update-user-lifecycle.js +14 -17
  567. package/dist/user-data-rights/run-export-jobs.d.ts +38 -42
  568. package/dist/user-data-rights/run-export-jobs.js +130 -237
  569. package/dist/user-data-rights/run-forget-cleanup.d.ts +16 -20
  570. package/dist/user-data-rights/run-forget-cleanup.js +65 -83
  571. package/dist/user-data-rights/run-user-export.d.ts +34 -33
  572. package/dist/user-data-rights/run-user-export.js +55 -57
  573. package/dist/user-data-rights/schema/download-attempt.js +11 -11
  574. package/dist/user-data-rights/schema/download-token.js +37 -41
  575. package/dist/user-data-rights/schema/export-job.d.ts +4 -4
  576. package/dist/user-data-rights/schema/export-job.js +65 -67
  577. package/dist/user-data-rights/token-helpers.d.ts +8 -9
  578. package/dist/user-data-rights/token-helpers.js +21 -25
  579. package/dist/user-data-rights/web/client-plugin.d.ts +5 -5
  580. package/dist/user-data-rights/web/client-plugin.js +1 -1
  581. package/dist/user-data-rights/web/confirm-deletion-screen.js +6 -5
  582. package/dist/user-data-rights/web/constants.js +1 -1
  583. package/dist/user-data-rights/web/index.js +5 -5
  584. package/dist/user-data-rights/web/privacy-center-screen.js +14 -13
  585. package/dist/user-data-rights/web/public-deletion-gate.d.ts +4 -4
  586. package/dist/user-data-rights/web/request-deletion-screen.js +6 -6
  587. package/dist/user-data-rights/zip-path.d.ts +15 -15
  588. package/dist/user-data-rights/zip-path.js +41 -40
  589. package/dist/user-data-rights-defaults/feature.js +15 -15
  590. package/dist/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.js +3 -5
  591. package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +45 -51
  592. package/dist/user-data-rights-defaults/hooks/job-run.userdata-hook.js +2 -2
  593. package/dist/user-data-rights-defaults/hooks/notification-preference.userdata-hook.js +1 -1
  594. package/dist/user-data-rights-defaults/hooks/user-session.userdata-hook.js +3 -3
  595. package/dist/user-data-rights-defaults/hooks/user.userdata-hook.js +32 -31
  596. package/dist/user-profile/handlers/change-email.write.js +13 -13
  597. package/dist/user-profile/index.js +3 -3
  598. package/dist/user-profile/web/client-plugin.js +1 -1
  599. package/dist/user-profile/web/constants.js +1 -1
  600. package/dist/user-profile/web/profile-screen.js +1 -1
  601. package/dist/waitlist/handlers/export-purge.write.js +2 -1
  602. package/dist/waitlist/handlers/submit.write.js +5 -5
  603. package/dist/waitlist/import.d.ts +1 -0
  604. package/dist/waitlist/import.js +4 -0
  605. package/dist/waitlist/legacy-migrate.js +17 -6
  606. package/dist/waitlist/link-on-signup.js +18 -6
  607. package/dist/waitlist/options.d.ts +3 -0
  608. package/dist/workflow-runner/aggregate-id.d.ts +1 -1
  609. package/dist/workflow-runner/aggregate-id.js +1 -1
  610. package/dist/workflow-runner/event-subscriber.js +4 -5
  611. package/dist/workflow-runner/event-trigger.js +4 -17
  612. package/dist/workflow-runner/feature.js +2 -2
  613. package/dist/workflow-runner/handlers/resume-run.write.js +4 -4
  614. package/dist/workflow-runner/pending-projection.js +6 -7
  615. package/dist/workflow-runner/runner.js +6 -6
  616. package/dist/workflow-runner/tables.js +6 -6
  617. package/dist/workflow-runner/workflow-registry.js +1 -1
  618. package/package.json +20 -10
  619. package/src/agent-tools/changes.json +6 -0
  620. package/src/audit/changes.json +12 -0
  621. package/src/auth-mfa/changes.json +6 -0
  622. package/src/billing-foundation/changes.json +55 -0
  623. package/src/cap-counter/changes.json +18 -0
  624. package/src/channel-email/changes.json +6 -0
  625. package/src/config/changes.json +12 -0
  626. package/src/crypto-shredding/changes.json +6 -0
  627. package/src/delivery/changes.json +30 -0
  628. package/src/form-draft/changes.json +6 -0
  629. package/src/host-routing/changes.json +6 -0
  630. package/src/inbound-mail-foundation/changes.json +18 -0
  631. package/src/jobs/changes.json +13 -0
  632. package/src/mail-foundation/changes.json +6 -0
  633. package/src/renderer-pdf/changes.json +6 -0
  634. package/src/step-dispatcher/changes.json +6 -0
  635. package/src/subscription-dodo/changes.json +14 -0
  636. package/src/subscription-stripe/changes.json +12 -0
  637. package/src/template-resolver/changes.json +48 -0
  638. package/src/tenant/changes.json +6 -0
  639. package/src/tenant-lifecycle/changes.json +6 -0
  640. package/src/tier-engine/changes.json +6 -0
  641. package/src/waitlist/changes.json +18 -0
  642. package/dist/billing-foundation/consumer-protection/stored-user-email.d.ts +0 -1
  643. package/dist/billing-foundation/consumer-protection/stored-user-email.js +0 -8
@@ -108,8 +108,8 @@ export function createEnableConfirmPreauthHandler(opts) {
108
108
  // re-enrollment until an operator manually disables MFA. NOT fixed by
109
109
  // this ordering: burnToken() above still runs before this check, so a
110
110
  // mid-flight restricted/deleted user still burns their setup token
111
- // (framework#1523/5) — moving the burn later would need care around
112
- // the existing userId-unique-constraint backstop between the
111
+ // — moving the burn later would need care around the
112
+ // existing userId-unique-constraint backstop between the
113
113
  // `existing` check above and executor.create() below.
114
114
  const systemUser = createSystemUser(tenantId, ["SystemAdmin"]);
115
115
  const userRow = (await ctx.queryAs(systemUser, UserQueries.findForAuth, {
@@ -150,10 +150,10 @@ export function createEnableConfirmPreauthHandler(opts) {
150
150
  id: userId,
151
151
  tenantId,
152
152
  roles: mergedRoles,
153
- // This endpoint logs the user in directly (fw#1760), so a stored
153
+ // This endpoint logs the user in directly, so a stored
154
154
  // timezone must survive the preauth-MFA-enrollment login just like
155
155
  // a plain password login does — same helper as verify.write.ts /
156
- // invite-accept-with-login.write.ts (#1759).
156
+ // invite-accept-with-login.write.ts.
157
157
  ...sessionTimezoneField(userRow?.timezone),
158
158
  ...sessionLocaleField(userRow?.locale),
159
159
  };
@@ -38,8 +38,8 @@ export function createEnableConfirmHandler(opts) {
38
38
  // already rules that out).
39
39
  if (verify.payload.userId !== event.user.id)
40
40
  return invalidSetupToken();
41
- // Fail closed, matching verify.write.ts/enable-confirm-preauth.write.ts
42
- // (#1467): session-authed (lower risk — the caller already has a
41
+ // Fail closed, matching verify.write.ts/enable-confirm-preauth.write.ts:
42
+ // session-authed (lower risk — the caller already has a
43
43
  // valid JWT), but silently skipping the single-use burn here would
44
44
  // still let a double-confirm race complete twice instead of the
45
45
  // second seeing invalidSetupToken(). Checked before the TOTP verify
@@ -37,7 +37,7 @@ export function createEnableStartPreauthHandler(opts) {
37
37
  const verified = verifyMfaPreauthSetupToken(event.payload.preauthSetupToken, opts.challengeTokenSecret);
38
38
  // Same generic "sign in again" error as an expired/invalid MFA
39
39
  // challenge token — both mean the login-flow token expired, no
40
- // distinct i18n needed (see #1465's scoping comment).
40
+ // distinct i18n needed.
41
41
  if (!verified.ok)
42
42
  return invalidChallengeToken();
43
43
  const { userId, tenantId } = verified.payload;
@@ -97,7 +97,7 @@ export async function mfaReencryptJob(rawPayload, ctx, db) {
97
97
  }
98
98
  // After PII peel (when configured), values must be current-cipher
99
99
  // envelopes — non-JSON/non-envelope is not a supported legacy decrypt
100
- // path (#1541, mirrors config #1513).
100
+ // path, matching config behavior.
101
101
  const totpClass = classifyStoredEnvelope(envelopeValues.totpSecret, targetVersion);
102
102
  const recoveryClass = classifyStoredEnvelope(envelopeValues.recoveryCodes, targetVersion);
103
103
  if (totpClass === "unrecognized" || recoveryClass === "unrecognized") {
@@ -36,11 +36,11 @@ export function createRegenerateRecoveryHandler(opts) {
36
36
  // count, since either way the presented code is consumed/irrelevant
37
37
  // afterward.
38
38
  //
39
- // Fail closed, matching verify.write.ts/enable-confirm-preauth.write.ts
40
- // (#1467): without ctx.redis, verifyMfaFactor's TOTP-replay burn is
41
- // skipped, letting an observed code be replayed within the ±1-step
42
- // window to regenerate (and thus invalidate) recovery codes — a
43
- // session-authed route, so pre-unwiring JWTs stay a valid attack path.
39
+ // Fail closed, matching verify.write.ts/enable-confirm-preauth.write.ts:
40
+ // without ctx.redis, verifyMfaFactor's TOTP-replay burn is skipped,
41
+ // letting an observed code be replayed within the ±1-step window to
42
+ // regenerate (and thus invalidate) recovery codes — a session-authed
43
+ // route, so pre-unwiring JWTs stay a valid attack path.
44
44
  if (!ctx.redis) {
45
45
  return writeFailure(new InternalError({
46
46
  message: "regenerate-recovery (mfa) requires ctx.redis for TOTP replay protection",
@@ -42,7 +42,7 @@ export function createMfaVerifyHandler(opts) {
42
42
  if (!verified.ok)
43
43
  return invalidChallengeToken();
44
44
  const { userId, tenantId } = verified.payload;
45
- // Fail closed, matching enable-confirm-preauth.write.ts (#1467):
45
+ // Fail closed, matching enable-confirm-preauth.write.ts:
46
46
  // this route sits behind a challenge token from a successful login,
47
47
  // but its own secret is still just a short TOTP/recovery code —
48
48
  // silently skipping the brute-force cap and single-use burn when
@@ -6,7 +6,7 @@ const SETUP_TOKEN_PURPOSE = "mfa-setup-token-v1";
6
6
  const CHALLENGE_TOKEN_PURPOSE = "mfa-challenge-token-v1";
7
7
  // A blank or whitespace-only override is treated as absent — an empty env
8
8
  // var (MFA_SETUP_TOKEN_SECRET="") must fall back to derivation, not sign
9
- // tokens with an empty/near-empty HMAC key (fw#1623).
9
+ // tokens with an empty/near-empty HMAC key.
10
10
  function resolveSecret(override, fallback) {
11
11
  return override !== undefined && override.trim() !== "" ? override : fallback();
12
12
  }
@@ -1,7 +1,7 @@
1
1
  import type { TranslationsByLocale } from "@cosmicdrift/kumiko-renderer";
2
2
  import type { ComponentType, ReactNode } from "react";
3
3
  export type AuthMfaClientOptions = {
4
- /** Key-Overrides pro Locale, gemerged mit den Default-Bundles (de/en). */
4
+ /** Key overrides per locale, merged with the default bundles (de/en). */
5
5
  readonly translations?: TranslationsByLocale;
6
6
  };
7
7
  export type AuthMfaClientFeature = {
@@ -1,4 +1,4 @@
1
- // Client-Feature-Factory für auth-mfa — mirrors emailPasswordClient()'s
1
+ // Client feature factory for auth-mfa — mirrors emailPasswordClient()'s
2
2
  // shape so apps register both the same way:
3
3
  // createKumikoApp({ clientFeatures: [emailPasswordClient(), authMfaClient()] }).
4
4
  // No gates: MfaVerifyScreen isn't a route gate, it's a state swap wired
@@ -1,6 +1,6 @@
1
- // Default-Bundles für die auth-mfa Feature-UI. Merged in mit
2
- // authMfaClient() analog zu emailPasswordClient() aus auth-email-password.
3
- // Keys folgen `auth.mfa.<area>.<slug>`.
1
+ // Default bundles for the auth-mfa feature UI, merged by
2
+ // authMfaClient() analogously to emailPasswordClient() from auth-email-password.
3
+ // Keys follow `auth.mfa.<area>.<slug>`.
4
4
  export const defaultTranslations = {
5
5
  en: {
6
6
  "screen:auth-mfa-enable.title": "Two-factor authentication",
@@ -10,6 +10,10 @@ export const defaultTranslations = {
10
10
  "auth.mfa.verify.submit": "Verify",
11
11
  "auth.mfa.verify.submitting": "…",
12
12
  "auth.mfa.verify.backToLogin": "Back to login",
13
+ "auth.mfa.verify.recoveryCode": "Recovery code",
14
+ "auth.mfa.verify.recoverySubtitle": "Enter one of the recovery codes you saved when you set up two-factor authentication.",
15
+ "auth.mfa.verify.useAuthenticatorCode": "Use the code from your app",
16
+ "auth.mfa.verify.useRecoveryCode": "Use a recovery code instead",
13
17
  "auth.mfa.errors.invalidCode": "Invalid code. Please try again.",
14
18
  "auth.mfa.errors.invalidTotpCode": "Invalid code. Please try again.",
15
19
  "auth.mfa.errors.invalidChallengeToken": "Your sign-in has expired. Please sign in again.",
@@ -35,7 +39,7 @@ export const defaultTranslations = {
35
39
  "auth.mfa.setup.confirm": "Complete setup",
36
40
  "auth.mfa.disable.title": "Disable two-factor authentication",
37
41
  "auth.mfa.disable.description": "Confirm with a code from your authenticator app or a recovery code. Your account will then be protected by your password alone.",
38
- "auth.mfa.disable.code": "Code from your authenticator app or a recovery code",
42
+ "auth.mfa.disable.code": "Code from your authenticator app",
39
43
  "auth.mfa.disable.confirm": "Disable",
40
44
  "auth.mfa.disable.cancel": "Cancel",
41
45
  "auth.mfa.disable.trigger": "Disable two-factor authentication",
@@ -1,7 +1,7 @@
1
- // Public exports für die Browser-Seite des auth-mfa Features. Sub-Path-
2
- // Export `@cosmicdrift/kumiko-bundled-features/auth-mfa/web` — Server-Seite
3
- // (defineFeature) bleibt frei von React-/DOM-Deps, siehe auth-email-
4
- // password/web/index.ts für die selbe Trennung.
1
+ // Public exports for the browser side of the auth-mfa feature. The subpath
2
+ // export `@cosmicdrift/kumiko-bundled-features/auth-mfa/web` keeps the server
3
+ // side (`defineFeature`) free of React/DOM dependencies, matching the same
4
+ // separation in auth-email-password/web/index.ts.
5
5
  export { authMfaClient } from "./client-plugin.js";
6
6
  export { defaultTranslations, mergeTranslations } from "./i18n.js";
7
7
  export { confirmMfaSetupPreauth, startMfaSetupPreauth, verifyMfaChallenge } from "./mfa-client.js";
@@ -1,10 +1,10 @@
1
- // Browser-Seite von POST /api/auth/mfa/verify — completes the two-step
2
- // login LoginScreen started when the server responded mfaRequired. Same
3
- // fetch/CSRF pattern as auth-email-password's login() (pre-session, no
4
- // csrfHeader() — there's no session yet to protect). Response shape is
1
+ // Browser side of POST /api/auth/mfa/verify — completes the two-step
2
+ // login that LoginScreen started when the server responded with mfaRequired.
3
+ // Uses the same fetch/CSRF pattern as auth-email-password's login(), but runs
4
+ // before a session exists, so it does not call csrfHeader(). The response is
5
5
  // byte-identical to /auth/login's ({isSuccess, token, user} | {isSuccess:
6
- // false, error}) since auth-routes.ts mints via the same
7
- // mintSessionAndRespond() for both routes.
6
+ // false, error}) because auth-routes.ts uses mintSessionAndRespond() for both
7
+ // routes.
8
8
  // kumiko-lint-ignore cross-feature-import client-only types, the feature's server barrel has no web/ re-export
9
9
  import { toLoginResponse, } from "../../auth-email-password/web/index.js";
10
10
  export async function verifyMfaChallenge(challengeToken, code) {
@@ -40,11 +40,12 @@ export async function verifyMfaChallenge(challengeToken, code) {
40
40
  },
41
41
  };
42
42
  }
43
- // Browser-Seite von POST /api/auth/mfa/preauth-enable-start — lets a user
43
+ // Browser side of POST /api/auth/mfa/preauth-enable-start — lets a user
44
44
  // blocked at login by MFA enforcement generate a TOTP secret/QR without a
45
- // session, using the preauthSetupToken login()'s mfa-setup-required result
46
- // carries. No JWT/cookie here (see auth-routes.ts's route comment) — the
47
- // response's setupToken is consumed by confirmMfaSetupPreauth below.
45
+ // session, using the preauthSetupToken carried by login()'s
46
+ // mfa-setup-required result. No JWT/cookie is issued here (see the route
47
+ // comment in auth-routes.ts); the response's
48
+ // setupToken is consumed by confirmMfaSetupPreauth below.
48
49
  export async function startMfaSetupPreauth(preauthSetupToken, accountLabel) {
49
50
  const res = await fetch("/api/auth/mfa/preauth-enable-start", {
50
51
  method: "POST",
@@ -85,12 +86,12 @@ export async function startMfaSetupPreauth(preauthSetupToken, accountLabel) {
85
86
  },
86
87
  };
87
88
  }
88
- // Browser-Seite von POST /api/auth/mfa/preauth-confirm — completes both
89
- // the enrollment startMfaSetupPreauth began and the login mfa-setup-
90
- // required block. Response shape mirrors /auth/mfa/verify's (mints via
91
- // the same mintSessionAndRespond()), same as verifyMfaChallenge above —
92
- // kept as its own function rather than a shared helper so this file's
93
- // two already-tested request shapes stay independently editable.
89
+ // Browser side of POST /api/auth/mfa/preauth-confirm — completes both the
90
+ // enrollment started by startMfaSetupPreauth and the login
91
+ // mfa-setup-required block. Its response mirrors /auth/mfa/verify's and is
92
+ // minted via the same mintSessionAndRespond(). It remains a separate function
93
+ // so this file's two already-tested request shapes stay independently
94
+ // editable.
94
95
  export async function confirmMfaSetupPreauth(setupToken, code) {
95
96
  const res = await fetch("/api/auth/mfa/preauth-confirm", {
96
97
  method: "POST",
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from "react";
2
+ export type MfaCodeOrRecoveryFieldProps = {
3
+ readonly id: string;
4
+ readonly codeLabel: string;
5
+ readonly code: string;
6
+ readonly onCodeChange: (code: string) => void;
7
+ readonly useRecovery: boolean;
8
+ readonly onUseRecoveryChange: (useRecovery: boolean) => void;
9
+ readonly disabled?: boolean;
10
+ };
11
+ export declare function MfaCodeOrRecoveryField({ id, codeLabel, code, onCodeChange, useRecovery, onUseRecoveryChange, disabled, }: MfaCodeOrRecoveryFieldProps): ReactNode;
@@ -0,0 +1,12 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
3
+ export function MfaCodeOrRecoveryField({ id, codeLabel, code, onCodeChange, useRecovery, onUseRecoveryChange, disabled, }) {
4
+ const t = useTranslation();
5
+ const { Button, Field, Input } = usePrimitives();
6
+ return (_jsxs(_Fragment, { children: [_jsx(Field, { id: id, label: useRecovery ? t("auth.mfa.verify.recoveryCode") : codeLabel, required: true, children: useRecovery ? (_jsx(Input, { kind: "text", id: id, name: id, value: code, onChange: onCodeChange, disabled: disabled, autoFocus: true, autoComplete: "off" })) : (_jsx(Input, { kind: "code", length: 6, id: id, name: id, value: code, onChange: onCodeChange, disabled: disabled, autoFocus: true })) }), _jsx(Button, { type: "button", variant: "link", disabled: disabled, onClick: () => {
7
+ onUseRecoveryChange(!useRecovery);
8
+ onCodeChange("");
9
+ }, children: useRecovery
10
+ ? t("auth.mfa.verify.useAuthenticatorCode")
11
+ : t("auth.mfa.verify.useRecoveryCode") })] }));
12
+ }
@@ -12,20 +12,31 @@ import { jsx as _jsx } from "react/jsx-runtime";
12
12
  import { useDispatcher, usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
13
13
  import { useState } from "react";
14
14
  import { AuthMfaHandlers } from "./constants.js";
15
+ import { MfaCodeOrRecoveryField } from "./mfa-code-or-recovery-field.js";
15
16
  // kumiko-lint-ignore no-custom-primitives Uses usePrimitives().Dialog internally, name is an MFA domain action, not a primitive reimplementation
16
17
  export function MfaDisableDialog({ open, onOpenChange, onDisabled, onError, }) {
17
18
  const t = useTranslation();
18
- const { Dialog, Field, Input } = usePrimitives();
19
+ const { Dialog } = usePrimitives();
19
20
  const dispatcher = useDispatcher();
20
21
  const [code, setCode] = useState("");
22
+ const [useRecovery, setUseRecovery] = useState(false);
23
+ const reset = () => {
24
+ setCode("");
25
+ setUseRecovery(false);
26
+ };
27
+ const handleOpenChange = (nextOpen) => {
28
+ if (!nextOpen)
29
+ reset();
30
+ onOpenChange(nextOpen);
31
+ };
21
32
  const confirm = async () => {
22
33
  const res = await dispatcher.write(AuthMfaHandlers.disable, { code });
23
- setCode("");
34
+ reset();
24
35
  if (!res.isSuccess) {
25
36
  onError(res.error.i18nKey);
26
37
  return;
27
38
  }
28
39
  onDisabled();
29
40
  };
30
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, variant: "danger", title: t("auth.mfa.disable.title"), description: t("auth.mfa.disable.description"), confirmLabel: t("auth.mfa.disable.confirm"), cancelLabel: t("auth.mfa.disable.cancel"), onConfirm: confirm, testId: "mfa-disable-dialog", children: _jsx(Field, { id: "mfa-disable-code", label: t("auth.mfa.disable.code"), required: true, children: _jsx(Input, { kind: "text", id: "mfa-disable-code", name: "mfa-disable-code", value: code, onChange: setCode, autoComplete: "one-time-code" }) }) }));
41
+ return (_jsx(Dialog, { open: open, onOpenChange: handleOpenChange, variant: "danger", title: t("auth.mfa.disable.title"), description: t("auth.mfa.disable.description"), confirmLabel: t("auth.mfa.disable.confirm"), cancelLabel: t("auth.mfa.disable.cancel"), onConfirm: confirm, testId: "mfa-disable-dialog", children: _jsx(MfaCodeOrRecoveryField, { id: "mfa-disable-code", codeLabel: t("auth.mfa.disable.code"), code: code, onCodeChange: setCode, useRecovery: useRecovery, onUseRecoveryChange: setUseRecovery }) }));
31
42
  }
@@ -9,20 +9,31 @@ import { jsx as _jsx } from "react/jsx-runtime";
9
9
  import { useDispatcher, usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
10
10
  import { useState } from "react";
11
11
  import { AuthMfaHandlers } from "./constants.js";
12
+ import { MfaCodeOrRecoveryField } from "./mfa-code-or-recovery-field.js";
12
13
  // kumiko-lint-ignore no-custom-primitives Uses usePrimitives().Dialog internally, name is an MFA domain action, not a primitive reimplementation
13
14
  export function MfaRegenerateRecoveryDialog({ open, onOpenChange, onRegenerated, onError, }) {
14
15
  const t = useTranslation();
15
- const { Dialog, Field, Input } = usePrimitives();
16
+ const { Dialog } = usePrimitives();
16
17
  const dispatcher = useDispatcher();
17
18
  const [code, setCode] = useState("");
19
+ const [useRecovery, setUseRecovery] = useState(false);
20
+ const reset = () => {
21
+ setCode("");
22
+ setUseRecovery(false);
23
+ };
24
+ const handleOpenChange = (nextOpen) => {
25
+ if (!nextOpen)
26
+ reset();
27
+ onOpenChange(nextOpen);
28
+ };
18
29
  const confirm = async () => {
19
30
  const res = await dispatcher.write(AuthMfaHandlers.regenerateRecovery, { code });
20
- setCode("");
31
+ reset();
21
32
  if (!res.isSuccess) {
22
33
  onError(res.error.i18nKey);
23
34
  return;
24
35
  }
25
36
  onRegenerated(res.data.recoveryCodes);
26
37
  };
27
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, title: t("auth.mfa.regenerate.title"), description: t("auth.mfa.regenerate.description"), confirmLabel: t("auth.mfa.regenerate.confirm"), cancelLabel: t("auth.mfa.regenerate.cancel"), onConfirm: confirm, testId: "mfa-regenerate-recovery-dialog", children: _jsx(Field, { id: "mfa-regenerate-code", label: t("auth.mfa.regenerate.code"), required: true, children: _jsx(Input, { kind: "text", id: "mfa-regenerate-code", name: "mfa-regenerate-code", value: code, onChange: setCode, autoComplete: "one-time-code" }) }) }));
38
+ return (_jsx(Dialog, { open: open, onOpenChange: handleOpenChange, title: t("auth.mfa.regenerate.title"), description: t("auth.mfa.regenerate.description"), confirmLabel: t("auth.mfa.regenerate.confirm"), cancelLabel: t("auth.mfa.regenerate.cancel"), onConfirm: confirm, testId: "mfa-regenerate-recovery-dialog", children: _jsx(MfaCodeOrRecoveryField, { id: "mfa-regenerate-code", codeLabel: t("auth.mfa.regenerate.code"), code: code, onCodeChange: setCode, useRecovery: useRecovery, onUseRecoveryChange: setUseRecovery }) }));
28
39
  }
@@ -48,6 +48,7 @@ export function MfaSetupPreauthScreen({ preauthSetupToken, accountLabel, title,
48
48
  const [setup, setSetup] = useState(null);
49
49
  const [acknowledged, setAcknowledged] = useState(false);
50
50
  const [code, setCode] = useState("");
51
+ const [attempt, setAttempt] = useState(0);
51
52
  const [busy, setBusy] = useState(false);
52
53
  const [error, setError] = useState(null);
53
54
  const [retryAfterSeconds, setRetryAfterSeconds] = useState(null);
@@ -78,14 +79,14 @@ export function MfaSetupPreauthScreen({ preauthSetupToken, accountLabel, title,
78
79
  setBusy(false);
79
80
  }
80
81
  };
81
- const confirmSetup = async () => {
82
- if (!setup)
82
+ const confirmSetup = async (submittedCode) => {
83
+ if (!setup || busy)
83
84
  return;
84
85
  setBusy(true);
85
86
  setError(null);
86
87
  setRetryAfterSeconds(null);
87
88
  try {
88
- const res = await confirmMfaSetupPreauth(setup.setupToken, code);
89
+ const res = await confirmMfaSetupPreauth(setup.setupToken, submittedCode);
89
90
  if (res.kind !== "success") {
90
91
  setError(res.error.reason);
91
92
  setRetryAfterSeconds(res.error.retryAfterSeconds ?? null);
@@ -98,7 +99,11 @@ export function MfaSetupPreauthScreen({ preauthSetupToken, accountLabel, title,
98
99
  setSetup(null);
99
100
  setAcknowledged(false);
100
101
  setCode("");
102
+ return;
101
103
  }
104
+ // Remount: the field was disabled during confirm and lost focus.
105
+ setCode("");
106
+ setAttempt((n) => n + 1);
102
107
  return;
103
108
  }
104
109
  onSuccess?.(res.data.landingPath);
@@ -112,7 +117,7 @@ export function MfaSetupPreauthScreen({ preauthSetupToken, accountLabel, title,
112
117
  };
113
118
  const onSubmit = (e) => {
114
119
  e?.preventDefault();
115
- void confirmSetup();
120
+ void confirmSetup(code);
116
121
  };
117
122
  return (_jsxs(AuthCard, { title: title ?? t("auth.mfa.setup.title"), subtitle: subtitle ?? t("auth.mfa.setup.subtitle"), children: [error !== null ? (_jsx(Banner, { variant: "error", children: error === "too_many_attempts" && retryAfterSeconds !== null
118
123
  ? t("auth.mfa.errors.tooManyAttemptsWithSeconds", { seconds: retryAfterSeconds })
@@ -124,5 +129,5 @@ export function MfaSetupPreauthScreen({ preauthSetupToken, accountLabel, title,
124
129
  copyable: false,
125
130
  multiline: false,
126
131
  },
127
- ], copyLabel: t("kumiko.secretMint.copy"), copiedLabel: t("kumiko.secretMint.copied"), testId: "mfa-setup-preauth-qr" })) : (_jsx("span", { className: "text-sm font-semibold", children: t("auth.mfa.enable.scanTitle") })), _jsx("span", { className: "text-xs text-muted-foreground", children: t("auth.mfa.enable.manualEntry") }), _jsx("code", { className: "inline-block break-all rounded bg-muted px-3 py-2 font-mono text-sm", children: setup.secretParam })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-sm font-semibold", children: t("auth.mfa.enable.recoveryTitle") }), _jsx("span", { className: "text-xs text-muted-foreground", children: t("auth.mfa.enable.recoveryHint") }), _jsx("code", { className: "block whitespace-pre-wrap break-all rounded bg-muted px-3 py-2 font-mono text-sm", children: setup.recoveryCodes.join("\n") }), _jsx(Field, { id: "mfa-setup-preauth-ack", label: t("auth.mfa.enable.acknowledge"), children: _jsx(Input, { kind: "boolean", id: "mfa-setup-preauth-ack", name: "mfa-setup-preauth-ack", value: acknowledged, onChange: setAcknowledged }) })] }), _jsx(Field, { id: "mfa-setup-preauth-code", label: t("auth.mfa.enable.code"), required: true, children: _jsx(Input, { kind: "text", id: "mfa-setup-preauth-code", name: "mfa-setup-preauth-code", value: code, onChange: setCode, disabled: busy || !acknowledged, autoComplete: "one-time-code" }) }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { type: "submit", variant: "primary", loading: busy, disabled: busy || !acknowledged || code.length !== 6, children: t("auth.mfa.setup.confirm") }) })] }) })), onCancel ? (_jsx("div", { className: "flex justify-center", children: _jsx(Button, { type: "button", variant: "secondary", onClick: onCancel, disabled: busy, children: t("auth.mfa.verify.backToLogin") }) })) : null] }));
132
+ ], copyLabel: t("kumiko.secretMint.copy"), copiedLabel: t("kumiko.secretMint.copied"), testId: "mfa-setup-preauth-qr" })) : (_jsx("span", { className: "text-sm font-semibold", children: t("auth.mfa.enable.scanTitle") })), _jsx("span", { className: "text-xs text-muted-foreground", children: t("auth.mfa.enable.manualEntry") }), _jsx("code", { className: "inline-block break-all rounded bg-muted px-3 py-2 font-mono text-sm", children: setup.secretParam })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-sm font-semibold", children: t("auth.mfa.enable.recoveryTitle") }), _jsx("span", { className: "text-xs text-muted-foreground", children: t("auth.mfa.enable.recoveryHint") }), _jsx("code", { className: "block whitespace-pre-wrap break-all rounded bg-muted px-3 py-2 font-mono text-sm", children: setup.recoveryCodes.join("\n") }), _jsx(Field, { id: "mfa-setup-preauth-ack", label: t("auth.mfa.enable.acknowledge"), children: _jsx(Input, { kind: "boolean", id: "mfa-setup-preauth-ack", name: "mfa-setup-preauth-ack", value: acknowledged, onChange: setAcknowledged }) })] }), _jsx(Field, { id: "mfa-setup-preauth-code", label: t("auth.mfa.enable.code"), required: true, children: _jsx(Input, { kind: "code", length: 6, id: "mfa-setup-preauth-code", name: "mfa-setup-preauth-code", value: code, onChange: setCode, onComplete: (next) => void confirmSetup(next), disabled: busy || !acknowledged, hasError: error !== null, autoFocus: attempt > 0 }, attempt) }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { type: "submit", variant: "primary", loading: busy, disabled: busy || !acknowledged || code.length !== 6, children: t("auth.mfa.setup.confirm") }) })] }) })), onCancel ? (_jsx("div", { className: "flex justify-center", children: _jsx(Button, { type: "button", variant: "secondary", onClick: onCancel, disabled: busy, children: t("auth.mfa.verify.backToLogin") }) })) : null] }));
128
133
  }
@@ -1,15 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- // Login-Challenge-Step, gerendert wenn LoginScreen's onMfaChallenge feuert
3
- // (/auth/login antwortete mfaRequired). Stilistisch wie login-screen.tsx:
4
- // useState + direkter fetch-Call, kein Dispatcher — es existiert noch kein
5
- // JWT. Nach Erfolg ruft es session.refresh() statt eines Hard-Reloads, weil
6
- // der Aufrufer (typisch ein Wizard-State im App-Root) sofort neu rendern
7
- // will, sobald status:"authenticated" ankommt.
2
+ // Login challenge step, rendered when LoginScreen's onMfaChallenge fires
3
+ // (/auth/login returned mfaRequired). It follows login-screen.tsx:
4
+ // useState plus a direct fetch call, without a dispatcher because no JWT
5
+ // exists yet. After success, it calls session.refresh() instead of a hard
6
+ // reload so the caller (typically wizard state in the app root) can render
7
+ // immediately when status:"authenticated" arrives.
8
8
  import { usePrimitives, useTranslation } from "@cosmicdrift/kumiko-renderer";
9
9
  import { useState } from "react";
10
10
  // kumiko-lint-ignore cross-feature-import client-only barrel, the feature's server barrel has no web/ re-export
11
11
  import { AuthCard, useSession } from "../../auth-email-password/web/index.js";
12
12
  import { verifyMfaChallenge } from "./mfa-client.js";
13
+ const MFA_CODE_LENGTH = 6;
13
14
  function reasonToKey(reason) {
14
15
  switch (reason) {
15
16
  case "invalid_totp_code":
@@ -31,13 +32,17 @@ export function MfaVerifyScreen({ challengeToken, title, subtitle, submitLabel,
31
32
  const t = useTranslation();
32
33
  const { Form, Field, Input, Button, Banner } = usePrimitives();
33
34
  const session = useSession();
35
+ const [mode, setMode] = useState("authenticator");
34
36
  const [code, setCode] = useState("");
37
+ const [attempt, setAttempt] = useState(0);
35
38
  const [submitting, setSubmitting] = useState(false);
36
39
  const [error, setError] = useState(null);
37
- const doSubmit = async () => {
40
+ const doSubmit = async (submittedCode) => {
41
+ if (submitting)
42
+ return;
38
43
  setSubmitting(true);
39
44
  setError(null);
40
- const res = await verifyMfaChallenge(challengeToken, code);
45
+ const res = await verifyMfaChallenge(challengeToken, submittedCode);
41
46
  if (res.kind === "success") {
42
47
  await session.refresh();
43
48
  setSubmitting(false);
@@ -46,12 +51,28 @@ export function MfaVerifyScreen({ challengeToken, title, subtitle, submitLabel,
46
51
  }
47
52
  setSubmitting(false);
48
53
  setError(res.error.reason);
54
+ // Remount: the input was disabled during submit and lost focus.
55
+ setAttempt((n) => n + 1);
56
+ if (mode === "authenticator")
57
+ setCode("");
49
58
  };
50
59
  const onSubmit = (e) => {
51
60
  e?.preventDefault();
52
- void doSubmit();
61
+ void doSubmit(code);
62
+ };
63
+ const toggleMode = () => {
64
+ setMode(mode === "authenticator" ? "recovery" : "authenticator");
65
+ setCode("");
66
+ setError(null);
53
67
  };
54
- return (_jsx(AuthCard, { title: title ?? t("auth.mfa.verify.title"), subtitle: subtitle ?? t("auth.mfa.verify.subtitle"), children: _jsxs(Form, { onSubmit: onSubmit, children: [_jsx(Field, { id: "mfa-verify-code", label: t("auth.mfa.verify.code"), required: true, children: _jsx(Input, { kind: "text", id: "mfa-verify-code", name: "mfa-verify-code", value: code, onChange: setCode, disabled: submitting, required: true, autoComplete: "one-time-code" }) }), error !== null ? _jsx(Banner, { variant: "error", children: t(reasonToKey(error)) }) : null, _jsx(Button, { type: "submit", loading: submitting, disabled: submitting, children: submitting
68
+ const isRecovery = mode === "recovery";
69
+ const fieldId = isRecovery ? "mfa-verify-recovery-code" : "mfa-verify-code";
70
+ const defaultSubtitle = isRecovery
71
+ ? t("auth.mfa.verify.recoverySubtitle")
72
+ : t("auth.mfa.verify.subtitle");
73
+ return (_jsx(AuthCard, { title: title ?? t("auth.mfa.verify.title"), subtitle: subtitle ?? defaultSubtitle, children: _jsxs(Form, { onSubmit: onSubmit, children: [_jsx(Field, { id: fieldId, label: isRecovery ? t("auth.mfa.verify.recoveryCode") : t("auth.mfa.verify.code"), required: true, children: isRecovery ? (_jsx(Input, { kind: "text", id: fieldId, name: fieldId, value: code, onChange: setCode, disabled: submitting, required: true, autoFocus: true, autoComplete: "off" }, `${mode}-${attempt}`)) : (_jsx(Input, { kind: "code", length: MFA_CODE_LENGTH, id: fieldId, name: fieldId, value: code, onChange: setCode, onComplete: (next) => void doSubmit(next), disabled: submitting, hasError: error !== null, required: true, autoFocus: true }, `${mode}-${attempt}`)) }), error !== null ? _jsx(Banner, { variant: "error", children: t(reasonToKey(error)) }) : null, _jsx(Button, { type: "submit", loading: submitting, disabled: submitting, children: submitting
55
74
  ? t("auth.mfa.verify.submitting")
56
- : (submitLabel ?? t("auth.mfa.verify.submit")) }), onCancel ? (_jsx(Button, { type: "button", variant: "secondary", onClick: onCancel, disabled: submitting, children: t("auth.mfa.verify.backToLogin") })) : null] }) }));
75
+ : (submitLabel ?? t("auth.mfa.verify.submit")) }), _jsx(Button, { type: "button", variant: "link", onClick: toggleMode, disabled: submitting, children: isRecovery
76
+ ? t("auth.mfa.verify.useAuthenticatorCode")
77
+ : t("auth.mfa.verify.useRecoveryCode") }), onCancel ? (_jsx(Button, { type: "button", variant: "secondary", onClick: onCancel, disabled: submitting, children: t("auth.mfa.verify.backToLogin") })) : null] }) }));
57
78
  }
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Deterministic aggregate-id für die subscription eines Plattform-
3
- * Tenants. EINE Subscription pro Tenant (Add-Ons sind line-items in
4
- * derselben subscription, nicht eigene). Provider-Wechsel (z.B.
5
- * Stripe→Mollie-Migration) appended einen neuen event auf denselben
6
- * Stream — selber Tenant, selber Aggregate-Stream.
2
+ * Deterministic aggregate ID for a platform tenant's subscription. Exactly
3
+ * one subscription exists per tenant; add-ons are line items in that
4
+ * subscription, not separate aggregates. A provider change, such as a
5
+ * Stripe→Mollie migration, appends a new event to the same stream because
6
+ * the tenant and aggregate stream remain the same.
7
7
  */
8
8
  export declare function subscriptionAggregateId(tenantId: string): string;
9
9
  /**
@@ -1,10 +1,9 @@
1
1
  import { v5 as uuidv5 } from "uuid";
2
- // Fixed UUID-namespace für die deterministic aggregate-id-Ableitung.
3
- // Generiert einmalig (2026-05-03), in Stein gemeißelt: ein Wechsel
4
- // würde jeden existing aggregate-Stream re-keyen → kaputter
5
- // Subscription-State, kaputte Audit-History. Drift-Pin in
6
- // __tests__/feature.test.ts pinnt die UUID.
7
- /** Pro Plattform-Tenant existiert genau EIN subscription-Aggregate. */
2
+ // Fixed UUID namespace for deterministic aggregate-id derivation.
3
+ // Generated once and immutable: changing it would re-key every existing
4
+ // aggregate stream, breaking subscription state and audit history. The drift
5
+ // pin in __tests__/feature.test.ts locks the UUID.
6
+ /** Exactly one subscription aggregate exists per platform tenant. */
8
7
  const SUBSCRIPTION_NAMESPACE = "5c3b2d1e-9a4f-4e8c-b7a3-1f8d6c2e9a4b";
9
8
  /** Exactly ONE payment-aggregate-stream exists per platform tenant (it
10
9
  * collects many payment-received events — one stream per tenant, not one
@@ -20,11 +19,11 @@ const PAYMENT_ROW_NAMESPACE = "3a1c9f4e-6b2d-4c8a-9e5f-7d4b1a2c8e6f";
20
19
  * declaration (one stream per request). Generated 2026-10-03, set in stone. */
21
20
  const TERMINATION_UNMATCHED_NAMESPACE = "7e2f4b9a-3c1d-4a68-8d05-b6c9e1f27a34";
22
21
  /**
23
- * Deterministic aggregate-id für die subscription eines Plattform-
24
- * Tenants. EINE Subscription pro Tenant (Add-Ons sind line-items in
25
- * derselben subscription, nicht eigene). Provider-Wechsel (z.B.
26
- * Stripe→Mollie-Migration) appended einen neuen event auf denselben
27
- * Stream — selber Tenant, selber Aggregate-Stream.
22
+ * Deterministic aggregate ID for a platform tenant's subscription. Exactly
23
+ * one subscription exists per tenant; add-ons are line items in that
24
+ * subscription, not separate aggregates. A provider change, such as a
25
+ * Stripe→Mollie migration, appends a new event to the same stream because
26
+ * the tenant and aggregate stream remain the same.
28
27
  */
29
28
  // @wrapper-known uuid-domain
30
29
  export function subscriptionAggregateId(tenantId) {
@@ -17,20 +17,15 @@ export type ResolvedProvider = {
17
17
  * entityName is registered. */
18
18
  export declare function findProviderPlugin(ctx: HandlerContext, providerName: string): ResolvedProvider | null;
19
19
  export declare function resolveProviderPlugin(ctx: HandlerContext, providerName: string): ResolvedProvider;
20
- /** Picks the catalog's provider — an explicit `catalog.providerName`, or the
21
- * single registered provider exposing `priceToTier` when there is exactly
22
- * one. Returns null when `providerName` is unset and no registered plugin
23
- * exposes `priceToTier` — a state `billing-plans` renders as
24
- * `enabled: false` instead of erroring. An explicit `providerName` that is
25
- * not registered, or more than one candidate, is a config bug and throws
26
- * `UnconfiguredError`. */
27
- export declare function findCatalogProvider(ctx: HandlerContext, catalog: BillingPlanCatalog): ResolvedProvider | null;
28
- /** `findCatalogProvider` for callers that require a provider to proceed —
29
- * start-plan-checkout and switch-plan. `FeatureDisabledError` (not
30
- * `UnconfiguredError`) when none is found: an unconfigured provider is
31
- * the same "billing isn't live yet" state as `isPluginBillingEnabled`
32
- * returning false, and the panel already renders both the same way. */
33
- export declare function resolveCatalogProvider(ctx: HandlerContext, catalog: BillingPlanCatalog, handlerName: string): ResolvedProvider;
20
+ /** The providers a catalog sells through, in registration order: an explicit
21
+ * `catalog.providerName` (only that one), otherwise every registered provider
22
+ * exposing `priceToTier`. Empty when none qualifies, a state `billing-plans`
23
+ * renders as `enabled: false` instead of erroring. An explicit `providerName`
24
+ * that is not registered is a config bug and throws `UnconfiguredError`. */
25
+ export declare function listCatalogProviders(ctx: HandlerContext, catalog: BillingPlanCatalog): readonly ResolvedProvider[];
26
+ export declare function filterOfferedProviders(ctx: HandlerContext, providers: readonly ResolvedProvider[]): Promise<readonly ResolvedProvider[]>;
27
+ /** The catalog providers that are live right now: what a buyer can choose from. */
28
+ export declare function listOfferedCatalogProviders(ctx: HandlerContext, catalog: BillingPlanCatalog): Promise<readonly ResolvedProvider[]>;
34
29
  /** `plugin.isBillingEnabled?.(ctx) ?? true` — the one place this fallback is
35
30
  * spelled out, so no call-site forgets to await the optional method before
36
31
  * applying the `?? true` default. */