@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.
- package/LICENSE +21 -57
- package/dist/agent-tools/tool-catalog.js +1 -1
- package/dist/agent-tools/tool-dispatch.d.ts +7 -7
- package/dist/agent-tools/tool-dispatch.js +9 -9
- package/dist/audit/handlers/audit-events.js +1 -1
- package/dist/audit/screens.js +1 -1
- package/dist/auth-email-password/feature.js +1 -1
- package/dist/auth-email-password/handlers/change-password.write.js +3 -3
- package/dist/auth-email-password/handlers/confirm-token-flow.js +5 -5
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +1 -1
- package/dist/auth-email-password/handlers/invite-accept.write.js +18 -18
- package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
- package/dist/auth-email-password/handlers/invite-create.write.js +10 -10
- package/dist/auth-email-password/handlers/invite-info.query.js +1 -1
- package/dist/auth-email-password/handlers/invite-signup-complete.write.js +24 -26
- package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/login.write.js +1 -1
- package/dist/auth-email-password/handlers/self-registration-status.query.js +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.js +34 -37
- package/dist/auth-email-password/handlers/signup-request.write.js +6 -7
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +1 -4
- package/dist/auth-email-password/index.js +5 -5
- package/dist/auth-email-password/lockout-store.js +1 -1
- package/dist/auth-email-password/seeding.d.ts +42 -45
- package/dist/auth-email-password/seeding.js +51 -53
- package/dist/auth-email-password/signup-token-store.js +8 -8
- package/dist/auth-email-password/testing.js +3 -3
- package/dist/auth-email-password/web/auth-client.d.ts +2 -2
- package/dist/auth-email-password/web/auth-client.js +36 -37
- package/dist/auth-email-password/web/auth-form-primitives.js +10 -10
- package/dist/auth-email-password/web/auth-gate.js +15 -16
- package/dist/auth-email-password/web/client-plugin.d.ts +7 -7
- package/dist/auth-email-password/web/client-plugin.js +4 -5
- package/dist/auth-email-password/web/confirm-account-unlock-screen.js +1 -1
- package/dist/auth-email-password/web/constants.js +3 -3
- package/dist/auth-email-password/web/default-topbar-actions.d.ts +8 -8
- package/dist/auth-email-password/web/default-topbar-actions.js +6 -6
- package/dist/auth-email-password/web/forgot-password-screen.d.ts +2 -2
- package/dist/auth-email-password/web/forgot-password-screen.js +9 -9
- package/dist/auth-email-password/web/index.js +5 -5
- package/dist/auth-email-password/web/invite-accept-screen.d.ts +1 -1
- package/dist/auth-email-password/web/invite-accept-screen.js +16 -16
- package/dist/auth-email-password/web/login-screen.d.ts +1 -1
- package/dist/auth-email-password/web/login-screen.js +2 -3
- package/dist/auth-email-password/web/reset-password-screen.d.ts +4 -4
- package/dist/auth-email-password/web/reset-password-screen.js +12 -13
- package/dist/auth-email-password/web/session.d.ts +10 -9
- package/dist/auth-email-password/web/session.js +28 -29
- package/dist/auth-email-password/web/signup-complete-screen.js +3 -3
- package/dist/auth-email-password/web/signup-screen.d.ts +1 -1
- package/dist/auth-email-password/web/signup-screen.js +13 -13
- package/dist/auth-email-password/web/tenant-switcher.d.ts +4 -4
- package/dist/auth-email-password/web/tenant-switcher.js +21 -22
- package/dist/auth-email-password/web/use-shell-user.js +12 -13
- package/dist/auth-email-password/web/user-menu.d.ts +4 -4
- package/dist/auth-email-password/web/user-menu.js +12 -13
- package/dist/auth-email-password/web/verify-email-screen.d.ts +2 -2
- package/dist/auth-email-password/web/verify-email-screen.js +9 -10
- package/dist/auth-foundation/boot-checks.js +4 -4
- package/dist/auth-foundation/resolve-anonymous-access.d.ts +1 -1
- package/dist/auth-foundation/resolve-anonymous-access.js +3 -3
- package/dist/auth-foundation/types.js +2 -2
- package/dist/auth-mfa/feature-exports.d.ts +2 -2
- package/dist/auth-mfa/feature.js +8 -0
- package/dist/auth-mfa/handlers/disable.write.js +3 -3
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +4 -4
- package/dist/auth-mfa/handlers/enable-confirm.write.js +2 -2
- package/dist/auth-mfa/handlers/enable-start-preauth.write.js +1 -1
- package/dist/auth-mfa/handlers/reencrypt.job.js +1 -1
- package/dist/auth-mfa/handlers/regenerate-recovery.write.js +5 -5
- package/dist/auth-mfa/handlers/verify.write.js +1 -1
- package/dist/auth-mfa/token-secrets.js +1 -1
- package/dist/auth-mfa/web/client-plugin.d.ts +1 -1
- package/dist/auth-mfa/web/client-plugin.js +1 -1
- package/dist/auth-mfa/web/i18n.js +8 -4
- package/dist/auth-mfa/web/index.js +4 -4
- package/dist/auth-mfa/web/mfa-client.js +17 -16
- package/dist/auth-mfa/web/mfa-code-or-recovery-field.d.ts +11 -0
- package/dist/auth-mfa/web/mfa-code-or-recovery-field.js +12 -0
- package/dist/auth-mfa/web/mfa-disable-dialog.js +14 -3
- package/dist/auth-mfa/web/mfa-regenerate-recovery-dialog.js +14 -3
- package/dist/auth-mfa/web/mfa-setup-preauth-screen.js +10 -5
- package/dist/auth-mfa/web/mfa-verify-screen.js +32 -11
- package/dist/billing-foundation/aggregate-id.d.ts +5 -5
- package/dist/billing-foundation/aggregate-id.js +10 -11
- package/dist/billing-foundation/checkout-core.d.ts +9 -14
- package/dist/billing-foundation/checkout-core.js +24 -39
- package/dist/billing-foundation/consumer-protection/confirmation-mail.js +8 -2
- package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +1 -0
- package/dist/billing-foundation/consumer-protection/consent-gate.js +1 -0
- package/dist/billing-foundation/consumer-protection/issue-confirmation.js +9 -3
- package/dist/billing-foundation/consumer-protection/mail-notifications.js +3 -1
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.d.ts +4 -2
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +6 -5
- package/dist/billing-foundation/consumer-protection/termination-record.d.ts +5 -1
- package/dist/billing-foundation/consumer-protection/termination-record.js +27 -17
- package/dist/billing-foundation/consumer-protection/termination-request.js +12 -7
- package/dist/billing-foundation/consumer-protection/termination-revoke.js +12 -7
- package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +1 -1
- package/dist/billing-foundation/consumer-protection/termination-texts.js +3 -0
- package/dist/billing-foundation/consumer-protection/withdrawal-period.d.ts +5 -2
- package/dist/billing-foundation/consumer-protection/withdrawal-period.js +8 -9
- package/dist/billing-foundation/entities.js +27 -27
- package/dist/billing-foundation/events.d.ts +1 -0
- package/dist/billing-foundation/events.js +19 -17
- package/dist/billing-foundation/feature.js +45 -44
- package/dist/billing-foundation/get-subscription-for-tenant.d.ts +3 -3
- package/dist/billing-foundation/get-subscription-for-tenant.js +3 -5
- package/dist/billing-foundation/handlers/billing-plans.query.js +35 -5
- package/dist/billing-foundation/handlers/create-checkout-session.write.js +19 -17
- package/dist/billing-foundation/handlers/create-portal-session.write.js +11 -12
- package/dist/billing-foundation/handlers/list-subscriptions.query.js +4 -4
- package/dist/billing-foundation/handlers/plan-switch-preview.query.d.ts +13 -0
- package/dist/billing-foundation/handlers/plan-switch-preview.query.js +48 -0
- package/dist/billing-foundation/handlers/process-event.write.d.ts +6 -0
- package/dist/billing-foundation/handlers/process-event.write.js +28 -22
- package/dist/billing-foundation/handlers/start-plan-checkout.write.js +32 -5
- package/dist/billing-foundation/handlers/switch-plan.write.d.ts +2 -2
- package/dist/billing-foundation/handlers/switch-plan.write.js +56 -64
- package/dist/billing-foundation/handlers/sync-subscription.write.js +12 -1
- package/dist/billing-foundation/i18n.js +22 -0
- package/dist/billing-foundation/index.d.ts +1 -1
- package/dist/billing-foundation/plan-catalog.d.ts +21 -5
- package/dist/billing-foundation/plan-catalog.js +25 -16
- package/dist/billing-foundation/plan-switch.d.ts +12 -0
- package/dist/billing-foundation/plan-switch.js +60 -0
- package/dist/billing-foundation/projection.d.ts +15 -11
- package/dist/billing-foundation/projection.js +38 -37
- package/dist/billing-foundation/subscription-tier-sync.js +3 -3
- package/dist/billing-foundation/tenant-destroy-hook.d.ts +2 -2
- package/dist/billing-foundation/tenant-destroy-hook.js +7 -7
- package/dist/billing-foundation/types.d.ts +97 -42
- package/dist/billing-foundation/types.js +29 -20
- package/dist/billing-foundation/validate-options.js +17 -0
- package/dist/billing-foundation/web/billing-plans-panel.js +82 -36
- package/dist/billing-foundation/web/checkout-consent-dialog.d.ts +17 -3
- package/dist/billing-foundation/web/checkout-consent-dialog.js +14 -10
- package/dist/billing-foundation/web/constants.d.ts +4 -0
- package/dist/billing-foundation/web/constants.js +4 -0
- package/dist/billing-foundation/web/plan-price.d.ts +15 -0
- package/dist/billing-foundation/web/plan-price.js +32 -0
- package/dist/billing-foundation/web/plan-switch-confirm-dialog.d.ts +15 -0
- package/dist/billing-foundation/web/plan-switch-confirm-dialog.js +66 -0
- package/dist/cap-counter/aggregate-id.js +8 -9
- package/dist/cap-counter/cap-reservation.js +5 -1
- package/dist/cap-counter/constants.js +11 -12
- package/dist/cap-counter/enforce-cap.d.ts +59 -62
- package/dist/cap-counter/enforce-cap.js +49 -49
- package/dist/cap-counter/entity.js +29 -29
- package/dist/cap-counter/feature.js +34 -35
- package/dist/cap-counter/handlers/increment-rolling.write.d.ts +2 -2
- package/dist/cap-counter/handlers/increment-rolling.write.js +31 -31
- package/dist/cap-counter/handlers/mark-soft-warned.write.js +4 -5
- package/dist/cap-counter/stock-cap-guard.js +8 -2
- package/dist/cap-counter/with-cap-enforcement.d.ts +3 -5
- package/dist/channel-email/feature.js +1 -1
- package/dist/channel-email/smtp-transport.d.ts +18 -17
- package/dist/channel-email/smtp-transport.js +24 -21
- package/dist/channel-in-app/handlers/unread-count.query.js +3 -4
- package/dist/compliance-profiles/feature.js +4 -4
- package/dist/compliance-profiles/handlers/for-tenant.query.js +3 -6
- package/dist/compliance-profiles/handlers/list-profiles.query.js +5 -5
- package/dist/compliance-profiles/handlers/needs-profile.query.js +4 -4
- package/dist/compliance-profiles/handlers/set-profile.write.js +13 -14
- package/dist/compliance-profiles/handlers/sub-processors.query.js +9 -11
- package/dist/compliance-profiles/resolve-for-tenant.js +7 -7
- package/dist/compliance-profiles/schema/profile-selection.js +18 -23
- package/dist/compliance-profiles/seeding.js +14 -15
- package/dist/compliance-profiles-ops/handlers/tenants-missing-profile.query.js +3 -3
- package/dist/compliance-profiles-ops/index.js +2 -2
- package/dist/config/handlers/cascade.query.d.ts +5 -0
- package/dist/config/handlers/cascade.query.js +4 -3
- package/dist/config/handlers/readiness.query.d.ts +4 -4
- package/dist/config/handlers/readiness.query.js +1 -1
- package/dist/config/handlers/reencrypt.job.js +4 -5
- package/dist/config/handlers/set.write.js +4 -4
- package/dist/config/handlers/values.query.d.ts +5 -0
- package/dist/config/handlers/values.query.js +4 -3
- package/dist/config/resolver.js +4 -4
- package/dist/config/table.js +10 -10
- package/dist/config/write-helpers.d.ts +1 -0
- package/dist/config/write-helpers.js +19 -10
- package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +2 -2
- package/dist/crypto-shredding/handlers/forget-subject.write.js +25 -21
- package/dist/custom-fields/aggregate-id.d.ts +12 -13
- package/dist/custom-fields/aggregate-id.js +16 -17
- package/dist/custom-fields/entity.js +49 -47
- package/dist/custom-fields/events.js +7 -8
- package/dist/custom-fields/feature.d.ts +14 -14
- package/dist/custom-fields/feature.js +53 -54
- package/dist/custom-fields/handlers/clear-custom-field.write.d.ts +3 -2
- package/dist/custom-fields/handlers/clear-custom-field.write.js +9 -8
- package/dist/custom-fields/handlers/define-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/define-tenant-field.write.js +2 -2
- package/dist/custom-fields/handlers/delete-system-field.write.js +6 -7
- package/dist/custom-fields/handlers/delete-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/delete-tenant-field.write.js +3 -3
- package/dist/custom-fields/handlers/set-custom-field.write.d.ts +3 -3
- package/dist/custom-fields/handlers/set-custom-field.write.js +25 -28
- package/dist/custom-fields/handlers/update-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/update-tenant-field.write.js +1 -1
- package/dist/custom-fields/lib/field-access.js +2 -2
- package/dist/custom-fields/lib/parse-serialized-field.js +1 -1
- package/dist/custom-fields/lib/quota.js +1 -1
- package/dist/custom-fields/lib/value-schema.js +10 -12
- package/dist/custom-fields/run-retention.js +3 -3
- package/dist/custom-fields/schemas.js +14 -14
- package/dist/custom-fields/web/client-plugin.js +2 -5
- package/dist/custom-fields/web/constants.js +2 -2
- package/dist/custom-fields/web/custom-fields-form-section.d.ts +4 -4
- package/dist/custom-fields/web/custom-fields-form-section.js +22 -22
- package/dist/custom-fields/wire-for-entity.js +65 -67
- package/dist/custom-fields/wire-user-data-rights.js +3 -3
- package/dist/data-retention/_internal/parse-override.js +2 -3
- package/dist/data-retention/feature.js +11 -18
- package/dist/data-retention/handlers/policy-for.query.js +6 -6
- package/dist/data-retention/keep-for.d.ts +7 -7
- package/dist/data-retention/keep-for.js +15 -15
- package/dist/data-retention/override-schema.d.ts +3 -4
- package/dist/data-retention/override-schema.js +13 -15
- package/dist/data-retention/presets.d.ts +4 -5
- package/dist/data-retention/presets.js +18 -23
- package/dist/data-retention/resolve-tenant-preset.js +17 -16
- package/dist/data-retention/resolver.d.ts +12 -12
- package/dist/data-retention/resolver.js +24 -23
- package/dist/data-retention/run-retention-cleanup.d.ts +8 -3
- package/dist/data-retention/run-retention-cleanup.js +61 -51
- package/dist/data-retention/schema/tenant-retention-override.js +13 -11
- package/dist/delivery/channel-context.js +6 -3
- package/dist/delivery/db/queries/preferences.js +2 -2
- package/dist/delivery/delivery-service.js +22 -7
- package/dist/delivery/feature.js +2 -2
- package/dist/delivery/handlers/log.query.js +1 -1
- package/dist/delivery/handlers/preferences.query.d.ts +1 -1
- package/dist/delivery/handlers/preferences.query.js +20 -1
- package/dist/delivery/handlers/unsubscribe-page-chrome.query.js +1 -1
- package/dist/delivery/tables.js +9 -8
- package/dist/delivery/unsubscribe-link.d.ts +3 -1
- package/dist/delivery/unsubscribe-page-strings.d.ts +1 -0
- package/dist/delivery/unsubscribe-page-strings.js +1 -0
- package/dist/delivery/unsubscribe-page.d.ts +2 -0
- package/dist/delivery/unsubscribe-page.js +5 -0
- package/dist/delivery/unsubscribe.js +29 -11
- package/dist/document-ingest-foundation/entity.js +4 -4
- package/dist/document-ingest-foundation/events.js +2 -2
- package/dist/document-ingest-foundation/feature.js +2 -2
- package/dist/document-ingest-foundation/pages.js +1 -1
- package/dist/document-ingest-foundation/tenant-destroy-hook.js +4 -4
- package/dist/feature-toggles/compose-tier-resolver.js +1 -1
- package/dist/feature-toggles/feature.js +1 -1
- package/dist/feature-toggles/global-feature-state-table.js +1 -1
- package/dist/feature-toggles/handlers/registered.query.js +5 -5
- package/dist/feature-toggles/toggle-sync-signal.js +1 -1
- package/dist/feature-toggles/web/constants.js +1 -1
- package/dist/file-derivatives/feature.d.ts +1 -1
- package/dist/file-derivatives/feature.js +1 -1
- package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
- package/dist/file-derivatives/presets.js +1 -1
- package/dist/file-foundation/feature.js +6 -6
- package/dist/file-foundation/index.d.ts +1 -1
- package/dist/file-foundation/index.js +1 -1
- package/dist/file-foundation/read-file-ref-content.d.ts +3 -0
- package/dist/file-foundation/read-file-ref-content.js +6 -1
- package/dist/file-provider-inmemory/feature.d.ts +1 -1
- package/dist/file-provider-inmemory/feature.js +16 -16
- package/dist/file-provider-s3/env-named-feature.d.ts +0 -2
- package/dist/file-provider-s3/feature.js +16 -16
- package/dist/file-provider-s3/lib/s3-provider.js +6 -6
- package/dist/files/feature.js +6 -6
- package/dist/files-tenant-data/handlers/sweep-orphaned-derivatives.job.js +4 -4
- package/dist/files-tenant-data/hooks.js +24 -26
- package/dist/files-tenant-data/index.js +1 -1
- package/dist/folders/feature.d.ts +1 -1
- package/dist/folders/web/folder-manager.js +2 -2
- package/dist/folders/web/tree.js +5 -5
- package/dist/form-draft/db/queries/cleanup.js +3 -3
- package/dist/form-draft/db/queries/owned-file-refs.js +1 -1
- package/dist/form-draft/decrypt-draft.js +5 -1
- package/dist/form-draft/entity.js +3 -3
- package/dist/form-draft/feature.js +4 -5
- package/dist/form-draft/handlers/discard.write.js +1 -1
- package/dist/form-draft/release-file-refs.js +6 -6
- package/dist/form-draft/schemas.js +3 -3
- package/dist/form-draft/web/constants.js +0 -2
- package/dist/host-routing/dispatch.js +11 -20
- package/dist/inbound-mail-foundation/aggregate-id.d.ts +13 -13
- package/dist/inbound-mail-foundation/aggregate-id.js +19 -19
- package/dist/inbound-mail-foundation/connect-routes.js +4 -3
- package/dist/inbound-mail-foundation/constants.d.ts +22 -24
- package/dist/inbound-mail-foundation/constants.js +30 -33
- package/dist/inbound-mail-foundation/entities.d.ts +12 -12
- package/dist/inbound-mail-foundation/entities.js +47 -48
- package/dist/inbound-mail-foundation/events.d.ts +4 -3
- package/dist/inbound-mail-foundation/events.js +50 -47
- package/dist/inbound-mail-foundation/feature.js +39 -39
- package/dist/inbound-mail-foundation/handlers/account-state.js +8 -8
- package/dist/inbound-mail-foundation/handlers/connect-account.write.js +25 -26
- package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +7 -7
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +50 -51
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +8 -8
- package/dist/inbound-mail-foundation/handlers/list-messages.query.js +10 -10
- package/dist/inbound-mail-foundation/handlers/scope-visibility.js +3 -4
- package/dist/inbound-mail-foundation/handlers/update-account.write.d.ts +2 -2
- package/dist/inbound-mail-foundation/handlers/update-account.write.js +22 -14
- package/dist/inbound-mail-foundation/i18n.js +0 -1
- package/dist/inbound-mail-foundation/oauth-state.d.ts +5 -5
- package/dist/inbound-mail-foundation/oauth-state.js +11 -10
- package/dist/inbound-mail-foundation/projection.d.ts +4 -4
- package/dist/inbound-mail-foundation/projection.js +31 -29
- package/dist/inbound-mail-foundation/provider-factory.d.ts +4 -4
- package/dist/inbound-mail-foundation/provider-factory.js +9 -9
- package/dist/inbound-mail-foundation/retention-sweep.d.ts +5 -3
- package/dist/inbound-mail-foundation/retention-sweep.js +38 -36
- package/dist/inbound-mail-foundation/screens.js +0 -3
- package/dist/inbound-mail-foundation/tenant-destroy-hook.js +14 -16
- package/dist/inbound-mail-foundation/types.d.ts +49 -49
- package/dist/inbound-mail-foundation/types.js +21 -21
- package/dist/inbound-mail-foundation/watch-supervisor.d.ts +10 -10
- package/dist/inbound-mail-foundation/watch-supervisor.js +85 -67
- package/dist/inbound-provider-imap/credential-document.js +13 -13
- package/dist/inbound-provider-imap/feature.d.ts +2 -2
- package/dist/inbound-provider-imap/feature.js +42 -40
- package/dist/inbound-provider-imap/imap-client.d.ts +12 -11
- package/dist/inbound-provider-imap/imap-client.js +20 -19
- package/dist/inbound-provider-inmemory/feature.d.ts +9 -9
- package/dist/inbound-provider-inmemory/feature.js +19 -19
- package/dist/jobs/db/queries/retention.js +2 -2
- package/dist/jobs/db/queries/stale-run-sweep.js +3 -3
- package/dist/jobs/events.js +1 -1
- package/dist/jobs/feature.d.ts +1 -0
- package/dist/jobs/feature.js +13 -10
- package/dist/jobs/handlers/detail.query.js +4 -4
- package/dist/jobs/handlers/list.query.js +1 -1
- package/dist/jobs/handlers/projection-rebuild.job.js +1 -1
- package/dist/jobs/handlers/reindex-entity.job.js +1 -1
- package/dist/jobs/handlers/retry.write.js +1 -1
- package/dist/jobs/handlers/tenant-failures.query.d.ts +1 -1
- package/dist/jobs/handlers/tenant-failures.query.js +45 -41
- package/dist/jobs/handlers/tenant-runs.query.d.ts +1 -1
- package/dist/jobs/handlers/tenant-runs.query.js +54 -50
- package/dist/jobs/job-instant.js +1 -1
- package/dist/jobs/job-run-logger.js +15 -15
- package/dist/jobs/job-run-table.js +3 -3
- package/dist/jobs/screens.js +1 -1
- package/dist/jobs/tenant-job-failure-table.js +1 -1
- package/dist/jobs/tenant-job-run-table.js +1 -1
- package/dist/ledger/entity.js +5 -5
- package/dist/ledger/feature.js +3 -3
- package/dist/ledger/handlers/confirm-schedule-period.write.js +3 -3
- package/dist/ledger/recurring.js +12 -12
- package/dist/ledger/schemas.js +1 -1
- package/dist/ledger/web/constants.js +2 -2
- package/dist/ledger/web/index.js +7 -7
- package/dist/legal-pages/feature.d.ts +4 -4
- package/dist/legal-pages/feature.js +25 -26
- package/dist/legal-pages/markdown.js +3 -3
- package/dist/legal-pages/web/client-plugin.js +23 -24
- package/dist/legal-pages/web/index.js +0 -1
- package/dist/mail-foundation/feature.d.ts +5 -5
- package/dist/mail-foundation/feature.js +27 -25
- package/dist/mail-transport-inmemory/feature.d.ts +4 -4
- package/dist/mail-transport-inmemory/feature.js +27 -28
- package/dist/mail-transport-smtp/feature.js +20 -20
- package/dist/managed-pages/branding.js +6 -6
- package/dist/managed-pages/feature.d.ts +26 -26
- package/dist/managed-pages/feature.js +34 -34
- package/dist/managed-pages/handlers/by-slug.query.js +5 -6
- package/dist/managed-pages/handlers/by-tenant-published.query.js +2 -2
- package/dist/managed-pages/handlers/set.write.js +5 -7
- package/dist/managed-pages/screens/page-screens.js +11 -11
- package/dist/managed-pages/seeding.js +8 -8
- package/dist/managed-pages/table.js +13 -12
- package/dist/managed-pages/web/client-plugin.d.ts +1 -1
- package/dist/managed-pages/web/client-plugin.js +5 -5
- package/dist/managed-pages/web/i18n.js +1 -1
- package/dist/notes-history/entity.js +4 -4
- package/dist/notes-history/feature.d.ts +2 -2
- package/dist/notes-history-user-data/hooks.js +1 -1
- package/dist/page-render/layout.js +9 -9
- package/dist/page-render/markdown.js +11 -11
- package/dist/page-render/security-headers.js +7 -6
- package/dist/personal-access-tokens/feature.d.ts +1 -1
- package/dist/personal-access-tokens/feature.js +9 -5
- package/dist/personal-access-tokens/roles.js +2 -2
- package/dist/readiness/handlers/status.query.js +3 -3
- package/dist/renderer-foundation/api.js +12 -13
- package/dist/renderer-foundation/constants.js +2 -2
- package/dist/renderer-foundation/feature.js +7 -7
- package/dist/renderer-pdf/render-table-pdf.d.ts +1 -1
- package/dist/renderer-pdf/render-table-pdf.js +22 -10
- package/dist/secrets/feature.js +9 -9
- package/dist/secrets/web/constants.js +2 -2
- package/dist/seo/feature.js +3 -3
- package/dist/sessions/constants.d.ts +1 -1
- package/dist/sessions/constants.js +1 -1
- package/dist/sessions/feature.d.ts +2 -2
- package/dist/sessions/feature.js +12 -8
- package/dist/sessions/handlers/revoke-all-for-user.write.js +2 -2
- package/dist/sessions/handlers/revoke-all-others.write.js +2 -2
- package/dist/sessions/handlers/revoke.write.js +1 -1
- package/dist/sessions/schema/user-session.js +4 -4
- package/dist/sessions/screens.js +2 -2
- package/dist/sessions/session-callbacks.js +2 -2
- package/dist/sessions/session-revoked-event.js +2 -2
- package/dist/shared/assert-no-where-rule-in-ownership-write.js +1 -1
- package/dist/shared/decrypt-stored-pii.d.ts +1 -0
- package/dist/shared/decrypt-stored-pii.js +7 -2
- package/dist/shared/encrypt-for-direct-write.js +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/mail-template-body.d.ts +6 -2
- package/dist/shared/mail-template-body.js +7 -4
- package/dist/shared/map-with-concurrency.js +5 -4
- package/dist/shared/parent-visibility.js +2 -2
- package/dist/shared/password-hashing.js +1 -1
- package/dist/shared/signup-handover.js +1 -2
- package/dist/shared/single-use-token-store.js +1 -1
- package/dist/step-dispatcher/feature.js +1 -0
- package/dist/subscription-dodo/client.d.ts +17 -0
- package/dist/subscription-dodo/client.js +31 -0
- package/dist/subscription-dodo/constants.d.ts +12 -0
- package/dist/subscription-dodo/constants.js +19 -0
- package/dist/subscription-dodo/feature.d.ts +37 -0
- package/dist/subscription-dodo/feature.js +144 -0
- package/dist/subscription-dodo/index.d.ts +2 -0
- package/dist/subscription-dodo/index.js +4 -0
- package/dist/subscription-dodo/plugin-methods.d.ts +23 -0
- package/dist/subscription-dodo/plugin-methods.js +260 -0
- package/dist/subscription-dodo/runtime.d.ts +27 -0
- package/dist/subscription-dodo/runtime.js +89 -0
- package/dist/subscription-dodo/verify-webhook.d.ts +37 -0
- package/dist/subscription-dodo/verify-webhook.js +189 -0
- package/dist/subscription-mollie/constants.js +15 -17
- package/dist/subscription-mollie/feature.d.ts +17 -18
- package/dist/subscription-mollie/feature.js +24 -24
- package/dist/subscription-mollie/index.js +3 -3
- package/dist/subscription-mollie/plugin-methods.d.ts +2 -2
- package/dist/subscription-mollie/plugin-methods.js +15 -14
- package/dist/subscription-mollie/verify-webhook.d.ts +10 -10
- package/dist/subscription-mollie/verify-webhook.js +27 -27
- package/dist/subscription-stripe/constants.js +4 -6
- package/dist/subscription-stripe/feature.d.ts +14 -14
- package/dist/subscription-stripe/feature.js +36 -34
- package/dist/subscription-stripe/index.js +6 -6
- package/dist/subscription-stripe/plugin-methods.js +18 -18
- package/dist/subscription-stripe/runtime.d.ts +1 -1
- package/dist/subscription-stripe/runtime.js +8 -7
- package/dist/subscription-stripe/verify-webhook.d.ts +11 -11
- package/dist/subscription-stripe/verify-webhook.js +59 -61
- package/dist/tags/feature.d.ts +3 -3
- package/dist/tags/screens.js +1 -1
- package/dist/template-resolver/api.d.ts +3 -2
- package/dist/template-resolver/api.js +34 -12
- package/dist/template-resolver/compile-document-body.js +1 -0
- package/dist/template-resolver/feature.js +2 -2
- package/dist/template-resolver/handlers/bundle-import.js +3 -3
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +5 -1
- package/dist/template-resolver/handlers/copy-locale.write.js +22 -7
- package/dist/template-resolver/handlers/diff-to-system.query.js +3 -2
- package/dist/template-resolver/handlers/document-base.js +3 -2
- package/dist/template-resolver/handlers/find-by-id.query.js +8 -7
- package/dist/template-resolver/handlers/list.query.js +6 -7
- package/dist/template-resolver/handlers/overview.query.d.ts +3 -0
- package/dist/template-resolver/handlers/overview.query.js +6 -3
- package/dist/template-resolver/handlers/shared.js +2 -2
- package/dist/template-resolver/handlers/template-write.js +3 -3
- package/dist/template-resolver/handlers/tenant-override-counts.query.js +1 -3
- package/dist/template-resolver/handlers/tenant-override-selection.d.ts +2 -1
- package/dist/template-resolver/handlers/tenant-overrides.query.js +28 -5
- package/dist/template-resolver/handlers/upsert-system.write.js +3 -4
- package/dist/template-resolver/handlers/upsert-tenant.write.js +4 -5
- package/dist/template-resolver/handlers/usage.query.d.ts +2 -1
- package/dist/template-resolver/handlers/usage.query.js +26 -16
- package/dist/template-resolver/handlers/validate.query.js +7 -2
- package/dist/template-resolver/seeding.js +30 -14
- package/dist/template-resolver/store-template-resource.d.ts +1 -0
- package/dist/template-resolver/store-template-resource.js +19 -4
- package/dist/template-resolver/table.js +10 -11
- package/dist/template-resolver/template-delivery-policy.d.ts +0 -5
- package/dist/template-resolver/template-delivery-policy.js +0 -6
- package/dist/template-resolver/template-meta.js +7 -4
- package/dist/template-resolver/template-preview.js +4 -3
- package/dist/template-resolver/web/client-plugin.js +2 -2
- package/dist/template-resolver/web/constants.d.ts +2 -2
- package/dist/template-resolver/web/constants.js +3 -3
- package/dist/template-resolver/web/index.d.ts +1 -0
- package/dist/template-resolver/web/index.js +1 -0
- package/dist/template-resolver/web/mail-layout-constants.d.ts +5 -0
- package/dist/template-resolver/web/mail-layout-constants.js +7 -0
- package/dist/tenant/feature.js +7 -7
- package/dist/tenant/handlers/member-directory.query.d.ts +1 -0
- package/dist/tenant/handlers/member-directory.query.js +11 -4
- package/dist/tenant/handlers/members.query.js +1 -1
- package/dist/tenant/handlers/team-list.query.js +1 -1
- package/dist/tenant/handlers/tenant-directory.query.d.ts +1 -0
- package/dist/tenant/handlers/tenant-directory.query.js +9 -5
- package/dist/tenant/invitation-table.js +37 -40
- package/dist/tenant/membership-table.js +3 -3
- package/dist/tenant/schema/tenant.js +7 -9
- package/dist/tenant/screens.js +1 -1
- package/dist/tenant/seeding.d.ts +6 -5
- package/dist/tenant/seeding.js +12 -12
- package/dist/tenant/testing.js +5 -6
- package/dist/tenant/web/constants.js +2 -2
- package/dist/tenant-handover/events.js +5 -7
- package/dist/tenant-handover/feature.js +1 -1
- package/dist/tenant-handover/grant.js +1 -1
- package/dist/tenant-handover/handlers/claim.write.js +1 -1
- package/dist/tenant-handover/move-entity-graph.js +8 -9
- package/dist/tenant-handover/signup-handover-provider.js +1 -2
- package/dist/tenant-handover/transfer-graph.js +4 -5
- package/dist/tenant-lifecycle/boot-checks.js +1 -1
- package/dist/tenant-lifecycle/constants.d.ts +1 -0
- package/dist/tenant-lifecycle/constants.js +4 -0
- package/dist/tenant-lifecycle/feature.js +1 -1
- package/dist/tenant-lifecycle/run-tenant-destroy.js +2 -2
- package/dist/tenant-settings/constants.js +1 -1
- package/dist/tenant-settings/tenant-defaults.d.ts +1 -1
- package/dist/tenant-settings/tenant-defaults.js +9 -2
- package/dist/tier-engine/aggregate-id.d.ts +11 -11
- package/dist/tier-engine/aggregate-id.js +15 -16
- package/dist/tier-engine/compose-app.d.ts +14 -14
- package/dist/tier-engine/compose-app.js +1 -1
- package/dist/tier-engine/entity.js +18 -19
- package/dist/tier-engine/feature.d.ts +19 -17
- package/dist/tier-engine/feature.js +68 -69
- package/dist/tier-engine/handlers/active-tier.query.js +6 -7
- package/dist/tier-engine/handlers/get-tenant-tier.query.js +4 -4
- package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +7 -8
- package/dist/tier-engine/handlers/set-tenant-tier.write.js +7 -4
- package/dist/tier-engine/tier-resolver.js +2 -2
- package/dist/tier-engine/trial.js +8 -8
- package/dist/tier-engine/web/constants.js +2 -2
- package/dist/tier-engine/web/index.js +4 -4
- package/dist/user/db/queries/stream-tenant-backfill.js +8 -8
- package/dist/user/handlers/create.write.js +4 -4
- package/dist/user/schema/user.d.ts +11 -11
- package/dist/user/schema/user.js +45 -44
- package/dist/user/seeding.d.ts +15 -15
- package/dist/user/seeding.js +28 -28
- package/dist/user/testing.js +3 -3
- package/dist/user-data-rights/audit-download.d.ts +8 -8
- package/dist/user-data-rights/audit-download.js +19 -20
- package/dist/user-data-rights/boot-checks.js +1 -1
- package/dist/user-data-rights/deletion-token.js +1 -1
- package/dist/user-data-rights/feature.d.ts +49 -50
- package/dist/user-data-rights/feature.js +54 -53
- package/dist/user-data-rights/handlers/cancel-deletion.write.js +9 -9
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +8 -8
- package/dist/user-data-rights/handlers/download-by-job.query.js +29 -30
- package/dist/user-data-rights/handlers/download-by-token.query.js +40 -42
- package/dist/user-data-rights/handlers/export-status.query.js +8 -9
- package/dist/user-data-rights/handlers/lift-restriction.write.js +7 -8
- package/dist/user-data-rights/handlers/list-download-attempts.query.js +2 -2
- package/dist/user-data-rights/handlers/my-audit-log.query.js +4 -4
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +5 -5
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.js +16 -15
- package/dist/user-data-rights/handlers/request-deletion.write.d.ts +2 -2
- package/dist/user-data-rights/handlers/request-deletion.write.js +7 -9
- package/dist/user-data-rights/handlers/request-export.write.js +42 -41
- package/dist/user-data-rights/handlers/restrict-account.write.js +22 -19
- package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +8 -8
- package/dist/user-data-rights/index.js +8 -10
- package/dist/user-data-rights/lib/default-mailers.js +3 -3
- package/dist/user-data-rights/lib/update-user-lifecycle.js +14 -17
- package/dist/user-data-rights/run-export-jobs.d.ts +38 -42
- package/dist/user-data-rights/run-export-jobs.js +130 -237
- package/dist/user-data-rights/run-forget-cleanup.d.ts +16 -20
- package/dist/user-data-rights/run-forget-cleanup.js +65 -83
- package/dist/user-data-rights/run-user-export.d.ts +34 -33
- package/dist/user-data-rights/run-user-export.js +55 -57
- package/dist/user-data-rights/schema/download-attempt.js +11 -11
- package/dist/user-data-rights/schema/download-token.js +37 -41
- package/dist/user-data-rights/schema/export-job.d.ts +4 -4
- package/dist/user-data-rights/schema/export-job.js +65 -67
- package/dist/user-data-rights/token-helpers.d.ts +8 -9
- package/dist/user-data-rights/token-helpers.js +21 -25
- package/dist/user-data-rights/web/client-plugin.d.ts +5 -5
- package/dist/user-data-rights/web/client-plugin.js +1 -1
- package/dist/user-data-rights/web/confirm-deletion-screen.js +6 -5
- package/dist/user-data-rights/web/constants.js +1 -1
- package/dist/user-data-rights/web/index.js +5 -5
- package/dist/user-data-rights/web/privacy-center-screen.js +14 -13
- package/dist/user-data-rights/web/public-deletion-gate.d.ts +4 -4
- package/dist/user-data-rights/web/request-deletion-screen.js +6 -6
- package/dist/user-data-rights/zip-path.d.ts +15 -15
- package/dist/user-data-rights/zip-path.js +41 -40
- package/dist/user-data-rights-defaults/feature.js +15 -15
- package/dist/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.js +3 -5
- package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +45 -51
- package/dist/user-data-rights-defaults/hooks/job-run.userdata-hook.js +2 -2
- package/dist/user-data-rights-defaults/hooks/notification-preference.userdata-hook.js +1 -1
- package/dist/user-data-rights-defaults/hooks/user-session.userdata-hook.js +3 -3
- package/dist/user-data-rights-defaults/hooks/user.userdata-hook.js +32 -31
- package/dist/user-profile/handlers/change-email.write.js +13 -13
- package/dist/user-profile/index.js +3 -3
- package/dist/user-profile/web/client-plugin.js +1 -1
- package/dist/user-profile/web/constants.js +1 -1
- package/dist/user-profile/web/profile-screen.js +1 -1
- package/dist/waitlist/handlers/export-purge.write.js +2 -1
- package/dist/waitlist/handlers/submit.write.js +5 -5
- package/dist/waitlist/import.d.ts +1 -0
- package/dist/waitlist/import.js +4 -0
- package/dist/waitlist/legacy-migrate.js +17 -6
- package/dist/waitlist/link-on-signup.js +18 -6
- package/dist/waitlist/options.d.ts +3 -0
- package/dist/workflow-runner/aggregate-id.d.ts +1 -1
- package/dist/workflow-runner/aggregate-id.js +1 -1
- package/dist/workflow-runner/event-subscriber.js +4 -5
- package/dist/workflow-runner/event-trigger.js +4 -17
- package/dist/workflow-runner/feature.js +2 -2
- package/dist/workflow-runner/handlers/resume-run.write.js +4 -4
- package/dist/workflow-runner/pending-projection.js +6 -7
- package/dist/workflow-runner/runner.js +6 -6
- package/dist/workflow-runner/tables.js +6 -6
- package/dist/workflow-runner/workflow-registry.js +1 -1
- package/package.json +20 -10
- package/src/agent-tools/changes.json +6 -0
- package/src/audit/changes.json +12 -0
- package/src/auth-mfa/changes.json +6 -0
- package/src/billing-foundation/changes.json +55 -0
- package/src/cap-counter/changes.json +18 -0
- package/src/channel-email/changes.json +6 -0
- package/src/config/changes.json +12 -0
- package/src/crypto-shredding/changes.json +6 -0
- package/src/delivery/changes.json +30 -0
- package/src/form-draft/changes.json +6 -0
- package/src/host-routing/changes.json +6 -0
- package/src/inbound-mail-foundation/changes.json +18 -0
- package/src/jobs/changes.json +13 -0
- package/src/mail-foundation/changes.json +6 -0
- package/src/renderer-pdf/changes.json +6 -0
- package/src/step-dispatcher/changes.json +6 -0
- package/src/subscription-dodo/changes.json +14 -0
- package/src/subscription-stripe/changes.json +12 -0
- package/src/template-resolver/changes.json +48 -0
- package/src/tenant/changes.json +6 -0
- package/src/tenant-lifecycle/changes.json +6 -0
- package/src/tier-engine/changes.json +6 -0
- package/src/waitlist/changes.json +18 -0
- package/dist/billing-foundation/consumer-protection/stored-user-email.d.ts +0 -1
- package/dist/billing-foundation/consumer-protection/stored-user-email.js +0 -8
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
// Domain
|
|
1
|
+
// Domain events for subscription-foundation.
|
|
2
2
|
//
|
|
3
|
-
// **Pattern:** event-sourced —
|
|
4
|
-
//
|
|
5
|
-
// (=
|
|
6
|
-
//
|
|
3
|
+
// **Pattern:** event-sourced — each provider webhook (after plugin
|
|
4
|
+
// verification) becomes a domain event on the subscription stream
|
|
5
|
+
// (= one stream per tenant via subscriptionAggregateId(tenantId)).
|
|
6
|
+
// The read model is an inline projection in the `subscriptions` table.
|
|
7
7
|
//
|
|
8
|
-
//
|
|
9
|
-
// bucket — events
|
|
10
|
-
// accounting-export, churn-analytics)
|
|
11
|
-
//
|
|
8
|
+
// Five fine-grained event types instead of a generic "webhook-received"
|
|
9
|
+
// bucket — events are business facts: future consumers (billing-history,
|
|
10
|
+
// accounting-export, churn-analytics) listen directly for the event type
|
|
11
|
+
// without a payload discriminator.
|
|
12
12
|
import * as z from "zod";
|
|
13
13
|
import { BILLING_FOUNDATION_FEATURE, SubscriptionStatuses } from "./web/constants.js";
|
|
14
14
|
import { CONSENT_LOCALES } from "./web/consumer-protection/consent-locale.js";
|
|
15
|
-
// Aggregate
|
|
16
|
-
//
|
|
15
|
+
// Aggregate type for the event store. One subscription per tenant is
|
|
16
|
+
// one stream; the subscriptionAggregateId helper provides the stream ID.
|
|
17
17
|
export const SUBSCRIPTION_AGGREGATE_TYPE = "subscription";
|
|
18
|
-
// Event-name
|
|
19
|
-
// FQN (
|
|
18
|
+
// Event-name constants — short form (for r.defineEvent) and qualified
|
|
19
|
+
// FQN (for ctx.unsafeAppendEvent and projection-apply keys).
|
|
20
20
|
export const SUBSCRIPTION_CREATED_EVENT_SHORT = "subscription-created";
|
|
21
21
|
export const SUBSCRIPTION_UPDATED_EVENT_SHORT = "subscription-updated";
|
|
22
22
|
export const SUBSCRIPTION_CANCELED_EVENT_SHORT = "subscription-canceled";
|
|
@@ -27,7 +27,7 @@ export const SUBSCRIPTION_UPDATED_EVENT_QN = `${BILLING_FOUNDATION_FEATURE}:even
|
|
|
27
27
|
export const SUBSCRIPTION_CANCELED_EVENT_QN = `${BILLING_FOUNDATION_FEATURE}:event:${SUBSCRIPTION_CANCELED_EVENT_SHORT}`;
|
|
28
28
|
export const INVOICE_PAID_EVENT_QN = `${BILLING_FOUNDATION_FEATURE}:event:${INVOICE_PAID_EVENT_SHORT}`;
|
|
29
29
|
export const INVOICE_PAYMENT_FAILED_EVENT_QN = `${BILLING_FOUNDATION_FEATURE}:event:${INVOICE_PAYMENT_FAILED_EVENT_SHORT}`;
|
|
30
|
-
// Status
|
|
30
|
+
// Status enum for event payloads (= the provider's subscription-state snapshot).
|
|
31
31
|
const statusEnum = z.enum([
|
|
32
32
|
SubscriptionStatuses.active,
|
|
33
33
|
SubscriptionStatuses.trialing,
|
|
@@ -35,10 +35,11 @@ const statusEnum = z.enum([
|
|
|
35
35
|
SubscriptionStatuses.canceled,
|
|
36
36
|
SubscriptionStatuses.incomplete,
|
|
37
37
|
]);
|
|
38
|
-
// Common payload —
|
|
39
|
-
// snapshot.
|
|
38
|
+
// Common payload — all five events carry the same subscription-state
|
|
39
|
+
// snapshot. The event type identifies what happened; the payload contains
|
|
40
|
+
// the resulting state.
|
|
40
41
|
// The raw provider event is deliberately not stored: it carries plaintext
|
|
41
|
-
// PII and event headers
|
|
42
|
+
// PII, and event headers cannot be crypto-shredded.
|
|
42
43
|
export const subscriptionEventPayloadSchema = z.object({
|
|
43
44
|
providerName: z.string().min(1).max(50),
|
|
44
45
|
// 1000, not 200: these two are `tenantOwned: true` on the entity (see
|
|
@@ -103,6 +104,7 @@ export const checkoutConsentRecordedPayloadSchema = z.object({
|
|
|
103
104
|
termsTemplateVersion: z.number(),
|
|
104
105
|
locale: z.string().min(2).max(35),
|
|
105
106
|
actorUserId: z.string().min(1),
|
|
107
|
+
providerName: z.string().min(1).max(100).optional(),
|
|
106
108
|
itemLabelKey: z.string().min(1).max(200).optional(),
|
|
107
109
|
itemLabelParams: z.record(z.string(), z.union([z.string().max(100), z.number()])).optional(),
|
|
108
110
|
});
|
|
@@ -1,54 +1,53 @@
|
|
|
1
1
|
// kumiko-feature-version: 1
|
|
2
2
|
//
|
|
3
|
-
// subscription-foundation
|
|
3
|
+
// subscription-foundation as a Kumiko bundled feature (plugin host).
|
|
4
4
|
//
|
|
5
|
-
// **Multi-
|
|
6
|
-
// parallel
|
|
7
|
-
// subscription-apple-iap + ...)
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// aktiv.
|
|
5
|
+
// **Multi-provider from day one** — the app builder can mount multiple plugins
|
|
6
|
+
// in parallel (subscription-stripe + subscription-paypal +
|
|
7
|
+
// subscription-apple-iap + ...), and the end user chooses between
|
|
8
|
+
// card/PayPal/Apple Pay/Klarna/SEPA at checkout (Disney+ pattern).
|
|
9
|
+
// There is no global `provider` config key — all mounted plugins are active.
|
|
11
10
|
//
|
|
12
|
-
// **
|
|
13
|
-
// 1. **Plugin
|
|
11
|
+
// **What this foundation provides:**
|
|
12
|
+
// 1. **Plugin API** for subscription providers via `r.extendsRegistrar(
|
|
14
13
|
// "subscriptionProvider", ...)`.
|
|
15
|
-
// 2. **5
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// 3. **Inline
|
|
19
|
-
//
|
|
14
|
+
// 2. **5 domain events** on the `subscription` stream (one stream ID per
|
|
15
|
+
// tenant): created/updated/canceled/invoice-paid/invoice-payment-failed.
|
|
16
|
+
// Audit history is provided automatically by the event store.
|
|
17
|
+
// 3. **Inline projection** onto `read_subscriptions` (current state per
|
|
18
|
+
// tenant). Apply runs in the same TX as the event append
|
|
20
19
|
// → read-your-own-write semantics.
|
|
21
|
-
// 4. **process-event
|
|
22
|
-
// webhook
|
|
20
|
+
// 4. **process-event handler**: a programmatic write handler called by the
|
|
21
|
+
// webhook handler that dispatches to the type-matched appendEvent.
|
|
23
22
|
// 5. **createSubscriptionWebhookRoute**: factory for the `entry:"signature"`
|
|
24
23
|
// extraRoute `/api/subscription/webhook/:providerName`.
|
|
25
|
-
// 6. **payment-received event + read_payments projection**: one-off
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
24
|
+
// 6. **payment-received event + read_payments projection**: one-off payments
|
|
25
|
+
// (checkout mode "payment") get their own event, per-tenant aggregate,
|
|
26
|
+
// and `process-payment-event` write handler — not a sixth
|
|
27
|
+
// SubscriptionEventTypes value.
|
|
29
28
|
// 7. **Optional billing-plans catalog** (`createBillingFoundationFeature({
|
|
30
29
|
// baseUrl, catalog })`): a `billing-foundation:query:billing-plans`
|
|
31
|
-
// query plus `start-plan-checkout`/`switch-plan` write
|
|
30
|
+
// query plus `start-plan-checkout`/`switch-plan` write handlers and a
|
|
32
31
|
// dormant `billing-plans` dashboard screen/panel, all derived from the
|
|
33
|
-
//
|
|
32
|
+
// single `catalog` option — no separate per-app price wiring.
|
|
34
33
|
//
|
|
35
|
-
// **
|
|
36
|
-
// -
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
// -
|
|
40
|
-
//
|
|
41
|
-
// -
|
|
42
|
-
// -
|
|
43
|
-
//
|
|
34
|
+
// **What this foundation does NOT do:**
|
|
35
|
+
// - No r.entity for `subscription`. The table is a read-only projection —
|
|
36
|
+
// there is no CRUD path. It is written exclusively via projection-apply,
|
|
37
|
+
// triggered by one of the 5 events.
|
|
38
|
+
// - No tier sync with tier-engine. App owners can read the subscription row
|
|
39
|
+
// via `getSubscriptionForTenant(ctx, tenantId)` if needed.
|
|
40
|
+
// - No provider-specific configs.
|
|
41
|
+
// - No marketplace use case (the app tenant bills end customers via Stripe
|
|
42
|
+
// Connect). That comes later as a separate `marketplace-foundation`.
|
|
44
43
|
//
|
|
45
|
-
// **Provider
|
|
46
|
-
//
|
|
47
|
-
// event
|
|
48
|
-
// existing row
|
|
49
|
-
// business
|
|
50
|
-
//
|
|
51
|
-
// `subscription-provider-changed
|
|
44
|
+
// **Provider changes:** The Disney+ pattern (the tenant cancels a Stripe
|
|
45
|
+
// subscription and starts a new one with PayPal) is currently modeled as a
|
|
46
|
+
// second `subscription-created` event. UPSERT in projection-apply overwrites
|
|
47
|
+
// the existing row with the new providerName. This is sufficient for the MVP.
|
|
48
|
+
// If the business fact "provider change" needs its own domain event (for
|
|
49
|
+
// example, for analytics such as "how many changes per month?"), a
|
|
50
|
+
// `subscription-provider-changed` event type can be added later.
|
|
52
51
|
import { defineFeature, EXT_TENANT_DATA, } from "@cosmicdrift/kumiko-framework/engine";
|
|
53
52
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
54
53
|
import { registerConsumerProtection } from "./consumer-protection/register-consent.js";
|
|
@@ -58,6 +57,7 @@ import { createBillingPlansQuery } from "./handlers/billing-plans.query.js";
|
|
|
58
57
|
import { createCheckoutSessionHandler } from "./handlers/create-checkout-session.write.js";
|
|
59
58
|
import { createPortalSessionHandler } from "./handlers/create-portal-session.write.js";
|
|
60
59
|
import { listSubscriptionsQuery } from "./handlers/list-subscriptions.query.js";
|
|
60
|
+
import { createPlanSwitchPreviewQuery } from "./handlers/plan-switch-preview.query.js";
|
|
61
61
|
import { createProcessEventHandler } from "./handlers/process-event.write.js";
|
|
62
62
|
import { processPaymentEventHandler } from "./handlers/process-payment-event.write.js";
|
|
63
63
|
import { createStartPlanCheckoutHandler } from "./handlers/start-plan-checkout.write.js";
|
|
@@ -90,7 +90,7 @@ export function createBillingFoundationFeature(options = {}) {
|
|
|
90
90
|
}),
|
|
91
91
|
};
|
|
92
92
|
return defineFeature(BILLING_FOUNDATION_FEATURE, (r) => {
|
|
93
|
-
r.describe("Plugin host for subscription billing
|
|
93
|
+
r.describe("Plugin host for subscription billing. Manages the `read_subscriptions` projection table and exposes 5 domain events (subscription created/updated/canceled, invoice paid/failed) appended by the foundation's own `billing-foundation:write:process-event` write-handler after provider plugins verify and normalize each webhook. Also manages a separate `read_payments` projection table (one row per one-off-payment) fed by its own `payment-received` event and `billing-foundation:write:process-payment-event` write-handler. Also ships `billing-foundation:write:create-checkout-session` and `billing-foundation:write:create-portal-session` write-handlers, a `billing-foundation:query:subscription:list` query handler, and a `createSubscriptionWebhookRoute` factory for the `/api/subscription/webhook/:providerName` extraRoute. `createBillingFoundationFeature({ baseUrl, catalog })` additionally derives a `billing-foundation:query:billing-plans` query, `start-plan-checkout`/`switch-plan` write-handlers, a `plan-switch-preview` query and a dormant billing-plans dashboard screen/panel from the catalog. Several providers can be mounted at once: new checkouts are offered by every enabled provider (enabled means keys set and billingLive on, or simply mounted for plugins without `isBillingEnabled`), the customer picks one in the plan panel and `start-plan-checkout` takes a `providerName`. An existing subscription stays with the provider that stored it, so the plan list and every switch follow that provider. A plan switch is either hosted (`createPlanSwitchSession` answers a url) or in-app (`previewPlanSwitch` shows the proration, `applyPlanSwitch` runs after the customer confirms; `switch-plan` then answers `{ applied: true }` and records the consent). `consumerProtection.providerTexts` overrides the checkout note and the VAT note per provider. Also ships `billing-foundation:write:sync-subscription` (pulls a provider plugin's live subscription state via `retrieveSubscription` and appends drift as a `subscription.updated` event, which catches changes made on the provider's own dashboard that never reached us as a webhook) and the `sync-subscriptions` job (manual-trigger + runOnBoot, perTenant) that dispatches it. Low-level building block: use `subscription-stripe`, `subscription-mollie` or `subscription-dodo` unless you are writing a new payment provider.");
|
|
94
94
|
r.uiHints({
|
|
95
95
|
displayLabel: "Billing · Foundation",
|
|
96
96
|
category: "billing",
|
|
@@ -147,18 +147,18 @@ export function createBillingFoundationFeature(options = {}) {
|
|
|
147
147
|
[PAYMENT_RECEIVED_EVENT_QN]: applyPaymentReceived,
|
|
148
148
|
},
|
|
149
149
|
});
|
|
150
|
-
// Plugin extension
|
|
150
|
+
// Plugin extension point. Provider plugins register here.
|
|
151
151
|
r.extendsRegistrar(SUBSCRIPTION_PROVIDER_EXTENSION, {
|
|
152
152
|
onRegister: () => {
|
|
153
153
|
// No side-effects at register-time.
|
|
154
154
|
},
|
|
155
155
|
});
|
|
156
|
-
// Custom write
|
|
157
|
-
// - process-event: programmatic entry
|
|
158
|
-
//
|
|
156
|
+
// Custom write handlers:
|
|
157
|
+
// - process-event: programmatic entry point called by the webhook
|
|
158
|
+
// handler; dispatches to the type-matched appendEvent
|
|
159
159
|
// - create-checkout-session: bare priceId-driven checkout (hardened,
|
|
160
160
|
// see checkout-core)
|
|
161
|
-
// - create-portal-session: Tenant
|
|
161
|
+
// - create-portal-session: Tenant Admin "Manage Subscription" flow
|
|
162
162
|
r.writeHandler(createProcessEventHandler(widened.now));
|
|
163
163
|
r.writeHandler(createCheckoutSessionHandler(widened));
|
|
164
164
|
r.writeHandler(createPortalSessionHandler(widened));
|
|
@@ -184,6 +184,7 @@ export function createBillingFoundationFeature(options = {}) {
|
|
|
184
184
|
if (widened.catalog) {
|
|
185
185
|
const { catalog } = widened;
|
|
186
186
|
r.queryHandler(createBillingPlansQuery(widened, catalog));
|
|
187
|
+
r.queryHandler(createPlanSwitchPreviewQuery(catalog));
|
|
187
188
|
r.writeHandler(createStartPlanCheckoutHandler(widened, catalog));
|
|
188
189
|
r.writeHandler(createSwitchPlanHandler(widened, catalog));
|
|
189
190
|
r.screen(createBillingPlansScreen(catalog.viewRoles));
|
|
@@ -15,7 +15,7 @@ export type SubscriptionView = {
|
|
|
15
15
|
* apply-function started stamping it from `event.createdAt`. */
|
|
16
16
|
readonly lastChangedAt: Temporal.Instant;
|
|
17
17
|
};
|
|
18
|
-
/**
|
|
19
|
-
* aggregate
|
|
20
|
-
*
|
|
18
|
+
/** Returns the tenant's only subscription row (deterministic
|
|
19
|
+
* aggregate ID), or null if the tenant has never subscribed. The status
|
|
20
|
+
* may be active/canceled/past_due/etc.; the caller decides what to do. */
|
|
21
21
|
export declare function getSubscriptionForTenant(ctx: HandlerContext, tenantId: string): Promise<SubscriptionView | null>;
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/// <reference types="temporal-polyfill/global" preserve="true" />
|
|
2
|
-
// Resolver-helper: liest die current subscription-row für einen Tenant
|
|
3
|
-
// aus der read_subscriptions-projection.
|
|
4
2
|
import { configuredPiiSubjectKms, decryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
5
3
|
import { subscriptionAggregateId } from "./aggregate-id.js";
|
|
6
4
|
import { SUBSCRIPTION_PII_FIELDS } from "./entities.js";
|
|
7
5
|
import { subscriptionsProjectionTable } from "./projection.js";
|
|
8
|
-
/**
|
|
9
|
-
* aggregate
|
|
10
|
-
*
|
|
6
|
+
/** Returns the tenant's only subscription row (deterministic
|
|
7
|
+
* aggregate ID), or null if the tenant has never subscribed. The status
|
|
8
|
+
* may be active/canceled/past_due/etc.; the caller decides what to do. */
|
|
11
9
|
export async function getSubscriptionForTenant(ctx, tenantId) {
|
|
12
10
|
const aggId = subscriptionAggregateId(tenantId);
|
|
13
11
|
const rows = await ctx.db.selectMany(subscriptionsProjectionTable, { id: aggId }, { limit: 1 });
|
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
// benefits, the caller's current tier and per-plan action. Only registered
|
|
3
3
|
// when `createBillingFoundationFeature` gets a `catalog`.
|
|
4
4
|
import * as z from "zod";
|
|
5
|
-
import {
|
|
5
|
+
import { filterOfferedProviders, listCatalogProviders, } from "../checkout-core.js";
|
|
6
6
|
import { CONSENT_TEXTS, consentTextVersion, } from "../consumer-protection/consent-text.js";
|
|
7
|
-
import {
|
|
7
|
+
import { buildProviderPlanViews, loadBillingPlansBase, toBillingPlansResult, } from "../plan-catalog.js";
|
|
8
8
|
import { isLegalLinkSet, } from "../types.js";
|
|
9
9
|
const billingPlansSchema = z.object({}).strict();
|
|
10
10
|
export function createBillingPlansQuery(options, catalog) {
|
|
11
11
|
return {
|
|
12
12
|
name: "billing-plans",
|
|
13
|
-
description: "Lists the purchasable plans with live price, benefits, the caller's current tier and which action (checkout | switch | current | unavailable | paymentPending) each plan offers, plus the optional price-less contact card.",
|
|
13
|
+
description: "Lists the purchasable plans with live price, benefits, the caller's current tier and which action (checkout | switch | current | unavailable | paymentPending) each plan offers, plus the optional price-less contact card. With several live providers it also lists each provider's own plans under `providers`.",
|
|
14
14
|
schema: billingPlansSchema,
|
|
15
15
|
access: { roles: catalog.viewRoles },
|
|
16
16
|
handler: async (_query, ctx) => {
|
|
17
|
-
const
|
|
18
|
-
const result = await
|
|
17
|
+
const base = await loadBillingPlansBase(ctx, catalog, options.now);
|
|
18
|
+
const result = await resolveProviderPlans(ctx, options, catalog, base);
|
|
19
19
|
const { consumerProtection } = options;
|
|
20
20
|
if (!consumerProtection)
|
|
21
21
|
return result;
|
|
@@ -32,6 +32,36 @@ export function createBillingPlansQuery(options, catalog) {
|
|
|
32
32
|
},
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
+
// A tenant with a blocking subscription sees only the provider holding it
|
|
36
|
+
// (a second provider's prices would be a checkout it cannot start); otherwise
|
|
37
|
+
// every live provider is offered.
|
|
38
|
+
async function resolveProviderPlans(ctx, options, catalog, base) {
|
|
39
|
+
const candidates = listCatalogProviders(ctx, catalog);
|
|
40
|
+
if (base.blockingSubscription && base.subscriptionView) {
|
|
41
|
+
const holder = candidates.find((c) => c.name === base.subscriptionView?.providerName);
|
|
42
|
+
return toBillingPlansResult(catalog, base, await buildProviderPlanViews(ctx, holder?.plugin ?? null, catalog, base));
|
|
43
|
+
}
|
|
44
|
+
const offered = await filterOfferedProviders(ctx, candidates);
|
|
45
|
+
const first = offered[0] ?? candidates[0] ?? null;
|
|
46
|
+
const firstViews = await buildProviderPlanViews(ctx, first?.plugin ?? null, catalog, base);
|
|
47
|
+
const result = toBillingPlansResult(catalog, base, firstViews);
|
|
48
|
+
const providerTexts = options.consumerProtection?.providerTexts;
|
|
49
|
+
const hasNote = first !== null && providerTexts?.[first.name]?.checkoutNote !== undefined;
|
|
50
|
+
if (offered.length <= 1 && !(offered.length === 1 && hasNote))
|
|
51
|
+
return result;
|
|
52
|
+
const providers = await Promise.all(offered.map((provider, index) => providerView(ctx, catalog, base, provider, providerTexts, index === 0 ? firstViews : null)));
|
|
53
|
+
return { ...result, providers };
|
|
54
|
+
}
|
|
55
|
+
async function providerView(ctx, catalog, base, provider, providerTexts, known) {
|
|
56
|
+
const views = known ?? (await buildProviderPlanViews(ctx, provider.plugin, catalog, base));
|
|
57
|
+
const note = providerTexts?.[provider.name]?.checkoutNote;
|
|
58
|
+
return {
|
|
59
|
+
name: provider.name,
|
|
60
|
+
displayName: provider.plugin.displayName ?? provider.name,
|
|
61
|
+
...(note !== undefined && { note }),
|
|
62
|
+
plans: views.plans,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
35
65
|
function legalLinksPerLocale(legalLinks) {
|
|
36
66
|
return isLegalLinkSet(legalLinks) ? { de: legalLinks, en: legalLinks } : legalLinks;
|
|
37
67
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
// create-checkout-session — Tenant
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// `plugin.createCheckoutSession(ctx, ...)`,
|
|
5
|
-
//
|
|
1
|
+
// create-checkout-session — Tenant admin clicks "Upgrade to Pro" (or
|
|
2
|
+
// chooses between card/PayPal/Apple Pay). The handler reads providerName
|
|
3
|
+
// from the payload, looks up the plugin, calls
|
|
4
|
+
// `plugin.createCheckoutSession(ctx, ...)`, and returns the hosted-page URL.
|
|
5
|
+
// The tenant admin is redirected there.
|
|
6
6
|
//
|
|
7
|
-
// **Multi-
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
7
|
+
// **Multi-provider path:** payload.providerName is the entityName
|
|
8
|
+
// of a registered plugin ("stripe" / "paypal" / "mollie" / ...).
|
|
9
|
+
// The app builder shows a UI list of mounted providers; the customer
|
|
10
|
+
// selects one, and this handler dispatches to the matching plugin.
|
|
11
11
|
//
|
|
12
|
-
// **Tenant
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// **Tenant resolution:** ctx.user.tenantId is passed to the provider as
|
|
13
|
+
// metadata. On the subsequent webhook (subscription.created),
|
|
14
|
+
// verifyAndParseWebhook reads it back from the provider payload and
|
|
15
|
+
// resolves the tenant.
|
|
16
16
|
//
|
|
17
17
|
// **Hardening:** successUrl/cancelUrl must share `options.baseUrl`'s
|
|
18
18
|
// origin; mode:"subscription" requires priceId to be a known price of the
|
|
@@ -31,12 +31,13 @@ const createCheckoutSessionSchema = z.object({
|
|
|
31
31
|
providerName: z.string().min(1).max(50),
|
|
32
32
|
/** Provider-eigene price/plan-ID. */
|
|
33
33
|
priceId: z.string().min(1).max(200),
|
|
34
|
-
/**
|
|
34
|
+
/** Where the customer lands after a successful checkout. */
|
|
35
35
|
successUrl: z.string().url(),
|
|
36
|
-
/**
|
|
36
|
+
/** Where the customer lands if they cancel. */
|
|
37
37
|
cancelUrl: z.string().url(),
|
|
38
|
-
/** Optional:
|
|
39
|
-
*
|
|
38
|
+
/** Optional: existing provider customer when the tenant already
|
|
39
|
+
* has an account with the provider (= plan change instead of a new
|
|
40
|
+
* registration). */
|
|
40
41
|
providerCustomerId: z.string().max(200).optional(),
|
|
41
42
|
/** Optional: `"payment"` for a one-off checkout (e.g. credit top-up)
|
|
42
43
|
* instead of a recurring subscription. Defaults to `"subscription"`. */
|
|
@@ -89,6 +90,7 @@ export function createCheckoutSessionHandler(options) {
|
|
|
89
90
|
tier: mode === "subscription" ? (plugin.priceToTier?.[payload.priceId] ?? null) : null,
|
|
90
91
|
priceId: payload.priceId,
|
|
91
92
|
price,
|
|
93
|
+
providerName: payload.providerName,
|
|
92
94
|
...(orderItem && { orderItem }),
|
|
93
95
|
});
|
|
94
96
|
return {
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
// create-portal-session — Tenant
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// `plugin.createPortalSession(ctx, ...)`,
|
|
1
|
+
// create-portal-session — Tenant admin clicks "Manage Subscription".
|
|
2
|
+
// The handler finds the tenant's current subscription, looks up the matching
|
|
3
|
+
// plugin (= the `subscription.providerName` column), calls
|
|
4
|
+
// `plugin.createPortalSession(ctx, ...)`, and returns the hosted portal URL.
|
|
5
5
|
//
|
|
6
|
-
// **Provider
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// kennt.
|
|
6
|
+
// **Provider resolution:** unlike create-checkout-session (where the tenant
|
|
7
|
+
// chooses a NEW provider when subscribing), the provider here is fixed by the
|
|
8
|
+
// existing subscription row. The tenant cannot access another provider's
|
|
9
|
+
// portal because the subscription does not belong to that provider.
|
|
11
10
|
//
|
|
12
|
-
// **returnUrl:** computed server-side via `portalReturnUrl` (catalog's
|
|
13
|
-
// returnPath, or baseUrl itself)
|
|
14
|
-
//
|
|
11
|
+
// **returnUrl:** computed server-side via `portalReturnUrl` (the catalog's
|
|
12
|
+
// returnPath, or baseUrl itself). The client no longer supplies one, so there
|
|
13
|
+
// is no client-controlled redirect to harden against.
|
|
15
14
|
import { configuredPiiSubjectKms, decryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
16
15
|
import { selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
17
16
|
import * as z from "zod";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// list-subscriptions — sysadmin
|
|
2
|
-
// `read_subscriptions
|
|
3
|
-
// subscription
|
|
4
|
-
// tenant-scoped
|
|
1
|
+
// list-subscriptions — sysadmin cross-tenant list query on the
|
|
2
|
+
// `read_subscriptions` projection. Tenant admins read their own
|
|
3
|
+
// subscription through the getSubscriptionForTenant helper (= ctx.db is
|
|
4
|
+
// tenant-scoped and automatically returns only the caller's row).
|
|
5
5
|
import { configuredPiiSubjectKms, decryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
6
6
|
import * as z from "zod";
|
|
7
7
|
import { SUBSCRIPTION_PII_FIELDS } from "../entities.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { QueryHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import type { BillingPlanCatalog, PlanSwitchPreview } from "../types.ts";
|
|
3
|
+
export type PlanSwitchPreviewResult = {
|
|
4
|
+
readonly preview: PlanSwitchPreview;
|
|
5
|
+
readonly target: {
|
|
6
|
+
readonly tier: string;
|
|
7
|
+
readonly unitAmount: number;
|
|
8
|
+
readonly currency: string;
|
|
9
|
+
readonly interval: "day" | "week" | "month" | "year" | null;
|
|
10
|
+
readonly intervalCount: number | null;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export declare function createPlanSwitchPreviewQuery(catalog: BillingPlanCatalog): QueryHandlerDef;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// plan-switch-preview — what switching to a tier would cost right now, for
|
|
2
|
+
// providers that switch plans in-app (previewPlanSwitch + applyPlanSwitch).
|
|
3
|
+
// Only registered when `createBillingFoundationFeature` gets a `catalog`.
|
|
4
|
+
import { UnprocessableError } from "@cosmicdrift/kumiko-framework/errors";
|
|
5
|
+
import * as z from "zod";
|
|
6
|
+
import { isNonEmptyStringArray } from "../checkout-core.js";
|
|
7
|
+
import { purchaseRolesOf } from "../plan-catalog.js";
|
|
8
|
+
import { preparePlanSwitch } from "../plan-switch.js";
|
|
9
|
+
export function createPlanSwitchPreviewQuery(catalog) {
|
|
10
|
+
const plans = catalog.plans;
|
|
11
|
+
if (!isNonEmptyStringArray(plans)) {
|
|
12
|
+
throw new Error("plan-switch-preview: catalog.plans must not be empty");
|
|
13
|
+
}
|
|
14
|
+
const schema = z.object({ tier: z.enum(plans) }).strict();
|
|
15
|
+
return {
|
|
16
|
+
name: "plan-switch-preview",
|
|
17
|
+
description: "Previews what switching the existing subscription to another plan tier costs right now (amount due, tax, credit, effective date) plus the target plan's recurring price. Only for providers that switch plans in-app.",
|
|
18
|
+
schema,
|
|
19
|
+
access: { roles: purchaseRolesOf(catalog) },
|
|
20
|
+
handler: async (query, ctx) => {
|
|
21
|
+
// @cast-boundary engine-payload — dispatcher-zod-validated payload
|
|
22
|
+
const { tier } = query.payload;
|
|
23
|
+
const prepared = await preparePlanSwitch(ctx, catalog, tier, "plan-switch-preview");
|
|
24
|
+
const { plugin } = prepared.provider;
|
|
25
|
+
if (!plugin.previewPlanSwitch || !plugin.applyPlanSwitch) {
|
|
26
|
+
throw new UnprocessableError("plan_switch_not_supported", {
|
|
27
|
+
i18nKey: "billing-foundation.errors.planSwitchNotSupported",
|
|
28
|
+
message: `subscription-foundation: provider "${prepared.provider.name}" switches plans on a hosted page and has no in-app preview`,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const preview = await plugin.previewPlanSwitch(ctx, {
|
|
32
|
+
providerSubscriptionId: prepared.subscription.providerSubscriptionId,
|
|
33
|
+
targetPriceId: prepared.target.priceId,
|
|
34
|
+
});
|
|
35
|
+
const { price } = prepared.target;
|
|
36
|
+
return {
|
|
37
|
+
preview,
|
|
38
|
+
target: {
|
|
39
|
+
tier,
|
|
40
|
+
unitAmount: price.unitAmount ?? 0,
|
|
41
|
+
currency: price.currency,
|
|
42
|
+
interval: price.interval,
|
|
43
|
+
intervalCount: price.intervalCount,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -27,6 +27,12 @@ export declare const processEventSchema: z.ZodObject<{
|
|
|
27
27
|
consentId: z.ZodOptional<z.ZodString>;
|
|
28
28
|
}, z.core.$strip>;
|
|
29
29
|
export type ProcessEventPayload = z.infer<typeof processEventSchema>;
|
|
30
|
+
export declare function isLateStateEvent(storedEvents: readonly {
|
|
31
|
+
readonly payload: unknown;
|
|
32
|
+
readonly metadata: {
|
|
33
|
+
readonly headers?: Record<string, unknown>;
|
|
34
|
+
};
|
|
35
|
+
}[], payload: ProcessEventPayload): boolean;
|
|
30
36
|
export declare function clampProviderEventAt(payload: ProcessEventPayload, now: Temporal.Instant): ProcessEventPayload;
|
|
31
37
|
export declare function appendSubscriptionEvent(ctx: HandlerContext, tenantId: string, rawPayload: ProcessEventPayload, now?: Temporal.Instant): Promise<{
|
|
32
38
|
readonly duplicate: boolean;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
// process-event — programmatic write
|
|
2
|
-
// (createSubscriptionWebhookRoute)
|
|
3
|
-
//
|
|
1
|
+
// process-event — programmatic write handler called by the webhook handler
|
|
2
|
+
// (createSubscriptionWebhookRoute) AFTER the plugin has verified the raw body
|
|
3
|
+
// and normalized it into a SubscriptionEvent.
|
|
4
4
|
//
|
|
5
|
-
// **ES
|
|
6
|
-
// 1. Idempotency
|
|
7
|
-
//
|
|
8
|
-
// (Stripe
|
|
9
|
-
//
|
|
10
|
-
// 2. Type
|
|
11
|
-
// →
|
|
12
|
-
// 3. ctx.unsafeAppendEvent —
|
|
13
|
-
// `read_subscriptions
|
|
5
|
+
// **ES pattern:**
|
|
6
|
+
// 1. Idempotency check: loads the subscription stream and scans for an
|
|
7
|
+
// already-seen `metadata.providerEventId`. A provider replay
|
|
8
|
+
// (Stripe retry storm) has the same event ID → duplicate=true,
|
|
9
|
+
// with no second append.
|
|
10
|
+
// 2. Type mapping: SubscriptionEvent.type (= normalized by the plugin)
|
|
11
|
+
// → one of the 5 ES event types.
|
|
12
|
+
// 3. ctx.unsafeAppendEvent — the inline projection materializes the
|
|
13
|
+
// `read_subscriptions` row in the same TX.
|
|
14
14
|
import { configuredPiiSubjectKms, encryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
15
15
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
16
16
|
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
@@ -21,9 +21,9 @@ import { INVOICE_PAID_EVENT_QN, INVOICE_PAYMENT_FAILED_EVENT_QN, SUBSCRIPTION_AG
|
|
|
21
21
|
import { getSubscriptionForTenant } from "../get-subscription-for-tenant.js";
|
|
22
22
|
import { BILLING_SUBSCRIPTION_LOCK_NAMESPACE, isSubscriptionBlockingCheckout, isSwitchableSubscriptionStatus, SubscriptionEventTypes, SubscriptionStatuses, } from "../web/constants.js";
|
|
23
23
|
// =============================================================================
|
|
24
|
-
// Input
|
|
25
|
-
//
|
|
26
|
-
// programmatic
|
|
24
|
+
// Input schema = the normalized SubscriptionEvent (without tenantId, which
|
|
25
|
+
// comes from event.user.tenantId — the webhook handler sets the
|
|
26
|
+
// programmatic user with the tenantId resolved by the plugin).
|
|
27
27
|
// =============================================================================
|
|
28
28
|
const eventTypeSchema = z.enum([
|
|
29
29
|
SubscriptionEventTypes.created,
|
|
@@ -65,7 +65,7 @@ const STATE_EVENT_TYPES = new Set([
|
|
|
65
65
|
SubscriptionEventTypes.updated,
|
|
66
66
|
SubscriptionEventTypes.canceled,
|
|
67
67
|
]);
|
|
68
|
-
function isLateStateEvent(storedEvents, payload) {
|
|
68
|
+
export function isLateStateEvent(storedEvents, payload) {
|
|
69
69
|
if (payload.providerEventAtIso === undefined || !STATE_EVENT_TYPES.has(payload.type)) {
|
|
70
70
|
return false;
|
|
71
71
|
}
|
|
@@ -79,7 +79,13 @@ function isLateStateEvent(storedEvents, payload) {
|
|
|
79
79
|
// A stored value that does not parse can never prove a newer event; skip it instead of
|
|
80
80
|
// failing every later delivery of this tenant's stream.
|
|
81
81
|
try {
|
|
82
|
-
|
|
82
|
+
const order = Temporal.Instant.compare(Temporal.Instant.from(storedAt), incoming);
|
|
83
|
+
if (order !== 0)
|
|
84
|
+
return order > 0;
|
|
85
|
+
// Provider timestamps have second resolution: on a tie a stored cancellation wins over
|
|
86
|
+
// a non-terminal state, so a reordered same-second `updated` cannot revive a canceled one.
|
|
87
|
+
return (propertyOf(stored.payload, "status") === SubscriptionStatuses.canceled &&
|
|
88
|
+
payload.status !== SubscriptionStatuses.canceled);
|
|
83
89
|
}
|
|
84
90
|
catch {
|
|
85
91
|
return false;
|
|
@@ -153,9 +159,9 @@ export async function appendSubscriptionEvent(ctx, tenantId, rawPayload, now = T
|
|
|
153
159
|
// read_subscriptions row with a single call. The subject is the
|
|
154
160
|
// TENANT (tenantOwned) — tenant-destroy's subject-keys stage
|
|
155
161
|
// (eraseSubjectKeys) erases exactly this key, so both copies become
|
|
156
|
-
// genuinely unreadable
|
|
162
|
+
// genuinely unreadable once that stage runs, not just
|
|
157
163
|
// "encrypted at rest". No adapter configured = engine off (fields
|
|
158
|
-
// stay plaintext
|
|
164
|
+
// stay plaintext) — mirrors how the
|
|
159
165
|
// event-store-executor treats an absent piiKms().
|
|
160
166
|
// ---------------------------------------------------------------
|
|
161
167
|
const piiKms = configuredPiiSubjectKms();
|
|
@@ -203,9 +209,9 @@ export async function appendSubscriptionEvent(ctx, tenantId, rawPayload, now = T
|
|
|
203
209
|
// Handler
|
|
204
210
|
// =============================================================================
|
|
205
211
|
//
|
|
206
|
-
// SystemAdmin-only:
|
|
207
|
-
// webhook
|
|
208
|
-
//
|
|
212
|
+
// SystemAdmin-only: this handler is called exclusively by the programmatic
|
|
213
|
+
// webhook handler (with an internal SystemUser), never directly by the
|
|
214
|
+
// tenant admin.
|
|
209
215
|
// One subscription aggregate exists per tenant, but a tenant can legitimately hold two provider
|
|
210
216
|
// subscriptions over time (a stale `incomplete` one superseded by a fresh checkout). A late or
|
|
211
217
|
// retried event of the old subscription must not overwrite the row of the live one, so an event of a
|