@cosmicdrift/kumiko-samples 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/README.md +1 -1
- package/package.json +2 -2
- package/packages/bundled-features/package.json +9 -2
- package/packages/bundled-features/src/agent-tools/changes.json +6 -0
- package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -1
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +9 -9
- package/packages/bundled-features/src/audit/changes.json +12 -0
- package/packages/bundled-features/src/audit/handlers/audit-events.ts +1 -1
- package/packages/bundled-features/src/audit/screens.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +10 -10
- package/packages/bundled-features/src/auth-email-password/feature.ts +6 -6
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +18 -18
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +12 -12
- package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +24 -26
- package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +34 -37
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +6 -7
- package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +1 -5
- package/packages/bundled-features/src/auth-email-password/index.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/lockout-store.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/seeding.ts +63 -66
- package/packages/bundled-features/src/auth-email-password/signup-token-store.ts +8 -8
- package/packages/bundled-features/src/auth-email-password/testing.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +57 -57
- package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +15 -16
- package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +11 -12
- package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +1 -1
- package/packages/bundled-features/src/auth-email-password/web/constants.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/web/default-topbar-actions.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -11
- package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +17 -17
- package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +3 -4
- package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +16 -17
- package/packages/bundled-features/src/auth-email-password/web/session.tsx +34 -34
- package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +3 -3
- package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/tenant-switcher.tsx +26 -27
- package/packages/bundled-features/src/auth-email-password/web/use-shell-user.ts +12 -13
- package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +17 -18
- package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +11 -12
- package/packages/bundled-features/src/auth-foundation/boot-checks.ts +4 -4
- package/packages/bundled-features/src/auth-foundation/resolve-anonymous-access.ts +4 -4
- package/packages/bundled-features/src/auth-foundation/types.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/changes.json +6 -0
- package/packages/bundled-features/src/auth-mfa/feature-exports.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/feature.ts +6 -0
- package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -5
- package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/token-secrets.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/web/client-plugin.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/web/i18n.ts +9 -4
- package/packages/bundled-features/src/auth-mfa/web/index.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +17 -16
- package/packages/bundled-features/src/auth-mfa/web/mfa-code-or-recovery-field.tsx +68 -0
- package/packages/bundled-features/src/auth-mfa/web/mfa-disable-dialog.tsx +23 -13
- package/packages/bundled-features/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +23 -13
- package/packages/bundled-features/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +15 -6
- package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +71 -24
- package/packages/bundled-features/src/billing-foundation/aggregate-id.ts +10 -11
- package/packages/bundled-features/src/billing-foundation/changes.json +55 -0
- package/packages/bundled-features/src/billing-foundation/checkout-core.ts +32 -42
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +8 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +14 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +3 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +12 -6
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +35 -19
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +12 -8
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +14 -8
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +5 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +8 -10
- package/packages/bundled-features/src/billing-foundation/entities.ts +27 -27
- package/packages/bundled-features/src/billing-foundation/events.ts +23 -21
- package/packages/bundled-features/src/billing-foundation/feature.ts +45 -44
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +3 -5
- package/packages/bundled-features/src/billing-foundation/handlers/billing-plans.query.ts +71 -6
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +19 -17
- package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +11 -12
- package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -4
- package/packages/bundled-features/src/billing-foundation/handlers/plan-switch-preview.query.ts +65 -0
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +29 -22
- package/packages/bundled-features/src/billing-foundation/handlers/start-plan-checkout.write.ts +44 -8
- package/packages/bundled-features/src/billing-foundation/handlers/switch-plan.write.ts +72 -82
- package/packages/bundled-features/src/billing-foundation/handlers/sync-subscription.write.ts +14 -1
- package/packages/bundled-features/src/billing-foundation/i18n.ts +22 -0
- package/packages/bundled-features/src/billing-foundation/index.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +62 -27
- package/packages/bundled-features/src/billing-foundation/plan-switch.ts +84 -0
- package/packages/bundled-features/src/billing-foundation/projection.ts +38 -37
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/tenant-destroy-hook.ts +7 -8
- package/packages/bundled-features/src/billing-foundation/types.ts +144 -66
- package/packages/bundled-features/src/billing-foundation/validate-options.ts +19 -0
- package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +123 -51
- package/packages/bundled-features/src/billing-foundation/web/checkout-consent-dialog.tsx +89 -42
- package/packages/bundled-features/src/billing-foundation/web/constants.ts +4 -0
- package/packages/bundled-features/src/billing-foundation/web/plan-price.ts +55 -0
- package/packages/bundled-features/src/billing-foundation/web/plan-switch-confirm-dialog.tsx +224 -0
- package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +1 -1
- package/packages/bundled-features/src/cap-counter/aggregate-id.ts +8 -9
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +5 -1
- package/packages/bundled-features/src/cap-counter/changes.json +18 -0
- package/packages/bundled-features/src/cap-counter/constants.ts +11 -12
- package/packages/bundled-features/src/cap-counter/enforce-cap.ts +66 -68
- package/packages/bundled-features/src/cap-counter/entity.ts +29 -29
- package/packages/bundled-features/src/cap-counter/feature.ts +34 -35
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +31 -31
- package/packages/bundled-features/src/cap-counter/handlers/mark-soft-warned.write.ts +4 -5
- package/packages/bundled-features/src/cap-counter/stock-cap-guard.ts +9 -2
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -5
- package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -3
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
- package/packages/bundled-features/src/channel-email/changes.json +6 -0
- package/packages/bundled-features/src/channel-email/feature.ts +1 -1
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +34 -31
- package/packages/bundled-features/src/channel-in-app/handlers/unread-count.query.ts +3 -4
- package/packages/bundled-features/src/compliance-profiles/feature.ts +15 -16
- package/packages/bundled-features/src/compliance-profiles/handlers/for-tenant.query.ts +3 -6
- package/packages/bundled-features/src/compliance-profiles/handlers/list-profiles.query.ts +5 -5
- package/packages/bundled-features/src/compliance-profiles/handlers/needs-profile.query.ts +4 -4
- package/packages/bundled-features/src/compliance-profiles/handlers/set-profile.write.ts +13 -14
- package/packages/bundled-features/src/compliance-profiles/handlers/sub-processors.query.ts +9 -11
- package/packages/bundled-features/src/compliance-profiles/resolve-for-tenant.ts +7 -7
- package/packages/bundled-features/src/compliance-profiles/schema/profile-selection.ts +18 -23
- package/packages/bundled-features/src/compliance-profiles/seeding.ts +14 -15
- package/packages/bundled-features/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +3 -3
- package/packages/bundled-features/src/compliance-profiles-ops/index.ts +2 -2
- package/packages/bundled-features/src/config/changes.json +12 -0
- package/packages/bundled-features/src/config/handlers/cascade.query.ts +4 -2
- package/packages/bundled-features/src/config/handlers/readiness.query.ts +7 -7
- package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +4 -5
- package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
- package/packages/bundled-features/src/config/handlers/values.query.ts +4 -2
- package/packages/bundled-features/src/config/resolver.ts +4 -4
- package/packages/bundled-features/src/config/table.ts +10 -10
- package/packages/bundled-features/src/config/write-helpers.ts +28 -9
- package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +23 -21
- package/packages/bundled-features/src/custom-fields/aggregate-id.ts +16 -17
- package/packages/bundled-features/src/custom-fields/entity.ts +49 -47
- package/packages/bundled-features/src/custom-fields/events.ts +7 -8
- package/packages/bundled-features/src/custom-fields/feature.ts +70 -72
- package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +9 -8
- package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +28 -26
- package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +6 -7
- package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +12 -13
- package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +25 -28
- package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +6 -6
- package/packages/bundled-features/src/custom-fields/lib/field-access.ts +2 -2
- package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
- package/packages/bundled-features/src/custom-fields/lib/quota.ts +1 -1
- package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -12
- package/packages/bundled-features/src/custom-fields/run-retention.ts +3 -3
- package/packages/bundled-features/src/custom-fields/schemas.ts +14 -14
- package/packages/bundled-features/src/custom-fields/web/client-plugin.tsx +2 -5
- package/packages/bundled-features/src/custom-fields/web/constants.ts +2 -2
- package/packages/bundled-features/src/custom-fields/web/custom-fields-form-section.tsx +26 -26
- package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +65 -67
- package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +3 -3
- package/packages/bundled-features/src/data-retention/_internal/parse-override.ts +2 -3
- package/packages/bundled-features/src/data-retention/feature.ts +11 -18
- package/packages/bundled-features/src/data-retention/handlers/policy-for.query.ts +6 -6
- package/packages/bundled-features/src/data-retention/keep-for.ts +17 -17
- package/packages/bundled-features/src/data-retention/override-schema.ts +13 -15
- package/packages/bundled-features/src/data-retention/presets.ts +18 -23
- package/packages/bundled-features/src/data-retention/resolve-for-tenant.ts +1 -1
- package/packages/bundled-features/src/data-retention/resolve-tenant-preset.ts +17 -16
- package/packages/bundled-features/src/data-retention/resolver.ts +33 -32
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +71 -58
- package/packages/bundled-features/src/data-retention/schema/tenant-retention-override.ts +13 -11
- package/packages/bundled-features/src/delivery/changes.json +30 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +9 -3
- package/packages/bundled-features/src/delivery/db/queries/preferences.ts +2 -2
- package/packages/bundled-features/src/delivery/delivery-service.ts +35 -5
- package/packages/bundled-features/src/delivery/feature.ts +3 -3
- package/packages/bundled-features/src/delivery/handlers/log.query.ts +1 -1
- package/packages/bundled-features/src/delivery/handlers/preferences.query.ts +20 -1
- package/packages/bundled-features/src/delivery/handlers/unsubscribe-page-chrome.query.ts +1 -1
- package/packages/bundled-features/src/delivery/tables.ts +9 -8
- package/packages/bundled-features/src/delivery/unsubscribe-link.ts +3 -1
- package/packages/bundled-features/src/delivery/unsubscribe-page-strings.ts +1 -0
- package/packages/bundled-features/src/delivery/unsubscribe-page.ts +6 -0
- package/packages/bundled-features/src/delivery/unsubscribe.ts +40 -19
- package/packages/bundled-features/src/delivery/upsert-preference.ts +3 -4
- package/packages/bundled-features/src/document-ingest-foundation/entity.ts +5 -5
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +2 -2
- package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
- package/packages/bundled-features/src/document-ingest-foundation/pages.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/tenant-destroy-hook.ts +4 -4
- package/packages/bundled-features/src/feature-toggles/compose-tier-resolver.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/feature.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/global-feature-state-table.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +5 -5
- package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/web/constants.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/presets.ts +1 -1
- package/packages/bundled-features/src/file-foundation/feature.ts +6 -6
- package/packages/bundled-features/src/file-foundation/index.ts +1 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -1
- package/packages/bundled-features/src/file-provider-inmemory/feature.ts +16 -16
- package/packages/bundled-features/src/file-provider-s3/env-named-feature.ts +0 -2
- package/packages/bundled-features/src/file-provider-s3/feature.ts +16 -16
- package/packages/bundled-features/src/file-provider-s3/lib/s3-provider.ts +6 -6
- package/packages/bundled-features/src/files/README.md +1 -1
- package/packages/bundled-features/src/files/feature.ts +6 -6
- package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +5 -5
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +24 -26
- package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
- package/packages/bundled-features/src/folders/feature.ts +1 -1
- package/packages/bundled-features/src/folders/web/folder-manager.tsx +2 -2
- package/packages/bundled-features/src/folders/web/tree.ts +5 -5
- package/packages/bundled-features/src/form-draft/changes.json +6 -0
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +3 -3
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +1 -1
- package/packages/bundled-features/src/form-draft/decrypt-draft.ts +4 -1
- package/packages/bundled-features/src/form-draft/entity.ts +3 -3
- package/packages/bundled-features/src/form-draft/feature.ts +4 -5
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
- package/packages/bundled-features/src/form-draft/release-file-refs.ts +6 -6
- package/packages/bundled-features/src/form-draft/schemas.ts +3 -3
- package/packages/bundled-features/src/form-draft/web/constants.ts +0 -2
- package/packages/bundled-features/src/host-routing/README.md +1 -1
- package/packages/bundled-features/src/host-routing/changes.json +6 -0
- package/packages/bundled-features/src/host-routing/dispatch.ts +16 -17
- package/packages/bundled-features/src/host-routing/options.ts +2 -1
- package/packages/bundled-features/src/inbound-mail-foundation/aggregate-id.ts +19 -19
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +18 -0
- package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +4 -3
- package/packages/bundled-features/src/inbound-mail-foundation/constants.ts +30 -33
- package/packages/bundled-features/src/inbound-mail-foundation/entities.ts +47 -48
- package/packages/bundled-features/src/inbound-mail-foundation/events.ts +54 -50
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +39 -39
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +8 -8
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +25 -26
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +7 -7
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +50 -51
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +8 -8
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +10 -10
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +3 -4
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +27 -15
- package/packages/bundled-features/src/inbound-mail-foundation/i18n.ts +0 -1
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +16 -15
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +31 -29
- package/packages/bundled-features/src/inbound-mail-foundation/provider-factory.ts +9 -9
- package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +45 -41
- package/packages/bundled-features/src/inbound-mail-foundation/screens.ts +0 -3
- package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +14 -16
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +70 -70
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +101 -84
- package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +13 -13
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +42 -40
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +20 -19
- package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +21 -21
- package/packages/bundled-features/src/jobs/changes.json +13 -0
- package/packages/bundled-features/src/jobs/db/queries/retention.ts +2 -2
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +3 -3
- package/packages/bundled-features/src/jobs/events.ts +1 -1
- package/packages/bundled-features/src/jobs/feature.ts +20 -11
- package/packages/bundled-features/src/jobs/handlers/detail.query.ts +4 -4
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/tenant-failures.query.ts +45 -40
- package/packages/bundled-features/src/jobs/handlers/tenant-runs.query.ts +63 -59
- package/packages/bundled-features/src/jobs/job-instant.ts +1 -1
- package/packages/bundled-features/src/jobs/job-run-logger.ts +19 -19
- package/packages/bundled-features/src/jobs/job-run-table.ts +3 -3
- package/packages/bundled-features/src/jobs/screens.ts +1 -1
- package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +1 -1
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +1 -1
- package/packages/bundled-features/src/ledger/entity.ts +5 -5
- package/packages/bundled-features/src/ledger/feature.ts +3 -3
- package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +3 -3
- package/packages/bundled-features/src/ledger/recurring.ts +12 -12
- package/packages/bundled-features/src/ledger/schemas.ts +1 -1
- package/packages/bundled-features/src/ledger/web/constants.ts +2 -2
- package/packages/bundled-features/src/ledger/web/index.ts +7 -7
- package/packages/bundled-features/src/legal-pages/README.md +1 -1
- package/packages/bundled-features/src/legal-pages/feature.ts +40 -41
- package/packages/bundled-features/src/legal-pages/markdown.ts +3 -3
- package/packages/bundled-features/src/legal-pages/web/client-plugin.ts +23 -24
- package/packages/bundled-features/src/legal-pages/web/index.ts +0 -2
- package/packages/bundled-features/src/mail-foundation/changes.json +6 -0
- package/packages/bundled-features/src/mail-foundation/feature.ts +31 -30
- package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +27 -28
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +20 -20
- package/packages/bundled-features/src/managed-pages/branding.ts +6 -6
- package/packages/bundled-features/src/managed-pages/feature.ts +62 -62
- package/packages/bundled-features/src/managed-pages/handlers/by-slug.query.ts +5 -6
- package/packages/bundled-features/src/managed-pages/handlers/by-tenant-published.query.ts +8 -8
- package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +5 -7
- package/packages/bundled-features/src/managed-pages/screens/page-screens.ts +11 -11
- package/packages/bundled-features/src/managed-pages/seeding.ts +8 -8
- package/packages/bundled-features/src/managed-pages/table.ts +16 -15
- package/packages/bundled-features/src/managed-pages/web/client-plugin.tsx +6 -6
- package/packages/bundled-features/src/managed-pages/web/i18n.ts +1 -1
- package/packages/bundled-features/src/notes-history/entity.ts +4 -4
- package/packages/bundled-features/src/notes-history/feature.ts +2 -2
- package/packages/bundled-features/src/notes-history-user-data/hooks.ts +1 -1
- package/packages/bundled-features/src/page-render/layout.ts +9 -9
- package/packages/bundled-features/src/page-render/markdown.ts +11 -11
- package/packages/bundled-features/src/page-render/security-headers.ts +7 -6
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +9 -6
- package/packages/bundled-features/src/personal-access-tokens/roles.ts +2 -2
- package/packages/bundled-features/src/readiness/handlers/status.query.ts +3 -3
- package/packages/bundled-features/src/renderer-foundation/api.ts +12 -13
- package/packages/bundled-features/src/renderer-foundation/constants.ts +2 -2
- package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
- package/packages/bundled-features/src/renderer-foundation/types.ts +11 -12
- package/packages/bundled-features/src/renderer-pdf/changes.json +6 -0
- package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +27 -10
- package/packages/bundled-features/src/secrets/feature.ts +9 -9
- package/packages/bundled-features/src/secrets/web/constants.ts +2 -2
- package/packages/bundled-features/src/seo/feature.ts +3 -3
- package/packages/bundled-features/src/sessions/constants.ts +1 -1
- package/packages/bundled-features/src/sessions/feature.ts +16 -11
- package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/sessions/schema/user-session.ts +4 -4
- package/packages/bundled-features/src/sessions/screens.ts +2 -2
- package/packages/bundled-features/src/sessions/session-callbacks.ts +2 -2
- package/packages/bundled-features/src/sessions/session-revoked-event.ts +2 -2
- package/packages/bundled-features/src/shared/assert-no-where-rule-in-ownership-write.ts +1 -1
- package/packages/bundled-features/src/shared/decrypt-stored-pii.ts +11 -1
- package/packages/bundled-features/src/shared/encrypt-for-direct-write.ts +1 -1
- package/packages/bundled-features/src/shared/index.ts +1 -1
- package/packages/bundled-features/src/shared/mail-template-body.ts +10 -3
- package/packages/bundled-features/src/shared/map-with-concurrency.ts +5 -4
- package/packages/bundled-features/src/shared/parent-visibility.ts +2 -2
- package/packages/bundled-features/src/shared/password-hashing.ts +1 -1
- package/packages/bundled-features/src/shared/signup-handover.ts +1 -2
- package/packages/bundled-features/src/shared/single-use-token-store.ts +1 -1
- package/packages/bundled-features/src/step-dispatcher/changes.json +6 -0
- package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -0
- package/packages/bundled-features/src/subscription-dodo/changes.json +14 -0
- package/packages/bundled-features/src/subscription-dodo/client.ts +45 -0
- package/packages/bundled-features/src/subscription-dodo/constants.ts +24 -0
- package/packages/bundled-features/src/subscription-dodo/feature.ts +207 -0
- package/packages/bundled-features/src/subscription-dodo/index.ts +16 -0
- package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +345 -0
- package/packages/bundled-features/src/subscription-dodo/runtime.ts +126 -0
- package/packages/bundled-features/src/subscription-dodo/verify-webhook.ts +262 -0
- package/packages/bundled-features/src/subscription-mollie/constants.ts +15 -17
- package/packages/bundled-features/src/subscription-mollie/feature.ts +41 -42
- package/packages/bundled-features/src/subscription-mollie/index.ts +3 -3
- package/packages/bundled-features/src/subscription-mollie/plugin-methods.ts +17 -16
- package/packages/bundled-features/src/subscription-mollie/verify-webhook.ts +34 -34
- package/packages/bundled-features/src/subscription-stripe/changes.json +12 -0
- package/packages/bundled-features/src/subscription-stripe/constants.ts +4 -6
- package/packages/bundled-features/src/subscription-stripe/feature.ts +45 -43
- package/packages/bundled-features/src/subscription-stripe/index.ts +6 -6
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +22 -27
- package/packages/bundled-features/src/subscription-stripe/runtime.ts +9 -8
- package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +62 -64
- package/packages/bundled-features/src/tags/feature.ts +3 -3
- package/packages/bundled-features/src/tags/screens.ts +1 -1
- package/packages/bundled-features/src/template-resolver/api.ts +41 -14
- package/packages/bundled-features/src/template-resolver/changes.json +48 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +1 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +27 -5
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/document-base.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +8 -7
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +19 -20
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +15 -5
- package/packages/bundled-features/src/template-resolver/handlers/shared.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +3 -3
- package/packages/bundled-features/src/template-resolver/handlers/tenant-override-counts.query.ts +9 -5
- package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +8 -6
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +44 -5
- package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +3 -4
- package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +4 -5
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +37 -26
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +7 -2
- package/packages/bundled-features/src/template-resolver/seeding.ts +36 -18
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +22 -4
- package/packages/bundled-features/src/template-resolver/table.ts +13 -14
- package/packages/bundled-features/src/template-resolver/template-delivery-policy.ts +0 -11
- package/packages/bundled-features/src/template-resolver/template-meta.ts +12 -4
- package/packages/bundled-features/src/template-resolver/template-preview.ts +11 -3
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +2 -2
- package/packages/bundled-features/src/template-resolver/web/constants.ts +7 -5
- package/packages/bundled-features/src/template-resolver/web/index.ts +7 -0
- package/packages/bundled-features/src/template-resolver/web/mail-layout-constants.ts +8 -0
- package/packages/bundled-features/src/tenant/changes.json +6 -0
- package/packages/bundled-features/src/tenant/feature.ts +7 -7
- package/packages/bundled-features/src/tenant/handlers/member-directory.query.ts +10 -4
- package/packages/bundled-features/src/tenant/handlers/members.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +8 -5
- package/packages/bundled-features/src/tenant/invitation-table.ts +37 -40
- package/packages/bundled-features/src/tenant/membership-table.ts +3 -3
- package/packages/bundled-features/src/tenant/schema/tenant.ts +7 -9
- package/packages/bundled-features/src/tenant/screens.ts +1 -1
- package/packages/bundled-features/src/tenant/seeding.ts +19 -18
- package/packages/bundled-features/src/tenant/testing.ts +5 -6
- package/packages/bundled-features/src/tenant/web/constants.ts +2 -2
- package/packages/bundled-features/src/tenant-handover/events.ts +5 -7
- package/packages/bundled-features/src/tenant-handover/feature.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/grant.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +8 -9
- package/packages/bundled-features/src/tenant-handover/signup-handover-provider.ts +1 -2
- package/packages/bundled-features/src/tenant-handover/transfer-graph.ts +4 -5
- package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/changes.json +6 -0
- package/packages/bundled-features/src/tenant-lifecycle/constants.ts +5 -0
- package/packages/bundled-features/src/tenant-lifecycle/feature.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +7 -3
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +1 -1
- package/packages/bundled-features/src/tenant-settings/constants.ts +1 -1
- package/packages/bundled-features/src/tenant-settings/tenant-defaults.ts +12 -5
- package/packages/bundled-features/src/tier-engine/aggregate-id.ts +15 -16
- package/packages/bundled-features/src/tier-engine/changes.json +6 -0
- package/packages/bundled-features/src/tier-engine/compose-app.ts +14 -14
- package/packages/bundled-features/src/tier-engine/entity.ts +18 -19
- package/packages/bundled-features/src/tier-engine/feature.ts +88 -87
- package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +6 -7
- package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +4 -4
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +12 -9
- package/packages/bundled-features/src/tier-engine/tier-resolver.ts +2 -2
- package/packages/bundled-features/src/tier-engine/trial.ts +13 -13
- package/packages/bundled-features/src/tier-engine/web/constants.ts +3 -3
- package/packages/bundled-features/src/tier-engine/web/index.ts +4 -4
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +8 -8
- package/packages/bundled-features/src/user/handlers/create.write.ts +4 -4
- package/packages/bundled-features/src/user/schema/user.ts +45 -44
- package/packages/bundled-features/src/user/seeding.ts +40 -40
- package/packages/bundled-features/src/user/testing.ts +3 -3
- package/packages/bundled-features/src/user-data-rights/audit-download.ts +27 -28
- package/packages/bundled-features/src/user-data-rights/boot-checks.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/feature.ts +112 -113
- package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +9 -9
- package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +8 -8
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +29 -30
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +40 -42
- package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +8 -9
- package/packages/bundled-features/src/user-data-rights/handlers/lift-restriction.write.ts +7 -8
- package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +4 -4
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts +30 -29
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +15 -18
- package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +42 -41
- package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +22 -19
- package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +8 -8
- package/packages/bundled-features/src/user-data-rights/index.ts +8 -10
- package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +3 -3
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +24 -27
- package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +169 -280
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +87 -108
- package/packages/bundled-features/src/user-data-rights/run-user-export.ts +67 -69
- package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +11 -11
- package/packages/bundled-features/src/user-data-rights/schema/download-token.ts +37 -41
- package/packages/bundled-features/src/user-data-rights/schema/export-job.ts +65 -67
- package/packages/bundled-features/src/user-data-rights/token-helpers.ts +21 -25
- package/packages/bundled-features/src/user-data-rights/web/client-plugin.tsx +6 -6
- package/packages/bundled-features/src/user-data-rights/web/confirm-deletion-screen.tsx +6 -5
- package/packages/bundled-features/src/user-data-rights/web/constants.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/web/index.ts +5 -5
- package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +14 -13
- package/packages/bundled-features/src/user-data-rights/web/public-deletion-gate.tsx +13 -12
- package/packages/bundled-features/src/user-data-rights/web/request-deletion-screen.tsx +6 -6
- package/packages/bundled-features/src/user-data-rights/zip-path.ts +41 -40
- package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +15 -15
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +3 -5
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +45 -51
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +2 -2
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +1 -1
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +3 -3
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +32 -31
- package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +13 -13
- package/packages/bundled-features/src/user-profile/index.ts +3 -3
- package/packages/bundled-features/src/user-profile/web/client-plugin.ts +1 -1
- package/packages/bundled-features/src/user-profile/web/constants.ts +1 -1
- package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +1 -1
- package/packages/bundled-features/src/waitlist/changes.json +18 -0
- package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +2 -1
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +5 -5
- package/packages/bundled-features/src/waitlist/import.ts +5 -0
- package/packages/bundled-features/src/waitlist/legacy-migrate.ts +22 -3
- package/packages/bundled-features/src/waitlist/link-on-signup.ts +20 -10
- package/packages/bundled-features/src/waitlist/options.ts +3 -0
- package/packages/bundled-features/src/workflow-runner/aggregate-id.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -5
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +4 -15
- package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +4 -4
- package/packages/bundled-features/src/workflow-runner/pending-projection.ts +6 -7
- package/packages/bundled-features/src/workflow-runner/runner.ts +6 -6
- package/packages/bundled-features/src/workflow-runner/tables.ts +6 -6
- package/packages/bundled-features/src/workflow-runner/workflow-registry.ts +1 -1
- package/samples/apps/styleguide/src/features/widgets/i18n.ts +11 -0
- package/samples/apps/styleguide/src/features/widgets/web/Widgets.tsx +24 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-constants.ts +5 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +90 -15
- package/samples/apps/use-all-bundled/e2e-billing-gallery/server.ts +16 -6
- package/samples/apps/use-all-bundled/feature-manifest.json +120 -3
- package/samples/apps/use-all-bundled/src/run-config.ts +9 -3
- package/samples/recipes/auth-mfa/README.md +5 -0
- package/samples/recipes/auth-mfa/package.json +5 -2
- package/samples/recipes/auth-mfa/src/web/confirmation-code-step.tsx +49 -0
- package/samples/recipes/auth-mfa/tsconfig.json +8 -0
- package/samples/recipes/env-composition/README.md +3 -1
- package/samples/recipes/env-composition/src/env.ts +2 -0
- package/samples/recipes/legal-pages/README.md +1 -1
- package/samples/recipes/rate-limiting/README.md +18 -0
- package/samples/recipes/rate-limiting/src/feature.ts +12 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/stored-user-email.ts +0 -12
package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts
CHANGED
|
@@ -14,11 +14,9 @@ import * as z from "zod";
|
|
|
14
14
|
import { subscriptionAggregateId } from "../aggregate-id.ts";
|
|
15
15
|
import {
|
|
16
16
|
CONTRACT_TERMINATION_DECLARED_EVENT_QN,
|
|
17
|
-
CONTRACT_TERMINATION_REQUESTED_EVENT_QN,
|
|
18
17
|
CONTRACT_TERMINATION_REVOKED_EVENT_QN,
|
|
19
18
|
type ContractTerminationRevokedPayload,
|
|
20
19
|
contractTerminationDeclaredPayloadSchema,
|
|
21
|
-
contractTerminationRequestedPayloadSchema,
|
|
22
20
|
SUBSCRIPTION_AGGREGATE_TYPE,
|
|
23
21
|
} from "../events.ts";
|
|
24
22
|
import { getSubscriptionForTenant } from "../get-subscription-for-tenant.ts";
|
|
@@ -26,7 +24,7 @@ import type { ConsumerProtectionOptions, ResolvedBillingFoundationOptions } from
|
|
|
26
24
|
import { SubscriptionFoundationHandlers } from "../web/constants.ts";
|
|
27
25
|
import type { ConsentLocale } from "./consent-text.ts";
|
|
28
26
|
import { notifyOperator } from "./termination-notify.ts";
|
|
29
|
-
import { hasRevocation, resumeAtProvider } from "./termination-record.ts";
|
|
27
|
+
import { findRecordedTermination, hasRevocation, resumeAtProvider } from "./termination-record.ts";
|
|
30
28
|
import type { OperatorNoticeReason } from "./termination-texts.ts";
|
|
31
29
|
|
|
32
30
|
export const REVOCATION_PATHS: Readonly<Record<ConsentLocale, string>> = {
|
|
@@ -152,10 +150,7 @@ export function createRecordContractTerminationRevocationHandler(
|
|
|
152
150
|
}
|
|
153
151
|
if (hasRevocation(stream.events, payload.requestId)) return outcome("already-revoked");
|
|
154
152
|
|
|
155
|
-
const requested = stream.events
|
|
156
|
-
.filter((e) => e.type === CONTRACT_TERMINATION_REQUESTED_EVENT_QN)
|
|
157
|
-
.map((e) => contractTerminationRequestedPayloadSchema.safeParse(e.payload).data)
|
|
158
|
-
.find((candidate) => candidate?.requestId === payload.requestId);
|
|
153
|
+
const requested = findRecordedTermination(stream.events, payload.requestId);
|
|
159
154
|
const subscription = await getSubscriptionForTenant(ctx, tenantId);
|
|
160
155
|
const effectiveAtIso = requested?.effectiveAtIso ?? subscription?.currentPeriodEnd.toString();
|
|
161
156
|
const now = options.now();
|
|
@@ -175,6 +170,17 @@ export function createRecordContractTerminationRevocationHandler(
|
|
|
175
170
|
aggregateId: subscriptionAggregateId(tenantId),
|
|
176
171
|
aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
|
|
177
172
|
});
|
|
173
|
+
// A cancel recorded between the first fetch and now was never resumed above.
|
|
174
|
+
const requestedMeanwhile =
|
|
175
|
+
requested === undefined
|
|
176
|
+
? findRecordedTermination(streamAfterProviderCall.events, payload.requestId)
|
|
177
|
+
: undefined;
|
|
178
|
+
if (requestedMeanwhile?.providerCancel === "period-end") {
|
|
179
|
+
reasons.push(
|
|
180
|
+
...(await resumeAtProvider(ctx, await getSubscriptionForTenant(ctx, tenantId))),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
const providerCancel = (requested ?? requestedMeanwhile)?.providerCancel;
|
|
178
184
|
const revoked: ContractTerminationRevokedPayload = {
|
|
179
185
|
requestId: payload.requestId,
|
|
180
186
|
revokedAtIso: now.toString(),
|
|
@@ -194,7 +200,7 @@ export function createRecordContractTerminationRevocationHandler(
|
|
|
194
200
|
},
|
|
195
201
|
reasons,
|
|
196
202
|
tenantId,
|
|
197
|
-
...(
|
|
203
|
+
...(providerCancel !== undefined && { providerCancel }),
|
|
198
204
|
});
|
|
199
205
|
return outcome("revoked");
|
|
200
206
|
},
|
package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts
CHANGED
|
@@ -91,6 +91,7 @@ export const OPERATOR_NOTICE_REASONS = [
|
|
|
91
91
|
"recording_failed",
|
|
92
92
|
"withdrawal_period_expired",
|
|
93
93
|
"receipt_rate_limited",
|
|
94
|
+
"receipt_not_sent",
|
|
94
95
|
"revoked",
|
|
95
96
|
"revocation_resume_manual",
|
|
96
97
|
"revocation_resume_failed",
|
|
@@ -145,6 +146,8 @@ export const TERMINATION_TEXTS: Readonly<Record<ConsentLocale, TerminationTexts>
|
|
|
145
146
|
"Widerruf nach Ablauf der Widerrufsfrist: zum Periodenende gekündigt, Anspruch prüfen",
|
|
146
147
|
receipt_rate_limited:
|
|
147
148
|
"Keine Eingangsbestätigung versendet: Limit pro Empfängeradresse erreicht",
|
|
149
|
+
receipt_not_sent:
|
|
150
|
+
"Keine Eingangsbestätigung versendet: Limit-Prüfung nicht verfügbar (Redis-Fehler oder fehlende Konfiguration)",
|
|
148
151
|
revoked:
|
|
149
152
|
"Die Erklärung wurde vom Postfachinhaber über „Diese Kündigung war ich nicht“ zurückgenommen",
|
|
150
153
|
revocation_resume_manual:
|
|
@@ -256,6 +259,8 @@ export const TERMINATION_TEXTS: Readonly<Record<ConsentLocale, TerminationTexts>
|
|
|
256
259
|
withdrawal_period_expired:
|
|
257
260
|
"Withdrawal after the withdrawal period: cancelled at period end, review the claim",
|
|
258
261
|
receipt_rate_limited: "No receipt mail sent: the per-recipient limit was reached",
|
|
262
|
+
receipt_not_sent:
|
|
263
|
+
"No receipt mail sent: the limit check was unavailable (limiter error or missing configuration)",
|
|
259
264
|
revoked:
|
|
260
265
|
'The declaration was taken back by the mailbox owner ("I did not request this cancellation")',
|
|
261
266
|
revocation_resume_manual:
|
package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
|
-
import {
|
|
2
|
+
import { SUBSCRIPTION_CREATED_EVENT_QN } from "../events.ts";
|
|
3
3
|
|
|
4
4
|
// § 355 BGB: the withdrawal period is 14 days from conclusion of the contract.
|
|
5
5
|
export const WITHDRAWAL_PERIOD_DAYS = 14;
|
|
6
6
|
const HOURS_PER_DAY = 24;
|
|
7
7
|
|
|
8
|
-
const PERIOD_START_EVENT_TYPES: ReadonlySet<string> = new Set([
|
|
9
|
-
CHECKOUT_CONSENT_RECORDED_EVENT_QN,
|
|
10
|
-
SUBSCRIPTION_CREATED_EVENT_QN,
|
|
11
|
-
]);
|
|
12
|
-
|
|
13
8
|
type PeriodStartEvent = { readonly type: string; readonly createdAt: Temporal.Instant };
|
|
14
9
|
|
|
15
|
-
/** The period runs from the latest
|
|
16
|
-
*
|
|
10
|
+
/** The period runs from the latest subscription start on the stream. A checkout
|
|
11
|
+
* consent alone never restarts it: it is written before the buyer reaches the
|
|
12
|
+
* provider, so an abandoned plan-change checkout would otherwise reopen the
|
|
13
|
+
* period of a long-concluded contract. A stream without a subscription start
|
|
14
|
+
* has no provable start and counts as expired. */
|
|
17
15
|
export function isWithinWithdrawalPeriod(
|
|
18
16
|
events: readonly PeriodStartEvent[],
|
|
19
17
|
now: Temporal.Instant,
|
|
20
18
|
): boolean {
|
|
21
19
|
let latestStart: Temporal.Instant | undefined;
|
|
22
20
|
for (const event of events) {
|
|
23
|
-
if (
|
|
21
|
+
if (event.type !== SUBSCRIPTION_CREATED_EVENT_QN) continue;
|
|
24
22
|
if (
|
|
25
23
|
latestStart === undefined ||
|
|
26
24
|
event.createdAt.epochMilliseconds > latestStart.epochMilliseconds
|
|
@@ -28,7 +26,7 @@ export function isWithinWithdrawalPeriod(
|
|
|
28
26
|
latestStart = event.createdAt;
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
|
-
// skip: no
|
|
29
|
+
// skip: no subscription-created event, nothing proves a running period
|
|
32
30
|
if (latestStart === undefined) return false;
|
|
33
31
|
const periodEnd = latestStart.add({ hours: WITHDRAWAL_PERIOD_DAYS * HOURS_PER_DAY });
|
|
34
32
|
return now.epochMilliseconds <= periodEnd.epochMilliseconds;
|
|
@@ -4,39 +4,39 @@ import {
|
|
|
4
4
|
createTextField,
|
|
5
5
|
createTimestampField,
|
|
6
6
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
7
|
-
// `subscription` — current state
|
|
7
|
+
// `subscription` — current state per platform tenant (= read model)
|
|
8
8
|
// =============================================================================
|
|
9
9
|
//
|
|
10
|
-
// Inline
|
|
11
|
-
// apply (
|
|
12
|
-
//
|
|
10
|
+
// Inline projection target. Written by the `subscription` projection-
|
|
11
|
+
// apply (see feature.ts), NEVER directly by the handler. Source of truth is
|
|
12
|
+
// the event-store stream `subscription` with aggregate-id =
|
|
13
13
|
// uuidv5(SUBSCRIPTION_NAMESPACE, tenantId).
|
|
14
14
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
15
|
+
// ONE row per platform tenant. The aggregate ID is deterministic so that
|
|
16
|
+
// webhook replays (Stripe sends up to 5 times in 4 hours when delivery is
|
|
17
|
+
// stalled) write to the same stream instead of creating two rows.
|
|
18
18
|
//
|
|
19
|
-
// **
|
|
20
|
-
// - providerName: "stripe" / "mollie" —
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
// - providerCustomerId / providerSubscriptionId: provider-
|
|
24
|
-
// IDs.
|
|
19
|
+
// **Fields:**
|
|
20
|
+
// - providerName: "stripe" / "mollie" — the provider holding the
|
|
21
|
+
// subscription. A provider change is a new event on the same stream;
|
|
22
|
+
// the projection overwrites the value.
|
|
23
|
+
// - providerCustomerId / providerSubscriptionId: provider-specific IDs.
|
|
25
24
|
// - status: active / past_due / canceled / trialing / incomplete —
|
|
26
|
-
//
|
|
27
|
-
// - tier: "free" / "pro" / ... —
|
|
28
|
-
// price-to-tier
|
|
29
|
-
// - currentPeriodEnd:
|
|
25
|
+
// normalized across providers.
|
|
26
|
+
// - tier: "free" / "pro" / ... — consumed by the tier engine. Resolved
|
|
27
|
+
// from the price-to-tier map in the plugin.
|
|
28
|
+
// - currentPeriodEnd: when the current billing period ends.
|
|
30
29
|
// - cancelAt: when a scheduled cancellation ends the subscription;
|
|
31
30
|
// nullable because a renewing subscription has none.
|
|
32
31
|
//
|
|
33
|
-
// **
|
|
34
|
-
// - invoice-history, payment-method, line-items, tax-info
|
|
35
|
-
//
|
|
32
|
+
// **Not included here:**
|
|
33
|
+
// - invoice-history, payment-method, line-items, tax-info — the tenant
|
|
34
|
+
// fetches all of these directly from the provider via a
|
|
35
|
+
// customer-portal-session.
|
|
36
36
|
//
|
|
37
|
-
// **Audit/event
|
|
38
|
-
// stream —
|
|
39
|
-
// audit
|
|
37
|
+
// **Audit/event history:** lives in the event store under the `subscription`
|
|
38
|
+
// stream — there is NO separate `subscription-event` table (= ES is the
|
|
39
|
+
// audit source of truth and can be replayed through upcasters).
|
|
40
40
|
export const subscriptionEntity = createEntity({
|
|
41
41
|
table: "read_subscriptions",
|
|
42
42
|
fields: {
|
|
@@ -47,10 +47,10 @@ export const subscriptionEntity = createEntity({
|
|
|
47
47
|
reason: "technical_reference",
|
|
48
48
|
}),
|
|
49
49
|
// `personal: "tenant"` (not `encrypted`): the field must crypto-shred when
|
|
50
|
-
// eraseSubjectKeys erases the tenant's subject key on tenant-destroy
|
|
51
|
-
//
|
|
50
|
+
// eraseSubjectKeys erases the tenant's subject key on tenant-destroy.
|
|
51
|
+
// `encrypted: true` uses the app-wide master key instead — that
|
|
52
52
|
// key is never erased per-tenant, so it would only add encryption-at-
|
|
53
|
-
// rest, not the erasure guarantee
|
|
53
|
+
// rest, not the erasure guarantee.
|
|
54
54
|
// maxLength 1000, not 200: the stored value is PII-ciphertext
|
|
55
55
|
// (`kumiko-pii:v1:<subject>:<blob>`), not the raw provider id — a
|
|
56
56
|
// 200-char plaintext id becomes ~300+ chars of ciphertext. Matches
|
|
@@ -112,7 +112,7 @@ export const paymentEntity = createEntity({
|
|
|
112
112
|
reason: "technical_reference",
|
|
113
113
|
}),
|
|
114
114
|
// Same `personal: "tenant"` rationale as subscriptionEntity above —
|
|
115
|
-
// crypto-shreds on tenant-destroy
|
|
115
|
+
// crypto-shreds on tenant-destroy via eraseSubjectKeys.
|
|
116
116
|
providerCustomerId: createTextField({
|
|
117
117
|
required: true,
|
|
118
118
|
maxLength: 1000,
|
|
@@ -1,25 +1,25 @@
|
|
|
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
|
|
|
13
13
|
import * as z from "zod";
|
|
14
14
|
import { BILLING_FOUNDATION_FEATURE, SubscriptionStatuses } from "./web/constants.ts";
|
|
15
15
|
import { CONSENT_LOCALES } from "./web/consumer-protection/consent-locale.ts";
|
|
16
16
|
|
|
17
|
-
// Aggregate
|
|
18
|
-
//
|
|
17
|
+
// Aggregate type for the event store. One subscription per tenant is
|
|
18
|
+
// one stream; the subscriptionAggregateId helper provides the stream ID.
|
|
19
19
|
export const SUBSCRIPTION_AGGREGATE_TYPE = "subscription" as const;
|
|
20
20
|
|
|
21
|
-
// Event-name
|
|
22
|
-
// FQN (
|
|
21
|
+
// Event-name constants — short form (for r.defineEvent) and qualified
|
|
22
|
+
// FQN (for ctx.unsafeAppendEvent and projection-apply keys).
|
|
23
23
|
export const SUBSCRIPTION_CREATED_EVENT_SHORT = "subscription-created" as const;
|
|
24
24
|
export const SUBSCRIPTION_UPDATED_EVENT_SHORT = "subscription-updated" as const;
|
|
25
25
|
export const SUBSCRIPTION_CANCELED_EVENT_SHORT = "subscription-canceled" as const;
|
|
@@ -37,7 +37,7 @@ export const INVOICE_PAID_EVENT_QN =
|
|
|
37
37
|
export const INVOICE_PAYMENT_FAILED_EVENT_QN =
|
|
38
38
|
`${BILLING_FOUNDATION_FEATURE}:event:${INVOICE_PAYMENT_FAILED_EVENT_SHORT}` as const;
|
|
39
39
|
|
|
40
|
-
// Status
|
|
40
|
+
// Status enum for event payloads (= the provider's subscription-state snapshot).
|
|
41
41
|
const statusEnum = z.enum([
|
|
42
42
|
SubscriptionStatuses.active,
|
|
43
43
|
SubscriptionStatuses.trialing,
|
|
@@ -46,10 +46,11 @@ const statusEnum = z.enum([
|
|
|
46
46
|
SubscriptionStatuses.incomplete,
|
|
47
47
|
]);
|
|
48
48
|
|
|
49
|
-
// Common payload —
|
|
50
|
-
// snapshot.
|
|
49
|
+
// Common payload — all five events carry the same subscription-state
|
|
50
|
+
// snapshot. The event type identifies what happened; the payload contains
|
|
51
|
+
// the resulting state.
|
|
51
52
|
// The raw provider event is deliberately not stored: it carries plaintext
|
|
52
|
-
// PII and event headers
|
|
53
|
+
// PII, and event headers cannot be crypto-shredded.
|
|
53
54
|
export const subscriptionEventPayloadSchema = z.object({
|
|
54
55
|
providerName: z.string().min(1).max(50),
|
|
55
56
|
// 1000, not 200: these two are `tenantOwned: true` on the entity (see
|
|
@@ -72,10 +73,10 @@ export const subscriptionEventPayloadSchema = z.object({
|
|
|
72
73
|
});
|
|
73
74
|
export type SubscriptionEventPayload = z.infer<typeof subscriptionEventPayloadSchema>;
|
|
74
75
|
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
// Idempotency
|
|
78
|
-
//
|
|
76
|
+
// Header shape — persisted in the event store as event.metadata.headers
|
|
77
|
+
// (an open-shape JSONB column containing primitives only).
|
|
78
|
+
// Idempotency anchor: providerEventId is checked per provider before append
|
|
79
|
+
// to determine whether it has already been seen.
|
|
79
80
|
export type SubscriptionEventHeaders = {
|
|
80
81
|
readonly providerEventId: string;
|
|
81
82
|
readonly providerName: string;
|
|
@@ -140,6 +141,7 @@ export const checkoutConsentRecordedPayloadSchema = z.object({
|
|
|
140
141
|
termsTemplateVersion: z.number(),
|
|
141
142
|
locale: z.string().min(2).max(35),
|
|
142
143
|
actorUserId: z.string().min(1),
|
|
144
|
+
providerName: z.string().min(1).max(100).optional(),
|
|
143
145
|
itemLabelKey: z.string().min(1).max(200).optional(),
|
|
144
146
|
itemLabelParams: z.record(z.string(), z.union([z.string().max(100), z.number()])).optional(),
|
|
145
147
|
});
|
|
@@ -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
|
|
|
53
52
|
import {
|
|
54
53
|
defineFeature,
|
|
@@ -80,6 +79,7 @@ import { createBillingPlansQuery } from "./handlers/billing-plans.query.ts";
|
|
|
80
79
|
import { createCheckoutSessionHandler } from "./handlers/create-checkout-session.write.ts";
|
|
81
80
|
import { createPortalSessionHandler } from "./handlers/create-portal-session.write.ts";
|
|
82
81
|
import { listSubscriptionsQuery } from "./handlers/list-subscriptions.query.ts";
|
|
82
|
+
import { createPlanSwitchPreviewQuery } from "./handlers/plan-switch-preview.query.ts";
|
|
83
83
|
import { createProcessEventHandler } from "./handlers/process-event.write.ts";
|
|
84
84
|
import { processPaymentEventHandler } from "./handlers/process-payment-event.write.ts";
|
|
85
85
|
import { createStartPlanCheckoutHandler } from "./handlers/start-plan-checkout.write.ts";
|
|
@@ -138,7 +138,7 @@ export function createBillingFoundationFeature<TTier extends string = string>(
|
|
|
138
138
|
BILLING_FOUNDATION_FEATURE,
|
|
139
139
|
(r) => {
|
|
140
140
|
r.describe(
|
|
141
|
-
"Plugin host for subscription billing
|
|
141
|
+
"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.",
|
|
142
142
|
);
|
|
143
143
|
r.uiHints({
|
|
144
144
|
displayLabel: "Billing · Foundation",
|
|
@@ -199,19 +199,19 @@ export function createBillingFoundationFeature<TTier extends string = string>(
|
|
|
199
199
|
},
|
|
200
200
|
});
|
|
201
201
|
|
|
202
|
-
// Plugin extension
|
|
202
|
+
// Plugin extension point. Provider plugins register here.
|
|
203
203
|
r.extendsRegistrar(SUBSCRIPTION_PROVIDER_EXTENSION, {
|
|
204
204
|
onRegister: () => {
|
|
205
205
|
// No side-effects at register-time.
|
|
206
206
|
},
|
|
207
207
|
});
|
|
208
208
|
|
|
209
|
-
// Custom write
|
|
210
|
-
// - process-event: programmatic entry
|
|
211
|
-
//
|
|
209
|
+
// Custom write handlers:
|
|
210
|
+
// - process-event: programmatic entry point called by the webhook
|
|
211
|
+
// handler; dispatches to the type-matched appendEvent
|
|
212
212
|
// - create-checkout-session: bare priceId-driven checkout (hardened,
|
|
213
213
|
// see checkout-core)
|
|
214
|
-
// - create-portal-session: Tenant
|
|
214
|
+
// - create-portal-session: Tenant Admin "Manage Subscription" flow
|
|
215
215
|
r.writeHandler(createProcessEventHandler(widened.now));
|
|
216
216
|
r.writeHandler(createCheckoutSessionHandler(widened));
|
|
217
217
|
r.writeHandler(createPortalSessionHandler(widened));
|
|
@@ -240,6 +240,7 @@ export function createBillingFoundationFeature<TTier extends string = string>(
|
|
|
240
240
|
if (widened.catalog) {
|
|
241
241
|
const { catalog } = widened;
|
|
242
242
|
r.queryHandler(createBillingPlansQuery(widened, catalog));
|
|
243
|
+
r.queryHandler(createPlanSwitchPreviewQuery(catalog));
|
|
243
244
|
r.writeHandler(createStartPlanCheckoutHandler(widened, catalog));
|
|
244
245
|
r.writeHandler(createSwitchPlanHandler(widened, catalog));
|
|
245
246
|
r.screen(createBillingPlansScreen(catalog.viewRoles));
|
|
@@ -1,6 +1,4 @@
|
|
|
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
|
|
|
5
3
|
import {
|
|
6
4
|
configuredPiiSubjectKms,
|
|
@@ -27,9 +25,9 @@ export type SubscriptionView = {
|
|
|
27
25
|
readonly lastChangedAt: Temporal.Instant;
|
|
28
26
|
};
|
|
29
27
|
|
|
30
|
-
/**
|
|
31
|
-
* aggregate
|
|
32
|
-
*
|
|
28
|
+
/** Returns the tenant's only subscription row (deterministic
|
|
29
|
+
* aggregate ID), or null if the tenant has never subscribed. The status
|
|
30
|
+
* may be active/canceled/past_due/etc.; the caller decides what to do. */
|
|
33
31
|
export async function getSubscriptionForTenant(
|
|
34
32
|
ctx: HandlerContext,
|
|
35
33
|
tenantId: string,
|
|
@@ -2,18 +2,29 @@
|
|
|
2
2
|
// benefits, the caller's current tier and per-plan action. Only registered
|
|
3
3
|
// when `createBillingFoundationFeature` gets a `catalog`.
|
|
4
4
|
|
|
5
|
-
import type { QueryHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
|
+
import type { HandlerContext, QueryHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
6
6
|
import * as z from "zod";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
filterOfferedProviders,
|
|
9
|
+
listCatalogProviders,
|
|
10
|
+
type ResolvedProvider,
|
|
11
|
+
} from "../checkout-core.ts";
|
|
8
12
|
import {
|
|
9
13
|
type CONSENT_LOCALES,
|
|
10
14
|
CONSENT_TEXTS,
|
|
11
15
|
consentTextVersion,
|
|
12
16
|
} from "../consumer-protection/consent-text.ts";
|
|
13
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
type BillingPlansBase,
|
|
19
|
+
buildProviderPlanViews,
|
|
20
|
+
loadBillingPlansBase,
|
|
21
|
+
type ProviderPlanViews,
|
|
22
|
+
toBillingPlansResult,
|
|
23
|
+
} from "../plan-catalog.ts";
|
|
14
24
|
import {
|
|
15
25
|
type BillingPlanCatalog,
|
|
16
26
|
type BillingPlansResult,
|
|
27
|
+
type BillingProviderView,
|
|
17
28
|
type ConsumerProtectionOptions,
|
|
18
29
|
isLegalLinkSet,
|
|
19
30
|
type LegalLinkSet,
|
|
@@ -29,12 +40,12 @@ export function createBillingPlansQuery(
|
|
|
29
40
|
return {
|
|
30
41
|
name: "billing-plans",
|
|
31
42
|
description:
|
|
32
|
-
"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.",
|
|
43
|
+
"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`.",
|
|
33
44
|
schema: billingPlansSchema,
|
|
34
45
|
access: { roles: catalog.viewRoles },
|
|
35
46
|
handler: async (_query, ctx): Promise<BillingPlansResult> => {
|
|
36
|
-
const
|
|
37
|
-
const result = await
|
|
47
|
+
const base = await loadBillingPlansBase(ctx, catalog, options.now);
|
|
48
|
+
const result = await resolveProviderPlans(ctx, options, catalog, base);
|
|
38
49
|
const { consumerProtection } = options;
|
|
39
50
|
if (!consumerProtection) return result;
|
|
40
51
|
return {
|
|
@@ -51,6 +62,60 @@ export function createBillingPlansQuery(
|
|
|
51
62
|
};
|
|
52
63
|
}
|
|
53
64
|
|
|
65
|
+
// A tenant with a blocking subscription sees only the provider holding it
|
|
66
|
+
// (a second provider's prices would be a checkout it cannot start); otherwise
|
|
67
|
+
// every live provider is offered.
|
|
68
|
+
async function resolveProviderPlans(
|
|
69
|
+
ctx: HandlerContext,
|
|
70
|
+
options: ResolvedBillingFoundationOptions,
|
|
71
|
+
catalog: BillingPlanCatalog,
|
|
72
|
+
base: BillingPlansBase,
|
|
73
|
+
): Promise<BillingPlansResult> {
|
|
74
|
+
const candidates = listCatalogProviders(ctx, catalog);
|
|
75
|
+
if (base.blockingSubscription && base.subscriptionView) {
|
|
76
|
+
const holder = candidates.find((c) => c.name === base.subscriptionView?.providerName);
|
|
77
|
+
return toBillingPlansResult(
|
|
78
|
+
catalog,
|
|
79
|
+
base,
|
|
80
|
+
await buildProviderPlanViews(ctx, holder?.plugin ?? null, catalog, base),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const offered = await filterOfferedProviders(ctx, candidates);
|
|
85
|
+
const first = offered[0] ?? candidates[0] ?? null;
|
|
86
|
+
const firstViews = await buildProviderPlanViews(ctx, first?.plugin ?? null, catalog, base);
|
|
87
|
+
const result = toBillingPlansResult(catalog, base, firstViews);
|
|
88
|
+
|
|
89
|
+
const providerTexts = options.consumerProtection?.providerTexts;
|
|
90
|
+
const hasNote = first !== null && providerTexts?.[first.name]?.checkoutNote !== undefined;
|
|
91
|
+
if (offered.length <= 1 && !(offered.length === 1 && hasNote)) return result;
|
|
92
|
+
|
|
93
|
+
const providers = await Promise.all(
|
|
94
|
+
offered.map((provider, index) =>
|
|
95
|
+
providerView(ctx, catalog, base, provider, providerTexts, index === 0 ? firstViews : null),
|
|
96
|
+
),
|
|
97
|
+
);
|
|
98
|
+
return { ...result, providers };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function providerView(
|
|
102
|
+
ctx: HandlerContext,
|
|
103
|
+
catalog: BillingPlanCatalog,
|
|
104
|
+
base: BillingPlansBase,
|
|
105
|
+
provider: ResolvedProvider,
|
|
106
|
+
providerTexts: ConsumerProtectionOptions["providerTexts"],
|
|
107
|
+
known: ProviderPlanViews | null,
|
|
108
|
+
): Promise<BillingProviderView> {
|
|
109
|
+
const views = known ?? (await buildProviderPlanViews(ctx, provider.plugin, catalog, base));
|
|
110
|
+
const note = providerTexts?.[provider.name]?.checkoutNote;
|
|
111
|
+
return {
|
|
112
|
+
name: provider.name,
|
|
113
|
+
displayName: provider.plugin.displayName ?? provider.name,
|
|
114
|
+
...(note !== undefined && { note }),
|
|
115
|
+
plans: views.plans,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
54
119
|
function legalLinksPerLocale(
|
|
55
120
|
legalLinks: ConsumerProtectionOptions["legalLinks"],
|
|
56
121
|
): Readonly<Record<(typeof CONSENT_LOCALES)[number], LegalLinkSet>> {
|