@cosmicdrift/kumiko-samples 0.361.0 → 0.363.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 +17 -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/auth-email-password/changes.json +14 -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/invite-create.write.ts +4 -5
- 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/token-request-handler.ts +3 -12
- 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-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 +7 -0
- package/packages/bundled-features/src/billing-foundation/constants.ts +5 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +38 -44
- 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 +115 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +30 -30
- 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-request.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +2 -2
- 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 +2 -2
- package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -6
- 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 +122 -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/handlers/set.write.ts +1 -5
- package/packages/bundled-features/src/custom-fields/db/queries/user-data-rights.ts +2 -2
- 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 +84 -0
- package/packages/bundled-features/src/delivery/changes.json +48 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +129 -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 +17 -2
- package/packages/bundled-features/src/delivery/jobs.ts +48 -5
- 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 +28 -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/aggregate-id.ts +10 -0
- package/packages/bundled-features/src/document-ingest-foundation/changes.json +6 -0
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +3 -1
- package/packages/bundled-features/src/document-ingest-foundation/feature.ts +5 -3
- package/packages/bundled-features/src/document-ingest-foundation/index.ts +1 -0
- 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-foundation/index.ts +7 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +86 -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/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-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 +40 -25
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +4 -3
- package/packages/bundled-features/src/jobs/handlers/catalog.query.ts +1 -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 +132 -35
- package/packages/bundled-features/src/renderer-pdf/feature.ts +7 -1
- 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/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 +25 -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/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 +63 -7
- package/packages/bundled-features/src/template-resolver/api.ts +83 -8
- package/packages/bundled-features/src/template-resolver/changes.json +118 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +214 -0
- package/packages/bundled-features/src/template-resolver/constants.ts +39 -11
- package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +335 -0
- package/packages/bundled-features/src/template-resolver/document-schema.ts +323 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +56 -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 +142 -0
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +72 -0
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +83 -0
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +3 -3
- 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 +108 -0
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +5 -7
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +250 -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 +106 -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 +184 -0
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +183 -0
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +55 -0
- package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +57 -10
- 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 +15 -76
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +17 -0
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +116 -0
- package/packages/bundled-features/src/template-resolver/index.ts +100 -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/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 +30 -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 +147 -17
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +416 -0
- package/packages/bundled-features/src/template-resolver/table.ts +14 -0
- 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-meta.ts +129 -0
- package/packages/bundled-features/src/template-resolver/template-preview.ts +573 -0
- package/packages/bundled-features/src/template-resolver/template-resources.ts +74 -0
- package/packages/bundled-features/src/template-resolver/template-usage.ts +48 -0
- package/packages/bundled-features/src/template-resolver/template-validation.ts +229 -0
- package/packages/bundled-features/src/template-resolver/testing.ts +5 -5
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +9 -6
- package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +162 -0
- package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +28 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +850 -0
- package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +61 -0
- package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +26 -0
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +100 -0
- package/packages/bundled-features/src/template-resolver/web/index.ts +19 -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 +169 -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/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/roles-input-schema.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/changes.json +14 -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/download-by-token.query.ts +5 -1
- package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +13 -1
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +5 -11
- package/packages/bundled-features/src/user-data-rights/index.ts +1 -0
- 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/schema/download-attempt.ts +6 -12
- package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +2 -1
- package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +10 -1
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/download-attempt.userdata-hook.ts +58 -0
- 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/workflow-runner/event-trigger.ts +1 -2
- package/samples/apps/ui-walkthrough/package.json +2 -0
- 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 +154 -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 +354 -0
- package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +68 -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/features/rental/screens.ts +1 -1
- package/samples/apps/ui-walkthrough/src/run-config.ts +37 -1
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +0 -2
- package/samples/apps/use-all-bundled/feature-manifest.json +123 -22
- package/samples/apps/use-all-bundled/kumiko/migrations/0044_download-attempt-ip-bidx.rebuild.json +4 -0
- 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
package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
2
|
import type { CheckoutConsentRecordedPayload } from "../events.js";
|
|
3
|
-
import {
|
|
3
|
+
import { type ConsentLocale, resolveConsentLocale } from "./consent-text.js";
|
|
4
4
|
|
|
5
5
|
// Inline de/en like the consent texts: the confirmation repeats the legal
|
|
6
6
|
// wording the buyer agreed to, so it cannot follow the en-only i18n convention.
|
|
@@ -22,7 +22,7 @@ type ConfirmationLabels = {
|
|
|
22
22
|
readonly every: string;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
const
|
|
25
|
+
export const CONFIRMATION_LABELS: Readonly<Record<ConsentLocale, ConfirmationLabels>> = {
|
|
26
26
|
de: {
|
|
27
27
|
subject: "Vertragsbestätigung",
|
|
28
28
|
intro:
|
|
@@ -71,19 +71,17 @@ const LABELS: Readonly<Record<ConsentLocale, ConfirmationLabels>> = {
|
|
|
71
71
|
},
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
| { readonly text: string }
|
|
76
|
-
| { readonly heading: string }
|
|
77
|
-
| { readonly markdown: string };
|
|
74
|
+
type TextRow = { readonly text: string };
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
readonly
|
|
82
|
-
readonly
|
|
83
|
-
readonly
|
|
76
|
+
/** Variables of the contract confirmation template. */
|
|
77
|
+
export type ContractConfirmationVariables = {
|
|
78
|
+
readonly contractLines: readonly TextRow[];
|
|
79
|
+
readonly consentGivenAt: string;
|
|
80
|
+
readonly termsParagraphs: readonly TextRow[];
|
|
81
|
+
readonly operatorEmail: string;
|
|
84
82
|
};
|
|
85
83
|
|
|
86
|
-
export type
|
|
84
|
+
export type ContractConfirmationArgs = {
|
|
87
85
|
readonly consent: CheckoutConsentRecordedPayload;
|
|
88
86
|
readonly timeZone: string;
|
|
89
87
|
/** Translated plan name; falls back to the raw tier. */
|
|
@@ -99,7 +97,7 @@ export type RenderContractConfirmationArgs = {
|
|
|
99
97
|
};
|
|
100
98
|
|
|
101
99
|
function formatPrice(consent: CheckoutConsentRecordedPayload, locale: ConsentLocale): string {
|
|
102
|
-
const labels =
|
|
100
|
+
const labels = CONFIRMATION_LABELS[locale];
|
|
103
101
|
if (consent.unitAmount === null || consent.currency === null) return "-";
|
|
104
102
|
let amount: string;
|
|
105
103
|
try {
|
|
@@ -140,45 +138,41 @@ function formatDate(iso: string, locale: ConsentLocale, timeZone: string): strin
|
|
|
140
138
|
}).format(epochMilliseconds);
|
|
141
139
|
}
|
|
142
140
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
)
|
|
141
|
+
// The mail template only knows plain text variables, so the Markdown terms
|
|
142
|
+
// become one row per paragraph; heading markers are dropped.
|
|
143
|
+
function termsParagraphsOf(markdown: string): readonly TextRow[] {
|
|
144
|
+
return markdown
|
|
145
|
+
.split(/\n\s*\n/)
|
|
146
|
+
.map((paragraph) => paragraph.replace(/^#{1,6}\s+/gm, "").trim())
|
|
147
|
+
.filter((paragraph) => paragraph.length > 0)
|
|
148
|
+
.map((text) => ({ text }));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function contractConfirmationVariables(
|
|
152
|
+
args: ContractConfirmationArgs,
|
|
153
|
+
): ContractConfirmationVariables {
|
|
148
154
|
const locale = resolveConsentLocale(args.consent.locale);
|
|
149
|
-
const labels =
|
|
150
|
-
const
|
|
151
|
-
const { consent } = args;
|
|
155
|
+
const labels = CONFIRMATION_LABELS[locale];
|
|
156
|
+
const { consent, timeZone } = args;
|
|
152
157
|
|
|
153
|
-
const { timeZone } = args;
|
|
154
158
|
const subjectLine =
|
|
155
159
|
consent.mode === "payment"
|
|
156
160
|
? `${labels.item}: ${args.itemLabel ?? labels.oneOffPayment}`
|
|
157
161
|
: `${labels.plan}: ${args.tierLabel ?? consent.tier ?? labels.oneOffPayment}`;
|
|
158
|
-
const contractLines = [
|
|
159
|
-
subjectLine,
|
|
160
|
-
`${labels.price}: ${formatPrice(consent, locale)}`,
|
|
161
|
-
`${labels.contractStart}: ${formatDate(args.contractStartIso, locale, timeZone)}`,
|
|
162
|
-
...(args.currentPeriodEndIso !== undefined
|
|
163
|
-
? [`${labels.currentPeriodEnd}: ${formatDate(args.currentPeriodEndIso, locale, timeZone)}`]
|
|
164
|
-
: []),
|
|
165
|
-
];
|
|
166
162
|
const vatNote = args.vatNote[locale] ?? args.vatNote[resolveConsentLocale()];
|
|
167
163
|
|
|
168
164
|
return {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
{
|
|
173
|
-
...
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
],
|
|
182
|
-
footer: `${labels.contact} ${args.operatorEmail}`,
|
|
165
|
+
contractLines: [
|
|
166
|
+
subjectLine,
|
|
167
|
+
`${labels.price}: ${formatPrice(consent, locale)}`,
|
|
168
|
+
`${labels.contractStart}: ${formatDate(args.contractStartIso, locale, timeZone)}`,
|
|
169
|
+
...(args.currentPeriodEndIso !== undefined
|
|
170
|
+
? [`${labels.currentPeriodEnd}: ${formatDate(args.currentPeriodEndIso, locale, timeZone)}`]
|
|
171
|
+
: []),
|
|
172
|
+
...(vatNote !== undefined ? [vatNote] : []),
|
|
173
|
+
].map((text) => ({ text })),
|
|
174
|
+
consentGivenAt: formatDate(args.consentGivenAtIso, locale, timeZone),
|
|
175
|
+
termsParagraphs: termsParagraphsOf(args.termsContent),
|
|
176
|
+
operatorEmail: args.operatorEmail,
|
|
183
177
|
};
|
|
184
178
|
}
|
package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
SUBSCRIPTION_AGGREGATE_TYPE,
|
|
28
28
|
} from "../events.js";
|
|
29
29
|
import type { ConsumerProtectionOptions, ResolvedBillingFoundationOptions } from "../types.js";
|
|
30
|
-
import {
|
|
30
|
+
import { contractConfirmationVariables } from "./confirmation-mail.js";
|
|
31
31
|
import { resolveConsentLocale } from "./consent-text.js";
|
|
32
32
|
|
|
33
33
|
export const issueContractConfirmationSchema = z
|
|
@@ -185,7 +185,7 @@ export function createIssueContractConfirmationHandler(
|
|
|
185
185
|
: undefined;
|
|
186
186
|
|
|
187
187
|
const issuedAtIso = options.now().toString();
|
|
188
|
-
const
|
|
188
|
+
const variables = contractConfirmationVariables({
|
|
189
189
|
consent,
|
|
190
190
|
timeZone: pickTimeZone(recipient.data.timezone, ctx.tz.tenant),
|
|
191
191
|
...(tierLabel !== undefined && { tierLabel }),
|
|
@@ -220,7 +220,7 @@ export function createIssueContractConfirmationHandler(
|
|
|
220
220
|
|
|
221
221
|
await ctx.notify(CONTRACT_CONFIRMATION_NOTIFICATION_TYPE, {
|
|
222
222
|
route: { email: recipient.data.email },
|
|
223
|
-
data:
|
|
223
|
+
data: variables,
|
|
224
224
|
locale,
|
|
225
225
|
priority: "critical",
|
|
226
226
|
});
|
package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type FeatureRegistrar,
|
|
3
|
+
type NotificationLocalizedTemplate,
|
|
4
|
+
type TemplateVariables,
|
|
5
|
+
templateVariable,
|
|
6
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
7
|
+
import { mailHeading, mailParagraph, mailRepeatedRows } from "../../shared/index.js";
|
|
8
|
+
import { CONFIRMATION_LABELS } from "./confirmation-mail.js";
|
|
9
|
+
import { CONSENT_LOCALES, CONSENT_TEXTS, type ConsentLocale } from "./consent-text.js";
|
|
10
|
+
import { TERMINATION_TEXTS } from "./termination-texts.js";
|
|
11
|
+
|
|
12
|
+
// Slot syntax for the translated texts: `{name}` becomes a template variable.
|
|
13
|
+
const OPERATOR_EMAIL_SLOT = "{operatorEmail}";
|
|
14
|
+
|
|
15
|
+
const textRows = (): TemplateVariables[string] =>
|
|
16
|
+
templateVariable.list({ text: templateVariable.text({ required: true }) }, { required: true });
|
|
17
|
+
|
|
18
|
+
export const TERMINATION_RECEIPT_VARIABLES: TemplateVariables = {
|
|
19
|
+
subject: templateVariable.text({ required: true }),
|
|
20
|
+
receivedAt: templateVariable.text({ required: true }),
|
|
21
|
+
requestId: templateVariable.text({ required: true }),
|
|
22
|
+
details: textRows(),
|
|
23
|
+
operatorEmail: templateVariable.text({ required: true }),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const OPERATOR_NOTICE_VARIABLES: TemplateVariables = {
|
|
27
|
+
subject: templateVariable.text({ required: true }),
|
|
28
|
+
lines: textRows(),
|
|
29
|
+
operatorEmail: templateVariable.text({ required: true }),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const CONTRACT_CONFIRMATION_VARIABLES: TemplateVariables = {
|
|
33
|
+
contractLines: textRows(),
|
|
34
|
+
consentGivenAt: templateVariable.text({ required: true }),
|
|
35
|
+
termsParagraphs: textRows(),
|
|
36
|
+
operatorEmail: templateVariable.text({ required: true }),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function perLocale(
|
|
40
|
+
build: (locale: ConsentLocale) => { body: string; subject: string },
|
|
41
|
+
): NotificationLocalizedTemplate {
|
|
42
|
+
return Object.fromEntries(
|
|
43
|
+
CONSENT_LOCALES.map((locale) => {
|
|
44
|
+
const { body, subject } = build(locale);
|
|
45
|
+
return [locale, { body, meta: { subject, category: "transactional" as const } }];
|
|
46
|
+
}),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function terminationReceiptTemplate(): NotificationLocalizedTemplate {
|
|
51
|
+
return perLocale((locale) => {
|
|
52
|
+
const texts = TERMINATION_TEXTS[locale];
|
|
53
|
+
return {
|
|
54
|
+
subject: "{{subject}}",
|
|
55
|
+
body: [
|
|
56
|
+
mailParagraph(texts.receiptGreeting),
|
|
57
|
+
mailParagraph(texts.receiptIntro),
|
|
58
|
+
mailParagraph(`${texts.receivedAt}: {receivedAt}`),
|
|
59
|
+
mailParagraph(`${texts.requestId}: {requestId}`),
|
|
60
|
+
mailRepeatedRows("details", "text"),
|
|
61
|
+
mailParagraph(texts.receiptNextSteps),
|
|
62
|
+
mailParagraph(texts.receiptRevoke(OPERATOR_EMAIL_SLOT)),
|
|
63
|
+
mailParagraph(`${texts.contact} {operatorEmail}`),
|
|
64
|
+
].join(""),
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function operatorNoticeTemplate(): NotificationLocalizedTemplate {
|
|
70
|
+
return perLocale((locale) => ({
|
|
71
|
+
subject: "{{subject}}",
|
|
72
|
+
body: [
|
|
73
|
+
mailParagraph(TERMINATION_TEXTS[locale].operatorIntro),
|
|
74
|
+
mailRepeatedRows("lines", "text"),
|
|
75
|
+
mailParagraph("{operatorEmail}"),
|
|
76
|
+
].join(""),
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function contractConfirmationTemplate(): NotificationLocalizedTemplate {
|
|
81
|
+
return perLocale((locale) => {
|
|
82
|
+
const labels = CONFIRMATION_LABELS[locale];
|
|
83
|
+
const consent = CONSENT_TEXTS[locale];
|
|
84
|
+
return {
|
|
85
|
+
subject: labels.subject,
|
|
86
|
+
body: [
|
|
87
|
+
mailParagraph(labels.intro),
|
|
88
|
+
mailRepeatedRows("contractLines", "text"),
|
|
89
|
+
mailHeading(labels.consentHeading),
|
|
90
|
+
mailParagraph(consent.earlyPerformance),
|
|
91
|
+
mailParagraph(consent.withdrawalLoss),
|
|
92
|
+
mailParagraph(`${labels.consentGivenAt}: {consentGivenAt}`),
|
|
93
|
+
mailHeading(labels.termsHeading),
|
|
94
|
+
mailRepeatedRows("termsParagraphs", "text"),
|
|
95
|
+
mailParagraph(`${labels.contact} {operatorEmail}`),
|
|
96
|
+
].join(""),
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// The registered notification names are the tail of the QN types in constants.ts.
|
|
102
|
+
export function registerConsumerProtectionMails(r: FeatureRegistrar): void {
|
|
103
|
+
r.notification("contract-confirmation", {
|
|
104
|
+
variables: CONTRACT_CONFIRMATION_VARIABLES,
|
|
105
|
+
localizedTemplate: contractConfirmationTemplate,
|
|
106
|
+
});
|
|
107
|
+
r.notification("contract-termination-receipt", {
|
|
108
|
+
variables: TERMINATION_RECEIPT_VARIABLES,
|
|
109
|
+
localizedTemplate: terminationReceiptTemplate,
|
|
110
|
+
});
|
|
111
|
+
r.notification("contract-termination-operator-notice", {
|
|
112
|
+
variables: OPERATOR_NOTICE_VARIABLES,
|
|
113
|
+
localizedTemplate: operatorNoticeTemplate,
|
|
114
|
+
});
|
|
115
|
+
}
|
package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "../events.js";
|
|
14
14
|
import type { ConsumerProtectionOptions, ResolvedBillingFoundationOptions } from "../types.js";
|
|
15
15
|
import { createIssueContractConfirmationHandler } from "./issue-confirmation.js";
|
|
16
|
+
import { registerConsumerProtectionMails } from "./mail-notifications.js";
|
|
16
17
|
import { registerContractTermination } from "./register-termination.js";
|
|
17
18
|
|
|
18
19
|
type ConfirmationTrigger = {
|
|
@@ -46,6 +47,7 @@ export function registerConsumerProtection(
|
|
|
46
47
|
consumerProtection: ConsumerProtectionOptions,
|
|
47
48
|
): void {
|
|
48
49
|
r.requires("template-resolver", "delivery", "user", "tenant");
|
|
50
|
+
registerConsumerProtectionMails(r);
|
|
49
51
|
registerContractTermination(r, options, consumerProtection);
|
|
50
52
|
r.defineEvent(CHECKOUT_CONSENT_RECORDED_EVENT_SHORT, checkoutConsentRecordedPayloadSchema, {
|
|
51
53
|
piiFields: "none",
|
package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts
CHANGED
|
@@ -7,11 +7,22 @@ import type {
|
|
|
7
7
|
import type { ConsentLocale } from "./consent-text.js";
|
|
8
8
|
import { type OperatorNoticeReason, TERMINATION_TEXTS } from "./termination-texts.js";
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
type TextRow = { readonly text: string };
|
|
11
|
+
|
|
12
|
+
/** Variables of the termination receipt template. */
|
|
13
|
+
export type TerminationReceiptVariables = {
|
|
11
14
|
readonly subject: string;
|
|
12
|
-
readonly
|
|
13
|
-
readonly
|
|
14
|
-
readonly
|
|
15
|
+
readonly receivedAt: string;
|
|
16
|
+
readonly requestId: string;
|
|
17
|
+
readonly details: readonly TextRow[];
|
|
18
|
+
readonly operatorEmail: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** Variables of the operator notice template. */
|
|
22
|
+
export type OperatorNoticeVariables = {
|
|
23
|
+
readonly subject: string;
|
|
24
|
+
readonly lines: readonly TextRow[];
|
|
25
|
+
readonly operatorEmail: string;
|
|
15
26
|
};
|
|
16
27
|
|
|
17
28
|
/** What the declarant entered; the only input of the receipt. */
|
|
@@ -53,7 +64,7 @@ export function formatReceivedAt(iso: string, locale: ConsentLocale): string {
|
|
|
53
64
|
function declarationLines(
|
|
54
65
|
declaration: OperatorNoticeDeclaration,
|
|
55
66
|
locale: ConsentLocale,
|
|
56
|
-
): readonly
|
|
67
|
+
): readonly TextRow[] {
|
|
57
68
|
const texts = TERMINATION_TEXTS[locale];
|
|
58
69
|
const lines = [
|
|
59
70
|
`${texts.declarationType}: ${texts.declarationTypeLabel[declaration.declarationType]}`,
|
|
@@ -68,7 +79,7 @@ function declarationLines(
|
|
|
68
79
|
return lines.map((text) => ({ text }));
|
|
69
80
|
}
|
|
70
81
|
|
|
71
|
-
export type
|
|
82
|
+
export type TerminationReceiptArgs = {
|
|
72
83
|
readonly locale: ConsentLocale;
|
|
73
84
|
readonly requestId: string;
|
|
74
85
|
readonly receivedAtIso: string;
|
|
@@ -78,28 +89,20 @@ export type RenderTerminationReceiptArgs = {
|
|
|
78
89
|
|
|
79
90
|
/** Built from the declarant's own input only: the content must not depend on
|
|
80
91
|
* whether (or how) the declaration matched a contract. */
|
|
81
|
-
export function
|
|
82
|
-
args:
|
|
83
|
-
):
|
|
92
|
+
export function terminationReceiptVariables(
|
|
93
|
+
args: TerminationReceiptArgs,
|
|
94
|
+
): TerminationReceiptVariables {
|
|
84
95
|
const texts = TERMINATION_TEXTS[args.locale];
|
|
85
|
-
const subject = texts.receiptSubject[args.declaration.declarationType];
|
|
86
96
|
return {
|
|
87
|
-
subject,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
{ text: `${texts.receivedAt}: ${formatReceivedAt(args.receivedAtIso, args.locale)}` },
|
|
93
|
-
{ text: `${texts.requestId}: ${args.requestId}` },
|
|
94
|
-
...declarationLines(args.declaration, args.locale),
|
|
95
|
-
{ text: texts.receiptNextSteps },
|
|
96
|
-
{ text: texts.receiptRevoke(args.operatorEmail) },
|
|
97
|
-
],
|
|
98
|
-
footer: `${texts.contact} ${args.operatorEmail}`,
|
|
97
|
+
subject: texts.receiptSubject[args.declaration.declarationType],
|
|
98
|
+
receivedAt: formatReceivedAt(args.receivedAtIso, args.locale),
|
|
99
|
+
requestId: args.requestId,
|
|
100
|
+
details: declarationLines(args.declaration, args.locale),
|
|
101
|
+
operatorEmail: args.operatorEmail,
|
|
99
102
|
};
|
|
100
103
|
}
|
|
101
104
|
|
|
102
|
-
export type
|
|
105
|
+
export type OperatorNoticeArgs = {
|
|
103
106
|
readonly locale: ConsentLocale;
|
|
104
107
|
readonly requestId: string;
|
|
105
108
|
readonly receivedAtIso: string;
|
|
@@ -111,14 +114,11 @@ export type RenderOperatorNoticeArgs = {
|
|
|
111
114
|
readonly operatorEmail: string;
|
|
112
115
|
};
|
|
113
116
|
|
|
114
|
-
export function
|
|
117
|
+
export function operatorNoticeVariables(args: OperatorNoticeArgs): OperatorNoticeVariables {
|
|
115
118
|
const texts = TERMINATION_TEXTS[args.locale];
|
|
116
|
-
const subject = texts.operatorSubject[args.declaration.declarationType];
|
|
117
119
|
return {
|
|
118
|
-
subject,
|
|
119
|
-
|
|
120
|
-
sections: [
|
|
121
|
-
{ text: texts.operatorIntro },
|
|
120
|
+
subject: texts.operatorSubject[args.declaration.declarationType],
|
|
121
|
+
lines: [
|
|
122
122
|
...args.reasons.map((reason) => ({
|
|
123
123
|
text: `${texts.noticeReasons}: ${texts.operatorReasons[reason]}`,
|
|
124
124
|
})),
|
|
@@ -131,6 +131,6 @@ export function renderOperatorNotice(args: RenderOperatorNoticeArgs): Terminatio
|
|
|
131
131
|
: []),
|
|
132
132
|
...declarationLines(args.declaration, args.locale),
|
|
133
133
|
],
|
|
134
|
-
|
|
134
|
+
operatorEmail: args.operatorEmail,
|
|
135
135
|
};
|
|
136
136
|
}
|
package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-notify.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { CONTRACT_TERMINATION_OPERATOR_NOTIFICATION_TYPE } from "../constants.js
|
|
|
4
4
|
import type { ProviderCancelOutcome } from "../events.js";
|
|
5
5
|
import type { ConsumerProtectionOptions } from "../types.js";
|
|
6
6
|
import type { ConsentLocale } from "./consent-text.js";
|
|
7
|
-
import { type OperatorNoticeDeclaration,
|
|
7
|
+
import { type OperatorNoticeDeclaration, operatorNoticeVariables } from "./termination-mail.js";
|
|
8
8
|
import type { OperatorNoticeReason } from "./termination-texts.js";
|
|
9
9
|
|
|
10
10
|
export type OperatorNoticePlan = {
|
|
@@ -35,7 +35,7 @@ export async function notifyOperator(
|
|
|
35
35
|
): Promise<void> {
|
|
36
36
|
await requireNotify(ctx)(CONTRACT_TERMINATION_OPERATOR_NOTIFICATION_TYPE, {
|
|
37
37
|
route: { email: consumerProtection.operatorEmail },
|
|
38
|
-
data:
|
|
38
|
+
data: operatorNoticeVariables({
|
|
39
39
|
...plan,
|
|
40
40
|
operatorEmail: consumerProtection.operatorEmail,
|
|
41
41
|
}),
|
package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-pages.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { type AnonymousExtraRoute, getSessionCookies } from "@cosmicdrift/kumiko-framework/api";
|
|
5
5
|
import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
|
|
6
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
6
7
|
import type { Context } from "hono";
|
|
7
8
|
import { getCookie } from "hono/cookie";
|
|
8
9
|
import {
|
|
@@ -98,10 +99,6 @@ function htmlResponse(
|
|
|
98
99
|
);
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
function isOneOf<T extends string>(options: readonly T[], value: string): value is T {
|
|
102
|
-
return (options as readonly string[]).includes(value);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
102
|
async function readForm(c: Context): Promise<{ step: string; values: FormValues }> {
|
|
106
103
|
const body = await c.req.parseBody();
|
|
107
104
|
const text = (key: string): string => {
|
package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
import { purchaseRolesOf } from "../plan-catalog.js";
|
|
32
32
|
import type { ConsumerProtectionOptions, ResolvedBillingFoundationOptions } from "../types.js";
|
|
33
33
|
import { resolveConsentLocale } from "./consent-text.js";
|
|
34
|
-
import {
|
|
34
|
+
import { type TerminationDeclaration, terminationReceiptVariables } from "./termination-mail.js";
|
|
35
35
|
import { notifyOperator, type OperatorNoticePlan, requireNotify } from "./termination-notify.js";
|
|
36
36
|
import type { RecordContractTerminationResult } from "./termination-record.js";
|
|
37
37
|
import type { OperatorNoticeReason } from "./termination-texts.js";
|
|
@@ -111,7 +111,7 @@ async function sendTerminationMails(
|
|
|
111
111
|
): Promise<void> {
|
|
112
112
|
await requireNotify(ctx)(CONTRACT_TERMINATION_RECEIPT_NOTIFICATION_TYPE, {
|
|
113
113
|
route: { email: plan.declaration.email },
|
|
114
|
-
data:
|
|
114
|
+
data: terminationReceiptVariables({
|
|
115
115
|
locale: plan.locale,
|
|
116
116
|
requestId: plan.requestId,
|
|
117
117
|
receivedAtIso: plan.receivedAtIso,
|
|
@@ -40,10 +40,10 @@ export async function getSubscriptionForTenant(
|
|
|
40
40
|
if (!row) return null;
|
|
41
41
|
const piiKms = configuredPiiSubjectKms();
|
|
42
42
|
const decrypted = piiKms
|
|
43
|
-
? await decryptPiiFieldValues(row
|
|
43
|
+
? await decryptPiiFieldValues(row, SUBSCRIPTION_PII_FIELDS, piiKms, {
|
|
44
44
|
requestId: `billing-foundation:get-subscription:${tenantId}`,
|
|
45
45
|
})
|
|
46
|
-
:
|
|
46
|
+
: row;
|
|
47
47
|
// @cast-boundary db-row — drizzle-row carries column-as-unknown
|
|
48
48
|
return {
|
|
49
49
|
tier: decrypted["tier"] as string,
|
package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts
CHANGED
|
@@ -52,15 +52,10 @@ export function createPortalSessionHandler(options: BillingFoundationOptions): W
|
|
|
52
52
|
}
|
|
53
53
|
const piiKms = configuredPiiSubjectKms();
|
|
54
54
|
const decrypted = piiKms
|
|
55
|
-
? await decryptPiiFieldValues(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
{
|
|
60
|
-
requestId: `billing-foundation:create-portal-session:${tenantId}`,
|
|
61
|
-
},
|
|
62
|
-
)
|
|
63
|
-
: (row as Record<string, unknown>);
|
|
55
|
+
? await decryptPiiFieldValues(row, SUBSCRIPTION_PII_FIELDS, piiKms, {
|
|
56
|
+
requestId: `billing-foundation:create-portal-session:${tenantId}`,
|
|
57
|
+
})
|
|
58
|
+
: row;
|
|
64
59
|
const providerName = row["providerName"] as string; // @cast-boundary db-row
|
|
65
60
|
const providerCustomerId = decrypted["providerCustomerId"] as string; // @cast-boundary db-row
|
|
66
61
|
|
package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts
CHANGED
|
@@ -27,7 +27,7 @@ export const listSubscriptionsQuery: QueryHandlerDef = {
|
|
|
27
27
|
if (!piiKms) return { rows };
|
|
28
28
|
const decryptedRows = await Promise.all(
|
|
29
29
|
rows.map((row) =>
|
|
30
|
-
decryptPiiFieldValues(row
|
|
30
|
+
decryptPiiFieldValues(row, SUBSCRIPTION_PII_FIELDS, piiKms, {
|
|
31
31
|
requestId: `billing-foundation:list-subscriptions:${ctx.user.tenantId}`,
|
|
32
32
|
}),
|
|
33
33
|
),
|
|
@@ -167,8 +167,8 @@ export async function appendSubscriptionEvent(
|
|
|
167
167
|
// ---------------------------------------------------------------
|
|
168
168
|
const eventPayload: SubscriptionEventPayload = {
|
|
169
169
|
providerName: payload.providerName,
|
|
170
|
-
providerCustomerId: encryptedFields
|
|
171
|
-
providerSubscriptionId: encryptedFields
|
|
170
|
+
providerCustomerId: encryptedFields.providerCustomerId,
|
|
171
|
+
providerSubscriptionId: encryptedFields.providerSubscriptionId,
|
|
172
172
|
status: payload.status,
|
|
173
173
|
tier: payload.tier,
|
|
174
174
|
currentPeriodEndIso: payload.currentPeriodEndIso,
|
package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts
CHANGED
|
@@ -95,7 +95,7 @@ export const processPaymentEventHandler: WriteHandlerDef = {
|
|
|
95
95
|
// ---------------------------------------------------------------
|
|
96
96
|
const eventPayload: PaymentEventPayload = {
|
|
97
97
|
providerName: payload.providerName,
|
|
98
|
-
providerCustomerId: encryptedFields
|
|
98
|
+
providerCustomerId: encryptedFields.providerCustomerId,
|
|
99
99
|
priceId: payload.priceId,
|
|
100
100
|
...(payload.consentId !== undefined && { consentId: payload.consentId }),
|
|
101
101
|
};
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
SignatureExtraRouteDeps,
|
|
15
15
|
} from "@cosmicdrift/kumiko-framework/api";
|
|
16
16
|
import type { TenantId, WriteResult } from "@cosmicdrift/kumiko-framework/engine";
|
|
17
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
17
18
|
import { subscriptionAggregateId } from "./aggregate-id.js";
|
|
18
19
|
import { SubscriptionFoundationQueries, SubscriptionStatuses } from "./constants.js";
|
|
19
20
|
import { createSubscriptionWebhookRoute } from "./webhook-handler.js";
|
|
@@ -52,12 +53,8 @@ export function effectiveTierFromSubscription<TTier extends string>(
|
|
|
52
53
|
// (handler bug, or the handlerQn resolving to a non-list query) fails
|
|
53
54
|
// loudly instead of crashing on `.find` against `undefined`.
|
|
54
55
|
function asRows(result: unknown): ReadonlyArray<Record<string, unknown>> {
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
result !== null &&
|
|
58
|
-
Array.isArray((result as { rows?: unknown }).rows)
|
|
59
|
-
) {
|
|
60
|
-
return (result as { rows: ReadonlyArray<Record<string, unknown>> }).rows;
|
|
56
|
+
if (isPlainObject(result) && Array.isArray(result["rows"])) {
|
|
57
|
+
return result["rows"];
|
|
61
58
|
}
|
|
62
59
|
throw new Error("expected a { rows: [...] } list-query result");
|
|
63
60
|
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type TenantId,
|
|
8
8
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
9
9
|
import { InternalError, ValidationError } from "@cosmicdrift/kumiko-framework/errors";
|
|
10
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
10
11
|
import * as z from "zod";
|
|
11
12
|
import { subscriptionsProjectionTable } from "../../billing-foundation/index.js";
|
|
12
13
|
import type { CapLimitContext } from "../../cap-counter/index.js";
|
|
@@ -79,7 +80,7 @@ async function resolveLimitsByTenantAndCap(
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
function isSortableField(field: string): field is SortableField {
|
|
82
|
-
return (SORTABLE_FIELDS
|
|
83
|
+
return isOneOf(SORTABLE_FIELDS, field);
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
// tier is a plain string with no defined ordering — lt/gt have no meaning
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
// @runtime client
|
|
2
|
+
|
|
2
3
|
import type { ColumnRendererProps } from "@cosmicdrift/kumiko-renderer";
|
|
4
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
3
5
|
import type { ReactNode } from "react";
|
|
4
6
|
import type { CapUsage } from "../types.js";
|
|
5
7
|
import { CapUsageBar } from "./cap-usage-bar.js";
|
|
6
8
|
|
|
7
9
|
function isCapUsage(value: unknown): value is CapUsage {
|
|
8
|
-
if (
|
|
9
|
-
const used =
|
|
10
|
-
const limit =
|
|
10
|
+
if (!isPlainObject(value)) return false;
|
|
11
|
+
const used = value["used"];
|
|
12
|
+
const limit = value["limit"];
|
|
11
13
|
return (
|
|
12
14
|
(typeof used === "number" || used === null) &&
|
|
13
15
|
(typeof limit === "number" || limit === null) &&
|
|
14
|
-
typeof
|
|
16
|
+
typeof value["fraction"] === "number"
|
|
15
17
|
);
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.363.0",
|
|
4
|
+
"type": "breaking",
|
|
5
|
+
"title": "A notification without a mail template fails on the email channel",
|
|
6
|
+
"detail": "The renderer option of createChannelEmailFeature is removed. A notification routed to the email channel without template + meta fails its delivery permanently with \"has no mail template, declare template + meta on r.notification\". r.notification declares no channels, so this is not checked at boot; an app's own mail tests over the inmemory transport catch it.",
|
|
7
|
+
"migration": "Drop the renderer option. Declare template + meta (or localizedTemplate) and variables on every r.notification that reaches the email channel, or accept that its email delivery fails. Run the app's notification tests after the bump: every email notification without a template now fails there."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"version": "0.363.0",
|
|
11
|
+
"type": "improvement",
|
|
12
|
+
"title": "Email channel sends rendered-pdf attachments",
|
|
13
|
+
"detail": "The channel resolves rendered-pdf entries from notify data next to file entries; without a locale the message locale is used. A render failure fails the send job, so the retry runs it again."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"version": "0.363.0",
|
|
17
|
+
"type": "improvement",
|
|
18
|
+
"title": "Email channel sends file attachments and inline cid images",
|
|
19
|
+
"detail": "EmailMessage.attachments is sent through the SMTP transport (inline images with Content-ID). notify data.attachments with kind file entries are resolved through the file store; the PII guard rejects ciphertext file names."
|
|
20
|
+
},
|
|
2
21
|
{
|
|
3
22
|
"version": "0.350.0",
|
|
4
23
|
"type": "improvement",
|