@cosmicdrift/kumiko-samples 0.362.0 → 0.364.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/package.json +1 -1
- package/packages/bundled-features/package.json +23 -6
- package/packages/bundled-features/src/admin-shell/changes.json +6 -0
- package/packages/bundled-features/src/admin-shell/feature.ts +8 -1
- package/packages/bundled-features/src/agent-tools/agent-manifest.ts +3 -4
- package/packages/bundled-features/src/agent-tools/filter-ops.ts +1 -3
- package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -3
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +2 -1
- package/packages/bundled-features/src/audit/changes.json +6 -0
- package/packages/bundled-features/src/audit/handlers/list.query.ts +10 -5
- package/packages/bundled-features/src/auth-email-password/bootstrap.ts +29 -8
- package/packages/bundled-features/src/auth-email-password/changes.json +51 -0
- package/packages/bundled-features/src/auth-email-password/email-rate-limits.ts +16 -0
- package/packages/bundled-features/src/auth-email-password/email-templates.ts +12 -138
- package/packages/bundled-features/src/auth-email-password/feature.ts +30 -9
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +6 -2
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -5
- package/packages/bundled-features/src/auth-email-password/handlers/logout.write.ts +7 -2
- package/packages/bundled-features/src/auth-email-password/handlers/request-account-unlock.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/request-email-verification.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/request-password-reset.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +3 -4
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +2 -1
- package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +5 -13
- package/packages/bundled-features/src/auth-email-password/index.ts +2 -17
- package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +30 -22
- package/packages/bundled-features/src/auth-email-password/mail-notifications.ts +158 -0
- package/packages/bundled-features/src/auth-email-password/signup-activation.ts +2 -3
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +24 -5
- package/packages/bundled-features/src/auth-foundation/types.ts +8 -18
- package/packages/bundled-features/src/auth-mfa/feature.ts +8 -10
- package/packages/bundled-features/src/auth-mfa/hmac-token-codec.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/mfa-challenge-token.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/mfa-preauth-setup-token.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/mfa-setup-token.ts +8 -8
- package/packages/bundled-features/src/billing-foundation/changes.json +43 -0
- package/packages/bundled-features/src/billing-foundation/constants.ts +5 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +90 -44
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +22 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +118 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +11 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +39 -33
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-notify.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-pages.ts +1 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +41 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +10 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +36 -20
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +9 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +35 -0
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +11 -3
- package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +4 -9
- package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +12 -6
- package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/i18n.ts +3 -0
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +20 -11
- package/packages/bundled-features/src/billing-foundation/types.ts +7 -2
- package/packages/bundled-features/src/cap-counter/calendar-cap-usage-reservation.ts +2 -0
- package/packages/bundled-features/src/cap-counter/changes.json +6 -0
- package/packages/bundled-features/src/cap-counter/enforce-cap.ts +75 -5
- package/packages/bundled-features/src/cap-counter/index.ts +1 -0
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -0
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +2 -1
- package/packages/bundled-features/src/cap-overview/web/cap-usage-cell.tsx +6 -4
- package/packages/bundled-features/src/channel-email/changes.json +19 -0
- package/packages/bundled-features/src/channel-email/email-channel.ts +135 -29
- package/packages/bundled-features/src/channel-email/feature.ts +6 -3
- package/packages/bundled-features/src/channel-email/index.ts +7 -1
- package/packages/bundled-features/src/channel-email/pii-guard.ts +13 -0
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +15 -1
- package/packages/bundled-features/src/channel-email/types.ts +23 -0
- package/packages/bundled-features/src/channel-in-app/handlers/mark-read.write.ts +2 -2
- package/packages/bundled-features/src/config/changes.json +6 -0
- package/packages/bundled-features/src/config/handlers/set.write.ts +3 -7
- package/packages/bundled-features/src/config/resolver.ts +15 -12
- package/packages/bundled-features/src/config/write-helpers.ts +23 -0
- package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +48 -27
- package/packages/bundled-features/src/custom-fields/changes.json +6 -0
- package/packages/bundled-features/src/custom-fields/db/queries/user-data-rights.ts +2 -2
- package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +0 -1
- package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -11
- package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +7 -6
- package/packages/bundled-features/src/data-retention/changes.json +6 -0
- package/packages/bundled-features/src/data-retention/index.ts +1 -0
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +8 -6
- package/packages/bundled-features/src/delivery/attachment-ref.ts +105 -0
- package/packages/bundled-features/src/delivery/changes.json +66 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +152 -4
- package/packages/bundled-features/src/delivery/constants.ts +2 -0
- package/packages/bundled-features/src/delivery/delivery-service.ts +104 -26
- package/packages/bundled-features/src/delivery/feature.ts +1 -1
- package/packages/bundled-features/src/delivery/index.ts +19 -2
- package/packages/bundled-features/src/delivery/jobs.ts +97 -17
- package/packages/bundled-features/src/delivery/notify-factory.ts +14 -9
- package/packages/bundled-features/src/delivery/testing.ts +9 -1
- package/packages/bundled-features/src/delivery/types.ts +34 -18
- package/packages/bundled-features/src/delivery/unsubscribe.ts +5 -2
- package/packages/bundled-features/src/derivatives-sharp/render.ts +1 -22
- package/packages/bundled-features/src/document-ingest-foundation/pages.ts +6 -3
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +4 -4
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/packages/bundled-features/src/file-foundation/changes.json +6 -0
- package/packages/bundled-features/src/file-foundation/index.ts +7 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +89 -0
- package/packages/bundled-features/src/files-tenant-data/changes.json +6 -0
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +4 -0
- package/packages/bundled-features/src/folders/web/folder-manager.tsx +5 -2
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +11 -11
- package/packages/bundled-features/src/form-draft/db/queries/draft-count.ts +2 -2
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +2 -2
- package/packages/bundled-features/src/form-draft/release-file-refs.ts +3 -2
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +6 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +6 -6
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +2 -2
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-access-token.ts +13 -8
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +7 -7
- package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +2 -6
- package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +5 -13
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +4 -6
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +42 -25
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +4 -3
- package/packages/bundled-features/src/jobs/changes.json +19 -0
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +12 -4
- package/packages/bundled-features/src/jobs/feature.ts +7 -4
- package/packages/bundled-features/src/jobs/handlers/catalog.query.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +3 -1
- package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +5 -2
- package/packages/bundled-features/src/jobs/job-run-logger.ts +52 -17
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +7 -2
- package/packages/bundled-features/src/ledger/changes.json +7 -0
- package/packages/bundled-features/src/ledger/entity.ts +3 -1
- package/packages/bundled-features/src/ledger/recurring.ts +1 -3
- package/packages/bundled-features/src/managed-pages/branding.ts +33 -9
- package/packages/bundled-features/src/managed-pages/changes.json +6 -0
- package/packages/bundled-features/src/managed-pages/feature.ts +1 -0
- package/packages/bundled-features/src/managed-pages/i18n.ts +1 -0
- package/packages/bundled-features/src/managed-pages/screens/branding-screen.ts +8 -0
- package/packages/bundled-features/src/page-render/branding.ts +3 -0
- package/packages/bundled-features/src/page-render/markdown.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +2 -4
- package/packages/bundled-features/src/personal-access-tokens/handlers/list.query.ts +2 -1
- package/packages/bundled-features/src/personal-access-tokens/index.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/revoke-for-user.ts +2 -1
- package/packages/bundled-features/src/renderer-foundation/README.md +11 -10
- package/packages/bundled-features/src/renderer-foundation/api.ts +14 -14
- package/packages/bundled-features/src/renderer-foundation/changes.json +39 -0
- package/packages/bundled-features/src/renderer-foundation/constants.ts +6 -8
- package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
- package/packages/bundled-features/src/renderer-foundation/index.ts +13 -2
- package/packages/bundled-features/src/renderer-foundation/types.ts +134 -35
- package/packages/bundled-features/src/renderer-pdf/changes.json +12 -0
- package/packages/bundled-features/src/renderer-pdf/feature.ts +7 -1
- package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +162 -82
- package/packages/bundled-features/src/secrets/changes.json +6 -0
- package/packages/bundled-features/src/secrets/feature.ts +4 -3
- package/packages/bundled-features/src/secrets/secrets-context.ts +23 -3
- package/packages/bundled-features/src/sessions/db/queries/cleanup.ts +2 -2
- package/packages/bundled-features/src/sessions/feature.ts +2 -4
- package/packages/bundled-features/src/sessions/handlers/list.query.ts +2 -1
- package/packages/bundled-features/src/shared/attachment-file-name.ts +39 -0
- package/packages/bundled-features/src/shared/changes.json +6 -0
- package/packages/bundled-features/src/shared/chunked-entity-migration.ts +4 -1
- package/packages/bundled-features/src/shared/index.ts +6 -0
- package/packages/bundled-features/src/shared/is-tenant-db.ts +1 -1
- package/packages/bundled-features/src/shared/mail-template-body.ts +42 -0
- package/packages/bundled-features/src/shared/sharp-input-limits.ts +26 -0
- package/packages/bundled-features/src/shared/signup-handover.ts +4 -4
- package/packages/bundled-features/src/subscription-stripe/changes.json +6 -0
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +19 -11
- package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +3 -1
- package/packages/bundled-features/src/tags/handlers/assign-tag.write.ts +5 -1
- package/packages/bundled-features/src/tags/web/tag-chip.tsx +2 -1
- package/packages/bundled-features/src/template-resolver/README.md +66 -8
- package/packages/bundled-features/src/template-resolver/api.ts +85 -9
- package/packages/bundled-features/src/template-resolver/changes.json +186 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +278 -0
- package/packages/bundled-features/src/template-resolver/constants.ts +49 -11
- package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +335 -0
- package/packages/bundled-features/src/template-resolver/document-schema.ts +345 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +59 -6
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +188 -0
- package/packages/bundled-features/src/template-resolver/handlers/collection-set.write.ts +30 -2
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +160 -0
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +33 -0
- package/packages/bundled-features/src/template-resolver/handlers/discard-draft.write.ts +52 -0
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +138 -0
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +9 -6
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle-system.write.ts +42 -0
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle.write.ts +22 -0
- package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +151 -0
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +19 -18
- package/packages/bundled-features/src/template-resolver/handlers/override-diff.ts +49 -0
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +323 -0
- package/packages/bundled-features/src/template-resolver/handlers/preview.query.ts +25 -0
- package/packages/bundled-features/src/template-resolver/handlers/reset-to-system.write.ts +58 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-link-pdf.write.ts +34 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-remove.write.ts +50 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-shared.ts +24 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-upload.write.ts +135 -0
- package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +135 -0
- package/packages/bundled-features/src/template-resolver/handlers/set.write.ts +32 -3
- package/packages/bundled-features/src/template-resolver/handlers/shared.ts +7 -2
- package/packages/bundled-features/src/template-resolver/handlers/template-gates.ts +196 -0
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +233 -0
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +137 -0
- package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +91 -17
- package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +19 -71
- package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +16 -77
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +31 -0
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +116 -0
- package/packages/bundled-features/src/template-resolver/index.ts +111 -0
- package/packages/bundled-features/src/template-resolver/layout-appearance.ts +40 -0
- package/packages/bundled-features/src/template-resolver/layout-regions.ts +206 -0
- package/packages/bundled-features/src/template-resolver/linked-resources.ts +102 -0
- package/packages/bundled-features/src/template-resolver/pdf-constants.ts +12 -0
- package/packages/bundled-features/src/template-resolver/preview-display-html.ts +36 -0
- package/packages/bundled-features/src/template-resolver/print-document.ts +58 -0
- package/packages/bundled-features/src/template-resolver/qualified-names.ts +34 -0
- package/packages/bundled-features/src/template-resolver/record-mapping.ts +26 -0
- package/packages/bundled-features/src/template-resolver/resource-image.ts +47 -0
- package/packages/bundled-features/src/template-resolver/seeding.ts +171 -20
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +416 -0
- package/packages/bundled-features/src/template-resolver/table.ts +27 -1
- package/packages/bundled-features/src/template-resolver/template-bundle.ts +79 -0
- package/packages/bundled-features/src/template-resolver/template-diff.ts +78 -0
- package/packages/bundled-features/src/template-resolver/template-draft.ts +106 -0
- package/packages/bundled-features/src/template-resolver/template-meta.ts +192 -0
- package/packages/bundled-features/src/template-resolver/template-preview.ts +676 -0
- package/packages/bundled-features/src/template-resolver/template-resources.ts +114 -0
- package/packages/bundled-features/src/template-resolver/template-usage.ts +68 -0
- package/packages/bundled-features/src/template-resolver/template-validation.ts +230 -0
- package/packages/bundled-features/src/template-resolver/testing.ts +5 -5
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +20 -13
- package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +215 -0
- package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +37 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +1065 -0
- package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +72 -0
- package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +26 -0
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +121 -0
- package/packages/bundled-features/src/template-resolver/web/index.ts +26 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor-lazy.tsx +36 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +176 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-text.ts +37 -0
- package/packages/bundled-features/src/template-resolver/web/use-stable-by-key.ts +14 -0
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +3 -2
- package/packages/bundled-features/src/tenant/seeding.ts +41 -7
- package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +2 -1
- package/packages/bundled-features/src/tier-engine/changes.json +6 -0
- package/packages/bundled-features/src/tier-engine/feature.ts +11 -3
- package/packages/bundled-features/src/user/changes.json +6 -0
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +4 -4
- package/packages/bundled-features/src/user/handlers/detail.query.ts +3 -5
- package/packages/bundled-features/src/user/resolve-display-names.ts +8 -15
- package/packages/bundled-features/src/user/roles-input-schema.ts +2 -2
- package/packages/bundled-features/src/user/seeding.ts +19 -5
- package/packages/bundled-features/src/user-data-rights/changes.json +13 -0
- package/packages/bundled-features/src/user-data-rights/db/queries/forget-cleanup.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/email-templates.ts +5 -149
- package/packages/bundled-features/src/user-data-rights/feature.ts +72 -35
- package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +2 -1
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +5 -11
- package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +134 -53
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/mail-notifications.ts +157 -0
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +2 -1
- package/packages/bundled-features/src/user-profile/changes.json +1 -1
- package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +2 -1
- package/packages/bundled-features/src/waitlist/changes.json +7 -0
- package/packages/bundled-features/src/waitlist/feature.ts +17 -0
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +4 -5
- package/packages/bundled-features/src/waitlist/mail.ts +69 -18
- package/packages/bundled-features/src/waitlist/options.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +3 -3
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +7 -1
- package/samples/apps/ui-walkthrough/package.json +11 -0
- package/samples/apps/ui-walkthrough/playwright.config.ts +40 -7
- package/samples/apps/ui-walkthrough/src/app/auth-constants.ts +9 -0
- package/samples/apps/ui-walkthrough/src/app/client.tsx +6 -0
- package/samples/apps/ui-walkthrough/src/app/seed-templates.ts +239 -0
- package/samples/apps/ui-walkthrough/src/app/server.ts +5 -1
- package/samples/apps/ui-walkthrough/src/app/shell.tsx +12 -1
- package/samples/apps/ui-walkthrough/src/app/template-seed-data.ts +570 -0
- package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +72 -0
- package/samples/apps/ui-walkthrough/src/features/billing/i18n.ts +18 -0
- package/samples/apps/ui-walkthrough/src/features/billing/index.ts +2 -0
- package/samples/apps/ui-walkthrough/src/features/billing/web/index.ts +7 -0
- package/samples/apps/ui-walkthrough/src/run-config.ts +41 -1
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +0 -2
- package/samples/apps/use-all-bundled/feature-manifest.json +118 -21
- package/samples/apps/use-all-bundled/kumiko/migrations/0045_template-resource-meta.rebuild.json +4 -0
- package/samples/apps/use-all-bundled/kumiko/migrations/0046_pdf-rendered.rebuild.json +4 -0
- package/samples/apps/use-all-bundled/package.json +2 -0
- package/samples/apps/use-all-bundled/playwright.config.ts +38 -3
- package/samples/apps/use-all-bundled/schema/generate.ts +13 -3
- package/samples/apps/use-all-bundled/schema/schema.generated.ts +3 -0
- package/samples/apps/use-all-bundled/scripts/check-coverage.ts +3 -0
- package/samples/apps/use-all-bundled/src/app/auth-constants.ts +3 -0
- package/samples/apps/use-all-bundled/src/app/client.tsx +4 -0
- package/samples/apps/use-all-bundled/src/app/seed-documents.ts +128 -0
- package/samples/apps/use-all-bundled/src/app/seed.ts +4 -0
- package/samples/apps/use-all-bundled/src/run-config.ts +11 -11
- package/samples/recipes/delivery-notifications/README.md +26 -25
- package/samples/recipes/delivery-notifications/package.json +2 -1
- package/samples/recipes/delivery-notifications/src/feature.ts +64 -58
- package/samples/recipes/seed-migration/README.md +1 -1
- package/packages/bundled-features/src/renderer-simple/changes.json +0 -27
- package/packages/bundled-features/src/renderer-simple/feature.ts +0 -66
- package/packages/bundled-features/src/renderer-simple/index.ts +0 -8
- package/packages/bundled-features/src/renderer-simple/mail-blocks.ts +0 -138
- package/packages/bundled-features/src/renderer-simple/resolve-variables.ts +0 -57
- package/packages/bundled-features/src/renderer-simple/simple-renderer.ts +0 -304
|
@@ -1,27 +1,38 @@
|
|
|
1
|
-
// Default send*Email
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// send*Email-Opts, greifen diese Defaults nicht (Opt-Override im feature.ts).
|
|
1
|
+
// Default send*Email callbacks for the GDPR notifications. The app writes no
|
|
2
|
+
// callback code: it mounts delivery + channel-email (+ the template stack) and
|
|
3
|
+
// sets appExportDownloadUrl for export mails. Own send*Email options win over
|
|
4
|
+
// these defaults (opt-override in feature.ts).
|
|
6
5
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
6
|
+
// Export-ready/-failed and deletion-requested go through ctx.notify addressed
|
|
7
|
+
// to the user, so delivery resolves the address and the attempt row carries
|
|
8
|
+
// the recipientId the forget path erases. Deletion-executed cannot: the user
|
|
9
|
+
// is gone by then, and an attempt row written after the erasure would be new
|
|
10
|
+
// personal data. It renders the seeded template and sends over the tenant's
|
|
11
|
+
// mail transport, with no delivery log.
|
|
11
12
|
|
|
12
13
|
import type { EmailTransport } from "@cosmicdrift/kumiko-bundled-features/channel-email";
|
|
13
14
|
import { MAIL_TRANSPORT_EXTENSION } from "@cosmicdrift/kumiko-bundled-features/mail-foundation";
|
|
14
|
-
import type {
|
|
15
|
-
import {
|
|
15
|
+
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
16
|
+
import type {
|
|
17
|
+
ConfigAccessor,
|
|
18
|
+
NotifyFn,
|
|
19
|
+
NotifyOptions,
|
|
20
|
+
Registry,
|
|
21
|
+
TenantId,
|
|
22
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
23
|
+
import { hasMailTranslations, mailT } from "@cosmicdrift/kumiko-framework/i18n";
|
|
24
|
+
import { DELIVERY_CHANNEL_EXTENSION } from "../../delivery/index.js";
|
|
16
25
|
import {
|
|
26
|
+
collectRendererPlugins,
|
|
27
|
+
createRendererFoundationApi,
|
|
28
|
+
} from "../../renderer-foundation/index.js";
|
|
29
|
+
import {
|
|
30
|
+
formatGdprMailTimestamp,
|
|
17
31
|
type GdprMailLocale,
|
|
18
32
|
normalizeGdprMailLocale,
|
|
19
|
-
renderDeletionExecutedEmail,
|
|
20
|
-
renderDeletionRequestedEmail,
|
|
21
|
-
renderExportFailedEmail,
|
|
22
|
-
renderExportReadyEmail,
|
|
23
33
|
} from "../email-templates.js";
|
|
24
34
|
import type { SendDeletionRequestedEmailFn } from "../handlers/request-deletion.write.js";
|
|
35
|
+
import { GdprMailNotificationTypes, gdprMailTemplateLocale } from "../mail-notifications.js";
|
|
25
36
|
import type { SendExportFailedEmailFn, SendExportReadyEmailFn } from "../run-export-jobs.js";
|
|
26
37
|
import type { SendDeletionExecutedEmailFn } from "../run-forget-cleanup.js";
|
|
27
38
|
|
|
@@ -30,74 +41,116 @@ export type GdprMailDefaults = {
|
|
|
30
41
|
readonly appName?: string;
|
|
31
42
|
};
|
|
32
43
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
// The notify-based defaults need the delivery pipeline with an email channel.
|
|
45
|
+
export function isGdprMailDeliveryAvailable(registry: Registry): boolean {
|
|
46
|
+
return registry
|
|
47
|
+
.getExtensionUsages(DELIVERY_CHANNEL_EXTENSION)
|
|
48
|
+
.some((usage) => usage.entityName === "email");
|
|
49
|
+
}
|
|
50
|
+
|
|
37
51
|
export function isMailTransportAvailable(registry: Registry): boolean {
|
|
38
52
|
return registry.getExtensionUsages(MAIL_TRANSPORT_EXTENSION).length > 0;
|
|
39
53
|
}
|
|
40
54
|
|
|
41
|
-
|
|
55
|
+
export function isMailRendererAvailable(registry: Registry): boolean {
|
|
56
|
+
return collectRendererPlugins(registry).some((plugin) => plugin.kinds.includes("mail"));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type NotifyForTenant = (tenantId: TenantId) => NotifyFn;
|
|
42
60
|
|
|
43
61
|
// Per-recipient locale wins (user.locale); mailDefaults.locale is the fallback
|
|
44
|
-
// for unknown/unsupported user.locale values;
|
|
45
|
-
|
|
62
|
+
// for unknown/unsupported user.locale values; a locale without a template for
|
|
63
|
+
// this notification falls back to en.
|
|
64
|
+
function mailLocaleFor(
|
|
65
|
+
notificationType: string,
|
|
46
66
|
userLocale: string | null,
|
|
47
67
|
defaults: GdprMailDefaults,
|
|
48
|
-
):
|
|
68
|
+
): string {
|
|
49
69
|
const user = normalizeGdprMailLocale(userLocale);
|
|
50
|
-
|
|
51
|
-
return
|
|
70
|
+
const preferred = user !== undefined && hasMailTranslations(user) ? user : defaults.locale;
|
|
71
|
+
return gdprMailTemplateLocale(notificationType, preferred ?? "en");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function appNameFor(locale: string, defaults: GdprMailDefaults): string {
|
|
75
|
+
return defaults.appName ?? mailT(locale, "gdpr.mail.appNameDefault");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Delivery failures come back as results, not throws; the callers' best-effort
|
|
79
|
+
// handling only sees them if they throw.
|
|
80
|
+
async function notifyImmediately(
|
|
81
|
+
notify: NotifyFn,
|
|
82
|
+
notificationType: string,
|
|
83
|
+
options: NotifyOptions,
|
|
84
|
+
): Promise<void> {
|
|
85
|
+
const result = await notify(notificationType, { ...options, immediate: true });
|
|
86
|
+
const failed = result.deliveries.find((delivery) => delivery.status === "failed");
|
|
87
|
+
if (failed) {
|
|
88
|
+
throw new Error(`${notificationType} delivery failed: ${failed.error ?? "unknown"}`);
|
|
89
|
+
}
|
|
52
90
|
}
|
|
53
91
|
|
|
54
92
|
export function makeDefaultExportReadyEmail(
|
|
55
|
-
|
|
93
|
+
notifyFor: NotifyForTenant,
|
|
56
94
|
defaults: GdprMailDefaults = {},
|
|
57
95
|
): SendExportReadyEmailFn {
|
|
58
96
|
return async (args) => {
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
97
|
+
const type = GdprMailNotificationTypes.exportReady;
|
|
98
|
+
const locale = mailLocaleFor(type, args.userLocale, defaults);
|
|
99
|
+
// immediate: the plain magic-link token must not land in a delivery job payload.
|
|
100
|
+
await notifyImmediately(notifyFor(args.tenantId), type, {
|
|
101
|
+
to: args.userId,
|
|
102
|
+
locale,
|
|
103
|
+
data: {
|
|
104
|
+
appName: appNameFor(locale, defaults),
|
|
105
|
+
actionUrl: args.downloadUrl,
|
|
106
|
+
validUntil: formatGdprMailTimestamp(args.expiresAt),
|
|
107
|
+
},
|
|
65
108
|
});
|
|
66
|
-
await transport.send({ to: args.userEmail, subject, html, text });
|
|
67
109
|
};
|
|
68
110
|
}
|
|
69
111
|
|
|
70
112
|
export function makeDefaultExportFailedEmail(
|
|
71
|
-
|
|
113
|
+
notifyFor: NotifyForTenant,
|
|
72
114
|
defaults: GdprMailDefaults = {},
|
|
73
115
|
): SendExportFailedEmailFn {
|
|
74
116
|
return async (args) => {
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
117
|
+
const type = GdprMailNotificationTypes.exportFailed;
|
|
118
|
+
const locale = mailLocaleFor(type, args.userLocale, defaults);
|
|
119
|
+
await notifyImmediately(notifyFor(args.tenantId), type, {
|
|
120
|
+
to: args.userId,
|
|
121
|
+
locale,
|
|
122
|
+
data: { appName: appNameFor(locale, defaults) },
|
|
79
123
|
});
|
|
80
|
-
await transport.send({ to: args.userEmail, subject, html, text });
|
|
81
124
|
};
|
|
82
125
|
}
|
|
83
126
|
|
|
84
127
|
export function makeDefaultDeletionRequestedEmail(
|
|
85
|
-
|
|
128
|
+
notify: NotifyFn,
|
|
86
129
|
defaults: GdprMailDefaults = {},
|
|
87
130
|
): SendDeletionRequestedEmailFn {
|
|
88
131
|
return async (args) => {
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
132
|
+
const type = GdprMailNotificationTypes.deletionRequested;
|
|
133
|
+
const locale = mailLocaleFor(type, args.userLocale, defaults);
|
|
134
|
+
await notify(type, {
|
|
135
|
+
to: args.userId,
|
|
136
|
+
locale,
|
|
137
|
+
data: {
|
|
138
|
+
appName: appNameFor(locale, defaults),
|
|
139
|
+
deletionDate: formatGdprMailTimestamp(args.gracePeriodEnd),
|
|
140
|
+
},
|
|
94
141
|
});
|
|
95
|
-
await transport.send({ to: args.userEmail, subject, html, text });
|
|
96
142
|
};
|
|
97
143
|
}
|
|
98
144
|
|
|
145
|
+
export type DeletionExecutedMailDeps = {
|
|
146
|
+
readonly registry: Registry;
|
|
147
|
+
readonly db: DbConnection;
|
|
148
|
+
readonly resolveTransport: (tenantId: string) => Promise<EmailTransport>;
|
|
149
|
+
readonly configFor: (tenantId: TenantId) => ConfigAccessor | undefined;
|
|
150
|
+
};
|
|
151
|
+
|
|
99
152
|
export function makeDefaultDeletionExecutedEmail(
|
|
100
|
-
|
|
153
|
+
deps: DeletionExecutedMailDeps,
|
|
101
154
|
defaults: GdprMailDefaults = {},
|
|
102
155
|
): SendDeletionExecutedEmailFn {
|
|
103
156
|
return async (args) => {
|
|
@@ -109,12 +162,40 @@ export function makeDefaultDeletionExecutedEmail(
|
|
|
109
162
|
// skip: orphan user has no tenant whose transport could send the mail
|
|
110
163
|
return;
|
|
111
164
|
}
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
165
|
+
const type = GdprMailNotificationTypes.deletionExecuted;
|
|
166
|
+
const locale = mailLocaleFor(type, args.userLocale, defaults);
|
|
167
|
+
const plugin = createRendererFoundationApi(
|
|
168
|
+
collectRendererPlugins(deps.registry),
|
|
169
|
+
).createRendererForTenant({ tenantId, kind: "mail" });
|
|
170
|
+
const config = deps.configFor(tenantId);
|
|
171
|
+
const rendered = await plugin.render(
|
|
172
|
+
{
|
|
173
|
+
kind: "mail",
|
|
174
|
+
payload: {
|
|
175
|
+
slug: type,
|
|
176
|
+
locale,
|
|
177
|
+
variables: {
|
|
178
|
+
appName: appNameFor(locale, defaults),
|
|
179
|
+
executedAt: formatGdprMailTimestamp(args.executedAt),
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
db: deps.db,
|
|
185
|
+
registry: deps.registry,
|
|
186
|
+
tenantId,
|
|
187
|
+
...(config !== undefined && { config }),
|
|
188
|
+
},
|
|
189
|
+
);
|
|
190
|
+
if (rendered.kind !== "mail") {
|
|
191
|
+
throw new Error(`deletion-executed mail: renderer answered with kind "${rendered.kind}"`);
|
|
192
|
+
}
|
|
193
|
+
const transport = await deps.resolveTransport(tenantId);
|
|
194
|
+
await transport.send({
|
|
195
|
+
to: args.userEmail,
|
|
196
|
+
subject: rendered.subject,
|
|
197
|
+
html: rendered.html,
|
|
198
|
+
text: rendered.text,
|
|
117
199
|
});
|
|
118
|
-
await transport.send({ to: args.userEmail, subject, html, text });
|
|
119
200
|
};
|
|
120
201
|
}
|
|
@@ -92,12 +92,12 @@ export type BackfillResult = {
|
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
export async function backfillUserLifecycleEvents(conn: DbRunner): Promise<BackfillResult> {
|
|
95
|
-
const rows =
|
|
95
|
+
const rows = await selectMany<{
|
|
96
96
|
id: string;
|
|
97
97
|
status: string;
|
|
98
98
|
gracePeriodEnd: unknown;
|
|
99
99
|
pendingDeletionRequestId: unknown;
|
|
100
|
-
}
|
|
100
|
+
}>(conn, userTable, {});
|
|
101
101
|
|
|
102
102
|
let backfilled = 0;
|
|
103
103
|
const failed: Array<{ id: string; error: string }> = [];
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type FeatureRegistrar,
|
|
3
|
+
type NotificationLocalizedTemplate,
|
|
4
|
+
QnTypes,
|
|
5
|
+
qn,
|
|
6
|
+
type TemplateVariables,
|
|
7
|
+
templateVariable,
|
|
8
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
9
|
+
import {
|
|
10
|
+
hasMailTranslationKey,
|
|
11
|
+
listMailLocales,
|
|
12
|
+
mailT,
|
|
13
|
+
resolveMailLocale,
|
|
14
|
+
} from "@cosmicdrift/kumiko-framework/i18n";
|
|
15
|
+
import { escapeHtml } from "@cosmicdrift/kumiko-headless";
|
|
16
|
+
import { mailParagraph } from "../shared/index.js";
|
|
17
|
+
import { USER_DATA_RIGHTS_FEATURE } from "./constants.js";
|
|
18
|
+
|
|
19
|
+
const ALL_GDPR_MAIL_KINDS = [
|
|
20
|
+
"exportReady",
|
|
21
|
+
"exportFailed",
|
|
22
|
+
"deletionRequested",
|
|
23
|
+
"deletionExecuted",
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
export type GdprMailKind = (typeof ALL_GDPR_MAIL_KINDS)[number];
|
|
27
|
+
|
|
28
|
+
export const GDPR_MAIL_KINDS: Readonly<
|
|
29
|
+
Record<GdprMailKind, { readonly name: string; readonly translationKey: string }>
|
|
30
|
+
> = {
|
|
31
|
+
exportReady: { name: "export-ready", translationKey: "exportReady" },
|
|
32
|
+
exportFailed: { name: "export-failed", translationKey: "exportFailed" },
|
|
33
|
+
deletionRequested: { name: "deletion-requested", translationKey: "deletionRequested" },
|
|
34
|
+
deletionExecuted: { name: "deletion-executed", translationKey: "deletionExecuted" },
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const GdprMailNotificationTypes: Readonly<Record<GdprMailKind, string>> = {
|
|
38
|
+
exportReady: gdprMailType("exportReady"),
|
|
39
|
+
exportFailed: gdprMailType("exportFailed"),
|
|
40
|
+
deletionRequested: gdprMailType("deletionRequested"),
|
|
41
|
+
deletionExecuted: gdprMailType("deletionExecuted"),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function gdprMailType(kind: GdprMailKind): string {
|
|
45
|
+
return qn(USER_DATA_RIGHTS_FEATURE, QnTypes.notify, GDPR_MAIL_KINDS[kind].name);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function subjectKey(kind: GdprMailKind): string {
|
|
49
|
+
return `gdpr.mail.${GDPR_MAIL_KINDS[kind].translationKey}.subject`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const SUBJECT_KEY_BY_TYPE: ReadonlyMap<string, string> = new Map(
|
|
53
|
+
ALL_GDPR_MAIL_KINDS.map((kind) => [GdprMailNotificationTypes[kind], subjectKey(kind)]),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// A registered locale whose bundle lacks the subject key has no template (see
|
|
57
|
+
// gdprMailLocalizedTemplate); the renderer has no locale fallback, so the
|
|
58
|
+
// sender must pick a locale that has one or the mail would not go out at all.
|
|
59
|
+
export function gdprMailTemplateLocale(notificationType: string, requestedLocale: string): string {
|
|
60
|
+
const locale = resolveMailLocale(requestedLocale);
|
|
61
|
+
const key = SUBJECT_KEY_BY_TYPE.get(notificationType);
|
|
62
|
+
return key !== undefined && hasMailTranslationKey(locale, key) ? locale : "en";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const appName = () => templateVariable.text({ required: true });
|
|
66
|
+
|
|
67
|
+
export const GDPR_MAIL_VARIABLES: Readonly<Record<GdprMailKind, TemplateVariables>> = {
|
|
68
|
+
exportReady: {
|
|
69
|
+
appName: appName(),
|
|
70
|
+
actionUrl: templateVariable.link({ required: true }),
|
|
71
|
+
validUntil: templateVariable.text({ required: true }),
|
|
72
|
+
},
|
|
73
|
+
exportFailed: { appName: appName() },
|
|
74
|
+
deletionRequested: {
|
|
75
|
+
appName: appName(),
|
|
76
|
+
deletionDate: templateVariable.text({ required: true }),
|
|
77
|
+
},
|
|
78
|
+
deletionExecuted: { appName: appName(), executedAt: templateVariable.text({ required: true }) },
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Translation placeholder -> template variable per kind.
|
|
82
|
+
const PLACEHOLDER_SLOTS: Readonly<Record<GdprMailKind, Readonly<Record<string, string>>>> = {
|
|
83
|
+
exportReady: { app: "{appName}", when: "{validUntil}" },
|
|
84
|
+
exportFailed: { app: "{appName}" },
|
|
85
|
+
deletionRequested: { app: "{appName}", when: "{deletionDate}" },
|
|
86
|
+
deletionExecuted: { app: "{appName}", when: "{executedAt}" },
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
function paragraph(kind: GdprMailKind, locale: string, part: string): string {
|
|
90
|
+
const { translationKey } = GDPR_MAIL_KINDS[kind];
|
|
91
|
+
return mailParagraph(
|
|
92
|
+
mailT(locale, `gdpr.mail.${translationKey}.${part}`, PLACEHOLDER_SLOTS[kind]),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function greeting(locale: string): string {
|
|
97
|
+
return mailParagraph(mailT(locale, "gdpr.mail.greeting"));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function mailBody(kind: GdprMailKind, locale: string): string {
|
|
101
|
+
switch (kind) {
|
|
102
|
+
case "exportReady":
|
|
103
|
+
return [
|
|
104
|
+
greeting(locale),
|
|
105
|
+
paragraph(kind, locale, "intro"),
|
|
106
|
+
`<p><a data-button href="{{actionUrl}}">${escapeHtml(mailT(locale, "gdpr.mail.exportReady.button"))}</a></p>`,
|
|
107
|
+
paragraph(kind, locale, "expiry"),
|
|
108
|
+
].join("");
|
|
109
|
+
case "exportFailed":
|
|
110
|
+
return [greeting(locale), paragraph(kind, locale, "intro")].join("");
|
|
111
|
+
case "deletionRequested":
|
|
112
|
+
return [
|
|
113
|
+
greeting(locale),
|
|
114
|
+
paragraph(kind, locale, "intro"),
|
|
115
|
+
paragraph(kind, locale, "cancel"),
|
|
116
|
+
].join("");
|
|
117
|
+
case "deletionExecuted":
|
|
118
|
+
return [greeting(locale), paragraph(kind, locale, "intro")].join("");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Evaluated when the registry is built, after every locale feature has
|
|
123
|
+
// registered its mail translations. English always ships; another locale only
|
|
124
|
+
// gets a template when it has its own subject, so a missing translation never
|
|
125
|
+
// silently becomes an English mail in that locale's slot.
|
|
126
|
+
export function gdprMailLocalizedTemplate(kind: GdprMailKind): () => NotificationLocalizedTemplate {
|
|
127
|
+
const kindSubjectKey = subjectKey(kind);
|
|
128
|
+
return () => {
|
|
129
|
+
const locales = [
|
|
130
|
+
"en",
|
|
131
|
+
...listMailLocales().filter(
|
|
132
|
+
(locale) => locale !== "en" && hasMailTranslationKey(locale, kindSubjectKey),
|
|
133
|
+
),
|
|
134
|
+
];
|
|
135
|
+
return Object.fromEntries(
|
|
136
|
+
locales.map((locale) => [
|
|
137
|
+
locale,
|
|
138
|
+
{
|
|
139
|
+
body: mailBody(kind, locale),
|
|
140
|
+
meta: {
|
|
141
|
+
subject: mailT(locale, kindSubjectKey, { app: "{{appName}}" }),
|
|
142
|
+
category: "transactional" as const,
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
]),
|
|
146
|
+
);
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function registerGdprMails(r: FeatureRegistrar): void {
|
|
151
|
+
for (const kind of ALL_GDPR_MAIL_KINDS) {
|
|
152
|
+
r.notification(GDPR_MAIL_KINDS[kind].name, {
|
|
153
|
+
variables: GDPR_MAIL_VARIABLES[kind],
|
|
154
|
+
localizedTemplate: gdprMailLocalizedTemplate(kind),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -254,7 +254,7 @@ export async function runForgetCleanup(
|
|
|
254
254
|
|
|
255
255
|
// Step 1: Find users with expired grace period.
|
|
256
256
|
const dueUsers = await selectUsersDueForForgetCleanup(
|
|
257
|
-
db
|
|
257
|
+
db,
|
|
258
258
|
USER_STATUS.DeletionRequested,
|
|
259
259
|
now.toString(),
|
|
260
260
|
);
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
useTranslation,
|
|
18
18
|
} from "@cosmicdrift/kumiko-renderer";
|
|
19
19
|
import { postWithDownload } from "@cosmicdrift/kumiko-renderer-web";
|
|
20
|
+
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
20
21
|
import { type ReactNode, useEffect, useState } from "react";
|
|
21
22
|
import {
|
|
22
23
|
EXPORT_JOB_STATUS,
|
|
@@ -49,7 +50,7 @@ type SectionStatus =
|
|
|
49
50
|
// Dispatcher-Failures tragen i18nKey nur wenn der Handler einen setzt —
|
|
50
51
|
// Boundary-Read mit generischem Fallback.
|
|
51
52
|
function failureKey(error: unknown): string {
|
|
52
|
-
const key = (error
|
|
53
|
+
const key = propertyOf(error, "i18nKey");
|
|
53
54
|
return typeof key === "string" ? key : "userDataRights.privacyCenter.errors.generic";
|
|
54
55
|
}
|
|
55
56
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"type": "breaking",
|
|
17
17
|
"title": "user-profile now registers its own profile screen (id user-profile:screen:profile); app-side profile screen registrations must be removed (fw#2312).",
|
|
18
18
|
"detail": "`profile` is now a declarative `projectionDetail` screen bound to `user:query:user:me` instead of a custom React component: change-password and change-email stay `EditExtensionSection` components (re-auth flows a declarative action can't express), account deletion (request/cancel via `user-data-rights`, grace period) is now fully declarative fields + `actions: RowAction[]`, preserving the original confirm dialog and `visible: {field, eq/ne}` toggle 1:1. `userProfileClient()` now registers the two extension-section components via `extensionSectionComponents` instead of exposing a `ProfileScreen` component for apps to place in a `components` map. `ProfileScreen` is a dead export. New exports: `ChangeEmailSection`, `ChangePasswordSection` (the two surviving extension components, importable for tests but not meant to be placed manually). Known UI regression, accepted for this pass (same trade-off as `privacy-center`): the deletion confirm-dialog title is now always the action's `label`; the dynamically composed grace-period banner sentence is gone, replaced by a plain `gracePeriodEnd` date field; there is no cancel-deletion success toast.",
|
|
19
|
-
"migration": "The bundled feature now registers the `profile` screen itself (id `user-profile:screen:profile`) — an app's own `profile` custom-screen registration collides on boot with a duplicate short-id error. Delete the app-side registration entirely: `r.screen({ id: \"profile\", type: \"custom\", renderer: { react: { __component: \"UserProfileScreen\" } }, access: { openToAll: true } })` has no replacement to write, just remove the call. If you have an `r.nav({ screen: \"profile\" })` (or any other reference to the app-local `profile` screen id), repoint it at `\"user-profile:screen:profile\"`. Also remove `components: { UserProfileScreen: ProfileScreen }` from your renderer/client setup and the now-unused `ProfileScreen` import — the client no longer exposes that component. Known affected consumers (not modified here, out of this PR's scope): `money-horse`
|
|
19
|
+
"migration": "The bundled feature now registers the `profile` screen itself (id `user-profile:screen:profile`) — an app's own `profile` custom-screen registration collides on boot with a duplicate short-id error. Delete the app-side registration entirely: `r.screen({ id: \"profile\", type: \"custom\", renderer: { react: { __component: \"UserProfileScreen\" } }, access: { openToAll: true } })` has no replacement to write, just remove the call. If you have an `r.nav({ screen: \"profile\" })` (or any other reference to the app-local `profile` screen id), repoint it at `\"user-profile:screen:profile\"`. Also remove `components: { UserProfileScreen: ProfileScreen }` from your renderer/client setup and the now-unused `ProfileScreen` import — the client no longer exposes that component. Known affected consumers (not modified here, out of this PR's scope): `money-horse` and `offlot-app` both currently register their own `profile` custom screen this way."
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"version": "0.123.2",
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
usePrimitives,
|
|
14
14
|
useTranslation,
|
|
15
15
|
} from "@cosmicdrift/kumiko-renderer";
|
|
16
|
+
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
16
17
|
import { type ReactNode, useState } from "react";
|
|
17
18
|
import { AuthHandlers } from "../../auth-email-password/constants.js";
|
|
18
19
|
import { requestEmailVerification } from "../../auth-email-password/web/index.js";
|
|
@@ -27,7 +28,7 @@ type SectionStatus =
|
|
|
27
28
|
// Dispatcher failures carry an i18nKey only when the handler sets one —
|
|
28
29
|
// boundary read with a generic fallback.
|
|
29
30
|
function failureKey(error: unknown): string {
|
|
30
|
-
const key = (error
|
|
31
|
+
const key = propertyOf(error, "i18nKey");
|
|
31
32
|
return typeof key === "string" ? key : "profile.errors.generic";
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.363.0",
|
|
4
|
+
"type": "breaking",
|
|
5
|
+
"title": "Waitlist confirmation and admin notice use template variables",
|
|
6
|
+
"detail": "The confirmation and admin-notice mails are r.notification declarations (waitlist:notify:confirmation, waitlist:notify:admin-notice) with variables and localized templates. The notify data is the variable object; the confirmation wording is computed in code and passed as variables. The admin notice subject is the submitter name.",
|
|
7
|
+
"migration": "Update delivery preferences and tests that referenced the old types or read the sections data. An app that mounts it without includeBundled needs template-resolver, renderer-foundation and @cosmicdrift/kumiko-renderer-mail-html at boot (composeRendererStack() from dev-server mounts all of them), plus a delivery with the channel-email feature; apps without the template tables need a schema migration (`kumiko schema generate`)."
|
|
8
|
+
},
|
|
2
9
|
{
|
|
3
10
|
"version": "0.342.0",
|
|
4
11
|
"type": "improvement",
|
|
@@ -12,6 +12,13 @@ import { rejectHandler } from "./handlers/reject.write.js";
|
|
|
12
12
|
import { createSubmitHandler } from "./handlers/submit.write.js";
|
|
13
13
|
import { WAITLIST_I18N } from "./i18n.js";
|
|
14
14
|
import { adminAccess } from "./lib.js";
|
|
15
|
+
import {
|
|
16
|
+
WAITLIST_ADMIN_NOTICE_RECORD,
|
|
17
|
+
WAITLIST_ADMIN_NOTICE_VARIABLES,
|
|
18
|
+
WAITLIST_CONFIRMATION_VARIABLES,
|
|
19
|
+
waitlistAdminNoticeTemplate,
|
|
20
|
+
waitlistConfirmationTemplate,
|
|
21
|
+
} from "./mail.js";
|
|
15
22
|
import type { WaitlistOptions } from "./options.js";
|
|
16
23
|
import { waitlistListScreen } from "./screens.js";
|
|
17
24
|
import { waitlistEntryDeleteHook, waitlistEntryExportHook } from "./user-data-hooks.js";
|
|
@@ -26,6 +33,16 @@ export function createWaitlistFeature(opts: WaitlistOptions = {}): FeatureDefini
|
|
|
26
33
|
r.requires("tenant", "auth-email-password", "user-data-rights");
|
|
27
34
|
r.translations({ keys: WAITLIST_I18N });
|
|
28
35
|
|
|
36
|
+
r.notification("confirmation", {
|
|
37
|
+
variables: WAITLIST_CONFIRMATION_VARIABLES,
|
|
38
|
+
localizedTemplate: waitlistConfirmationTemplate,
|
|
39
|
+
});
|
|
40
|
+
r.notification("admin-notice", {
|
|
41
|
+
variables: WAITLIST_ADMIN_NOTICE_VARIABLES,
|
|
42
|
+
record: WAITLIST_ADMIN_NOTICE_RECORD,
|
|
43
|
+
localizedTemplate: waitlistAdminNoticeTemplate,
|
|
44
|
+
});
|
|
45
|
+
|
|
29
46
|
r.entity("waitlistEntry", waitlistEntryEntity);
|
|
30
47
|
r.writeHandler(createSubmitHandler(opts));
|
|
31
48
|
r.writeHandler(createInviteHandler(opts.invite));
|
|
@@ -10,10 +10,10 @@ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
|
10
10
|
import { WAITLIST_NOTIFICATION_TYPES, WAITLIST_STATUS } from "../constants.js";
|
|
11
11
|
import { normalizeEmail, platformActor, waitlistDb, waitlistExecutor } from "../lib.js";
|
|
12
12
|
import {
|
|
13
|
-
renderWaitlistAdminNoticeEmail,
|
|
14
|
-
renderWaitlistConfirmationEmail,
|
|
15
13
|
resolveWaitlistAdminMailLocale,
|
|
16
14
|
resolveWaitlistMailLocale,
|
|
15
|
+
waitlistAdminNoticeVariables,
|
|
16
|
+
waitlistConfirmationVariables,
|
|
17
17
|
} from "../mail.js";
|
|
18
18
|
import {
|
|
19
19
|
DEFAULT_SUBMIT_EMAIL_RATE_LIMITS,
|
|
@@ -130,7 +130,7 @@ export function createSubmitHandler(opts: WaitlistOptions) {
|
|
|
130
130
|
await sendBestEffort(ctx, "confirmation", (notify) =>
|
|
131
131
|
notify(WAITLIST_NOTIFICATION_TYPES.confirmation, {
|
|
132
132
|
route: { email },
|
|
133
|
-
data:
|
|
133
|
+
data: waitlistConfirmationVariables({
|
|
134
134
|
locale: payload.locale,
|
|
135
135
|
appName: opts.appName,
|
|
136
136
|
}),
|
|
@@ -145,12 +145,11 @@ export function createSubmitHandler(opts: WaitlistOptions) {
|
|
|
145
145
|
if (!adminRecipient) return;
|
|
146
146
|
await notify(WAITLIST_NOTIFICATION_TYPES.adminNotice, {
|
|
147
147
|
route: { email: adminRecipient },
|
|
148
|
-
data:
|
|
148
|
+
data: waitlistAdminNoticeVariables({
|
|
149
149
|
name: payload.name,
|
|
150
150
|
email,
|
|
151
151
|
company: payload.company,
|
|
152
152
|
message: payload.message,
|
|
153
|
-
adminLocale: ctx.locale,
|
|
154
153
|
}),
|
|
155
154
|
priority: "normal",
|
|
156
155
|
locale: resolveWaitlistAdminMailLocale(ctx.locale),
|