@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
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.388.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "Preferences query reports renamed notification types under the current name",
|
|
6
|
+
"detail": "Rows stored under a former notification name are returned with the current name and collapsed per channel with the delivery precedence (current-name row wins, otherwise any disabled row disables), so settings screens show the effective state."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.388.0",
|
|
10
|
+
"type": "fix",
|
|
11
|
+
"title": "Inline mail image retries on transient storage errors",
|
|
12
|
+
"detail": "Only a missing file storage for the file's tenant sends the mail without the inline image; any other read error (timeout, provider failure, DB) is rethrown so the send job retries instead of mailing without the logo."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.388.0",
|
|
16
|
+
"type": "fix",
|
|
17
|
+
"title": "Unsubscribe tenant origin resolved once per notify call",
|
|
18
|
+
"detail": "The app's tenantOrigin callback runs once per notify call and tenant instead of once per recipient and channel, so a newsletter no longer causes one lookup and one fallback warning per address. The docs state the callback must only return verified, active domains."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"version": "0.388.0",
|
|
22
|
+
"type": "fix",
|
|
23
|
+
"title": "One-click unsubscribe POST keeps the JSON rejection",
|
|
24
|
+
"detail": "RFC 8058 one-click POSTs (form body List-Unsubscribe=One-Click) answer an invalid or expired token with the JSON 400 again and skip the branding lookup; only browser form submits get the HTML page."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"version": "0.388.0",
|
|
28
|
+
"type": "fix",
|
|
29
|
+
"title": "Resubscribe limit page has its own text",
|
|
30
|
+
"detail": "A browser that hits the resubscribe limit sees that the address cannot be subscribed again here, instead of the generic \"try again later\" error (new key mail.unsubscribe.resubscribeLimitReached in en, de, es, fr, it)."
|
|
31
|
+
},
|
|
2
32
|
{
|
|
3
33
|
"version": "0.383.0",
|
|
4
34
|
"type": "fix",
|
|
@@ -5,7 +5,11 @@ import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
|
5
5
|
import type { FileContext } from "@cosmicdrift/kumiko-framework/files";
|
|
6
6
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
7
7
|
import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
type FileRefContent,
|
|
10
|
+
FileStorageUnavailableError,
|
|
11
|
+
readFileRefContent,
|
|
12
|
+
} from "../file-foundation/index.ts";
|
|
9
13
|
import {
|
|
10
14
|
collectRendererPlugins,
|
|
11
15
|
createRendererFoundationApi,
|
|
@@ -86,7 +90,8 @@ function createPdfRenderer(
|
|
|
86
90
|
};
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
// A decorative inline image (logo)
|
|
93
|
+
// A decorative inline image (logo) of a tenant without file storage must not fail the whole mail;
|
|
94
|
+
// transient read errors rethrow so the send job retries.
|
|
90
95
|
async function readInlineImageOrNull(
|
|
91
96
|
fileRefId: string,
|
|
92
97
|
read: () => Promise<FileRefContent | null>,
|
|
@@ -94,9 +99,10 @@ async function readInlineImageOrNull(
|
|
|
94
99
|
try {
|
|
95
100
|
return await read();
|
|
96
101
|
} catch (err) {
|
|
102
|
+
if (!(err instanceof FileStorageUnavailableError)) throw err;
|
|
97
103
|
createFallbackLogger("delivery").warn("inline image could not be read, sending without it", {
|
|
98
104
|
fileRefId,
|
|
99
|
-
errorName: err
|
|
105
|
+
errorName: err.name,
|
|
100
106
|
});
|
|
101
107
|
return null;
|
|
102
108
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
-
import { unsafeReadRetrying } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
+
import { toPostgresArrayLiteral, unsafeReadRetrying } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
|
|
5
5
|
export type NotificationPreferenceRow = {
|
|
@@ -27,6 +27,6 @@ export async function selectNotificationPreferences(
|
|
|
27
27
|
OR (notification_type = '*' AND channel = $4)
|
|
28
28
|
OR (notification_type = ANY($3::text[]) AND channel = '*')
|
|
29
29
|
)`,
|
|
30
|
-
[tenantId, userId, notificationTypeNames, channelName],
|
|
30
|
+
[tenantId, userId, toPostgresArrayLiteral(notificationTypeNames), channelName],
|
|
31
31
|
);
|
|
32
32
|
}
|
|
@@ -484,9 +484,37 @@ export function createDeliveryService(options: DeliveryServiceOptions): Delivery
|
|
|
484
484
|
}
|
|
485
485
|
}
|
|
486
486
|
|
|
487
|
+
// The app's tenantOrigin callback typically hits the DB: once per run and tenant, not per recipient.
|
|
488
|
+
const unsubscribeOriginsByRun = new WeakMap<NotifyRun, Map<TenantId, Promise<string>>>();
|
|
489
|
+
|
|
490
|
+
function unsubscribeOriginOnce(
|
|
491
|
+
run: NotifyRun,
|
|
492
|
+
links: UnsubscribeLinkConfig,
|
|
493
|
+
channelCtx: ChannelContext,
|
|
494
|
+
notificationType: string,
|
|
495
|
+
): Promise<string> {
|
|
496
|
+
let originsByTenant = unsubscribeOriginsByRun.get(run);
|
|
497
|
+
if (originsByTenant === undefined) {
|
|
498
|
+
originsByTenant = new Map();
|
|
499
|
+
unsubscribeOriginsByRun.set(run, originsByTenant);
|
|
500
|
+
}
|
|
501
|
+
let origin = originsByTenant.get(channelCtx.tenantId);
|
|
502
|
+
if (origin === undefined) {
|
|
503
|
+
origin = unsubscribeLinkOrigin(links, channelCtx, (reason) =>
|
|
504
|
+
logError.warn(`unsubscribe link falls back to the app origin: ${reason}`, {
|
|
505
|
+
tenantId: channelCtx.tenantId,
|
|
506
|
+
notificationType,
|
|
507
|
+
}),
|
|
508
|
+
);
|
|
509
|
+
originsByTenant.set(channelCtx.tenantId, origin);
|
|
510
|
+
}
|
|
511
|
+
return origin;
|
|
512
|
+
}
|
|
513
|
+
|
|
487
514
|
// A caller-supplied unsubscribeUrl wins; the recipient comes from the server-side notify
|
|
488
515
|
// context, never from the notification data.
|
|
489
516
|
async function withUnsubscribeUrl(
|
|
517
|
+
run: NotifyRun,
|
|
490
518
|
message: ChannelMessage,
|
|
491
519
|
channelName: string,
|
|
492
520
|
recipient: UnsubscribeLinkRecipient,
|
|
@@ -498,11 +526,11 @@ export function createDeliveryService(options: DeliveryServiceOptions): Delivery
|
|
|
498
526
|
}
|
|
499
527
|
const supplied = message.data?.["unsubscribeUrl"];
|
|
500
528
|
if (typeof supplied === "string" && supplied !== "") return message;
|
|
501
|
-
const origin = await
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
529
|
+
const origin = await unsubscribeOriginOnce(
|
|
530
|
+
run,
|
|
531
|
+
unsubscribeLinks,
|
|
532
|
+
channelCtx,
|
|
533
|
+
message.notificationType,
|
|
506
534
|
);
|
|
507
535
|
const unsubscribeUrl = await buildUnsubscribeUrl(unsubscribeLinks, origin, recipient, {
|
|
508
536
|
tenantId: channelCtx.tenantId,
|
|
@@ -811,6 +839,7 @@ export function createDeliveryService(options: DeliveryServiceOptions): Delivery
|
|
|
811
839
|
channel,
|
|
812
840
|
address,
|
|
813
841
|
message: await withUnsubscribeUrl(
|
|
842
|
+
run,
|
|
814
843
|
message,
|
|
815
844
|
channel.name,
|
|
816
845
|
{ kind: "user", userId },
|
|
@@ -914,6 +943,7 @@ export function createDeliveryService(options: DeliveryServiceOptions): Delivery
|
|
|
914
943
|
channel,
|
|
915
944
|
address,
|
|
916
945
|
message: await withUnsubscribeUrl(
|
|
946
|
+
run,
|
|
917
947
|
message,
|
|
918
948
|
channel.name,
|
|
919
949
|
{ kind: "address", address },
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
export type DeliveryFeatureOptions = {
|
|
47
47
|
// Access gate for the delivery-log screen and the `log` query it reads —
|
|
48
48
|
// both move together so the nav entry a role sees always matches a
|
|
49
|
-
// callable handler
|
|
49
|
+
// callable handler. Default access.admin includes TenantAdmin,
|
|
50
50
|
// who in a line-of-business app is the operator, not the platform — pass
|
|
51
51
|
// access.systemAdmin to keep this platform-only and out of their nav.
|
|
52
52
|
readonly access?: readonly string[];
|
|
@@ -99,10 +99,10 @@ export function createDeliveryFeature(options?: DeliveryFeatureOptions): Feature
|
|
|
99
99
|
// boot-validator accepts the projection below because at least one
|
|
100
100
|
// apply-key is a registered domain-event (DELIVERY_ATTEMPT_EVENT).
|
|
101
101
|
// recipientAddress is the real PII (email address); recipientId stays
|
|
102
|
-
// plaintext — pseudonymous fk, same line as config.userId
|
|
102
|
+
// plaintext — pseudonymous fk, same line as config.userId.
|
|
103
103
|
// recipientId is nullable (system-triggered sends to non-users), so the
|
|
104
104
|
// address falls back to the tenant key instead of the plaintext it used
|
|
105
|
-
// to be — still shreddable, one tenant wide
|
|
105
|
+
// to be — still shreddable, one tenant wide.
|
|
106
106
|
r.defineEvent("attempt", deliveryAttemptSchema, {
|
|
107
107
|
piiFields: {
|
|
108
108
|
recipientAddress: { personal: { of: "recipientId", whenAbsent: "tenant" } },
|
|
@@ -208,7 +208,7 @@ export const logQuery = definePagedQueryHandler({
|
|
|
208
208
|
const nextCursor =
|
|
209
209
|
rows.length === limit && lastRow ? encodeSortCursor(sortColumn, lastRow) : null;
|
|
210
210
|
|
|
211
|
-
// recipientAddress is stored encrypted under the recipient's DEK
|
|
211
|
+
// recipientAddress is stored encrypted under the recipient's DEK
|
|
212
212
|
// — decrypt for the admin log view; forgotten subjects show [[erased]].
|
|
213
213
|
// The notificationType/recipientAddress → type/recipient rename happens
|
|
214
214
|
// here, not in the client: a projectionList screen has no entity to
|
|
@@ -2,6 +2,7 @@ import { selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
|
2
2
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import * as z from "zod";
|
|
5
|
+
import { currentNotificationType } from "../notification-type-aliases.ts";
|
|
5
6
|
import { notificationPreferencesTable } from "../tables.ts";
|
|
6
7
|
|
|
7
8
|
export const preferencesQuery = defineQueryHandler({
|
|
@@ -37,6 +38,24 @@ export const preferencesQuery = defineQueryHandler({
|
|
|
37
38
|
userId: query.user.id,
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
// Rows stored under a former notification name are reported under the current
|
|
42
|
+
// one, collapsed with the same precedence as delivery: an exact row wins,
|
|
43
|
+
// otherwise any disabled former-name row disables.
|
|
44
|
+
const collapsed = new Map<string, { row: (typeof rows)[number]; isExact: boolean }>();
|
|
45
|
+
for (const row of rows) {
|
|
46
|
+
const notificationType = currentNotificationType(ctx.registry, row.notificationType);
|
|
47
|
+
const key = `${notificationType}\u0000${row.channel}`;
|
|
48
|
+
const isExact = row.notificationType === notificationType;
|
|
49
|
+
const existing = collapsed.get(key);
|
|
50
|
+
if (existing?.isExact) continue;
|
|
51
|
+
const formerNameRowDisablesAlready =
|
|
52
|
+
existing !== undefined &&
|
|
53
|
+
!isExact &&
|
|
54
|
+
(existing.row.enabled === false || row.enabled !== false);
|
|
55
|
+
if (formerNameRowDisablesAlready) continue;
|
|
56
|
+
collapsed.set(key, { row: { ...row, notificationType }, isExact });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { rows: [...collapsed.values()].map((entry) => entry.row) };
|
|
41
60
|
},
|
|
42
61
|
});
|
|
@@ -33,7 +33,7 @@ export const unsubscribePageChromeQuery = defineQueryHandler({
|
|
|
33
33
|
// or createUnsubscribePageChromeReader for a tenant the app resolved server-side.
|
|
34
34
|
agent: { expose: false },
|
|
35
35
|
description:
|
|
36
|
-
"Returns the tenant's mail brand row and footer resolved for one locale, or null without a mail renderer; dispatched by the signed unsubscribe route to dress its pages like the mail
|
|
36
|
+
"Returns the tenant's mail brand row and footer resolved for one locale, or null without a mail renderer; dispatched by the signed unsubscribe route or by createUnsubscribePageChromeReader for a tenant the app resolved server-side, to dress its pages like the mail; not meant for UI callers.",
|
|
37
37
|
handler: async (query, ctx): Promise<MailChrome | null> => {
|
|
38
38
|
const { tenantId } = query.user;
|
|
39
39
|
if (!ctx.systemDb) {
|
|
@@ -53,14 +53,15 @@ export const deliveryAttemptsTable = pgTable(
|
|
|
53
53
|
(t) => [index(DELIVERY_ATTEMPTS_TENANT_CREATED_INDEX).on(t.tenantId, t.createdAt)],
|
|
54
54
|
);
|
|
55
55
|
|
|
56
|
-
// **Unmanaged table** —
|
|
57
|
-
// - id
|
|
58
|
-
// -
|
|
59
|
-
//
|
|
60
|
-
// - created_at
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
56
|
+
// **Unmanaged table** — deliberately has no createEntity because:
|
|
57
|
+
// - id comes from the aggregate stream (no gen_random_uuid()-DEFAULT)
|
|
58
|
+
// - no version/inserted_by/modified_by/modified_at: no in-place edits or
|
|
59
|
+
// audit columns are needed (idempotent-on-replay via PK conflict)
|
|
60
|
+
// - created_at rather than inserted_at reflects historical naming drift, not
|
|
61
|
+
// a bug
|
|
62
|
+
// The app is responsible for tenant scoping in queries and replay idempotency.
|
|
63
|
+
// pgTable remains the source of truth for the query API; the pgTable is derived
|
|
64
|
+
// from this metadata.
|
|
64
65
|
export const deliveryAttemptsTableMeta: EntityTableMeta = defineUnmanagedTable({
|
|
65
66
|
tableName: "store_delivery_attempts",
|
|
66
67
|
indexes: [{ name: DELIVERY_ATTEMPTS_TENANT_CREATED_INDEX, columns: ["tenant_id", "created_at"] }],
|
|
@@ -10,7 +10,9 @@ export type UnsubscribeLinkConfig = {
|
|
|
10
10
|
readonly secret: string;
|
|
11
11
|
readonly baseUrl: string;
|
|
12
12
|
/** The sending tenant's own public origin (subdomain or custom domain), null when it has
|
|
13
|
-
* none. The host must serve this app's `/api` routes
|
|
13
|
+
* none. The host must serve this app's `/api` routes, and the callback must only return a
|
|
14
|
+
* verified, active domain of the tenant (the check that lets the app serve under it), else null:
|
|
15
|
+
* the link carries a bearer token that unsubscribes and yields a resubscribe token. Null, a throw or an unusable value
|
|
14
16
|
* fall back to `baseUrl`, so a missing tenant origin never fails a send. */
|
|
15
17
|
readonly tenantOrigin?: (ctx: ChannelContext) => Promise<string | null | undefined>;
|
|
16
18
|
};
|
|
@@ -14,6 +14,7 @@ export const UNSUBSCRIBE_PAGE_EN = {
|
|
|
14
14
|
"mail.unsubscribe.resubscribedMessage": "You have been subscribed again.",
|
|
15
15
|
"mail.unsubscribe.failed": "That did not work. Please try again later.",
|
|
16
16
|
"mail.unsubscribe.failedTitle": "Something went wrong",
|
|
17
|
+
"mail.unsubscribe.resubscribeLimitReached": "This address cannot be subscribed again here.",
|
|
17
18
|
"mail.unsubscribe.invalidTitle": "Link no longer valid",
|
|
18
19
|
"mail.unsubscribe.invalidMessage":
|
|
19
20
|
"This link is invalid or has expired. Please use the unsubscribe link in a more recent email.",
|
|
@@ -11,6 +11,7 @@ export type UnsubscribePageState =
|
|
|
11
11
|
| { readonly kind: "unsubscribed"; readonly resubscribeToken?: string }
|
|
12
12
|
| { readonly kind: "resubscribed" }
|
|
13
13
|
| { readonly kind: "failed" }
|
|
14
|
+
| { readonly kind: "resubscribeLimitReached" }
|
|
14
15
|
| { readonly kind: "invalid" };
|
|
15
16
|
|
|
16
17
|
export type UnsubscribePageInput = {
|
|
@@ -162,6 +163,11 @@ ${tokenForm(DELIVERY_RESUBSCRIBE_PATH, state.resubscribeToken, mailT(lang, "mail
|
|
|
162
163
|
title: mailT(lang, "mail.unsubscribe.failedTitle"),
|
|
163
164
|
bodyHtml: paragraph("mail.unsubscribe.failed"),
|
|
164
165
|
};
|
|
166
|
+
case "resubscribeLimitReached":
|
|
167
|
+
return {
|
|
168
|
+
title: mailT(lang, "mail.unsubscribe.failedTitle"),
|
|
169
|
+
bodyHtml: paragraph("mail.unsubscribe.resubscribeLimitReached"),
|
|
170
|
+
};
|
|
165
171
|
case "invalid":
|
|
166
172
|
return {
|
|
167
173
|
title: mailT(lang, "mail.unsubscribe.invalidTitle"),
|
|
@@ -31,13 +31,13 @@ const localeClaimSchema = z
|
|
|
31
31
|
.regex(/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{1,8})*$/)
|
|
32
32
|
.optional();
|
|
33
33
|
|
|
34
|
-
// Shape
|
|
35
|
-
//
|
|
34
|
+
// Shape of verified JWT payloads. `tenantId` arrives from jose as a string and
|
|
35
|
+
// becomes a branded `TenantId` only after successful `parse()`—never via a blind cast.
|
|
36
36
|
//
|
|
37
37
|
// `kind` is explicitly typed as "absent or undefined, nothing else" here
|
|
38
38
|
// (not bare `.optional()`, which would also accept any other value on an
|
|
39
|
-
// unrelated key) so an address-token payload
|
|
40
|
-
// `kind: "address"
|
|
39
|
+
// unrelated key) so an address-token payload—which always carries
|
|
40
|
+
// `kind: "address"`—fails this schema instead of silently parsing with
|
|
41
41
|
// `addressHash` mistaken for a userId.
|
|
42
42
|
const unsubscribeJwtPayloadSchema = z.object({
|
|
43
43
|
kind: z.undefined().optional(),
|
|
@@ -569,11 +569,20 @@ async function verifyPost(
|
|
|
569
569
|
encodedSecret: Uint8Array,
|
|
570
570
|
purpose: TokenPurpose,
|
|
571
571
|
): Promise<VerifiedUnsubscribe | RejectedForPage> {
|
|
572
|
-
return isFormRequest(request.headers["content-type"])
|
|
572
|
+
return isFormRequest(request.headers["content-type"]) && !isOneClickPost(request)
|
|
573
573
|
? verifyForPage(token, encodedSecret, purpose, request.headers)
|
|
574
574
|
: verifyUnsubscribeToken(token, encodedSecret, purpose);
|
|
575
575
|
}
|
|
576
576
|
|
|
577
|
+
// RFC 8058 mail-provider POSTs are form-encoded too, but nobody looks at the answer: they keep
|
|
578
|
+
// the JSON rejection and get no branded page.
|
|
579
|
+
function isOneClickPost(request: SignatureExtraRouteVerifyRequest): boolean {
|
|
580
|
+
return (
|
|
581
|
+
isFormRequest(request.headers["content-type"]) &&
|
|
582
|
+
request.rawBody.trim() === DELIVERY_UNSUBSCRIBE_ONE_CLICK_HEADER_VALUE
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
|
|
577
586
|
function isFormRequest(contentType: string | undefined): boolean {
|
|
578
587
|
return contentType?.includes("application/x-www-form-urlencoded") === true;
|
|
579
588
|
}
|
|
@@ -635,22 +644,37 @@ export function createUnsubscribeRoutes(
|
|
|
635
644
|
: pageResponse(c, deps, { kind: "confirm", token: verified.token }, verified, 200),
|
|
636
645
|
});
|
|
637
646
|
|
|
638
|
-
const writeRoute = signatureRoute<
|
|
647
|
+
const writeRoute = signatureRoute<
|
|
648
|
+
(VerifiedUnsubscribe & { readonly oneClick: boolean }) | RejectedForPage
|
|
649
|
+
>({
|
|
639
650
|
method: "POST",
|
|
640
651
|
path: DELIVERY_UNSUBSCRIBE_PATH,
|
|
641
652
|
entry: "signature",
|
|
642
|
-
verify: async (request) =>
|
|
643
|
-
|
|
653
|
+
verify: async (request) => {
|
|
654
|
+
const verified = await verifyPost(
|
|
655
|
+
request,
|
|
656
|
+
tokenFromPostRequest(request),
|
|
657
|
+
encodedSecret,
|
|
658
|
+
"unsubscribe",
|
|
659
|
+
);
|
|
660
|
+
return verified.kind === "rejected"
|
|
661
|
+
? verified
|
|
662
|
+
: { ...verified, oneClick: isOneClickPost(request) };
|
|
663
|
+
},
|
|
644
664
|
handler: async (c, verified, deps) => {
|
|
645
665
|
if (verified.kind === "rejected") {
|
|
646
666
|
return pageResponse(c, deps, { kind: "invalid" }, verified, 400);
|
|
647
667
|
}
|
|
668
|
+
// No chrome lookup for a one-click client: it never renders the page.
|
|
669
|
+
const pageScope: PageScope = verified.oneClick
|
|
670
|
+
? { tenantId: undefined, locale: verified.locale }
|
|
671
|
+
: verified;
|
|
648
672
|
// Token-verify passed — everything below is a legitimate write. Don't
|
|
649
673
|
// swallow write-errors as "invalid token", that would mask real bugs
|
|
650
674
|
// (e.g. events-table missing, DB down) behind a misleading 400.
|
|
651
675
|
const succeeded = await dispatchUnsubscribeWrite(verified, deps);
|
|
652
676
|
if (!succeeded) {
|
|
653
|
-
return pageResponse(c, deps, { kind: "failed" },
|
|
677
|
+
return pageResponse(c, deps, { kind: "failed" }, pageScope, 500);
|
|
654
678
|
}
|
|
655
679
|
return pageResponse(
|
|
656
680
|
c,
|
|
@@ -659,7 +683,7 @@ export function createUnsubscribeRoutes(
|
|
|
659
683
|
kind: "unsubscribed",
|
|
660
684
|
resubscribeToken: await signResubscribeToken(verified, encodedSecret),
|
|
661
685
|
},
|
|
662
|
-
|
|
686
|
+
pageScope,
|
|
663
687
|
200,
|
|
664
688
|
);
|
|
665
689
|
},
|
|
@@ -681,15 +705,12 @@ export function createUnsubscribeRoutes(
|
|
|
681
705
|
const outcome = await dispatchResubscribeWrite(verified, deps);
|
|
682
706
|
// The undo button on the unsubscribed page posts a form; a browser should land on a page.
|
|
683
707
|
if (isFormRequest(c.req.header("content-type"))) {
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
verified,
|
|
691
|
-
outcome === "limit_reached" ? 409 : 500,
|
|
692
|
-
);
|
|
708
|
+
if (outcome === "success") {
|
|
709
|
+
return pageResponse(c, deps, { kind: "resubscribed" }, verified, 200);
|
|
710
|
+
}
|
|
711
|
+
return outcome === "limit_reached"
|
|
712
|
+
? pageResponse(c, deps, { kind: "resubscribeLimitReached" }, verified, 409)
|
|
713
|
+
: pageResponse(c, deps, { kind: "failed" }, verified, 500);
|
|
693
714
|
}
|
|
694
715
|
if (outcome === "limit_reached") {
|
|
695
716
|
return c.json(
|
|
@@ -28,10 +28,9 @@ const executor = createEventStoreExecutor(
|
|
|
28
28
|
{ entityName: "notification-preference" },
|
|
29
29
|
);
|
|
30
30
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// im Helper (db-row-boundary), nicht 4× pro Callsite.
|
|
31
|
+
// Concrete lookup result shape containing only the fields needed by the upsert update path. Avoids
|
|
32
|
+
// `row["x"] as T` index-access casts; the generic parameter on fetchOne centralizes the cast in the
|
|
33
|
+
// helper (`db-row-boundary`) instead of repeating it at each call site.
|
|
35
34
|
type PreferenceLookupRow = {
|
|
36
35
|
readonly id: string;
|
|
37
36
|
readonly version: number;
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
createTextField,
|
|
7
7
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
8
8
|
|
|
9
|
-
//
|
|
9
|
+
// (MVP) page shape — LiteParse fills this in.
|
|
10
10
|
// Kept minimal on purpose: downstream themes (provider, recipe) extend it.
|
|
11
11
|
// Stored as JSON.stringify(IngestPage[]) in the encrypted `pages` column —
|
|
12
12
|
// jsonb has no encryption support in the engine, and this column holds the
|
|
@@ -29,7 +29,7 @@ export type DocumentExtractMeta = {
|
|
|
29
29
|
|
|
30
30
|
// documentExtract — implicit entity-projection (r.entity, NOT r.projection):
|
|
31
31
|
// an explicit r.projection would make `forget` non-rebuild-safe and require
|
|
32
|
-
// archiveStream instead of the regular forget path
|
|
32
|
+
// archiveStream instead of the regular forget path.
|
|
33
33
|
// fileRefId points at the source file_refs row; storageKey duplicates it for
|
|
34
34
|
// direct provider access without a join.
|
|
35
35
|
export const documentExtractEntity = createEntity({
|
|
@@ -42,11 +42,11 @@ export const documentExtractEntity = createEntity({
|
|
|
42
42
|
reason: "technical_reference",
|
|
43
43
|
}),
|
|
44
44
|
// Holds the full extracted document text (PII). `personal: "tenant"`,
|
|
45
|
-
// NOT `encrypted: true
|
|
45
|
+
// NOT `encrypted: true`: the master-key path has no erasure subject,
|
|
46
46
|
// so nothing here would ever be shreddable. Tenant-subject ciphertext
|
|
47
|
-
// dies with eraseSubjectKeys on tenant-destroy
|
|
47
|
+
// dies with eraseSubjectKeys on tenant-destroy. Whose
|
|
48
48
|
// subject a third party named inside a document is stays open, same as
|
|
49
|
-
// inbound-mail-foundation
|
|
49
|
+
// inbound-mail-foundation. meta is provider telemetry only and
|
|
50
50
|
// stays plaintext jsonb.
|
|
51
51
|
pages: createLongTextField({
|
|
52
52
|
personal: "tenant",
|
|
@@ -4,8 +4,8 @@ import * as z from "zod";
|
|
|
4
4
|
// stream per fileRef with aggregateId derived from the fileRefId
|
|
5
5
|
// (documentIngestAggregateId), never the fileRefId itself: foreign events on
|
|
6
6
|
// the fileRef stream would break the fileRef's optimistic lock. Not backed by an r.entity;
|
|
7
|
-
// this is a pure saga-anchor event (kumiko-
|
|
8
|
-
//
|
|
7
|
+
// this is a pure saga-anchor event that the worker job (kumiko-enterprise LiteParse provider)
|
|
8
|
+
// subscribes to.
|
|
9
9
|
export const DOCUMENT_INGEST_AGGREGATE_TYPE = "document-ingest";
|
|
10
10
|
|
|
11
11
|
// Stream name of stored extract events; differs from the camelCase r.entity name
|
|
@@ -166,7 +166,7 @@ export function createDocumentIngestFoundationFeature() {
|
|
|
166
166
|
default: "deu+eng",
|
|
167
167
|
// Tesseract -l argument syntax: one or more 3-letter language codes
|
|
168
168
|
// joined by "+" (e.g. "deu+eng"). Unconstrained free text here would
|
|
169
|
-
// reach the OCR provider call unvalidated
|
|
169
|
+
// reach the OCR provider call unvalidated.
|
|
170
170
|
pattern: { regex: "^[a-z]{3}(\\+[a-z]{3})*$" },
|
|
171
171
|
write: access.roles("TenantAdmin", "SystemAdmin"),
|
|
172
172
|
read: access.roles("TenantAdmin", "SystemAdmin", "User"),
|
|
@@ -178,7 +178,7 @@ export function createDocumentIngestFoundationFeature() {
|
|
|
178
178
|
default: 50,
|
|
179
179
|
// Without a bound a tenant could set 0 (silently kills ingest) or an
|
|
180
180
|
// unbounded value (OCR worker runs per-file for as long as the PDF
|
|
181
|
-
// has pages — job-queue starvation)
|
|
181
|
+
// has pages — job-queue starvation).
|
|
182
182
|
bounds: { min: 1, max: 500 },
|
|
183
183
|
write: access.roles("TenantAdmin", "SystemAdmin"),
|
|
184
184
|
read: access.roles("TenantAdmin", "SystemAdmin", "User"),
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// longText (jsonb has no encryption support), so writers must persist
|
|
3
3
|
// JSON.stringify(IngestPage[]) and readers must parse back. Centralising
|
|
4
4
|
// the codec here keeps liteparse + recipe consumers lockstep with the
|
|
5
|
-
// entity shape
|
|
5
|
+
// entity shape.
|
|
6
6
|
|
|
7
7
|
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
8
8
|
import type { IngestPage } from "./entity.ts";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Tenant-destroy hook for the documentExtract entity-projection
|
|
1
|
+
// Tenant-destroy hook for the documentExtract entity-projection.
|
|
2
2
|
//
|
|
3
3
|
// Per-row forget() through the executor, not a bulk deleteMany: documentExtract
|
|
4
4
|
// is an ES-managed implicit projection, so a store-table write here would be
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
// resurrect every row this hook removed. forget() (Art. 17 hard-purge) is
|
|
7
7
|
// replayed by the implicit projection itself, which keeps the erasure
|
|
8
8
|
// rebuild-safe — the same reasoning as tenant-lifecycle's membership stage,
|
|
9
|
-
// and the reason the feature owns an r.entity instead of an r.projection
|
|
10
|
-
//
|
|
11
|
-
//
|
|
9
|
+
// and the reason the feature owns an r.entity instead of an r.projection.
|
|
10
|
+
// The `pages` ciphertext dies separately when the pipeline's later
|
|
11
|
+
// `subject-keys` stage erases the tenant subject key.
|
|
12
12
|
|
|
13
13
|
import { createSystemUser, type TenantDataDestroyHook } from "@cosmicdrift/kumiko-framework/engine";
|
|
14
14
|
import { documentExtractsTable } from "./entity.ts";
|
|
@@ -66,7 +66,7 @@ export function composeTierResolverWithGlobalToggles(
|
|
|
66
66
|
// Non-toggleable (always-on) names must keep reaching `result` through the
|
|
67
67
|
// globalCascade loop below even if this resolver's SYSTEM_TENANT_ID answer
|
|
68
68
|
// satisfies the emptiness guard above without also merging always-on into
|
|
69
|
-
// every per-tenant tierSet
|
|
69
|
+
// every per-tenant tierSet.
|
|
70
70
|
tierManaged = new Set(
|
|
71
71
|
[...managed].filter((name) => {
|
|
72
72
|
const feature = registry.getFeature(name);
|
|
@@ -104,7 +104,7 @@ export function createFeatureTogglesFeature(
|
|
|
104
104
|
r.translations({ keys: FEATURE_TOGGLES_I18N });
|
|
105
105
|
|
|
106
106
|
// toggle-cache-sync — multi-instance snapshot propagation. One shared
|
|
107
|
-
// cursor reads every toggle-set event once
|
|
107
|
+
// cursor reads every toggle-set event once; the winning
|
|
108
108
|
// process's handler calls runtime.broadcastToggle, which publishes on
|
|
109
109
|
// GlobalFeatureToggleRuntime's syncSignal so every process (including
|
|
110
110
|
// the one that won the cursor) applies the flip to its own snapshot —
|
|
@@ -38,7 +38,7 @@ export const globalFeatureStateTable: WritableSchemaTable & TenancyBrand<"global
|
|
|
38
38
|
// sees the table, so `kumiko schema generate` reports no changes and no
|
|
39
39
|
// app ever gets a migration for it (framework gap, not app-local). Derived
|
|
40
40
|
// from the pgTable above instead of hand-written a second time — the two
|
|
41
|
-
// used to drift independently
|
|
41
|
+
// used to drift independently: a column added only to
|
|
42
42
|
// the pgTable made queries type-check while the migration generator stayed
|
|
43
43
|
// blind to it, failing in prod with "column ... does not exist" while every
|
|
44
44
|
// mocked-stack test stayed green.
|
|
@@ -38,11 +38,11 @@ export const registeredQuery = defineQueryHandler({
|
|
|
38
38
|
const overrideRows = await db.global(globalFeatureStateTable).selectMany<OverrideRow>();
|
|
39
39
|
const overrides = new Map(overrideRows.map((r) => [r.featureName, r.enabled]));
|
|
40
40
|
|
|
41
|
-
// SystemAdmin operator
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
41
|
+
// SystemAdmin operator tooling: the listing must show platform truth
|
|
42
|
+
// (all features in the registry), not its own tier cut.
|
|
43
|
+
// The per-tenant signature requires SYSTEM_TENANT_ID; the app resolver
|
|
44
|
+
// returns the union of all tier features. The sentinel convention is
|
|
45
|
+
// documented in DispatcherOptions.effectiveFeatures.
|
|
46
46
|
const effective = ctx.effectiveFeatures?.(SYSTEM_TENANT_ID);
|
|
47
47
|
|
|
48
48
|
const rows: Array<{
|
|
@@ -16,7 +16,7 @@ import { createCacheSyncToggleSignal } from "./toggle-sync-signal.ts";
|
|
|
16
16
|
|
|
17
17
|
const log = createFallbackLogger("kumiko:feature-toggles");
|
|
18
18
|
|
|
19
|
-
// Cross-replica transport for a toggle flip
|
|
19
|
+
// Cross-replica transport for a toggle flip. Deliberately narrow
|
|
20
20
|
// (featureName + enabled, not a generic channel/payload pair) so a caller
|
|
21
21
|
// can't wire the wrong signal instance in by accident — see
|
|
22
22
|
// createRedisToggleSyncSignal (toggle-sync-signal.ts) for the Redis-backed
|
|
@@ -27,7 +27,7 @@ function isTogglePayload(value: unknown): value is { featureName: string; enable
|
|
|
27
27
|
);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// Gives toggle-cache-sync the cross-replica transport its shared
|
|
31
31
|
// dispatcher cursor needs — without this, only the one process that won
|
|
32
32
|
// the shared cursor's toggle-set event would ever learn about a flip.
|
|
33
33
|
// Standalone Redis transport for callers that want their own connections;
|
|
@@ -11,7 +11,7 @@ export const FEATURE_TOGGLE_AGGREGATE_TYPE = "feature-toggles";
|
|
|
11
11
|
|
|
12
12
|
// Shared acknowledgeCrossTenant() reason across all three handlers that read
|
|
13
13
|
// or write globalFeatureStateTable — one string so the three call sites
|
|
14
|
-
// can't drift
|
|
14
|
+
// can't drift.
|
|
15
15
|
export const FEATURE_TOGGLE_CROSS_TENANT_REASON =
|
|
16
16
|
"global feature-toggle state, no tenant scope by design";
|
|
17
17
|
|
|
@@ -35,13 +35,13 @@ const FEATURE_NAME = "file-derivatives";
|
|
|
35
35
|
// charset constraint (`Readonly<Record<string, VariantSpec>>`), so this
|
|
36
36
|
// stays permissive rather than guessing a convention — narrower than this
|
|
37
37
|
// would silently 404 a legitimately declared name again. Do not reintroduce
|
|
38
|
-
// a name list here "for safety"
|
|
38
|
+
// a name list here "for safety".
|
|
39
39
|
const VARIANT_NAME_RE = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
40
40
|
|
|
41
41
|
/** Host → tenantId for the anonymous media route.
|
|
42
42
|
* MUST be cheap/synchronous (in-memory map or cached lookup): it runs
|
|
43
43
|
* *before* the query-handler rate limit, so a DB round-trip here is an
|
|
44
|
-
* unthrottled DoS surface for anonymous callers rotating Host headers
|
|
44
|
+
* unthrottled DoS surface for anonymous callers rotating Host headers. */
|
|
45
45
|
export type PublicVariantResolveApexTenant = (
|
|
46
46
|
host: string,
|
|
47
47
|
) => Promise<TenantId | null> | TenantId | null;
|