@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
@@ -15,14 +15,18 @@ import { BILLING_FOUNDATION_FEATURE, isSubscriptionBlockingCheckout, SUBSCRIPTIO
15
15
  export function isNonEmptyStringArray(arr) {
16
16
  return arr.length > 0;
17
17
  }
18
+ /** Every registered provider plugin, in registration order. */
19
+ function listProviderPlugins(ctx) {
20
+ return ctx.registry.getExtensionUsages(SUBSCRIPTION_PROVIDER_EXTENSION).map((usage) => ({
21
+ name: usage.entityName,
22
+ // @cast-boundary engine-payload — extension-usage carries unknown options
23
+ plugin: usage.options,
24
+ }));
25
+ }
18
26
  /** Non-throwing provider lookup by name — null when no plugin with that
19
27
  * entityName is registered. */
20
28
  export function findProviderPlugin(ctx, providerName) {
21
- const usage = ctx.registry
22
- .getExtensionUsages(SUBSCRIPTION_PROVIDER_EXTENSION)
23
- .find((u) => u.entityName === providerName);
24
- // @cast-boundary engine-payload — extension-usage carries unknown options
25
- return usage ? { name: providerName, plugin: usage.options } : null;
29
+ return listProviderPlugins(ctx).find((provider) => provider.name === providerName) ?? null;
26
30
  }
27
31
  export function resolveProviderPlugin(ctx, providerName) {
28
32
  const found = findProviderPlugin(ctx, providerName);
@@ -35,14 +39,12 @@ export function resolveProviderPlugin(ctx, providerName) {
35
39
  }
36
40
  return found;
37
41
  }
38
- /** Picks the catalog's provider — an explicit `catalog.providerName`, or the
39
- * single registered provider exposing `priceToTier` when there is exactly
40
- * one. Returns null when `providerName` is unset and no registered plugin
41
- * exposes `priceToTier` — a state `billing-plans` renders as
42
- * `enabled: false` instead of erroring. An explicit `providerName` that is
43
- * not registered, or more than one candidate, is a config bug and throws
44
- * `UnconfiguredError`. */
45
- export function findCatalogProvider(ctx, catalog) {
42
+ /** The providers a catalog sells through, in registration order: an explicit
43
+ * `catalog.providerName` (only that one), otherwise every registered provider
44
+ * exposing `priceToTier`. Empty when none qualifies, a state `billing-plans`
45
+ * renders as `enabled: false` instead of erroring. An explicit `providerName`
46
+ * that is not registered is a config bug and throws `UnconfiguredError`. */
47
+ export function listCatalogProviders(ctx, catalog) {
46
48
  if (catalog.providerName) {
47
49
  const found = findProviderPlugin(ctx, catalog.providerName);
48
50
  if (!found) {
@@ -52,34 +54,17 @@ export function findCatalogProvider(ctx, catalog) {
52
54
  hint: `provider "${catalog.providerName}" not registered`,
53
55
  });
54
56
  }
55
- return found;
56
- }
57
- const usages = ctx.registry.getExtensionUsages(SUBSCRIPTION_PROVIDER_EXTENSION);
58
- const withPriceCatalog = usages.filter((u) => u.options.priceToTier !== undefined);
59
- if (withPriceCatalog.length > 1) {
60
- throw new UnconfiguredError({
61
- feature: "billing-foundation",
62
- key: "catalog.providerName",
63
- hint: `ambiguous — ${withPriceCatalog.length} registered plugins expose priceToTier, set catalog.providerName explicitly`,
64
- });
57
+ return [found];
65
58
  }
66
- const usage = withPriceCatalog[0];
67
- if (!usage)
68
- return null;
69
- // @cast-boundary engine-payload — extension-usage carries unknown options
70
- return { name: usage.entityName, plugin: usage.options };
59
+ return listProviderPlugins(ctx).filter(({ plugin }) => plugin.priceToTier !== undefined);
71
60
  }
72
- /** `findCatalogProvider` for callers that require a provider to proceed —
73
- * start-plan-checkout and switch-plan. `FeatureDisabledError` (not
74
- * `UnconfiguredError`) when none is found: an unconfigured provider is
75
- * the same "billing isn't live yet" state as `isPluginBillingEnabled`
76
- * returning false, and the panel already renders both the same way. */
77
- export function resolveCatalogProvider(ctx, catalog, handlerName) {
78
- const found = findCatalogProvider(ctx, catalog);
79
- if (!found) {
80
- throw new FeatureDisabledError(BILLING_FOUNDATION_FEATURE, handlerName);
81
- }
82
- return found;
61
+ export async function filterOfferedProviders(ctx, providers) {
62
+ const enabledFlags = await Promise.all(providers.map(({ plugin }) => isPluginBillingEnabled(ctx, plugin)));
63
+ return providers.filter((_, index) => enabledFlags[index]);
64
+ }
65
+ /** The catalog providers that are live right now: what a buyer can choose from. */
66
+ export async function listOfferedCatalogProviders(ctx, catalog) {
67
+ return filterOfferedProviders(ctx, listCatalogProviders(ctx, catalog));
83
68
  }
84
69
  /** `plugin.isBillingEnabled?.(ctx) ?? true` — the one place this fallback is
85
70
  * spelled out, so no call-site forgets to await the optional method before
@@ -112,8 +112,14 @@ inlineHtmlMarkdown.use({
112
112
  blockquote({ tokens }) {
113
113
  return this.parser.parse(tokens);
114
114
  },
115
- list({ items }) {
116
- return items.map((item) => this.parser.parse(item.tokens)).join("");
115
+ list({ items, ordered, start }) {
116
+ const firstNumber = typeof start === "number" ? start : 1;
117
+ return items
118
+ .map((item, index) => {
119
+ const marker = ordered ? `${firstNumber + index}. ` : "";
120
+ return `${marker}${this.parser.parse(item.tokens)}`;
121
+ })
122
+ .join("");
117
123
  },
118
124
  hr: () => "",
119
125
  space: () => "",
@@ -30,6 +30,7 @@ export type ConsentPriceDetails = {
30
30
  readonly priceId: string;
31
31
  readonly price: Pick<ProviderPrice, "unitAmount" | "currency" | "interval" | "intervalCount"> | null;
32
32
  readonly orderItem?: OrderItem;
33
+ readonly providerName?: string;
33
34
  };
34
35
  export declare function prepareConsent(ctx: HandlerContext, options: ConsumerProtectionOptions, consent: ConsentPayload | undefined, mode: CheckoutMode): Promise<PreparedConsent>;
35
36
  export declare function consentCheckoutFields(prepared: PreparedConsent): {
@@ -104,6 +104,7 @@ export async function recordConsent(ctx, prepared, details) {
104
104
  termsTemplateVersion: prepared.termsTemplateVersion,
105
105
  locale: prepared.locale,
106
106
  actorUserId: String(ctx.user.id),
107
+ ...(details.providerName !== undefined && { providerName: details.providerName }),
107
108
  ...(details.orderItem && {
108
109
  itemLabelKey: details.orderItem.labelKey,
109
110
  ...(details.orderItem.params && { itemLabelParams: details.orderItem.params }),
@@ -6,6 +6,7 @@ import { InternalError, UnprocessableError } from "@cosmicdrift/kumiko-framework
6
6
  import { createI18n } from "@cosmicdrift/kumiko-framework/i18n";
7
7
  import { isValidIanaTimeZone } from "@cosmicdrift/kumiko-framework/time";
8
8
  import * as z from "zod";
9
+ import { decryptStoredUserEmail } from "../../shared/index.js";
9
10
  import { requireTemplateResolver, TEXT_BLOCK_KIND } from "../../template-resolver/index.js";
10
11
  import { UserQueries } from "../../user/index.js";
11
12
  import { paymentAggregateId, subscriptionAggregateId } from "../aggregate-id.js";
@@ -13,7 +14,6 @@ import { CHECKOUT_CONSENT_RECORDED_EVENT_QN, CONTRACT_CONFIRMATION_ISSUED_EVENT_
13
14
  import { CONTRACT_CONFIRMATION_NOTIFICATION_TYPE } from "../web/constants.js";
14
15
  import { contractConfirmationVariables } from "./confirmation-mail.js";
15
16
  import { resolveConsentLocale } from "./consent-text.js";
16
- import { openStoredUserEmail } from "./stored-user-email.js";
17
17
  export const issueContractConfirmationSchema = z
18
18
  .object({
19
19
  consentId: z.string().min(1).max(100),
@@ -31,6 +31,12 @@ const recipientRowSchema = z.object({
31
31
  function translatedOrUndefined(translation, key) {
32
32
  return translation === key ? undefined : translation;
33
33
  }
34
+ function vatNoteFor(consumerProtection, providerName) {
35
+ const providerNote = providerName === undefined
36
+ ? undefined
37
+ : consumerProtection.providerTexts?.[providerName]?.vatNote;
38
+ return providerNote ?? consumerProtection.vatNote;
39
+ }
34
40
  function pickTimeZone(userZone, tenantZone) {
35
41
  if (userZone && isValidIanaTimeZone(userZone))
36
42
  return userZone;
@@ -102,7 +108,7 @@ export function createIssueContractConfirmationHandler(options, consumerProtecti
102
108
  id: consent.actorUserId,
103
109
  }));
104
110
  const recipientEmail = recipient.success && recipient.data.isDeleted !== true
105
- ? await openStoredUserEmail(recipient.data.email, "billing-foundation:issue-contract-confirmation")
111
+ ? await decryptStoredUserEmail(recipient.data.email, "billing-foundation:issue-contract-confirmation")
106
112
  : null;
107
113
  if (!recipient.success || recipientEmail === null) {
108
114
  ctx.log?.warn(`[billing-foundation:issue-contract-confirmation] recipient of consent ${payload.consentId} not found; no mail sent`);
@@ -142,7 +148,7 @@ export function createIssueContractConfirmationHandler(options, consumerProtecti
142
148
  ...(payload.currentPeriodEndIso !== undefined && {
143
149
  currentPeriodEndIso: payload.currentPeriodEndIso,
144
150
  }),
145
- vatNote: consumerProtection.vatNote,
151
+ vatNote: vatNoteFor(consumerProtection, consent.providerName),
146
152
  operatorEmail: consumerProtection.operatorEmail,
147
153
  termsContent: terms.content,
148
154
  });
@@ -44,7 +44,9 @@ export function terminationReceiptTemplate() {
44
44
  mailRepeatedRows("details", "text"),
45
45
  mailParagraph(texts.receiptNextSteps),
46
46
  mailParagraph(texts.receiptRevoke(OPERATOR_EMAIL_SLOT)),
47
- mailParagraphWithLink(texts.receiptNotMe, texts.receiptNotMeLink, "revokeUrl").replace("<p>", '<p data-if="revokeUrl">'),
47
+ mailParagraphWithLink(texts.receiptNotMe, texts.receiptNotMeLink, "revokeUrl", {
48
+ showIf: "revokeUrl",
49
+ }),
48
50
  mailParagraph(`${texts.contact} {operatorEmail}`),
49
51
  ].join(""),
50
52
  };
@@ -1,6 +1,8 @@
1
1
  import type { HandlerContext } from "@cosmicdrift/kumiko-framework/engine";
2
2
  export declare const RECEIPT_MAILS_PER_RECIPIENT = 3;
3
3
  export declare const RECEIPT_WINDOW_SECONDS: number;
4
+ export type ReceiptMailVerdict = "allowed" | "limited" | "unavailable";
4
5
  /** Consumes one receipt token for the recipient. Fails closed when the limiter
5
- * errors or no digest key is wired: the declaration is already recorded, a missing receipt is the lesser harm. */
6
- export declare function mayMailReceipt(ctx: HandlerContext, email: string): Promise<boolean>;
6
+ * errors or no digest key is wired ("unavailable"): the declaration is already
7
+ * recorded, a missing receipt is the lesser harm. */
8
+ export declare function mayMailReceipt(ctx: HandlerContext, email: string): Promise<ReceiptMailVerdict>;
@@ -10,25 +10,26 @@ function receiptBucketKey(ctx, email) {
10
10
  return digest === undefined ? undefined : `${RECEIPT_BUCKET_PREFIX}:${digest}`;
11
11
  }
12
12
  /** Consumes one receipt token for the recipient. Fails closed when the limiter
13
- * errors or no digest key is wired: the declaration is already recorded, a missing receipt is the lesser harm. */
13
+ * errors or no digest key is wired ("unavailable"): the declaration is already
14
+ * recorded, a missing receipt is the lesser harm. */
14
15
  export async function mayMailReceipt(ctx, email) {
15
16
  // skip: no rate limiter wired, nothing to count against
16
17
  if (!ctx.rateLimit)
17
- return true;
18
+ return "allowed";
18
19
  try {
19
20
  const bucketKey = receiptBucketKey(ctx, email);
20
21
  if (bucketKey === undefined) {
21
22
  ctx.log?.warn("[billing-foundation:termination] no rate-limit digest available, receipt mail withheld");
22
- return false;
23
+ return "unavailable";
23
24
  }
24
25
  const decision = await ctx.rateLimit.check(bucketKey, {
25
26
  limit: RECEIPT_MAILS_PER_RECIPIENT,
26
27
  windowSeconds: RECEIPT_WINDOW_SECONDS,
27
28
  });
28
- return decision.allowed;
29
+ return decision.allowed ? "allowed" : "limited";
29
30
  }
30
31
  catch (error) {
31
32
  ctx.log?.warn(`[billing-foundation:termination] receipt rate limiter failed: ${error instanceof Error ? error.message : String(error)}`);
32
- return false;
33
+ return "unavailable";
33
34
  }
34
35
  }
@@ -1,6 +1,6 @@
1
1
  import { type HandlerContext, type QueryHandlerDef, type WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import * as z from "zod";
3
- import { type ProviderCancelOutcome } from "../events.ts";
3
+ import { type ContractTerminationRequestedPayload, type ProviderCancelOutcome } from "../events.ts";
4
4
  import { type SubscriptionView } from "../get-subscription-for-tenant.ts";
5
5
  import type { ConsumerProtectionOptions, ResolvedBillingFoundationOptions } from "../types.ts";
6
6
  import type { OperatorNoticeReason } from "./termination-texts.ts";
@@ -74,6 +74,10 @@ export declare const terminableSubscriptionSchema: z.ZodObject<{}, z.core.$stric
74
74
  export declare function createTerminableSubscriptionQuery(): QueryHandlerDef;
75
75
  export declare function createDeclareContractTerminationHandler(): WriteHandlerDef;
76
76
  export declare function createRecordContractTerminationHandler(options: ResolvedBillingFoundationOptions, consumerProtection: ConsumerProtectionOptions): WriteHandlerDef;
77
+ export declare function findRecordedTermination(events: readonly {
78
+ type: string;
79
+ payload: unknown;
80
+ }[], requestId: string): ContractTerminationRequestedPayload | undefined;
77
81
  export declare function hasRevocation(events: readonly {
78
82
  type: string;
79
83
  payload: unknown;
@@ -173,23 +173,9 @@ export function createRecordContractTerminationHandler(options, consumerProtecti
173
173
  });
174
174
  // The public job runs at least once: a second run must not call the
175
175
  // provider or append again.
176
- const alreadyRecorded = stream.events
177
- .map((e) => e.type === CONTRACT_TERMINATION_REQUESTED_EVENT_QN
178
- ? contractTerminationRequestedPayloadSchema.safeParse(e.payload).data
179
- : undefined)
180
- .find((recordedPayload) => recordedPayload?.requestId === payload.requestId);
181
- if (alreadyRecorded) {
182
- return {
183
- isSuccess: true,
184
- data: {
185
- receivedAtIso: alreadyRecorded.receivedAtIso,
186
- effectiveAtIso: alreadyRecorded.effectiveAtIso,
187
- providerCancel: alreadyRecorded.providerCancel,
188
- noticeReason: null,
189
- withdrawalPeriodExpired: false,
190
- },
191
- };
192
- }
176
+ const alreadyRecorded = findRecordedTermination(stream.events, payload.requestId);
177
+ if (alreadyRecorded)
178
+ return recordedTerminationResult(alreadyRecorded);
193
179
  // A revoked public declaration never reaches the provider.
194
180
  if (hasRevocation(stream.events, payload.requestId)) {
195
181
  return revokedRecordResult(receivedAtIso);
@@ -214,6 +200,11 @@ export function createRecordContractTerminationHandler(options, consumerProtecti
214
200
  aggregateId: subscriptionAggregateId(tenantId),
215
201
  aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
216
202
  });
203
+ // A concurrent run with the same requestId may have recorded while we were
204
+ // at the provider; a second append would duplicate the event and the notice.
205
+ const recordedMeanwhile = findRecordedTermination(streamAfterProviderCall.events, payload.requestId);
206
+ if (recordedMeanwhile)
207
+ return recordedTerminationResult(recordedMeanwhile);
217
208
  // A revocation that landed during the provider call found no cancel to take back.
218
209
  if (hasRevocation(streamAfterProviderCall.events, payload.requestId)) {
219
210
  await takeBackLateRevokedCancel(ctx, consumerProtection, {
@@ -258,6 +249,25 @@ export function createRecordContractTerminationHandler(options, consumerProtecti
258
249
  },
259
250
  };
260
251
  }
252
+ export function findRecordedTermination(events, requestId) {
253
+ return events
254
+ .map((e) => e.type === CONTRACT_TERMINATION_REQUESTED_EVENT_QN
255
+ ? contractTerminationRequestedPayloadSchema.safeParse(e.payload).data
256
+ : undefined)
257
+ .find((recordedPayload) => recordedPayload?.requestId === requestId);
258
+ }
259
+ function recordedTerminationResult(recorded) {
260
+ return {
261
+ isSuccess: true,
262
+ data: {
263
+ receivedAtIso: recorded.receivedAtIso,
264
+ effectiveAtIso: recorded.effectiveAtIso,
265
+ providerCancel: recorded.providerCancel,
266
+ noticeReason: null,
267
+ withdrawalPeriodExpired: false,
268
+ },
269
+ };
270
+ }
261
271
  export function hasRevocation(events, requestId) {
262
272
  return events.some((e) => e.type === CONTRACT_TERMINATION_REVOKED_EVENT_QN &&
263
273
  contractTerminationRevokedPayloadSchema.safeParse(e.payload).data?.requestId === requestId);
@@ -6,13 +6,13 @@ import { createSystemUser, declareEscapeHatch, SYSTEM_TENANT_ID, } from "@cosmic
6
6
  import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
7
7
  import { generateId } from "@cosmicdrift/kumiko-framework/utils";
8
8
  import * as z from "zod";
9
+ import { decryptStoredUserEmail } from "../../shared/index.js";
9
10
  import { TenantQueries } from "../../tenant/index.js";
10
11
  import { UserQueries } from "../../user/index.js";
11
12
  import { CONTRACT_TERMINATION_DECLARATION_TYPES, CONTRACT_TERMINATION_KINDS, } from "../events.js";
12
13
  import { purchaseRolesOf } from "../plan-catalog.js";
13
14
  import { CONTRACT_TERMINATION_RECEIPT_NOTIFICATION_TYPE, SubscriptionCancelTimings, SubscriptionFoundationHandlers, SubscriptionFoundationQueries, } from "../web/constants.js";
14
15
  import { resolveConsentLocale } from "./consent-text.js";
15
- import { openStoredUserEmail } from "./stored-user-email.js";
16
16
  import { terminationReceiptVariables } from "./termination-mail.js";
17
17
  import { notifyOperator, requireNotify } from "./termination-notify.js";
18
18
  import { mayMailReceipt } from "./termination-receipt-limit.js";
@@ -54,9 +54,16 @@ function needsOperatorNotice(declaration) {
54
54
  ...(declaration.terminationKind === "extraordinary" ? ["extraordinary"] : []),
55
55
  ];
56
56
  }
57
+ function receiptWithheldReasons(verdict) {
58
+ if (verdict === "limited")
59
+ return ["receipt_rate_limited"];
60
+ if (verdict === "unavailable")
61
+ return ["receipt_not_sent"];
62
+ return [];
63
+ }
57
64
  async function sendTerminationMails(ctx, consumerProtection, plan) {
58
- const receiptAllowed = await mayMailReceipt(ctx, plan.declaration.email);
59
- if (receiptAllowed) {
65
+ const receiptVerdict = await mayMailReceipt(ctx, plan.declaration.email);
66
+ if (receiptVerdict === "allowed") {
60
67
  await requireNotify(ctx)(CONTRACT_TERMINATION_RECEIPT_NOTIFICATION_TYPE, {
61
68
  route: { email: plan.declaration.email },
62
69
  data: terminationReceiptVariables({
@@ -74,9 +81,7 @@ async function sendTerminationMails(ctx, consumerProtection, plan) {
74
81
  priority: "critical",
75
82
  });
76
83
  }
77
- const reasons = receiptAllowed
78
- ? plan.reasons
79
- : [...plan.reasons, "receipt_rate_limited"];
84
+ const reasons = [...plan.reasons, ...receiptWithheldReasons(receiptVerdict)];
80
85
  // skip: no operator notice needed for this declaration
81
86
  if (reasons.length === 0)
82
87
  return;
@@ -217,7 +222,7 @@ export function createTerminateContractHandler(options, consumerProtection) {
217
222
  id: event.user.id,
218
223
  }));
219
224
  const callerEmail = caller.success && caller.data.email && caller.data.isDeleted !== true
220
- ? await openStoredUserEmail(caller.data.email, "billing-foundation:terminate-contract")
225
+ ? await decryptStoredUserEmail(caller.data.email, "billing-foundation:terminate-contract")
221
226
  : null;
222
227
  if (callerEmail === null) {
223
228
  throw new InternalError({
@@ -7,11 +7,11 @@ import { access, createSystemUser, } from "@cosmicdrift/kumiko-framework/engine"
7
7
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
8
8
  import * as z from "zod";
9
9
  import { subscriptionAggregateId } from "../aggregate-id.js";
10
- import { CONTRACT_TERMINATION_DECLARED_EVENT_QN, CONTRACT_TERMINATION_REQUESTED_EVENT_QN, CONTRACT_TERMINATION_REVOKED_EVENT_QN, contractTerminationDeclaredPayloadSchema, contractTerminationRequestedPayloadSchema, SUBSCRIPTION_AGGREGATE_TYPE, } from "../events.js";
10
+ import { CONTRACT_TERMINATION_DECLARED_EVENT_QN, CONTRACT_TERMINATION_REVOKED_EVENT_QN, contractTerminationDeclaredPayloadSchema, SUBSCRIPTION_AGGREGATE_TYPE, } from "../events.js";
11
11
  import { getSubscriptionForTenant } from "../get-subscription-for-tenant.js";
12
12
  import { SubscriptionFoundationHandlers } from "../web/constants.js";
13
13
  import { notifyOperator } from "./termination-notify.js";
14
- import { hasRevocation, resumeAtProvider } from "./termination-record.js";
14
+ import { findRecordedTermination, hasRevocation, resumeAtProvider } from "./termination-record.js";
15
15
  export const REVOCATION_PATHS = {
16
16
  de: "/legal/kuendigen/zuruecknehmen",
17
17
  en: "/legal/cancel/revoke",
@@ -102,10 +102,7 @@ export function createRecordContractTerminationRevocationHandler(options, consum
102
102
  }
103
103
  if (hasRevocation(stream.events, payload.requestId))
104
104
  return outcome("already-revoked");
105
- const requested = stream.events
106
- .filter((e) => e.type === CONTRACT_TERMINATION_REQUESTED_EVENT_QN)
107
- .map((e) => contractTerminationRequestedPayloadSchema.safeParse(e.payload).data)
108
- .find((candidate) => candidate?.requestId === payload.requestId);
105
+ const requested = findRecordedTermination(stream.events, payload.requestId);
109
106
  const subscription = await getSubscriptionForTenant(ctx, tenantId);
110
107
  const effectiveAtIso = requested?.effectiveAtIso ?? subscription?.currentPeriodEnd.toString();
111
108
  const now = options.now();
@@ -122,6 +119,14 @@ export function createRecordContractTerminationRevocationHandler(options, consum
122
119
  aggregateId: subscriptionAggregateId(tenantId),
123
120
  aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
124
121
  });
122
+ // A cancel recorded between the first fetch and now was never resumed above.
123
+ const requestedMeanwhile = requested === undefined
124
+ ? findRecordedTermination(streamAfterProviderCall.events, payload.requestId)
125
+ : undefined;
126
+ if (requestedMeanwhile?.providerCancel === "period-end") {
127
+ reasons.push(...(await resumeAtProvider(ctx, await getSubscriptionForTenant(ctx, tenantId))));
128
+ }
129
+ const providerCancel = (requested ?? requestedMeanwhile)?.providerCancel;
125
130
  const revoked = {
126
131
  requestId: payload.requestId,
127
132
  revokedAtIso: now.toString(),
@@ -141,7 +146,7 @@ export function createRecordContractTerminationRevocationHandler(options, consum
141
146
  },
142
147
  reasons,
143
148
  tenantId,
144
- ...(requested !== undefined && { providerCancel: requested.providerCancel }),
149
+ ...(providerCancel !== undefined && { providerCancel }),
145
150
  });
146
151
  return outcome("revoked");
147
152
  },
@@ -74,6 +74,6 @@ export type RevokePageTexts = {
74
74
  readonly invalidTitle: string;
75
75
  readonly invalidBody: string;
76
76
  };
77
- export declare const OPERATOR_NOTICE_REASONS: readonly ["unmatched", "ambiguous", "withdrawal", "extraordinary", "provider_cannot_cancel", "provider_error", "no_active_subscription", "recording_failed", "withdrawal_period_expired", "receipt_rate_limited", "revoked", "revocation_resume_manual", "revocation_resume_failed"];
77
+ export declare const OPERATOR_NOTICE_REASONS: readonly ["unmatched", "ambiguous", "withdrawal", "extraordinary", "provider_cannot_cancel", "provider_error", "no_active_subscription", "recording_failed", "withdrawal_period_expired", "receipt_rate_limited", "receipt_not_sent", "revoked", "revocation_resume_manual", "revocation_resume_failed"];
78
78
  export type OperatorNoticeReason = (typeof OPERATOR_NOTICE_REASONS)[number];
79
79
  export declare const TERMINATION_TEXTS: Readonly<Record<ConsentLocale, TerminationTexts>>;
@@ -9,6 +9,7 @@ export const OPERATOR_NOTICE_REASONS = [
9
9
  "recording_failed",
10
10
  "withdrawal_period_expired",
11
11
  "receipt_rate_limited",
12
+ "receipt_not_sent",
12
13
  "revoked",
13
14
  "revocation_resume_manual",
14
15
  "revocation_resume_failed",
@@ -55,6 +56,7 @@ export const TERMINATION_TEXTS = {
55
56
  recording_failed: "Die Erklärung konnte nicht gespeichert werden",
56
57
  withdrawal_period_expired: "Widerruf nach Ablauf der Widerrufsfrist: zum Periodenende gekündigt, Anspruch prüfen",
57
58
  receipt_rate_limited: "Keine Eingangsbestätigung versendet: Limit pro Empfängeradresse erreicht",
59
+ receipt_not_sent: "Keine Eingangsbestätigung versendet: Limit-Prüfung nicht verfügbar (Redis-Fehler oder fehlende Konfiguration)",
58
60
  revoked: "Die Erklärung wurde vom Postfachinhaber über „Diese Kündigung war ich nicht“ zurückgenommen",
59
61
  revocation_resume_manual: "Die Kündigung beim Zahlungsanbieter bitte manuell zurücknehmen (keine Schnittstelle dafür)",
60
62
  revocation_resume_failed: "Die Kündigung beim Zahlungsanbieter konnte nicht zurückgenommen werden, bitte manuell zurücknehmen",
@@ -154,6 +156,7 @@ export const TERMINATION_TEXTS = {
154
156
  recording_failed: "The declaration could not be stored",
155
157
  withdrawal_period_expired: "Withdrawal after the withdrawal period: cancelled at period end, review the claim",
156
158
  receipt_rate_limited: "No receipt mail sent: the per-recipient limit was reached",
159
+ receipt_not_sent: "No receipt mail sent: the limit check was unavailable (limiter error or missing configuration)",
157
160
  revoked: 'The declaration was taken back by the mailbox owner ("I did not request this cancellation")',
158
161
  revocation_resume_manual: "Undo the cancellation at the payment provider manually (no API for it)",
159
162
  revocation_resume_failed: "Undoing the cancellation at the payment provider failed, undo it manually",
@@ -4,7 +4,10 @@ type PeriodStartEvent = {
4
4
  readonly type: string;
5
5
  readonly createdAt: Temporal.Instant;
6
6
  };
7
- /** The period runs from the latest consent or subscription start on the stream.
8
- * A stream with neither has no provable start and counts as expired. */
7
+ /** The period runs from the latest subscription start on the stream. A checkout
8
+ * consent alone never restarts it: it is written before the buyer reaches the
9
+ * provider, so an abandoned plan-change checkout would otherwise reopen the
10
+ * period of a long-concluded contract. A stream without a subscription start
11
+ * has no provable start and counts as expired. */
9
12
  export declare function isWithinWithdrawalPeriod(events: readonly PeriodStartEvent[], now: Temporal.Instant): boolean;
10
13
  export {};
@@ -1,24 +1,23 @@
1
- import { CHECKOUT_CONSENT_RECORDED_EVENT_QN, SUBSCRIPTION_CREATED_EVENT_QN } from "../events.js";
1
+ import { SUBSCRIPTION_CREATED_EVENT_QN } from "../events.js";
2
2
  // § 355 BGB: the withdrawal period is 14 days from conclusion of the contract.
3
3
  export const WITHDRAWAL_PERIOD_DAYS = 14;
4
4
  const HOURS_PER_DAY = 24;
5
- const PERIOD_START_EVENT_TYPES = new Set([
6
- CHECKOUT_CONSENT_RECORDED_EVENT_QN,
7
- SUBSCRIPTION_CREATED_EVENT_QN,
8
- ]);
9
- /** The period runs from the latest consent or subscription start on the stream.
10
- * A stream with neither has no provable start and counts as expired. */
5
+ /** The period runs from the latest subscription start on the stream. A checkout
6
+ * consent alone never restarts it: it is written before the buyer reaches the
7
+ * provider, so an abandoned plan-change checkout would otherwise reopen the
8
+ * period of a long-concluded contract. A stream without a subscription start
9
+ * has no provable start and counts as expired. */
11
10
  export function isWithinWithdrawalPeriod(events, now) {
12
11
  let latestStart;
13
12
  for (const event of events) {
14
- if (!PERIOD_START_EVENT_TYPES.has(event.type))
13
+ if (event.type !== SUBSCRIPTION_CREATED_EVENT_QN)
15
14
  continue;
16
15
  if (latestStart === undefined ||
17
16
  event.createdAt.epochMilliseconds > latestStart.epochMilliseconds) {
18
17
  latestStart = event.createdAt;
19
18
  }
20
19
  }
21
- // skip: no consent or subscription-created event, nothing proves a running period
20
+ // skip: no subscription-created event, nothing proves a running period
22
21
  if (latestStart === undefined)
23
22
  return false;
24
23
  const periodEnd = latestStart.add({ hours: WITHDRAWAL_PERIOD_DAYS * HOURS_PER_DAY });
@@ -1,38 +1,38 @@
1
1
  import { collectPiiSubjectFields } from "@cosmicdrift/kumiko-framework/crypto";
2
2
  import { createEntity, createTextField, createTimestampField, } from "@cosmicdrift/kumiko-framework/engine";
3
- // `subscription` — current state pro Plattform-Tenant (= Read-Model)
3
+ // `subscription` — current state per platform tenant (= read model)
4
4
  // =============================================================================
5
5
  //
6
- // Inline-Projection-Target. Geschrieben vom `subscription`-projection-
7
- // apply (siehe feature.ts), NIE direkt vom handler. Source-of-truth ist
8
- // der event-store stream `subscription` mit aggregate-id =
6
+ // Inline projection target. Written by the `subscription` projection-
7
+ // apply (see feature.ts), NEVER directly by the handler. Source of truth is
8
+ // the event-store stream `subscription` with aggregate-id =
9
9
  // uuidv5(SUBSCRIPTION_NAMESPACE, tenantId).
10
10
  //
11
- // EINE Row pro Plattform-Tenant. Aggregate-ID ist deterministic, damit
12
- // Webhook-Replays (Stripe sendet bei Hängern bis zu 5x in 4h) auf
13
- // denselben Stream schreiben statt zwei Rows zu erzeugen.
11
+ // ONE row per platform tenant. The aggregate ID is deterministic so that
12
+ // webhook replays (Stripe sends up to 5 times in 4 hours when delivery is
13
+ // stalled) write to the same stream instead of creating two rows.
14
14
  //
15
- // **Felder:**
16
- // - providerName: "stripe" / "mollie" — welcher Provider die
17
- // Subscription hält. Provider-Wechsel = neuer event auf demselben
18
- // Stream, projection überschreibt.
19
- // - providerCustomerId / providerSubscriptionId: provider-eigene
20
- // IDs.
15
+ // **Fields:**
16
+ // - providerName: "stripe" / "mollie" — the provider holding the
17
+ // subscription. A provider change is a new event on the same stream;
18
+ // the projection overwrites the value.
19
+ // - providerCustomerId / providerSubscriptionId: provider-specific IDs.
21
20
  // - status: active / past_due / canceled / trialing / incomplete —
22
- // normalisiert über provider-grenzen hinweg.
23
- // - tier: "free" / "pro" / ... — vom tier-engine konsumiert. Aus
24
- // price-to-tier-Map resolved im Plugin.
25
- // - currentPeriodEnd: wann läuft die aktuelle Billing-Period aus.
21
+ // normalized across providers.
22
+ // - tier: "free" / "pro" / ... — consumed by the tier engine. Resolved
23
+ // from the price-to-tier map in the plugin.
24
+ // - currentPeriodEnd: when the current billing period ends.
26
25
  // - cancelAt: when a scheduled cancellation ends the subscription;
27
26
  // nullable because a renewing subscription has none.
28
27
  //
29
- // **Was hier NICHT ist:**
30
- // - invoice-history, payment-method, line-items, tax-info → all das
31
- // fetcht der Tenant via customer-portal-session direkt vom Provider.
28
+ // **Not included here:**
29
+ // - invoice-history, payment-method, line-items, tax-info — the tenant
30
+ // fetches all of these directly from the provider via a
31
+ // customer-portal-session.
32
32
  //
33
- // **Audit/event-history:** lebt im event-store unter dem `subscription`-
34
- // stream — KEIN eigene `subscription-event`-Tabelle mehr (= ES ist die
35
- // audit-truth, replay-fähig durch upcasters).
33
+ // **Audit/event history:** lives in the event store under the `subscription`
34
+ // stream — there is NO separate `subscription-event` table (= ES is the
35
+ // audit source of truth and can be replayed through upcasters).
36
36
  export const subscriptionEntity = createEntity({
37
37
  table: "read_subscriptions",
38
38
  fields: {
@@ -43,10 +43,10 @@ export const subscriptionEntity = createEntity({
43
43
  reason: "technical_reference",
44
44
  }),
45
45
  // `personal: "tenant"` (not `encrypted`): the field must crypto-shred when
46
- // eraseSubjectKeys erases the tenant's subject key on tenant-destroy
47
- // (#800). `encrypted: true` uses the app-wide master key instead — that
46
+ // eraseSubjectKeys erases the tenant's subject key on tenant-destroy.
47
+ // `encrypted: true` uses the app-wide master key instead — that
48
48
  // key is never erased per-tenant, so it would only add encryption-at-
49
- // rest, not the erasure guarantee #800 actually asks for.
49
+ // rest, not the erasure guarantee.
50
50
  // maxLength 1000, not 200: the stored value is PII-ciphertext
51
51
  // (`kumiko-pii:v1:<subject>:<blob>`), not the raw provider id — a
52
52
  // 200-char plaintext id becomes ~300+ chars of ciphertext. Matches
@@ -106,7 +106,7 @@ export const paymentEntity = createEntity({
106
106
  reason: "technical_reference",
107
107
  }),
108
108
  // Same `personal: "tenant"` rationale as subscriptionEntity above —
109
- // crypto-shreds on tenant-destroy (#800) via eraseSubjectKeys.
109
+ // crypto-shreds on tenant-destroy via eraseSubjectKeys.
110
110
  providerCustomerId: createTextField({
111
111
  required: true,
112
112
  maxLength: 1000,
@@ -66,6 +66,7 @@ export declare const checkoutConsentRecordedPayloadSchema: z.ZodObject<{
66
66
  termsTemplateVersion: z.ZodNumber;
67
67
  locale: z.ZodString;
68
68
  actorUserId: z.ZodString;
69
+ providerName: z.ZodOptional<z.ZodString>;
69
70
  itemLabelKey: z.ZodOptional<z.ZodString>;
70
71
  itemLabelParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
71
72
  }, z.core.$strip>;