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