@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
@@ -0,0 +1,260 @@
1
+ // Dodo Payments plugin methods for the post-tenant phase. Option shapes are
2
+ // derived from the plugin contract so a contract change fails at compile time.
3
+ // Every response is validated with Zod; every non-2xx throws a DodoHttpError.
4
+ import { KNOWN_RECURRING_INTERVALS, SubscriptionCancelTimings, } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
5
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
6
+ import * as z from "zod";
7
+ import { DodoHttpError } from "./client.js";
8
+ import { dodoSubscriptionSchema, mapDodoSubscriptionState } from "./verify-webhook.js";
9
+ const NOT_FOUND = 404;
10
+ function subscriptionPath(providerSubscriptionId) {
11
+ return `/subscriptions/${encodeURIComponent(providerSubscriptionId)}`;
12
+ }
13
+ function forcedLanguage(locale) {
14
+ return (locale.split(/[-_]/)[0] ?? locale).toLowerCase();
15
+ }
16
+ // =============================================================================
17
+ // Checkout + portal + cancel/resume
18
+ // =============================================================================
19
+ const checkoutResponseSchema = z.object({
20
+ session_id: z.string(),
21
+ checkout_url: z.string().nullable(),
22
+ });
23
+ export function createDodoCheckoutSession(runtime) {
24
+ return async (ctx, options) => {
25
+ // No Dodo session may be created while billing is not live.
26
+ await runtime.assertBillingLive(ctx);
27
+ const response = checkoutResponseSchema.parse(await runtime.call(ctx, {
28
+ method: "POST",
29
+ path: "/checkouts",
30
+ body: {
31
+ product_cart: [{ product_id: options.priceId, quantity: 1 }],
32
+ ...(options.providerCustomerId && {
33
+ customer: { customer_id: options.providerCustomerId },
34
+ }),
35
+ return_url: options.successUrl,
36
+ cancel_url: options.cancelUrl,
37
+ metadata: {
38
+ tenantId: options.tenantId,
39
+ ...(options.consentId && { consentId: options.consentId }),
40
+ },
41
+ ...(options.locale && {
42
+ customization: { force_language: forcedLanguage(options.locale) },
43
+ }),
44
+ },
45
+ }));
46
+ if (!response.checkout_url) {
47
+ throw new Error("subscription-dodo: checkout session was created without a checkout_url");
48
+ }
49
+ return { url: response.checkout_url };
50
+ };
51
+ }
52
+ const portalResponseSchema = z.object({ link: z.string() });
53
+ export function createDodoPortalSession(runtime) {
54
+ return async (ctx, options) => {
55
+ const response = portalResponseSchema.parse(await runtime.call(ctx, {
56
+ method: "POST",
57
+ path: `/customers/${encodeURIComponent(options.providerCustomerId)}/customer-portal/session`,
58
+ query: { return_url: options.returnUrl },
59
+ }));
60
+ return { url: response.link };
61
+ };
62
+ }
63
+ export function createDodoCancelSubscription(runtime) {
64
+ return async (ctx, options) => {
65
+ await runtime.call(ctx, {
66
+ method: "PATCH",
67
+ path: subscriptionPath(options.providerSubscriptionId),
68
+ body: options.when === SubscriptionCancelTimings.periodEnd
69
+ ? { cancel_at_next_billing_date: true }
70
+ : { status: "cancelled" },
71
+ });
72
+ };
73
+ }
74
+ export function createDodoResumeSubscription(runtime) {
75
+ return async (ctx, options) => {
76
+ await runtime.call(ctx, {
77
+ method: "PATCH",
78
+ path: subscriptionPath(options.providerSubscriptionId),
79
+ body: { cancel_at_next_billing_date: false },
80
+ });
81
+ };
82
+ }
83
+ // =============================================================================
84
+ // Prices
85
+ // =============================================================================
86
+ const productSchema = z.object({
87
+ product_id: z.string(),
88
+ price: z.discriminatedUnion("type", [
89
+ z.object({
90
+ type: z.literal("recurring_price"),
91
+ price: z.number(),
92
+ currency: z.string(),
93
+ payment_frequency_interval: z.string(),
94
+ payment_frequency_count: z.number(),
95
+ }),
96
+ z.object({
97
+ type: z.literal("one_time_price"),
98
+ price: z.number(),
99
+ currency: z.string(),
100
+ }),
101
+ z.object({ type: z.literal("usage_based_price") }),
102
+ ]),
103
+ // nullish: an older product without metadata must not fail the whole price lookup.
104
+ metadata: z.record(z.string(), z.string()).nullish(),
105
+ });
106
+ function mapDodoProduct(product) {
107
+ const { price } = product;
108
+ if (price.type === "usage_based_price")
109
+ return null;
110
+ const base = {
111
+ priceId: product.product_id,
112
+ unitAmount: price.price,
113
+ currency: price.currency.toLowerCase(),
114
+ active: true,
115
+ metadata: product.metadata ?? {},
116
+ };
117
+ if (price.type === "one_time_price")
118
+ return { ...base, interval: null, intervalCount: 1 };
119
+ const rawInterval = price.payment_frequency_interval.toLowerCase();
120
+ const interval = KNOWN_RECURRING_INTERVALS.find((known) => known === rawInterval) ?? null;
121
+ return {
122
+ ...base,
123
+ interval,
124
+ intervalCount: interval ? price.payment_frequency_count : null,
125
+ };
126
+ }
127
+ // Keyed by product id alone: ids are unique per account and a cache hit must not
128
+ // trigger an audited secret read on every billing-plans render. Failed lookups
129
+ // are never cached. `null` is a cached "skipped" product (usage-based).
130
+ export function createDodoPriceCache({ ttlMs = 600_000, now = () => Temporal.Now.instant().epochMilliseconds, } = {}) {
131
+ const store = new Map();
132
+ return {
133
+ get: (priceId) => {
134
+ const entry = store.get(priceId);
135
+ if (!entry)
136
+ return undefined;
137
+ if (entry.expiresAt <= now()) {
138
+ store.delete(priceId);
139
+ return undefined;
140
+ }
141
+ return entry.price;
142
+ },
143
+ set: (priceId, price) => {
144
+ store.set(priceId, { price, expiresAt: now() + ttlMs });
145
+ },
146
+ };
147
+ }
148
+ async function fillPriceCache(runtime, cache, ctx, priceIds) {
149
+ const misses = priceIds.filter((priceId) => cache.get(priceId) === undefined);
150
+ const settled = await Promise.allSettled(misses.map(async (priceId) => ({
151
+ priceId,
152
+ product: productSchema.parse(await runtime.call(ctx, {
153
+ method: "GET",
154
+ path: `/products/${encodeURIComponent(priceId)}`,
155
+ })),
156
+ })));
157
+ for (const outcome of settled) {
158
+ if (outcome.status === "fulfilled") {
159
+ cache.set(outcome.value.priceId, mapDodoProduct(outcome.value.product));
160
+ }
161
+ else {
162
+ const message = outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason);
163
+ ctx.log?.warn("subscription-dodo: product lookup failed", { message });
164
+ }
165
+ }
166
+ }
167
+ export function createDodoRetrievePrices(runtime, cache) {
168
+ return async (ctx, priceIds) => {
169
+ await fillPriceCache(runtime, cache, ctx, priceIds);
170
+ return priceIds.flatMap((priceId) => cache.get(priceId) ?? []);
171
+ };
172
+ }
173
+ // =============================================================================
174
+ // Plan switch (in-app: preview + apply)
175
+ // =============================================================================
176
+ const previewResponseSchema = z.object({
177
+ immediate_charge: z.object({
178
+ summary: z.object({
179
+ total_amount: z.number(),
180
+ tax: z.number().nullable(),
181
+ currency: z.string(),
182
+ customer_credits: z.number(),
183
+ }),
184
+ effective_at: z.string(),
185
+ }),
186
+ });
187
+ function changePlanBody(targetPriceId) {
188
+ return {
189
+ product_id: targetPriceId,
190
+ quantity: 1,
191
+ proration_billing_mode: "prorated_immediately",
192
+ effective_at: "immediately",
193
+ };
194
+ }
195
+ export function createDodoPreviewPlanSwitch(runtime) {
196
+ return async (ctx, options) => {
197
+ const { immediate_charge: charge } = previewResponseSchema.parse(await runtime.call(ctx, {
198
+ method: "POST",
199
+ path: `${subscriptionPath(options.providerSubscriptionId)}/change-plan/preview`,
200
+ body: changePlanBody(options.targetPriceId),
201
+ }));
202
+ return {
203
+ amountDueNow: charge.summary.total_amount,
204
+ tax: charge.summary.tax,
205
+ currency: charge.summary.currency.toLowerCase(),
206
+ credit: charge.summary.customer_credits,
207
+ effectiveAt: charge.effective_at,
208
+ };
209
+ };
210
+ }
211
+ const applyResponseSchema = z.looseObject({
212
+ payment_id: z.string().nullish(),
213
+ payment_link: z.string().nullish(),
214
+ });
215
+ // The switch itself arrives later as a subscription.plan_changed webhook;
216
+ // prevent_change keeps the old plan if the immediate charge fails.
217
+ export function createDodoApplyPlanSwitch(runtime) {
218
+ return async (ctx, options) => {
219
+ applyResponseSchema.parse(await runtime.call(ctx, {
220
+ method: "POST",
221
+ path: `${subscriptionPath(options.providerSubscriptionId)}/change-plan`,
222
+ body: {
223
+ ...changePlanBody(options.targetPriceId),
224
+ on_payment_failure: "prevent_change",
225
+ metadata: {
226
+ tenantId: options.tenantId,
227
+ ...(options.consentId && { consentId: options.consentId }),
228
+ },
229
+ },
230
+ }));
231
+ };
232
+ }
233
+ // =============================================================================
234
+ // retrieveSubscription
235
+ // =============================================================================
236
+ export function createDodoRetrieveSubscription(runtime, options) {
237
+ return async (ctx, providerSubscriptionId) => {
238
+ let raw;
239
+ try {
240
+ raw = await runtime.call(ctx, {
241
+ method: "GET",
242
+ path: subscriptionPath(providerSubscriptionId),
243
+ });
244
+ }
245
+ catch (error) {
246
+ if (error instanceof DodoHttpError && error.status === NOT_FOUND)
247
+ return null;
248
+ throw error;
249
+ }
250
+ const sub = dodoSubscriptionSchema.parse(raw);
251
+ const state = mapDodoSubscriptionState(sub, options.priceToTier);
252
+ if (!state)
253
+ return null;
254
+ return {
255
+ providerCustomerId: sub.customer.customer_id,
256
+ providerSubscriptionId: sub.subscription_id,
257
+ ...state,
258
+ };
259
+ };
260
+ }
@@ -0,0 +1,27 @@
1
+ import { type ConfigKeyHandle, type HandlerContext } from "@cosmicdrift/kumiko-framework/engine";
2
+ import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
3
+ import { type DodoRequest, type DodoSend } from "./client.ts";
4
+ export type DodoRuntimeDeps = {
5
+ readonly apiKeyHandle: ConfigKeyHandle<"text">;
6
+ readonly webhookSecretHandle: ConfigKeyHandle<"text">;
7
+ readonly billingLiveHandle: ConfigKeyHandle<"boolean">;
8
+ readonly environmentHandle: ConfigKeyHandle<"select">;
9
+ readonly send: DodoSend;
10
+ readonly fallback: {
11
+ readonly apiKey?: string;
12
+ readonly webhookSecret?: string;
13
+ };
14
+ };
15
+ export type DodoCtxRuntime = {
16
+ readonly call: (ctx: HandlerContext, request: DodoRequest) => Promise<unknown>;
17
+ readonly assertBillingLive: (ctx: HandlerContext, handlerName?: string) => Promise<void>;
18
+ readonly isBillingEnabled: (ctx: HandlerContext) => Promise<boolean>;
19
+ };
20
+ export type DodoWebhookRuntime = {
21
+ readonly resolveWebhookSecret: (systemSecrets?: SecretsContext) => Promise<string>;
22
+ };
23
+ export type DodoRuntimes = {
24
+ readonly ctx: DodoCtxRuntime;
25
+ readonly webhook: DodoWebhookRuntime;
26
+ };
27
+ export declare function createDodoRuntimes(deps: DodoRuntimeDeps): DodoRuntimes;
@@ -0,0 +1,89 @@
1
+ // Call-time key/flag resolution for the subscription-dodo plugin, mirroring
2
+ // subscription-stripe/runtime.ts: credentials come from backing:"secrets"
3
+ // config keys under SYSTEM_TENANT_ID, so rotation and go-live need no redeploy.
4
+ // The factory options survive as env-bridge fallbacks.
5
+ //
6
+ // Two read paths: post-tenant handlers read the key audited through the
7
+ // HandlerContext; the pre-tenant webhook reads the secret un-audited from the
8
+ // system SecretsContext the foundation passes in.
9
+ import { requireSecretsContext } from "@cosmicdrift/kumiko-bundled-features/secrets";
10
+ import { SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
11
+ import { FeatureDisabledError, UnconfiguredError } from "@cosmicdrift/kumiko-framework/errors";
12
+ import { parseJsonSafe } from "@cosmicdrift/kumiko-framework/utils";
13
+ import { createDodoHttp } from "./client.js";
14
+ import { DODO_BASE_URLS, DodoEnvironments, SUBSCRIPTION_DODO_FEATURE } from "./constants.js";
15
+ const API_KEY_HINT = "Set the Dodo Payments API key via config:write:set on `subscription-dodo:config:api-key` (or seed it from DODO_PAYMENTS_API_KEY during the env bridge).";
16
+ // backing:"secrets" values are stored JSON-serialized, so the store read has to
17
+ // be parsed back; the env fallback is a plain string and stays raw. The error
18
+ // must not carry the input: the JSON parser's own message would repeat the
19
+ // credential into logs and, for webhooks, into the 401 body.
20
+ function parseStoredSecret(raw) {
21
+ if (raw === undefined || raw.length === 0)
22
+ return undefined;
23
+ const parsed = parseJsonSafe(raw, undefined);
24
+ if (parsed === undefined) {
25
+ throw new Error("subscription-dodo credential (backing:secrets) is not valid JSON");
26
+ }
27
+ return typeof parsed === "string" && parsed.length > 0 ? parsed : undefined;
28
+ }
29
+ export function createDodoRuntimes(deps) {
30
+ const http = createDodoHttp(deps.send);
31
+ async function ctxApiKey(ctx) {
32
+ let key;
33
+ if (ctx.secrets) {
34
+ const got = await requireSecretsContext(ctx, SUBSCRIPTION_DODO_FEATURE).get(SYSTEM_TENANT_ID, deps.apiKeyHandle.name);
35
+ key = parseStoredSecret(got?.reveal());
36
+ }
37
+ if (!key && deps.fallback.apiKey && deps.fallback.apiKey.length > 0) {
38
+ key = deps.fallback.apiKey;
39
+ }
40
+ if (!key) {
41
+ throw new UnconfiguredError({
42
+ feature: SUBSCRIPTION_DODO_FEATURE,
43
+ key: deps.apiKeyHandle.name,
44
+ hint: API_KEY_HINT,
45
+ });
46
+ }
47
+ return key;
48
+ }
49
+ async function baseUrlFor(ctx) {
50
+ const environment = ctx.config ? await ctx.config(deps.environmentHandle) : undefined;
51
+ return DODO_BASE_URLS[environment === DodoEnvironments.live ? "live" : "test"];
52
+ }
53
+ return {
54
+ ctx: {
55
+ call: async (ctx, request) => http({ baseUrl: await baseUrlFor(ctx), apiKey: await ctxApiKey(ctx) }, request),
56
+ assertBillingLive: async (ctx, handlerName = "create-checkout-session") => {
57
+ const live = ctx.config ? await ctx.config(deps.billingLiveHandle) : undefined;
58
+ if (live !== true)
59
+ throw new FeatureDisabledError(SUBSCRIPTION_DODO_FEATURE, handlerName);
60
+ },
61
+ isBillingEnabled: async (ctx) => {
62
+ const live = ctx.config ? await ctx.config(deps.billingLiveHandle) : undefined;
63
+ if (live !== true)
64
+ return false;
65
+ if (deps.fallback.apiKey !== undefined && deps.fallback.apiKey.length > 0)
66
+ return true;
67
+ // Existence-only probe on every billing-plans render: must stay un-audited.
68
+ return ctx.secrets
69
+ ? await ctx.secrets.has(SYSTEM_TENANT_ID, deps.apiKeyHandle.name)
70
+ : false;
71
+ },
72
+ },
73
+ webhook: {
74
+ resolveWebhookSecret: async (systemSecrets) => {
75
+ if (systemSecrets) {
76
+ // No auditCtx: every webhook would otherwise spam the audit trail.
77
+ const got = await systemSecrets.get(SYSTEM_TENANT_ID, deps.webhookSecretHandle.name);
78
+ const stored = parseStoredSecret(got?.reveal());
79
+ if (stored)
80
+ return stored;
81
+ }
82
+ const fallback = deps.fallback.webhookSecret;
83
+ if (fallback && fallback.length > 0)
84
+ return fallback;
85
+ throw new Error(`subscription-dodo: '${deps.webhookSecretHandle.name}' unresolved — no system-secret set and no factory fallback. Webhook cannot verify until it is configured.`);
86
+ },
87
+ },
88
+ };
89
+ }
@@ -0,0 +1,37 @@
1
+ import { type PaymentEvent, type SubscriptionEvent, type SubscriptionStatus } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
2
+ import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
3
+ import * as z from "zod";
4
+ import type { DodoWebhookRuntime } from "./runtime.ts";
5
+ export declare const WEBHOOK_TOLERANCE_SECONDS = 300;
6
+ export type StandardWebhookHeaders = Readonly<Record<string, string>>;
7
+ /** Throws unless the Standard Webhooks signature over `rawBody` is valid and
8
+ * the timestamp is within the tolerance in both directions. */
9
+ export declare function verifyStandardWebhook(rawBody: string, headers: StandardWebhookHeaders, secret: string, nowSeconds: number): void;
10
+ export declare const dodoSubscriptionSchema: z.ZodObject<{
11
+ subscription_id: z.ZodString;
12
+ product_id: z.ZodString;
13
+ status: z.ZodString;
14
+ customer: z.ZodObject<{
15
+ customer_id: z.ZodString;
16
+ }, z.core.$strip>;
17
+ metadata: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
18
+ next_billing_date: z.ZodISODateTime;
19
+ cancel_at_next_billing_date: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
20
+ }, z.core.$strip>;
21
+ export type DodoSubscription = z.infer<typeof dodoSubscriptionSchema>;
22
+ export type MappedDodoSubscriptionState = {
23
+ readonly status: SubscriptionStatus;
24
+ readonly tier: string;
25
+ readonly currentPeriodEnd: string;
26
+ readonly cancelAt: string | null;
27
+ };
28
+ /** Shared by the webhook and retrieveSubscription so both map Dodo state
29
+ * identically. Null when the status is unknown or the product is not in
30
+ * `priceToTier` (SubscriptionEvent.tier is required). */
31
+ export declare function mapDodoSubscriptionState(sub: DodoSubscription, priceToTier: Readonly<Record<string, string>>): MappedDodoSubscriptionState | null;
32
+ export type DodoWebhookOptions = {
33
+ readonly priceToTier: Readonly<Record<string, string>>;
34
+ /** Test seam: Unix seconds. Defaults to the wall clock. */
35
+ readonly nowSeconds?: () => number;
36
+ };
37
+ export declare function verifyAndParseDodoWebhook(runtime: DodoWebhookRuntime, options: DodoWebhookOptions): (rawBody: string, headers: Record<string, string>, systemSecrets?: SecretsContext) => Promise<SubscriptionEvent | PaymentEvent | null>;
@@ -0,0 +1,189 @@
1
+ // Dodo Payments webhook: Standard Webhooks signature verification plus mapping
2
+ // of Dodo's event envelope onto the foundation's normalized events.
3
+ //
4
+ // Verification always runs against the raw body string; re-serialized JSON
5
+ // would change whitespace and break the HMAC. The signature check happens
6
+ // before any parsing, so no path bypasses it.
7
+ import { createHmac, timingSafeEqual } from "node:crypto";
8
+ import { BillingEventKinds, parseProviderConsentId, SubscriptionEventTypes, SubscriptionStatuses, } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
9
+ import { parseJsonOrThrow } from "@cosmicdrift/kumiko-framework/utils";
10
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
11
+ import * as z from "zod";
12
+ import { DODO_PROVIDER_NAME } from "./constants.js";
13
+ export const WEBHOOK_TOLERANCE_SECONDS = 300;
14
+ const SECRET_PREFIX = "whsec_";
15
+ const SIGNATURE_VERSION = "v1";
16
+ const TIMESTAMP_PATTERN = /^\d+$/;
17
+ function signatureFor(secretKey, signedContent) {
18
+ return createHmac("sha256", secretKey).update(signedContent).digest();
19
+ }
20
+ function matchesAnyV1Signature(header, expected) {
21
+ return header.split(" ").some((entry) => {
22
+ const [version, encoded] = entry.split(",");
23
+ if (version !== SIGNATURE_VERSION || !encoded)
24
+ return false;
25
+ const candidate = Buffer.from(encoded, "base64");
26
+ return candidate.length === expected.length && timingSafeEqual(candidate, expected);
27
+ });
28
+ }
29
+ /** Throws unless the Standard Webhooks signature over `rawBody` is valid and
30
+ * the timestamp is within the tolerance in both directions. */
31
+ export function verifyStandardWebhook(rawBody, headers, secret, nowSeconds) {
32
+ const id = headers["webhook-id"];
33
+ const timestamp = headers["webhook-timestamp"];
34
+ const signatureHeader = headers["webhook-signature"];
35
+ if (!id || !timestamp || !signatureHeader) {
36
+ throw new Error("subscription-dodo: webhook-id, webhook-timestamp or webhook-signature missing");
37
+ }
38
+ if (!TIMESTAMP_PATTERN.test(timestamp)) {
39
+ throw new Error("subscription-dodo: webhook-timestamp is not a Unix timestamp in seconds");
40
+ }
41
+ if (Math.abs(nowSeconds - Number(timestamp)) > WEBHOOK_TOLERANCE_SECONDS) {
42
+ throw new Error("subscription-dodo: webhook timestamp outside the tolerance window");
43
+ }
44
+ const secretKey = Buffer.from(secret.startsWith(SECRET_PREFIX) ? secret.slice(SECRET_PREFIX.length) : secret, "base64");
45
+ const expected = signatureFor(secretKey, `${id}.${timestamp}.${rawBody}`);
46
+ if (!matchesAnyV1Signature(signatureHeader, expected)) {
47
+ throw new Error("subscription-dodo: webhook signature verify failed");
48
+ }
49
+ }
50
+ // =============================================================================
51
+ // Event mapping
52
+ // =============================================================================
53
+ const DODO_SUBSCRIPTION_EVENT_TYPES = {
54
+ "subscription.active": SubscriptionEventTypes.created,
55
+ "subscription.updated": SubscriptionEventTypes.updated,
56
+ "subscription.plan_changed": SubscriptionEventTypes.updated,
57
+ "subscription.paused": SubscriptionEventTypes.updated,
58
+ "subscription.unpaused": SubscriptionEventTypes.updated,
59
+ "subscription.update_payment_method": SubscriptionEventTypes.updated,
60
+ "subscription.renewed": SubscriptionEventTypes.invoicePaid,
61
+ "subscription.past_due": SubscriptionEventTypes.invoicePaymentFailed,
62
+ "subscription.on_hold": SubscriptionEventTypes.invoicePaymentFailed,
63
+ "subscription.cancelled": SubscriptionEventTypes.canceled,
64
+ "subscription.expired": SubscriptionEventTypes.canceled,
65
+ "subscription.failed": SubscriptionEventTypes.canceled,
66
+ };
67
+ const DODO_SUBSCRIPTION_STATUSES = {
68
+ pending: SubscriptionStatuses.incomplete,
69
+ active: SubscriptionStatuses.active,
70
+ on_hold: SubscriptionStatuses.pastDue,
71
+ past_due: SubscriptionStatuses.pastDue,
72
+ paused: SubscriptionStatuses.pastDue,
73
+ cancelled: SubscriptionStatuses.canceled,
74
+ failed: SubscriptionStatuses.canceled,
75
+ expired: SubscriptionStatuses.canceled,
76
+ };
77
+ const PAYMENT_SUCCEEDED = "payment.succeeded";
78
+ const metadataSchema = z.record(z.string(), z.unknown()).nullish();
79
+ const customerSchema = z.object({ customer_id: z.string().min(1) });
80
+ const envelopeSchema = z.object({
81
+ type: z.string(),
82
+ timestamp: z.iso.datetime({ offset: true }),
83
+ data: z.unknown(),
84
+ });
85
+ export const dodoSubscriptionSchema = z.object({
86
+ subscription_id: z.string().min(1),
87
+ product_id: z.string().min(1),
88
+ status: z.string(),
89
+ customer: customerSchema,
90
+ metadata: metadataSchema,
91
+ next_billing_date: z.iso.datetime({ offset: true }),
92
+ cancel_at_next_billing_date: z.boolean().nullish(),
93
+ });
94
+ const dodoPaymentSchema = z.object({
95
+ subscription_id: z.string().nullish(),
96
+ checkout_session_id: z.string().nullish(),
97
+ customer: customerSchema,
98
+ metadata: metadataSchema,
99
+ product_cart: z.array(z.object({ product_id: z.string().min(1) })).nullish(),
100
+ });
101
+ function metadataString(metadata, key) {
102
+ const value = metadata?.[key];
103
+ return typeof value === "string" && value.length > 0 ? value : undefined;
104
+ }
105
+ /** Shared by the webhook and retrieveSubscription so both map Dodo state
106
+ * identically. Null when the status is unknown or the product is not in
107
+ * `priceToTier` (SubscriptionEvent.tier is required). */
108
+ export function mapDodoSubscriptionState(sub, priceToTier) {
109
+ const status = DODO_SUBSCRIPTION_STATUSES[sub.status];
110
+ const tier = priceToTier[sub.product_id];
111
+ if (!status || !tier)
112
+ return null;
113
+ return {
114
+ status,
115
+ tier,
116
+ currentPeriodEnd: sub.next_billing_date,
117
+ cancelAt: sub.cancel_at_next_billing_date === true ? sub.next_billing_date : null,
118
+ };
119
+ }
120
+ function currentUnixSeconds() {
121
+ return Math.floor(Temporal.Now.instant().epochMilliseconds / 1000);
122
+ }
123
+ function parsePaymentEvent(providerEventId, data) {
124
+ const parsed = dodoPaymentSchema.safeParse(data);
125
+ if (!parsed.success)
126
+ return null;
127
+ const payment = parsed.data;
128
+ // A subscription's own payments are covered by its subscription.* events.
129
+ if (payment.subscription_id)
130
+ return null;
131
+ const tenantId = metadataString(payment.metadata, "tenantId");
132
+ const priceId = payment.product_cart?.[0]?.product_id;
133
+ if (!tenantId || !priceId)
134
+ return null;
135
+ const consentId = parseProviderConsentId(payment.metadata?.["consentId"]);
136
+ return {
137
+ kind: BillingEventKinds.payment,
138
+ providerEventId,
139
+ providerName: DODO_PROVIDER_NAME,
140
+ tenantId,
141
+ ...(consentId !== undefined && { consentId }),
142
+ ...(payment.checkout_session_id && { providerCheckoutId: payment.checkout_session_id }),
143
+ providerCustomerId: payment.customer.customer_id,
144
+ priceId,
145
+ };
146
+ }
147
+ function parseSubscriptionEvent(providerEventId, type, providerEventAt, data, priceToTier) {
148
+ const parsed = dodoSubscriptionSchema.safeParse(data);
149
+ if (!parsed.success)
150
+ return null;
151
+ const sub = parsed.data;
152
+ const tenantId = metadataString(sub.metadata, "tenantId");
153
+ const state = mapDodoSubscriptionState(sub, priceToTier);
154
+ if (!tenantId || !state)
155
+ return null;
156
+ const consentId = parseProviderConsentId(sub.metadata?.["consentId"]);
157
+ return {
158
+ providerEventId,
159
+ providerName: DODO_PROVIDER_NAME,
160
+ type,
161
+ tenantId,
162
+ ...(consentId !== undefined && { consentId }),
163
+ providerCustomerId: sub.customer.customer_id,
164
+ providerSubscriptionId: sub.subscription_id,
165
+ providerEventAt,
166
+ ...state,
167
+ };
168
+ }
169
+ export function verifyAndParseDodoWebhook(runtime, options) {
170
+ const nowSeconds = options.nowSeconds ?? currentUnixSeconds;
171
+ return async (rawBody, headers, systemSecrets) => {
172
+ const secret = await runtime.resolveWebhookSecret(systemSecrets);
173
+ verifyStandardWebhook(rawBody, headers, secret, nowSeconds());
174
+ // Past verification the body is trusted to be Dodo's; a malformed shape is
175
+ // schema drift, so it is dropped (200 ignored) instead of answered with a 401.
176
+ const envelope = envelopeSchema.safeParse(parseJsonOrThrow(rawBody, "subscription-dodo webhook body"));
177
+ // verifyStandardWebhook has checked webhook-id is present.
178
+ const providerEventId = headers["webhook-id"];
179
+ if (!envelope.success || !providerEventId)
180
+ return null;
181
+ const { type, timestamp, data } = envelope.data;
182
+ if (type === PAYMENT_SUCCEEDED)
183
+ return parsePaymentEvent(providerEventId, data);
184
+ const normalizedType = DODO_SUBSCRIPTION_EVENT_TYPES[type];
185
+ if (!normalizedType)
186
+ return null;
187
+ return parseSubscriptionEvent(providerEventId, normalizedType, timestamp, data, options.priceToTier);
188
+ };
189
+ }
@@ -1,31 +1,29 @@
1
1
  // Feature name
2
2
  export const SUBSCRIPTION_MOLLIE_FEATURE = "subscription-mollie";
3
- // entityName under den der Plugin gegen "subscriptionProvider"
4
- // registriert. Matcht den path-segment in der webhook-URL
3
+ // Entity name under which the plugin registers against
4
+ // "subscriptionProvider". Matches the path segment in the webhook URL
5
5
  // `/api/subscription/webhook/mollie`.
6
6
  export const MOLLIE_PROVIDER_NAME = "mollie";
7
7
  // =============================================================================
8
- // Mollie-Webhook-Pattern (anders als Stripe!)
8
+ // Mollie webhook pattern (unlike Stripe)
9
9
  // =============================================================================
10
10
  //
11
- // Mollie sendet bei JEDEM event nur die ID im body (form-urlencoded
12
- // oder JSON). Plugin muss IMMER lazy-fetchen via Mollie-API:
13
- // - `id=tr_xxx` → payment-id, fetch payment + subscription
14
- // - `id=sub_xxx` → subscription-id, fetch subscription direkt
11
+ // Mollie sends only the ID in the body for EVERY event (form-urlencoded
12
+ // or JSON). The plugin must ALWAYS fetch lazily via the Mollie API:
13
+ // - `id=tr_xxx` → payment ID, fetch payment + subscription
14
+ // - `id=sub_xxx` → subscription ID, fetch subscription directly
15
15
  //
16
- // **Keine native HMAC-sig-verify** in Mollie-SDK 4.5.0. Mollie's
17
- // Sicherheits-Modell stützt sich auf nicht-guessable IDs (~10^25
18
- // brute-force-space) + API-Lookup-Validation (garbage-id → 401 von
19
- // Mollie-API). Plus: App-Builder kann zusätzlich einen URL-Token-
20
- // Wrapper vor die Foundation-route schalten (eigener extraRoute der
21
- // einen secret-token im URL-Pfad verifiziert + dann an die Foundation
22
- // weiterreicht).
16
+ // **No native HMAC signature verification** in Mollie SDK 4.5.0. Mollie's
17
+ // security model relies on non-guessable IDs (~10^25 brute-force space)
18
+ // plus API lookup validation (garbage ID → 401 from the Mollie API). In
19
+ // addition, the app builder can place a URL-token wrapper before the
20
+ // foundation route (a custom extraRoute that verifies a secret token in
21
+ // the URL path and then forwards to the foundation).
23
22
  //
24
- // **Event-Type-Mapping** ist heuristisch — Mollie hat keine explicit-
25
- // typed events:
23
+ // **Event type mapping** is heuristic—Mollie has no explicitly typed events:
26
24
  // - sub_xxx + status=canceled/completed → SubscriptionEventTypes.canceled
27
25
  // - sub_xxx + status=active/pending → SubscriptionEventTypes.updated
28
26
  // - tr_xxx + sequenceType=first + paid → SubscriptionEventTypes.created
29
27
  // - tr_xxx + sequenceType=recurring + paid → SubscriptionEventTypes.invoicePaid
30
28
  // - tr_xxx + sequenceType=recurring + failed → SubscriptionEventTypes.invoicePaymentFailed
31
- // - alles andere → null (foundation 200 ignored)
29
+ // - everything else → null (foundation 200 ignored)