@cosmicdrift/kumiko-bundled-features 0.393.0 → 0.394.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.
- package/dist/account-security/feature.d.ts +2 -0
- package/dist/account-security/feature.js +3 -1
- package/dist/audit/event-classification.js +1 -0
- package/dist/auth-email-password/handlers/change-password.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/confirm-account-unlock.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-accept.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
- package/dist/auth-email-password/handlers/invite-signup-complete.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/logout.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-account-unlock.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-email-verification.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/reset-password.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-request.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/token-request-handler.d.ts +1 -1
- package/dist/auth-email-password/handlers/verify-email.write.d.ts +1 -1
- package/dist/auth-email-password/web/auth-client.d.ts +3 -7
- package/dist/auth-email-password/web/auth-client.js +3 -24
- package/dist/auth-email-password/web/auth-gate.d.ts +2 -4
- package/dist/auth-email-password/web/auth-redirect.d.ts +2 -2
- package/dist/auth-email-password/web/auth-redirect.js +3 -12
- package/dist/auth-email-password/web/index.d.ts +2 -1
- package/dist/auth-email-password/web/index.js +1 -0
- package/dist/auth-email-password/web/login-failure.d.ts +9 -0
- package/dist/auth-email-password/web/login-failure.js +32 -0
- package/dist/auth-email-password/web/login-url.d.ts +5 -0
- package/dist/auth-email-password/web/login-url.js +13 -0
- package/dist/auth-email-password/web/static-login.d.ts +42 -0
- package/dist/auth-email-password/web/static-login.js +122 -0
- package/dist/auth-mfa/handlers/disable.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-confirm.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-start-preauth.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-start.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/regenerate-recovery.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/verify.write.d.ts +1 -1
- package/dist/auth-mfa/web/mfa-client.js +4 -43
- package/dist/billing-foundation/checkout-core.d.ts +2 -1
- package/dist/billing-foundation/checkout-core.js +2 -11
- package/dist/billing-foundation/events.d.ts +1 -0
- package/dist/billing-foundation/events.js +2 -0
- package/dist/billing-foundation/feature.js +1 -1
- package/dist/billing-foundation/handlers/create-checkout-session.write.js +9 -2
- package/dist/billing-foundation/handlers/process-payment-event.write.d.ts +1 -0
- package/dist/billing-foundation/handlers/process-payment-event.write.js +2 -0
- package/dist/billing-foundation/index.d.ts +1 -1
- package/dist/billing-foundation/one-off-checkout-gate.d.ts +9 -0
- package/dist/billing-foundation/one-off-checkout-gate.js +30 -0
- package/dist/billing-foundation/one-off-customer.d.ts +6 -0
- package/dist/billing-foundation/one-off-customer.js +12 -0
- package/dist/billing-foundation/plan-catalog.js +1 -0
- package/dist/billing-foundation/types.d.ts +25 -0
- package/dist/billing-foundation/web/billing-plans-panel.js +1 -0
- package/dist/billing-foundation/web/plan-switch-confirm-dialog.js +7 -3
- package/dist/billing-foundation/web/plan-switch-recurring.d.ts +16 -0
- package/dist/billing-foundation/web/plan-switch-recurring.js +10 -0
- package/dist/billing-foundation/webhook-handler.js +1 -0
- package/dist/cap-overview/handlers/caps-usage.query.js +3 -3
- package/dist/cap-overview/types.d.ts +2 -0
- package/dist/channel-in-app/handlers/mark-all-read.write.d.ts +1 -1
- package/dist/channel-in-app/handlers/mark-read.write.d.ts +1 -1
- package/dist/compliance-profiles/handlers/set-profile.write.d.ts +1 -1
- package/dist/compliance-profiles/web/compliance-profile-catalog.js +8 -1
- package/dist/config/handlers/reset.write.d.ts +1 -1
- package/dist/config/handlers/set.write.d.ts +1 -1
- package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -1
- package/dist/delivery/handlers/resubscribe-address.write.d.ts +1 -1
- package/dist/delivery/handlers/resubscribe-user.write.d.ts +1 -1
- package/dist/delivery/handlers/set-preference.write.d.ts +1 -1
- package/dist/delivery/handlers/unsubscribe-address.write.d.ts +1 -1
- package/dist/delivery/handlers/unsubscribe-user.write.d.ts +1 -1
- package/dist/feature-toggles/handlers/set.write.d.ts +1 -1
- package/dist/file-foundation/read-file-ref-content.d.ts +2 -0
- package/dist/file-foundation/read-file-ref-content.js +9 -3
- package/dist/form-draft/handlers/discard.write.d.ts +1 -1
- package/dist/form-draft/handlers/save.write.d.ts +1 -1
- package/dist/inbound-mail-foundation/account-health.d.ts +17 -0
- package/dist/inbound-mail-foundation/account-health.js +101 -0
- package/dist/inbound-mail-foundation/account-poll-schedule.d.ts +19 -0
- package/dist/inbound-mail-foundation/account-poll-schedule.js +44 -0
- package/dist/inbound-mail-foundation/account-poll.d.ts +24 -0
- package/dist/inbound-mail-foundation/account-poll.js +157 -0
- package/dist/inbound-mail-foundation/account-record.d.ts +1 -0
- package/dist/inbound-mail-foundation/account-record.js +1 -0
- package/dist/inbound-mail-foundation/account-wake.d.ts +3 -0
- package/dist/inbound-mail-foundation/account-wake.js +17 -0
- package/dist/inbound-mail-foundation/connect-callback-verify.d.ts +10 -0
- package/dist/inbound-mail-foundation/connect-callback-verify.js +31 -0
- package/dist/inbound-mail-foundation/connect-route-responses.d.ts +13 -0
- package/dist/inbound-mail-foundation/connect-route-responses.js +16 -0
- package/dist/inbound-mail-foundation/connect-routes.js +52 -33
- package/dist/inbound-mail-foundation/constants.d.ts +50 -2
- package/dist/inbound-mail-foundation/constants.js +53 -3
- package/dist/inbound-mail-foundation/content-refetch.d.ts +14 -0
- package/dist/inbound-mail-foundation/content-refetch.js +93 -0
- package/dist/inbound-mail-foundation/describe-error.d.ts +1 -0
- package/dist/inbound-mail-foundation/describe-error.js +5 -0
- package/dist/inbound-mail-foundation/entities.d.ts +26 -0
- package/dist/inbound-mail-foundation/entities.js +43 -3
- package/dist/inbound-mail-foundation/events.d.ts +97 -0
- package/dist/inbound-mail-foundation/events.js +57 -2
- package/dist/inbound-mail-foundation/feature.d.ts +5 -2
- package/dist/inbound-mail-foundation/feature.js +122 -106
- package/dist/inbound-mail-foundation/file-storage-failure.d.ts +8 -0
- package/dist/inbound-mail-foundation/file-storage-failure.js +19 -0
- package/dist/inbound-mail-foundation/handlers/account-address.d.ts +16 -0
- package/dist/inbound-mail-foundation/handlers/account-address.js +51 -0
- package/dist/inbound-mail-foundation/handlers/acknowledge-quarantine.write.d.ts +8 -0
- package/dist/inbound-mail-foundation/handlers/acknowledge-quarantine.write.js +39 -0
- package/dist/inbound-mail-foundation/handlers/attach-message-content.write.d.ts +60 -0
- package/dist/inbound-mail-foundation/handlers/attach-message-content.write.js +128 -0
- package/dist/inbound-mail-foundation/handlers/connect-account.write.d.ts +3 -2
- package/dist/inbound-mail-foundation/handlers/connect-account.write.js +96 -55
- package/dist/inbound-mail-foundation/handlers/disconnect-account.write.d.ts +2 -1
- package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +55 -42
- package/dist/inbound-mail-foundation/handlers/ingest-message-prepare.d.ts +26 -0
- package/dist/inbound-mail-foundation/handlers/ingest-message-prepare.js +131 -0
- package/dist/inbound-mail-foundation/handlers/ingest-message-schema.d.ts +64 -0
- package/dist/inbound-mail-foundation/handlers/ingest-message-schema.js +39 -0
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.d.ts +74 -21
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +57 -134
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.d.ts +2 -1
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +60 -26
- package/dist/inbound-mail-foundation/handlers/list-messages.query.d.ts +2 -1
- package/dist/inbound-mail-foundation/handlers/list-messages.query.js +43 -29
- package/dist/inbound-mail-foundation/handlers/list-quarantine.query.d.ts +3 -0
- package/dist/inbound-mail-foundation/handlers/list-quarantine.query.js +75 -0
- package/dist/inbound-mail-foundation/handlers/mark-credential-stored.write.d.ts +7 -0
- package/dist/inbound-mail-foundation/handlers/mark-credential-stored.write.js +38 -0
- package/dist/inbound-mail-foundation/handlers/message-content.d.ts +70 -0
- package/dist/inbound-mail-foundation/handlers/message-content.js +137 -0
- package/dist/inbound-mail-foundation/handlers/message-content.query.d.ts +19 -0
- package/dist/inbound-mail-foundation/handlers/message-content.query.js +59 -0
- package/dist/inbound-mail-foundation/handlers/message-detail.query.d.ts +3 -0
- package/dist/inbound-mail-foundation/handlers/message-detail.query.js +23 -0
- package/dist/inbound-mail-foundation/handlers/reactivate-account.write.d.ts +8 -0
- package/dist/inbound-mail-foundation/handlers/reactivate-account.write.js +84 -0
- package/dist/inbound-mail-foundation/handlers/retry-quarantined-message.write.d.ts +8 -0
- package/dist/inbound-mail-foundation/handlers/retry-quarantined-message.write.js +34 -0
- package/dist/inbound-mail-foundation/handlers/scope-visibility.d.ts +10 -2
- package/dist/inbound-mail-foundation/handlers/scope-visibility.js +27 -8
- package/dist/inbound-mail-foundation/handlers/set-trusted-authserv-id.write.d.ts +13 -0
- package/dist/inbound-mail-foundation/handlers/set-trusted-authserv-id.write.js +54 -0
- package/dist/inbound-mail-foundation/handlers/thread-rollup.d.ts +24 -0
- package/dist/inbound-mail-foundation/handlers/thread-rollup.js +98 -0
- package/dist/inbound-mail-foundation/handlers/trusted-authserv-id.d.ts +3 -0
- package/dist/inbound-mail-foundation/handlers/trusted-authserv-id.js +3 -0
- package/dist/inbound-mail-foundation/handlers/update-account.write.d.ts +13 -3
- package/dist/inbound-mail-foundation/handlers/update-account.write.js +61 -45
- package/dist/inbound-mail-foundation/i18n.js +72 -3
- package/dist/inbound-mail-foundation/inbound-oauth-flow.d.ts +43 -0
- package/dist/inbound-mail-foundation/inbound-oauth-flow.js +1 -0
- package/dist/inbound-mail-foundation/index.d.ts +19 -6
- package/dist/inbound-mail-foundation/index.js +17 -6
- package/dist/inbound-mail-foundation/ingest-failures.d.ts +77 -0
- package/dist/inbound-mail-foundation/ingest-failures.js +79 -0
- package/dist/inbound-mail-foundation/ingest-payload.d.ts +7 -0
- package/dist/inbound-mail-foundation/ingest-payload.js +34 -0
- package/dist/inbound-mail-foundation/mail-roles.d.ts +21 -0
- package/dist/inbound-mail-foundation/mail-roles.js +83 -0
- package/dist/inbound-mail-foundation/normalize-inbound-message.d.ts +17 -0
- package/dist/inbound-mail-foundation/normalize-inbound-message.js +90 -0
- package/dist/inbound-mail-foundation/oauth-access-token.d.ts +3 -1
- package/dist/inbound-mail-foundation/oauth-access-token.js +13 -2
- package/dist/inbound-mail-foundation/outbound-attachment-access.d.ts +5 -0
- package/dist/inbound-mail-foundation/outbound-attachment-access.js +16 -0
- package/dist/inbound-mail-foundation/outbound-attachments.d.ts +18 -0
- package/dist/inbound-mail-foundation/outbound-attachments.js +77 -0
- package/dist/inbound-mail-foundation/outbound-mail.d.ts +15 -1
- package/dist/inbound-mail-foundation/outbound-mail.js +4 -0
- package/dist/inbound-mail-foundation/probe.js +1 -1
- package/dist/inbound-mail-foundation/projection.d.ts +28 -0
- package/dist/inbound-mail-foundation/projection.js +25 -3
- package/dist/inbound-mail-foundation/provider-errors.d.ts +28 -0
- package/dist/inbound-mail-foundation/provider-errors.js +66 -0
- package/dist/inbound-mail-foundation/quarantine-entities.d.ts +61 -0
- package/dist/inbound-mail-foundation/quarantine-entities.js +47 -0
- package/dist/inbound-mail-foundation/quarantine-header-import.d.ts +9 -0
- package/dist/inbound-mail-foundation/quarantine-header-import.js +53 -0
- package/dist/inbound-mail-foundation/quarantine-retry.d.ts +13 -0
- package/dist/inbound-mail-foundation/quarantine-retry.js +97 -0
- package/dist/inbound-mail-foundation/quarantine-screens.d.ts +4 -0
- package/dist/inbound-mail-foundation/quarantine-screens.js +57 -0
- package/dist/inbound-mail-foundation/quarantine.d.ts +13 -0
- package/dist/inbound-mail-foundation/quarantine.js +46 -0
- package/dist/inbound-mail-foundation/retention-job.d.ts +2 -0
- package/dist/inbound-mail-foundation/retention-job.js +62 -0
- package/dist/inbound-mail-foundation/retention-sweep.d.ts +4 -0
- package/dist/inbound-mail-foundation/retention-sweep.js +35 -8
- package/dist/inbound-mail-foundation/screens.d.ts +13 -2
- package/dist/inbound-mail-foundation/screens.js +216 -89
- package/dist/inbound-mail-foundation/send-from-account.d.ts +11 -1
- package/dist/inbound-mail-foundation/send-from-account.js +19 -2
- package/dist/inbound-mail-foundation/sender-auth.d.ts +63 -0
- package/dist/inbound-mail-foundation/sender-auth.js +198 -0
- package/dist/inbound-mail-foundation/supervisor-ingest.d.ts +43 -0
- package/dist/inbound-mail-foundation/supervisor-ingest.js +195 -0
- package/dist/inbound-mail-foundation/sync-account-now.d.ts +22 -0
- package/dist/inbound-mail-foundation/sync-account-now.js +29 -0
- package/dist/inbound-mail-foundation/sync-cursor-store.d.ts +9 -0
- package/dist/inbound-mail-foundation/sync-cursor-store.js +54 -0
- package/dist/inbound-mail-foundation/syncable-accounts.d.ts +16 -0
- package/dist/inbound-mail-foundation/syncable-accounts.js +79 -0
- package/dist/inbound-mail-foundation/tenant-destroy-hook.js +5 -1
- package/dist/inbound-mail-foundation/types.d.ts +61 -62
- package/dist/inbound-mail-foundation/types.js +0 -54
- package/dist/inbound-mail-foundation/wake-consumer.d.ts +2 -0
- package/dist/inbound-mail-foundation/wake-consumer.js +30 -0
- package/dist/inbound-mail-foundation/watch-catch-up.d.ts +19 -0
- package/dist/inbound-mail-foundation/watch-catch-up.js +33 -0
- package/dist/inbound-mail-foundation/watch-lease.d.ts +15 -0
- package/dist/inbound-mail-foundation/watch-lease.js +81 -0
- package/dist/inbound-mail-foundation/watch-supervisor-types.d.ts +83 -0
- package/dist/inbound-mail-foundation/watch-supervisor-types.js +1 -0
- package/dist/inbound-mail-foundation/watch-supervisor.d.ts +1 -53
- package/dist/inbound-mail-foundation/watch-supervisor.js +123 -290
- package/dist/inbound-mail-foundation/web/client-plugin.d.ts +14 -0
- package/dist/inbound-mail-foundation/web/client-plugin.js +40 -0
- package/dist/inbound-mail-foundation/web/constants.d.ts +5 -0
- package/dist/inbound-mail-foundation/web/constants.js +6 -0
- package/dist/inbound-mail-foundation/web/index.d.ts +1 -0
- package/dist/inbound-mail-foundation/web/index.js +1 -0
- package/dist/inbound-provider-imap/connect-mailbox.d.ts +21 -3
- package/dist/inbound-provider-imap/connect-mailbox.js +69 -20
- package/dist/inbound-provider-imap/connect-my-mailbox.d.ts +13 -0
- package/dist/inbound-provider-imap/connect-my-mailbox.js +25 -0
- package/dist/inbound-provider-imap/constants.d.ts +2 -1
- package/dist/inbound-provider-imap/constants.js +2 -1
- package/dist/inbound-provider-imap/credential-document.js +2 -2
- package/dist/inbound-provider-imap/download-batch.d.ts +6 -0
- package/dist/inbound-provider-imap/download-batch.js +24 -0
- package/dist/inbound-provider-imap/feature.d.ts +2 -2
- package/dist/inbound-provider-imap/feature.js +49 -37
- package/dist/inbound-provider-imap/fetch-content.d.ts +4 -0
- package/dist/inbound-provider-imap/fetch-content.js +68 -0
- package/dist/inbound-provider-imap/i18n.js +8 -1
- package/dist/inbound-provider-imap/imap-client.d.ts +27 -23
- package/dist/inbound-provider-imap/imap-client.js +30 -68
- package/dist/inbound-provider-imap/index.d.ts +2 -1
- package/dist/inbound-provider-imap/index.js +2 -1
- package/dist/inbound-provider-imap/mime-content.d.ts +11 -0
- package/dist/inbound-provider-imap/mime-content.js +66 -0
- package/dist/inbound-provider-imap/parse-message.d.ts +35 -0
- package/dist/inbound-provider-imap/parse-message.js +113 -0
- package/dist/inbound-provider-imap/reconnect-account.d.ts +2 -0
- package/dist/inbound-provider-imap/reconnect-account.js +4 -6
- package/dist/inbound-provider-imap/reconnect-mailbox.d.ts +4 -2
- package/dist/inbound-provider-imap/reconnect-mailbox.js +35 -12
- package/dist/inbound-provider-imap/reconnect-prefill.query.d.ts +2 -1
- package/dist/inbound-provider-imap/reconnect-prefill.query.js +11 -8
- package/dist/inbound-provider-imap/screens.d.ts +6 -2
- package/dist/inbound-provider-imap/screens.js +171 -109
- package/dist/inbound-provider-imap/send-mail.d.ts +2 -2
- package/dist/inbound-provider-imap/send-mail.js +8 -1
- package/dist/inbound-provider-imap/sender-auth-headers.d.ts +3 -0
- package/dist/inbound-provider-imap/sender-auth-headers.js +18 -0
- package/dist/inbound-provider-inmemory/feature.d.ts +15 -0
- package/dist/inbound-provider-inmemory/feature.js +57 -2
- package/dist/inbound-provider-inmemory/index.d.ts +1 -1
- package/dist/inbound-provider-inmemory/index.js +1 -1
- package/dist/jobs/handlers/consumer.write.d.ts +4 -4
- package/dist/jobs/handlers/retry.write.d.ts +1 -1
- package/dist/jobs/handlers/trigger.write.d.ts +1 -1
- package/dist/managed-pages/handlers/set.write.d.ts +1 -1
- package/dist/managed-pages/web/i18n.js +1 -0
- package/dist/personal-access-tokens/handlers/create.write.d.ts +1 -1
- package/dist/personal-access-tokens/handlers/revoke.write.d.ts +1 -1
- package/dist/readiness/handlers/run-probes.write.d.ts +1 -1
- package/dist/secrets/handlers/delete.write.d.ts +1 -1
- package/dist/secrets/handlers/set.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke-all-for-user.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke-all-others.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke.write.d.ts +1 -1
- package/dist/subscription-paddle/checkout-config.query.d.ts +3 -0
- package/dist/subscription-paddle/checkout-config.query.js +14 -0
- package/dist/subscription-paddle/checkout-methods.d.ts +6 -0
- package/dist/subscription-paddle/checkout-methods.js +85 -0
- package/dist/subscription-paddle/checkout-page.d.ts +19 -0
- package/dist/subscription-paddle/checkout-page.js +126 -0
- package/dist/subscription-paddle/client.d.ts +24 -0
- package/dist/subscription-paddle/client.js +60 -0
- package/dist/subscription-paddle/constants.d.ts +16 -0
- package/dist/subscription-paddle/constants.js +25 -0
- package/dist/subscription-paddle/feature.d.ts +49 -0
- package/dist/subscription-paddle/feature.js +192 -0
- package/dist/subscription-paddle/index.d.ts +2 -0
- package/dist/subscription-paddle/index.js +4 -0
- package/dist/subscription-paddle/plan-switch-methods.d.ts +6 -0
- package/dist/subscription-paddle/plan-switch-methods.js +114 -0
- package/dist/subscription-paddle/price-methods.d.ts +15 -0
- package/dist/subscription-paddle/price-methods.js +105 -0
- package/dist/subscription-paddle/runtime.d.ts +42 -0
- package/dist/subscription-paddle/runtime.js +109 -0
- package/dist/subscription-paddle/subscription-state.d.ts +34 -0
- package/dist/subscription-paddle/subscription-state.js +48 -0
- package/dist/subscription-paddle/tenant-binding.d.ts +8 -0
- package/dist/subscription-paddle/tenant-binding.js +26 -0
- package/dist/subscription-paddle/verify-webhook.d.ts +4 -0
- package/dist/subscription-paddle/verify-webhook.js +55 -0
- package/dist/subscription-paddle/webhook-events.d.ts +9 -0
- package/dist/subscription-paddle/webhook-events.js +124 -0
- package/dist/template-resolver/handlers/collection-item.query.js +2 -2
- package/dist/template-resolver/handlers/collection-list.query.js +4 -2
- package/dist/template-resolver/handlers/collection-set.write.d.ts +1 -1
- package/dist/template-resolver/handlers/collection-shared.d.ts +11 -0
- package/dist/template-resolver/handlers/collection-shared.js +16 -0
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +1 -1
- package/dist/template-resolver/handlers/discard-draft.write.d.ts +2 -2
- package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +1 -1
- package/dist/template-resolver/handlers/import-bundle.write.d.ts +1 -1
- package/dist/template-resolver/handlers/patch-draft.write.d.ts +1 -1
- package/dist/template-resolver/handlers/reset-to-system.write.d.ts +1 -1
- package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +1 -1
- package/dist/template-resolver/handlers/resource-remove.write.d.ts +1 -1
- package/dist/template-resolver/handlers/resource-upload.write.d.ts +1 -1
- package/dist/template-resolver/handlers/send-test.write.d.ts +1 -1
- package/dist/template-resolver/handlers/set.write.d.ts +1 -1
- package/dist/template-resolver/handlers/toggle-status.write.d.ts +4 -4
- package/dist/template-resolver/handlers/upsert-system.write.d.ts +1 -1
- package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +1 -1
- package/dist/template-resolver/web/client-plugin.d.ts +4 -0
- package/dist/template-resolver/web/client-plugin.js +134 -52
- package/dist/template-resolver/web/i18n.js +1 -1
- package/dist/tenant/handlers/add-member.write.d.ts +1 -1
- package/dist/tenant/handlers/cancel-invitation.write.d.ts +1 -1
- package/dist/tenant/handlers/create.write.d.ts +1 -1
- package/dist/tenant/handlers/remove-member.write.d.ts +1 -1
- package/dist/tenant/handlers/toggle-enabled.write.d.ts +2 -2
- package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
- package/dist/tenant/handlers/update.write.d.ts +1 -1
- package/dist/tenant-handover/handlers/claim.write.d.ts +1 -1
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +1 -1
- package/dist/tenant-lifecycle/handlers/request-destruction.write.d.ts +1 -1
- package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +1 -1
- package/dist/user/handlers/create.write.d.ts +1 -1
- package/dist/user/handlers/update.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/lift-restriction.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-export.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/restrict-account.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/run-forget-cleanup.write.d.ts +1 -1
- package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +2 -1
- package/dist/user-profile/handlers/change-email.write.d.ts +1 -1
- package/dist/waitlist/handlers/export-purge.write.d.ts +2 -2
- package/dist/waitlist/handlers/invite.write.d.ts +1 -1
- package/dist/waitlist/handlers/reject.write.d.ts +1 -1
- package/dist/waitlist/handlers/submit.write.d.ts +1 -1
- package/package.json +33 -17
- package/src/account-security/changes.json +6 -0
- package/src/auth-email-password/changes.json +6 -0
- package/src/billing-foundation/changes.json +30 -0
- package/src/cap-overview/changes.json +6 -0
- package/src/files/changes.json +18 -0
- package/src/inbound-mail-foundation/changes.json +143 -0
- package/src/inbound-provider-imap/changes.json +30 -0
- package/src/jobs/changes.json +18 -0
- package/src/subscription-paddle/changes.json +15 -0
- package/src/template-resolver/changes.json +12 -0
- package/dist/subscription-dodo/client.d.ts +0 -17
- package/dist/subscription-dodo/client.js +0 -31
- package/dist/subscription-dodo/constants.d.ts +0 -12
- package/dist/subscription-dodo/constants.js +0 -19
- package/dist/subscription-dodo/feature.d.ts +0 -37
- package/dist/subscription-dodo/feature.js +0 -144
- package/dist/subscription-dodo/index.d.ts +0 -2
- package/dist/subscription-dodo/index.js +0 -4
- package/dist/subscription-dodo/plugin-methods.d.ts +0 -23
- package/dist/subscription-dodo/plugin-methods.js +0 -260
- package/dist/subscription-dodo/runtime.d.ts +0 -27
- package/dist/subscription-dodo/runtime.js +0 -89
- package/dist/subscription-dodo/verify-webhook.d.ts +0 -37
- package/dist/subscription-dodo/verify-webhook.js +0 -189
- package/src/subscription-dodo/changes.json +0 -14
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// Paddle in-app plan switch (preview + apply) and retrieveSubscription.
|
|
2
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
import { paddleSubscriptionPath } from "./checkout-methods.js";
|
|
5
|
+
import { PaddleHttpError } from "./client.js";
|
|
6
|
+
import { paddleAmountSchema } from "./price-methods.js";
|
|
7
|
+
import { mapPaddleSubscriptionState, paddleSubscriptionSchema } from "./subscription-state.js";
|
|
8
|
+
import { signTenantBinding } from "./tenant-binding.js";
|
|
9
|
+
const NOT_FOUND = 404;
|
|
10
|
+
function parseAmount(amount) {
|
|
11
|
+
return Number.parseInt(amount, 10);
|
|
12
|
+
}
|
|
13
|
+
const previewResponseSchema = z.object({
|
|
14
|
+
data: z.object({
|
|
15
|
+
currency_code: z.string(),
|
|
16
|
+
immediate_transaction: z
|
|
17
|
+
.object({
|
|
18
|
+
details: z.object({
|
|
19
|
+
totals: z.object({
|
|
20
|
+
grand_total: paddleAmountSchema,
|
|
21
|
+
tax: paddleAmountSchema,
|
|
22
|
+
currency_code: z.string().nullish(),
|
|
23
|
+
}),
|
|
24
|
+
}),
|
|
25
|
+
})
|
|
26
|
+
.nullish(),
|
|
27
|
+
recurring_transaction_details: z
|
|
28
|
+
.object({
|
|
29
|
+
// `total` is the full period price incl. tax; `grand_total` is already
|
|
30
|
+
// reduced by credit, so it would understate the recurring price.
|
|
31
|
+
totals: z.object({
|
|
32
|
+
total: paddleAmountSchema.nullish(),
|
|
33
|
+
tax: paddleAmountSchema.nullish(),
|
|
34
|
+
}),
|
|
35
|
+
})
|
|
36
|
+
.nullish(),
|
|
37
|
+
update_summary: z.object({ credit: z.object({ amount: paddleAmountSchema }) }).nullish(),
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
function changePlanBody(targetPriceId) {
|
|
41
|
+
return {
|
|
42
|
+
items: [{ price_id: targetPriceId, quantity: 1 }],
|
|
43
|
+
proration_billing_mode: "prorated_immediately",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function createPaddlePreviewPlanSwitch(runtime, now) {
|
|
47
|
+
return async (ctx, options) => {
|
|
48
|
+
const { data } = previewResponseSchema.parse(await runtime.call(ctx, {
|
|
49
|
+
method: "PATCH",
|
|
50
|
+
path: `${paddleSubscriptionPath(options.providerSubscriptionId)}/preview`,
|
|
51
|
+
body: changePlanBody(options.targetPriceId),
|
|
52
|
+
}));
|
|
53
|
+
const totals = data.immediate_transaction?.details.totals;
|
|
54
|
+
const recurring = data.recurring_transaction_details?.totals;
|
|
55
|
+
return {
|
|
56
|
+
amountDueNow: totals ? parseAmount(totals.grand_total) : 0,
|
|
57
|
+
tax: totals ? parseAmount(totals.tax) : null,
|
|
58
|
+
currency: (totals?.currency_code ?? data.currency_code).toLowerCase(),
|
|
59
|
+
credit: data.update_summary ? parseAmount(data.update_summary.credit.amount) : 0,
|
|
60
|
+
...(recurring?.total != null && {
|
|
61
|
+
recurringAmount: parseAmount(recurring.total),
|
|
62
|
+
recurringTax: recurring.tax != null ? parseAmount(recurring.tax) : null,
|
|
63
|
+
}),
|
|
64
|
+
effectiveAt: Temporal.Instant.fromEpochMilliseconds(now()).toString(),
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// The switch itself arrives later as a subscription.updated webhook;
|
|
69
|
+
// prevent_change keeps the old plan if the immediate charge fails. PATCH
|
|
70
|
+
// replaces custom_data as a whole, so the tenant binding is sent again.
|
|
71
|
+
export function createPaddleApplyPlanSwitch(runtime) {
|
|
72
|
+
return async (ctx, options) => {
|
|
73
|
+
const bindingKey = await runtime.tenantBindingKey(ctx);
|
|
74
|
+
await runtime.call(ctx, {
|
|
75
|
+
method: "PATCH",
|
|
76
|
+
path: paddleSubscriptionPath(options.providerSubscriptionId),
|
|
77
|
+
body: {
|
|
78
|
+
...changePlanBody(options.targetPriceId),
|
|
79
|
+
on_payment_failure: "prevent_change",
|
|
80
|
+
custom_data: {
|
|
81
|
+
tenantId: options.tenantId,
|
|
82
|
+
tenantBinding: signTenantBinding(bindingKey, options.tenantId),
|
|
83
|
+
...(options.consentId && { consentId: options.consentId }),
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const subscriptionResponseSchema = z.object({ data: paddleSubscriptionSchema });
|
|
90
|
+
export function createPaddleRetrieveSubscription(runtime, options) {
|
|
91
|
+
return async (ctx, providerSubscriptionId) => {
|
|
92
|
+
let raw;
|
|
93
|
+
try {
|
|
94
|
+
raw = await runtime.call(ctx, {
|
|
95
|
+
method: "GET",
|
|
96
|
+
path: paddleSubscriptionPath(providerSubscriptionId),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (error instanceof PaddleHttpError && error.status === NOT_FOUND)
|
|
101
|
+
return null;
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
const { data: sub } = subscriptionResponseSchema.parse(raw);
|
|
105
|
+
const state = mapPaddleSubscriptionState(sub, options.priceToTier);
|
|
106
|
+
if (!state)
|
|
107
|
+
return null;
|
|
108
|
+
return {
|
|
109
|
+
providerCustomerId: sub.customer_id,
|
|
110
|
+
providerSubscriptionId: sub.id,
|
|
111
|
+
...state,
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ProviderPrice } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import type { PaddleCtxRuntime, PaddlePluginMethod } from "./runtime.ts";
|
|
4
|
+
/** Paddle sends amounts as integer strings in the smallest currency unit. */
|
|
5
|
+
export declare const paddleAmountSchema: z.ZodString;
|
|
6
|
+
export type PaddlePriceCache = {
|
|
7
|
+
readonly get: (priceId: string) => ProviderPrice | undefined;
|
|
8
|
+
readonly set: (priceId: string, price: ProviderPrice) => void;
|
|
9
|
+
};
|
|
10
|
+
export type PaddlePriceCacheOptions = {
|
|
11
|
+
readonly ttlMs?: number;
|
|
12
|
+
readonly now?: () => number;
|
|
13
|
+
};
|
|
14
|
+
export declare function createPaddlePriceCache({ ttlMs, now, }?: PaddlePriceCacheOptions): PaddlePriceCache;
|
|
15
|
+
export declare function createPaddleRetrievePrices(runtime: PaddleCtxRuntime, cache: PaddlePriceCache): PaddlePluginMethod<"retrievePrices">;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Paddle price lookup for the billing-plans catalog: one batched request for all
|
|
2
|
+
// cache misses, cached per price id.
|
|
3
|
+
import { KNOWN_RECURRING_INTERVALS, } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
4
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
5
|
+
import * as z from "zod";
|
|
6
|
+
const PRICES_PER_REQUEST = 200;
|
|
7
|
+
/** Paddle sends amounts as integer strings in the smallest currency unit. */
|
|
8
|
+
export const paddleAmountSchema = z.string().regex(/^-?\d+$/);
|
|
9
|
+
const customDataSchema = z.record(z.string(), z.unknown()).nullish();
|
|
10
|
+
const paddlePriceSchema = z.object({
|
|
11
|
+
id: z.string().min(1),
|
|
12
|
+
unit_price: z.object({ amount: paddleAmountSchema, currency_code: z.string() }),
|
|
13
|
+
billing_cycle: z.object({ interval: z.string(), frequency: z.number() }).nullable(),
|
|
14
|
+
status: z.string(),
|
|
15
|
+
custom_data: customDataSchema,
|
|
16
|
+
product: z.object({ custom_data: customDataSchema }).nullish(),
|
|
17
|
+
});
|
|
18
|
+
const pricesResponseSchema = z.object({ data: z.array(paddlePriceSchema) });
|
|
19
|
+
function stringEntries(customData) {
|
|
20
|
+
return Object.entries(customData ?? {}).flatMap(([key, value]) => {
|
|
21
|
+
if (typeof value === "string")
|
|
22
|
+
return [[key, value]];
|
|
23
|
+
if (typeof value === "number")
|
|
24
|
+
return [[key, String(value)]];
|
|
25
|
+
return [];
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
// Price custom data wins over the product's.
|
|
29
|
+
function metadataOf(price) {
|
|
30
|
+
return Object.fromEntries([
|
|
31
|
+
...stringEntries(price.product?.custom_data),
|
|
32
|
+
...stringEntries(price.custom_data),
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
function mapPaddlePrice(price) {
|
|
36
|
+
const base = {
|
|
37
|
+
priceId: price.id,
|
|
38
|
+
unitAmount: Number.parseInt(price.unit_price.amount, 10),
|
|
39
|
+
currency: price.unit_price.currency_code.toLowerCase(),
|
|
40
|
+
active: price.status === "active",
|
|
41
|
+
metadata: metadataOf(price),
|
|
42
|
+
};
|
|
43
|
+
const cycle = price.billing_cycle;
|
|
44
|
+
if (!cycle)
|
|
45
|
+
return { ...base, interval: null, intervalCount: 1 };
|
|
46
|
+
const interval = KNOWN_RECURRING_INTERVALS.find((known) => known === cycle.interval) ?? null;
|
|
47
|
+
return { ...base, interval, intervalCount: interval ? cycle.frequency : null };
|
|
48
|
+
}
|
|
49
|
+
// Keyed by price id alone: ids are unique per account and a cache hit must not
|
|
50
|
+
// trigger an audited secret read on every billing-plans render. Failed lookups
|
|
51
|
+
// are never cached.
|
|
52
|
+
export function createPaddlePriceCache({ ttlMs = 600_000, now = () => Temporal.Now.instant().epochMilliseconds, } = {}) {
|
|
53
|
+
const store = new Map();
|
|
54
|
+
return {
|
|
55
|
+
get: (priceId) => {
|
|
56
|
+
const entry = store.get(priceId);
|
|
57
|
+
if (!entry)
|
|
58
|
+
return undefined;
|
|
59
|
+
if (entry.expiresAt <= now()) {
|
|
60
|
+
store.delete(priceId);
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
return entry.price;
|
|
64
|
+
},
|
|
65
|
+
set: (priceId, price) => {
|
|
66
|
+
store.set(priceId, { price, expiresAt: now() + ttlMs });
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function chunked(items, size) {
|
|
71
|
+
const chunks = [];
|
|
72
|
+
for (let start = 0; start < items.length; start += size) {
|
|
73
|
+
chunks.push(items.slice(start, start + size));
|
|
74
|
+
}
|
|
75
|
+
return chunks;
|
|
76
|
+
}
|
|
77
|
+
async function fillPriceCache(runtime, cache, ctx, priceIds) {
|
|
78
|
+
const misses = [...new Set(priceIds.filter((priceId) => cache.get(priceId) === undefined))];
|
|
79
|
+
const settled = await Promise.allSettled(chunked(misses, PRICES_PER_REQUEST).map(async (ids) => pricesResponseSchema.parse(await runtime.call(ctx, {
|
|
80
|
+
method: "GET",
|
|
81
|
+
path: "/prices",
|
|
82
|
+
query: {
|
|
83
|
+
id: ids.join(","),
|
|
84
|
+
include: "product",
|
|
85
|
+
status: "active,archived",
|
|
86
|
+
per_page: String(PRICES_PER_REQUEST),
|
|
87
|
+
},
|
|
88
|
+
}))));
|
|
89
|
+
for (const outcome of settled) {
|
|
90
|
+
if (outcome.status === "fulfilled") {
|
|
91
|
+
for (const price of outcome.value.data)
|
|
92
|
+
cache.set(price.id, mapPaddlePrice(price));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const message = outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason);
|
|
96
|
+
ctx.obs.log?.warn("subscription-paddle: price lookup failed", { message });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function createPaddleRetrievePrices(runtime, cache) {
|
|
101
|
+
return async (ctx, priceIds) => {
|
|
102
|
+
await fillPriceCache(runtime, cache, ctx, priceIds);
|
|
103
|
+
return priceIds.flatMap((priceId) => cache.get(priceId) ?? []);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { SubscriptionProviderPlugin } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
2
|
+
import { type ConfigKeyHandle, type HandlerContext } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
|
|
4
|
+
import { type PaddleRequest, type PaddleSend } from "./client.ts";
|
|
5
|
+
import { type PaddleEnvironment } from "./constants.ts";
|
|
6
|
+
export type PaddlePluginMethod<TName extends keyof SubscriptionProviderPlugin> = NonNullable<SubscriptionProviderPlugin[TName]>;
|
|
7
|
+
export type PaddleFallbacks = {
|
|
8
|
+
readonly apiKey?: string;
|
|
9
|
+
readonly webhookSecret?: string;
|
|
10
|
+
readonly tenantBindingKey?: string;
|
|
11
|
+
readonly clientToken?: string;
|
|
12
|
+
};
|
|
13
|
+
export type PaddleRuntimeDeps = {
|
|
14
|
+
readonly apiKeyHandle: ConfigKeyHandle<"text">;
|
|
15
|
+
readonly webhookSecretHandle: ConfigKeyHandle<"text">;
|
|
16
|
+
readonly tenantBindingKeyHandle: ConfigKeyHandle<"text">;
|
|
17
|
+
readonly clientTokenHandle: ConfigKeyHandle<"text">;
|
|
18
|
+
readonly billingLiveHandle: ConfigKeyHandle<"boolean">;
|
|
19
|
+
readonly environmentHandle: ConfigKeyHandle<"select">;
|
|
20
|
+
readonly send: PaddleSend;
|
|
21
|
+
readonly fallback: PaddleFallbacks;
|
|
22
|
+
};
|
|
23
|
+
export type PaddleCtxRuntime = {
|
|
24
|
+
readonly call: (ctx: HandlerContext, request: PaddleRequest) => Promise<unknown>;
|
|
25
|
+
readonly assertBillingLive: (ctx: HandlerContext, handlerName?: string) => Promise<void>;
|
|
26
|
+
readonly isBillingEnabled: (ctx: HandlerContext) => Promise<boolean>;
|
|
27
|
+
readonly tenantBindingKey: (ctx: HandlerContext) => Promise<string>;
|
|
28
|
+
};
|
|
29
|
+
export type PaddleWebhookRuntime = {
|
|
30
|
+
readonly resolveWebhookSecret: (systemSecrets?: SecretsContext) => Promise<string>;
|
|
31
|
+
readonly resolveTenantBindingKey: (systemSecrets?: SecretsContext) => Promise<string>;
|
|
32
|
+
};
|
|
33
|
+
export type PaddleCheckoutConfig = {
|
|
34
|
+
readonly clientToken: string | null;
|
|
35
|
+
readonly environment: PaddleEnvironment;
|
|
36
|
+
};
|
|
37
|
+
export type PaddleRuntimes = {
|
|
38
|
+
readonly ctx: PaddleCtxRuntime;
|
|
39
|
+
readonly webhook: PaddleWebhookRuntime;
|
|
40
|
+
readonly readCheckoutConfig: (ctx: Pick<HandlerContext, "config">) => Promise<PaddleCheckoutConfig>;
|
|
41
|
+
};
|
|
42
|
+
export declare function createPaddleRuntimes(deps: PaddleRuntimeDeps): PaddleRuntimes;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Call-time key/flag resolution for the subscription-paddle plugin, mirroring
|
|
2
|
+
// subscription-stripe/runtime.ts: credentials come from backing:"secrets" config
|
|
3
|
+
// 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 secrets 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 { createPaddleHttp } from "./client.js";
|
|
14
|
+
import { PADDLE_BASE_URLS, PaddleEnvironments, SUBSCRIPTION_PADDLE_FEATURE, } from "./constants.js";
|
|
15
|
+
// backing:"secrets" values are stored JSON-serialized, so the store read has to
|
|
16
|
+
// be parsed back; the env fallback is a plain string and stays raw. The error
|
|
17
|
+
// must not carry the input: the JSON parser's own message would repeat the
|
|
18
|
+
// credential into logs and, for webhooks, into the 401 body.
|
|
19
|
+
function parseStoredSecret(raw) {
|
|
20
|
+
if (raw === undefined || raw.length === 0)
|
|
21
|
+
return undefined;
|
|
22
|
+
const parsed = parseJsonSafe(raw, undefined);
|
|
23
|
+
if (parsed === undefined) {
|
|
24
|
+
throw new Error("subscription-paddle credential (backing:secrets) is not valid JSON");
|
|
25
|
+
}
|
|
26
|
+
return typeof parsed === "string" && parsed.length > 0 ? parsed : undefined;
|
|
27
|
+
}
|
|
28
|
+
function hintFor(shortName) {
|
|
29
|
+
return `Set it via config:write:set on \`${SUBSCRIPTION_PADDLE_FEATURE}:config:${shortName}\` (or seed it from the matching PADDLE_* variable during the env bridge).`;
|
|
30
|
+
}
|
|
31
|
+
function environmentOf(value) {
|
|
32
|
+
return value === PaddleEnvironments.live ? PaddleEnvironments.live : PaddleEnvironments.sandbox;
|
|
33
|
+
}
|
|
34
|
+
export function createPaddleRuntimes(deps) {
|
|
35
|
+
const http = createPaddleHttp(deps.send);
|
|
36
|
+
async function ctxSecret(ctx, handle, fallback, shortName) {
|
|
37
|
+
let value;
|
|
38
|
+
if (ctx.services.secrets) {
|
|
39
|
+
const got = await requireSecretsContext({ secrets: ctx.services.secrets, user: ctx.user }, SUBSCRIPTION_PADDLE_FEATURE).get(SYSTEM_TENANT_ID, handle.name);
|
|
40
|
+
value = parseStoredSecret(got?.reveal());
|
|
41
|
+
}
|
|
42
|
+
if (!value && fallback && fallback.length > 0)
|
|
43
|
+
value = fallback;
|
|
44
|
+
if (!value) {
|
|
45
|
+
throw new UnconfiguredError({
|
|
46
|
+
feature: SUBSCRIPTION_PADDLE_FEATURE,
|
|
47
|
+
key: handle.name,
|
|
48
|
+
hint: hintFor(shortName),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
async function webhookSecret(systemSecrets, handle, fallback) {
|
|
54
|
+
if (systemSecrets) {
|
|
55
|
+
// No auditCtx: every webhook would otherwise spam the audit trail.
|
|
56
|
+
const got = await systemSecrets.get(SYSTEM_TENANT_ID, handle.name);
|
|
57
|
+
const stored = parseStoredSecret(got?.reveal());
|
|
58
|
+
if (stored)
|
|
59
|
+
return stored;
|
|
60
|
+
}
|
|
61
|
+
if (fallback && fallback.length > 0)
|
|
62
|
+
return fallback;
|
|
63
|
+
throw new Error(`subscription-paddle: '${handle.name}' unresolved — no system-secret set and no factory fallback. Webhook cannot verify until it is configured.`);
|
|
64
|
+
}
|
|
65
|
+
async function hasSecret(ctx, handle, fallback) {
|
|
66
|
+
if (fallback !== undefined && fallback.length > 0)
|
|
67
|
+
return true;
|
|
68
|
+
// Existence-only probe on every billing-plans render: must stay un-audited.
|
|
69
|
+
return ctx.services.secrets
|
|
70
|
+
? await ctx.services.secrets.has(SYSTEM_TENANT_ID, handle.name)
|
|
71
|
+
: false;
|
|
72
|
+
}
|
|
73
|
+
async function baseUrlFor(ctx) {
|
|
74
|
+
const environment = ctx.config ? await ctx.config(deps.environmentHandle) : undefined;
|
|
75
|
+
return PADDLE_BASE_URLS[environmentOf(environment)];
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
ctx: {
|
|
79
|
+
call: async (ctx, request) => http({
|
|
80
|
+
baseUrl: await baseUrlFor(ctx),
|
|
81
|
+
apiKey: await ctxSecret(ctx, deps.apiKeyHandle, deps.fallback.apiKey, "api-key"),
|
|
82
|
+
}, request),
|
|
83
|
+
assertBillingLive: async (ctx, handlerName = "create-checkout-session") => {
|
|
84
|
+
const live = ctx.config ? await ctx.config(deps.billingLiveHandle) : undefined;
|
|
85
|
+
if (live !== true) {
|
|
86
|
+
throw new FeatureDisabledError(SUBSCRIPTION_PADDLE_FEATURE, handlerName);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
isBillingEnabled: async (ctx) => {
|
|
90
|
+
const live = ctx.config ? await ctx.config(deps.billingLiveHandle) : undefined;
|
|
91
|
+
if (live !== true)
|
|
92
|
+
return false;
|
|
93
|
+
return ((await hasSecret(ctx, deps.apiKeyHandle, deps.fallback.apiKey)) &&
|
|
94
|
+
(await hasSecret(ctx, deps.tenantBindingKeyHandle, deps.fallback.tenantBindingKey)));
|
|
95
|
+
},
|
|
96
|
+
tenantBindingKey: (ctx) => ctxSecret(ctx, deps.tenantBindingKeyHandle, deps.fallback.tenantBindingKey, "tenant-binding-key"),
|
|
97
|
+
},
|
|
98
|
+
webhook: {
|
|
99
|
+
resolveWebhookSecret: (systemSecrets) => webhookSecret(systemSecrets, deps.webhookSecretHandle, deps.fallback.webhookSecret),
|
|
100
|
+
resolveTenantBindingKey: (systemSecrets) => webhookSecret(systemSecrets, deps.tenantBindingKeyHandle, deps.fallback.tenantBindingKey),
|
|
101
|
+
},
|
|
102
|
+
readCheckoutConfig: async (ctx) => {
|
|
103
|
+
const stored = ctx.config ? await ctx.config(deps.clientTokenHandle) : undefined;
|
|
104
|
+
const environment = ctx.config ? await ctx.config(deps.environmentHandle) : undefined;
|
|
105
|
+
const clientToken = stored || deps.fallback.clientToken || null;
|
|
106
|
+
return { clientToken, environment: environmentOf(environment) };
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type SubscriptionStatus } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
export declare const paddleCustomDataSchema: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
4
|
+
export declare const paddleSubscriptionSchema: z.ZodObject<{
|
|
5
|
+
id: z.ZodString;
|
|
6
|
+
status: z.ZodString;
|
|
7
|
+
customer_id: z.ZodString;
|
|
8
|
+
items: z.ZodArray<z.ZodObject<{
|
|
9
|
+
price: z.ZodObject<{
|
|
10
|
+
id: z.ZodString;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
quantity: z.ZodOptional<z.ZodNumber>;
|
|
13
|
+
}, z.core.$strip>>;
|
|
14
|
+
custom_data: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
15
|
+
current_billing_period: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
16
|
+
ends_at: z.ZodISODateTime;
|
|
17
|
+
}, z.core.$strip>>>;
|
|
18
|
+
next_billed_at: z.ZodOptional<z.ZodNullable<z.ZodISODateTime>>;
|
|
19
|
+
scheduled_change: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
20
|
+
action: z.ZodString;
|
|
21
|
+
effective_at: z.ZodISODateTime;
|
|
22
|
+
}, z.core.$strip>>>;
|
|
23
|
+
canceled_at: z.ZodOptional<z.ZodNullable<z.ZodISODateTime>>;
|
|
24
|
+
}, z.core.$strip>;
|
|
25
|
+
export type PaddleSubscription = z.infer<typeof paddleSubscriptionSchema>;
|
|
26
|
+
export type MappedPaddleSubscriptionState = {
|
|
27
|
+
readonly status: SubscriptionStatus;
|
|
28
|
+
readonly tier: string;
|
|
29
|
+
readonly currentPeriodEnd: string;
|
|
30
|
+
readonly cancelAt: string | null;
|
|
31
|
+
};
|
|
32
|
+
/** Null when the status is unknown, no item's price is in `priceToTier`
|
|
33
|
+
* (SubscriptionEvent.tier is required) or no period end can be derived. */
|
|
34
|
+
export declare function mapPaddleSubscriptionState(sub: PaddleSubscription, priceToTier: Readonly<Record<string, string>>): MappedPaddleSubscriptionState | null;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Paddle's subscription entity and its mapping onto the foundation's state.
|
|
2
|
+
// Shared by the webhook and retrieveSubscription so both map identically.
|
|
3
|
+
import { SubscriptionStatuses, } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
4
|
+
import * as z from "zod";
|
|
5
|
+
const PADDLE_SUBSCRIPTION_STATUSES = {
|
|
6
|
+
active: SubscriptionStatuses.active,
|
|
7
|
+
trialing: SubscriptionStatuses.trialing,
|
|
8
|
+
past_due: SubscriptionStatuses.pastDue,
|
|
9
|
+
paused: SubscriptionStatuses.pastDue,
|
|
10
|
+
canceled: SubscriptionStatuses.canceled,
|
|
11
|
+
};
|
|
12
|
+
const SCHEDULED_CANCEL_ACTION = "cancel";
|
|
13
|
+
const instantSchema = z.iso.datetime({ offset: true });
|
|
14
|
+
export const paddleCustomDataSchema = z.record(z.string(), z.unknown()).nullish();
|
|
15
|
+
export const paddleSubscriptionSchema = z.object({
|
|
16
|
+
id: z.string().min(1),
|
|
17
|
+
status: z.string(),
|
|
18
|
+
customer_id: z.string().min(1),
|
|
19
|
+
items: z.array(z.object({
|
|
20
|
+
price: z.object({ id: z.string().min(1) }),
|
|
21
|
+
quantity: z.number().int().positive().optional(),
|
|
22
|
+
})),
|
|
23
|
+
custom_data: paddleCustomDataSchema,
|
|
24
|
+
current_billing_period: z.object({ ends_at: instantSchema }).nullish(),
|
|
25
|
+
next_billed_at: instantSchema.nullish(),
|
|
26
|
+
scheduled_change: z.object({ action: z.string(), effective_at: instantSchema }).nullish(),
|
|
27
|
+
canceled_at: instantSchema.nullish(),
|
|
28
|
+
});
|
|
29
|
+
/** Null when the status is unknown, no item's price is in `priceToTier`
|
|
30
|
+
* (SubscriptionEvent.tier is required) or no period end can be derived. */
|
|
31
|
+
export function mapPaddleSubscriptionState(sub, priceToTier) {
|
|
32
|
+
const status = Object.hasOwn(PADDLE_SUBSCRIPTION_STATUSES, sub.status)
|
|
33
|
+
? PADDLE_SUBSCRIPTION_STATUSES[sub.status]
|
|
34
|
+
: undefined;
|
|
35
|
+
const tier = sub.items
|
|
36
|
+
.map((item) => Object.hasOwn(priceToTier, item.price.id) ? priceToTier[item.price.id] : undefined)
|
|
37
|
+
.find((name) => name);
|
|
38
|
+
const currentPeriodEnd = sub.current_billing_period?.ends_at ?? sub.next_billed_at ?? sub.canceled_at;
|
|
39
|
+
if (!status || !tier || !currentPeriodEnd)
|
|
40
|
+
return null;
|
|
41
|
+
const scheduled = sub.scheduled_change;
|
|
42
|
+
return {
|
|
43
|
+
status,
|
|
44
|
+
tier,
|
|
45
|
+
currentPeriodEnd,
|
|
46
|
+
cancelAt: scheduled?.action === SCHEDULED_CANCEL_ACTION ? scheduled.effective_at : null,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function signTenantBinding(key: string, tenantId: string): string;
|
|
2
|
+
export declare function isValidTenantBinding(key: string, tenantId: string, binding: string): boolean;
|
|
3
|
+
export type BoundTenant = {
|
|
4
|
+
readonly tenantId: string;
|
|
5
|
+
readonly consentId?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Null unless `customData` names a tenant together with a binding that `key` signed. */
|
|
8
|
+
export declare function boundTenantOf(customData: Readonly<Record<string, unknown>> | null | undefined, key: string): BoundTenant | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Paddle.js runs with a public client token and can open checkouts with any
|
|
2
|
+
// `customData` from the browser, so `custom_data.tenantId` alone proves nothing.
|
|
3
|
+
// The binding is an HMAC only the server can produce: webhooks whose tenantId
|
|
4
|
+
// does not carry a valid binding are ignored.
|
|
5
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
6
|
+
import { parseProviderConsentId } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
7
|
+
const BINDING_CONTEXT = "kumiko-paddle-tenant:v1:";
|
|
8
|
+
export function signTenantBinding(key, tenantId) {
|
|
9
|
+
return createHmac("sha256", key).update(`${BINDING_CONTEXT}${tenantId}`).digest("base64url");
|
|
10
|
+
}
|
|
11
|
+
export function isValidTenantBinding(key, tenantId, binding) {
|
|
12
|
+
const expected = Buffer.from(signTenantBinding(key, tenantId));
|
|
13
|
+
const candidate = Buffer.from(binding);
|
|
14
|
+
return candidate.length === expected.length && timingSafeEqual(candidate, expected);
|
|
15
|
+
}
|
|
16
|
+
/** Null unless `customData` names a tenant together with a binding that `key` signed. */
|
|
17
|
+
export function boundTenantOf(customData, key) {
|
|
18
|
+
const tenantId = customData?.["tenantId"];
|
|
19
|
+
const binding = customData?.["tenantBinding"];
|
|
20
|
+
if (typeof tenantId !== "string" || tenantId.length === 0)
|
|
21
|
+
return null;
|
|
22
|
+
if (typeof binding !== "string" || !isValidTenantBinding(key, tenantId, binding))
|
|
23
|
+
return null;
|
|
24
|
+
const consentId = parseProviderConsentId(customData?.["consentId"]);
|
|
25
|
+
return { tenantId, ...(consentId !== undefined && { consentId }) };
|
|
26
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const PADDLE_WEBHOOK_TOLERANCE_SECONDS = 300;
|
|
2
|
+
/** Throws unless one `h1` signature over `rawBody` is valid and the timestamp is
|
|
3
|
+
* within the tolerance in both directions. Header names must be lower-case. */
|
|
4
|
+
export declare function verifyPaddleSignature(rawBody: string, headers: Readonly<Record<string, string>>, secret: string, nowSeconds: number): void;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Paddle webhook signature check: `Paddle-Signature: ts=<unix>;h1=<hex>[;h1=<hex>]`,
|
|
2
|
+
// HMAC-SHA256 over `${ts}:${rawBody}` keyed with the raw secret string.
|
|
3
|
+
//
|
|
4
|
+
// Verification always runs against the raw body string; re-serialized JSON
|
|
5
|
+
// would change whitespace and break the HMAC. It happens before any parsing,
|
|
6
|
+
// so no path bypasses it.
|
|
7
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
8
|
+
// Paddle's own SDK accepts 5 s. 300 s matches Stripe here: the foundation
|
|
9
|
+
// de-duplicates by event_id and pod clocks drift.
|
|
10
|
+
export const PADDLE_WEBHOOK_TOLERANCE_SECONDS = 300;
|
|
11
|
+
const SIGNATURE_HEADER = "paddle-signature";
|
|
12
|
+
const TIMESTAMP_PATTERN = /^\d+$/;
|
|
13
|
+
const SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/i;
|
|
14
|
+
function parseSignatureHeader(header) {
|
|
15
|
+
let timestamp;
|
|
16
|
+
const signatures = [];
|
|
17
|
+
for (const part of header.split(";")) {
|
|
18
|
+
const separator = part.indexOf("=");
|
|
19
|
+
if (separator < 0)
|
|
20
|
+
continue;
|
|
21
|
+
const name = part.slice(0, separator).trim();
|
|
22
|
+
const value = part.slice(separator + 1).trim();
|
|
23
|
+
if (name === "ts")
|
|
24
|
+
timestamp = value;
|
|
25
|
+
else if (name === "h1")
|
|
26
|
+
signatures.push(value);
|
|
27
|
+
}
|
|
28
|
+
return timestamp === undefined ? null : { timestamp, signatures };
|
|
29
|
+
}
|
|
30
|
+
/** Throws unless one `h1` signature over `rawBody` is valid and the timestamp is
|
|
31
|
+
* within the tolerance in both directions. Header names must be lower-case. */
|
|
32
|
+
export function verifyPaddleSignature(rawBody, headers, secret, nowSeconds) {
|
|
33
|
+
const header = headers[SIGNATURE_HEADER];
|
|
34
|
+
if (!header)
|
|
35
|
+
throw new Error("subscription-paddle: paddle-signature header missing");
|
|
36
|
+
const parsed = parseSignatureHeader(header);
|
|
37
|
+
if (!parsed || parsed.signatures.length === 0) {
|
|
38
|
+
throw new Error("subscription-paddle: paddle-signature header is malformed");
|
|
39
|
+
}
|
|
40
|
+
if (!TIMESTAMP_PATTERN.test(parsed.timestamp)) {
|
|
41
|
+
throw new Error("subscription-paddle: webhook ts is not a Unix timestamp in seconds");
|
|
42
|
+
}
|
|
43
|
+
if (Math.abs(nowSeconds - Number(parsed.timestamp)) > PADDLE_WEBHOOK_TOLERANCE_SECONDS) {
|
|
44
|
+
throw new Error("subscription-paddle: webhook timestamp outside the tolerance window");
|
|
45
|
+
}
|
|
46
|
+
const expected = createHmac("sha256", secret).update(`${parsed.timestamp}:${rawBody}`).digest();
|
|
47
|
+
const matches = parsed.signatures.some((signature) => {
|
|
48
|
+
if (!SHA256_HEX_PATTERN.test(signature))
|
|
49
|
+
return false;
|
|
50
|
+
const candidate = Buffer.from(signature, "hex");
|
|
51
|
+
return candidate.length === expected.length && timingSafeEqual(candidate, expected);
|
|
52
|
+
});
|
|
53
|
+
if (!matches)
|
|
54
|
+
throw new Error("subscription-paddle: webhook signature verify failed");
|
|
55
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type PaymentEvent, type SubscriptionEvent } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
2
|
+
import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
|
|
3
|
+
import type { PaddleWebhookRuntime } from "./runtime.ts";
|
|
4
|
+
export type PaddleWebhookOptions = {
|
|
5
|
+
readonly priceToTier: Readonly<Record<string, string>>;
|
|
6
|
+
/** Test seam: Unix seconds. Defaults to the wall clock. */
|
|
7
|
+
readonly nowSeconds?: () => number;
|
|
8
|
+
};
|
|
9
|
+
export declare function verifyAndParsePaddleWebhook(runtime: PaddleWebhookRuntime, options: PaddleWebhookOptions): (rawBody: string, headers: Record<string, string>, systemSecrets?: SecretsContext) => Promise<SubscriptionEvent | PaymentEvent | null>;
|