@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
|
@@ -6,19 +6,31 @@
|
|
|
6
6
|
// Plugin-Renderer in `renderer-foundation` matchen auf kind; der
|
|
7
7
|
// Resolver hier ist kind-agnostisch — er lädt nur, das Content-Format
|
|
8
8
|
// (markdown/mjml/html) entscheidet wer's rendert.
|
|
9
|
-
export const RENDER_KINDS = [
|
|
10
|
-
"notification",
|
|
11
|
-
"mail-html",
|
|
12
|
-
"document-pdf",
|
|
13
|
-
"image-snapshot",
|
|
14
|
-
] as const;
|
|
9
|
+
export const RENDER_KINDS = ["mail", "document-pdf", "image-snapshot"] as const;
|
|
15
10
|
export type RenderKind = (typeof RENDER_KINDS)[number];
|
|
16
11
|
|
|
17
|
-
// Kinds
|
|
18
|
-
//
|
|
19
|
-
|
|
12
|
+
// Kinds whose html body is a rich document: the server rebuilds it through the
|
|
13
|
+
// document schema on every write (see document-sanitizer.ts).
|
|
14
|
+
export const DOCUMENT_HTML_KINDS = ["mail", "document-pdf"] as const;
|
|
15
|
+
export type DocumentHtmlKind = (typeof DOCUMENT_HTML_KINDS)[number];
|
|
16
|
+
|
|
17
|
+
// Kinds without a renderer plugin: `notification` is the retired sections-JSON
|
|
18
|
+
// notification kind, kept so rows stored before mails moved to `mail` stay
|
|
19
|
+
// valid on replay; nothing writes or renders it any more. `text-block` is the Markdown text store
|
|
20
|
+
// (ex text-content feature), `snippet` is the content of app collections such
|
|
21
|
+
// as reply snippets and signatures (own kind so they stay out of the text-block
|
|
22
|
+
// content tree), `ai-prompt` holds LLM prompts, `mail-layout` and
|
|
23
|
+
// `pdf-layout` hold the frame a mail/pdf body is rendered into. All are read,
|
|
24
|
+
// not rendered on their own — keeping them out of RENDER_KINDS is what stops
|
|
20
25
|
// DEFAULT_PLUGIN_BY_KIND from demanding a plugin for them.
|
|
21
|
-
export const NON_RENDER_KINDS = [
|
|
26
|
+
export const NON_RENDER_KINDS = [
|
|
27
|
+
"notification",
|
|
28
|
+
"text-block",
|
|
29
|
+
"snippet",
|
|
30
|
+
"ai-prompt",
|
|
31
|
+
"mail-layout",
|
|
32
|
+
"pdf-layout",
|
|
33
|
+
] as const;
|
|
22
34
|
export type NonRenderKind = (typeof NON_RENDER_KINDS)[number];
|
|
23
35
|
|
|
24
36
|
// Full kind domain of the entity; RENDER_KINDS is the renderer-foundation subset.
|
|
@@ -28,7 +40,7 @@ export type TemplateKind = (typeof TEMPLATE_KINDS)[number];
|
|
|
28
40
|
// Kinds written through upsertSystem/upsertTenant. `text-block` is absent on
|
|
29
41
|
// purpose: it goes through set.write (title + folder tree); two write paths
|
|
30
42
|
// onto the same row would drift apart.
|
|
31
|
-
export const UPSERT_KINDS = [...RENDER_KINDS, "ai-prompt"] as const;
|
|
43
|
+
export const UPSERT_KINDS = [...RENDER_KINDS, "ai-prompt", "mail-layout", "pdf-layout"] as const;
|
|
32
44
|
export type UpsertKind = (typeof UPSERT_KINDS)[number];
|
|
33
45
|
|
|
34
46
|
// The only kind served by the anonymous-capable by-slug/by-tenant queries and
|
|
@@ -36,6 +48,11 @@ export type UpsertKind = (typeof UPSERT_KINDS)[number];
|
|
|
36
48
|
// must not leak through the anonymous path.
|
|
37
49
|
export const TEXT_BLOCK_KIND = "text-block" as const;
|
|
38
50
|
|
|
51
|
+
// Kinds a content collection must not write: a tenant row of these kinds would
|
|
52
|
+
// shadow the system notification template or layout without the meta and
|
|
53
|
+
// placeholder gates that upsertTenant applies.
|
|
54
|
+
export const COLLECTION_REFUSED_KINDS: readonly string[] = ["mail", "mail-layout", "pdf-layout"];
|
|
55
|
+
|
|
39
56
|
export const CONTENT_FORMATS = ["markdown", "mjml", "html", "plain"] as const;
|
|
40
57
|
export type ContentFormat = (typeof CONTENT_FORMATS)[number];
|
|
41
58
|
|
|
@@ -45,6 +62,27 @@ export type TemplateScope = (typeof TEMPLATE_SCOPES)[number];
|
|
|
45
62
|
export const TEMPLATE_STATUSES = ["draft", "active", "archived"] as const;
|
|
46
63
|
export type TemplateStatus = (typeof TEMPLATE_STATUSES)[number];
|
|
47
64
|
|
|
65
|
+
export {
|
|
66
|
+
PDF_DEFAULT_MARGIN_MM,
|
|
67
|
+
PDF_FOOTER_MARGIN_MM,
|
|
68
|
+
PDF_HEADER_MARGIN_MM,
|
|
69
|
+
PDF_ORIENTATIONS,
|
|
70
|
+
PDF_PAGE_FORMATS,
|
|
71
|
+
type PdfOrientation,
|
|
72
|
+
type PdfPageFormat,
|
|
73
|
+
} from "./pdf-constants.js";
|
|
74
|
+
|
|
75
|
+
// Colors end up inside MJML attributes, so only a strict hex is ever written there.
|
|
76
|
+
export const HEX_COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/;
|
|
77
|
+
|
|
78
|
+
export const DEFAULT_MAIL_LAYOUT_SLUG = "default";
|
|
79
|
+
|
|
80
|
+
// The mail transport enforces no size limit of its own; 10 MiB is a common provider ceiling.
|
|
81
|
+
export const MAIL_PREVIEW_MAX_BYTES = 10 * 1024 * 1024;
|
|
82
|
+
|
|
83
|
+
export const TEST_MAIL_NOTIFICATION_TYPE = "template-resolver:test-mail";
|
|
84
|
+
export const TEST_MAIL_SUBJECT_PREFIX = "[TEST] ";
|
|
85
|
+
|
|
48
86
|
// System-Templates leben unter der canonical SYSTEM_TENANT_ID-Sentinel-UUID.
|
|
49
87
|
// Re-Export aus framework — single source of truth, vermeidet Drift.
|
|
50
88
|
export { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// Server-only: pulls in happy-dom via @tiptap/html/server, so it must stay out
|
|
2
|
+
// of client barrels and the document-schema subpath export.
|
|
3
|
+
import type { FieldIssue } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
5
|
+
import { generateHTML, generateJSON } from "@tiptap/html/server";
|
|
6
|
+
import { type Element as ScanElement, Window } from "happy-dom";
|
|
7
|
+
import type { ContentFormat } from "./constants.js";
|
|
8
|
+
import { DOCUMENT_HTML_KINDS } from "./constants.js";
|
|
9
|
+
import {
|
|
10
|
+
documentExtensions,
|
|
11
|
+
isAllowedDocumentLinkHref,
|
|
12
|
+
isDocumentPlaceholderHref,
|
|
13
|
+
isDocumentReferenceName,
|
|
14
|
+
isDocumentResourceKey,
|
|
15
|
+
} from "./document-schema.js";
|
|
16
|
+
|
|
17
|
+
export type DocumentViolationCode =
|
|
18
|
+
| "script_element"
|
|
19
|
+
| "event_handler_attribute"
|
|
20
|
+
| "disallowed_link_href"
|
|
21
|
+
| "external_image_source"
|
|
22
|
+
| "image_without_resource"
|
|
23
|
+
| "invalid_reference_name"
|
|
24
|
+
| "embedded_content"
|
|
25
|
+
| "unsafe_attribute_value"
|
|
26
|
+
| "button_href_not_placeholder"
|
|
27
|
+
| "document_requires_html_format"
|
|
28
|
+
| "layout_requires_mjml_format"
|
|
29
|
+
| "layout_invalid_structure"
|
|
30
|
+
| "layout_missing_content_slot"
|
|
31
|
+
| "layout_source_system_only";
|
|
32
|
+
|
|
33
|
+
export type DocumentViolation = {
|
|
34
|
+
readonly code: DocumentViolationCode;
|
|
35
|
+
readonly detail: string;
|
|
36
|
+
readonly message: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type SanitizeHtmlResult =
|
|
40
|
+
| { readonly ok: true; readonly html: string }
|
|
41
|
+
| { readonly ok: false; readonly violations: readonly DocumentViolation[] };
|
|
42
|
+
|
|
43
|
+
export type SanitizeBodyResult =
|
|
44
|
+
| { readonly ok: true; readonly content: string | null }
|
|
45
|
+
| { readonly ok: false; readonly violations: readonly DocumentViolation[] };
|
|
46
|
+
|
|
47
|
+
export function violation(
|
|
48
|
+
code: DocumentViolationCode,
|
|
49
|
+
detail: string,
|
|
50
|
+
message: string,
|
|
51
|
+
): DocumentViolation {
|
|
52
|
+
return { code, detail, message };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Same switches tiptap's generateJSON uses, plus navigation off: the scan
|
|
56
|
+
// window must never fetch or navigate anything.
|
|
57
|
+
function createScanWindow(): Window {
|
|
58
|
+
return new Window({
|
|
59
|
+
settings: {
|
|
60
|
+
disableJavaScriptEvaluation: true,
|
|
61
|
+
disableJavaScriptFileLoading: true,
|
|
62
|
+
disableCSSFileLoading: true,
|
|
63
|
+
disableIframePageLoading: true,
|
|
64
|
+
disableComputedStyleRendering: true,
|
|
65
|
+
navigation: {
|
|
66
|
+
disableMainFrameNavigation: true,
|
|
67
|
+
disableChildFrameNavigation: true,
|
|
68
|
+
disableChildPageNavigation: true,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function scanScriptAndHandlers(tag: string, element: ScanElement, out: DocumentViolation[]): void {
|
|
75
|
+
if (tag === "script") {
|
|
76
|
+
out.push(
|
|
77
|
+
violation("script_element", "script", "<script> elements are not allowed in template bodies"),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
for (const attribute of Array.from(element.attributes)) {
|
|
81
|
+
if (/^on/i.test(attribute.name)) {
|
|
82
|
+
out.push(
|
|
83
|
+
violation(
|
|
84
|
+
"event_handler_attribute",
|
|
85
|
+
attribute.name,
|
|
86
|
+
`Inline event handler "${attribute.name}" on <${tag}> is not allowed`,
|
|
87
|
+
),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function scanAnchor(element: ScanElement, out: DocumentViolation[]): void {
|
|
94
|
+
const href = element.getAttribute("href");
|
|
95
|
+
if (href !== null && !isAllowedDocumentLinkHref(href)) {
|
|
96
|
+
out.push(
|
|
97
|
+
violation(
|
|
98
|
+
"disallowed_link_href",
|
|
99
|
+
href,
|
|
100
|
+
`Link target "${href}" is not allowed; use https, http, mailto, tel or a {{placeholder}}`,
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (element.hasAttribute("data-button") && !isDocumentPlaceholderHref(href ?? "")) {
|
|
105
|
+
out.push(
|
|
106
|
+
violation(
|
|
107
|
+
"button_href_not_placeholder",
|
|
108
|
+
href ?? "",
|
|
109
|
+
`Button target "${href ?? ""}" must be a {{placeholder}}`,
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function scanImage(element: ScanElement, out: DocumentViolation[]): void {
|
|
116
|
+
const source = element.getAttribute("src") ?? element.getAttribute("srcset");
|
|
117
|
+
if (source !== null) {
|
|
118
|
+
out.push(
|
|
119
|
+
violation(
|
|
120
|
+
"external_image_source",
|
|
121
|
+
source,
|
|
122
|
+
`Images must reference an uploaded resource via data-resource; external or inline image source "${source}" is not allowed`,
|
|
123
|
+
),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (!element.hasAttribute("data-resource")) {
|
|
127
|
+
out.push(
|
|
128
|
+
violation(
|
|
129
|
+
"image_without_resource",
|
|
130
|
+
"img",
|
|
131
|
+
"Images must reference an uploaded resource via the data-resource attribute",
|
|
132
|
+
),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const REFERENCE_ATTRIBUTES: ReadonlyArray<readonly [string, (value: string) => boolean]> = [
|
|
138
|
+
["data-variable", isDocumentReferenceName],
|
|
139
|
+
["data-each", isDocumentReferenceName],
|
|
140
|
+
["data-resource", isDocumentResourceKey],
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
function scanReferences(tag: string, element: ScanElement, out: DocumentViolation[]): void {
|
|
144
|
+
for (const [attribute, isValid] of REFERENCE_ATTRIBUTES) {
|
|
145
|
+
const value = element.getAttribute(attribute);
|
|
146
|
+
if (value !== null && !isValid(value)) {
|
|
147
|
+
out.push(
|
|
148
|
+
violation(
|
|
149
|
+
"invalid_reference_name",
|
|
150
|
+
value,
|
|
151
|
+
`Value "${value}" of ${attribute} on <${tag}> is not a valid name`,
|
|
152
|
+
),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const EMBEDDING_TAGS: ReadonlySet<string> = new Set([
|
|
159
|
+
"iframe",
|
|
160
|
+
"frame",
|
|
161
|
+
"frameset",
|
|
162
|
+
"object",
|
|
163
|
+
"embed",
|
|
164
|
+
"applet",
|
|
165
|
+
"portal",
|
|
166
|
+
]);
|
|
167
|
+
const SVG_IMAGE_TAGS: ReadonlySet<string> = new Set(["image", "feimage", "use"]);
|
|
168
|
+
const URL_ATTRIBUTES = ["href", "xlink:href", "action", "formaction"] as const;
|
|
169
|
+
|
|
170
|
+
function scanEmbedding(tag: string, out: DocumentViolation[]): void {
|
|
171
|
+
if (EMBEDDING_TAGS.has(tag)) {
|
|
172
|
+
out.push(
|
|
173
|
+
violation("embedded_content", tag, `<${tag}> elements are not allowed in template bodies`),
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// html <a href> has its own rules in scanAnchor; everywhere else (svg, base,
|
|
179
|
+
// form controls) a URL attribute has no business in a template body.
|
|
180
|
+
function scanUrlAttributes(tag: string, element: ScanElement, out: DocumentViolation[]): void {
|
|
181
|
+
for (const name of URL_ATTRIBUTES) {
|
|
182
|
+
const value = element.getAttribute(name);
|
|
183
|
+
if (value === null || (tag === "a" && name === "href")) continue;
|
|
184
|
+
if (SVG_IMAGE_TAGS.has(tag) && name.endsWith("href")) {
|
|
185
|
+
out.push(
|
|
186
|
+
violation(
|
|
187
|
+
"external_image_source",
|
|
188
|
+
value,
|
|
189
|
+
`Images must reference an uploaded resource via data-resource; image source "${value}" is not allowed`,
|
|
190
|
+
),
|
|
191
|
+
);
|
|
192
|
+
} else if (tag === "base" || !isAllowedDocumentLinkHref(value)) {
|
|
193
|
+
out.push(
|
|
194
|
+
violation(
|
|
195
|
+
"disallowed_link_href",
|
|
196
|
+
value,
|
|
197
|
+
`Link target "${value}" is not allowed; use https, http, mailto, tel or a {{placeholder}}`,
|
|
198
|
+
),
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// happy-dom serializes < and > inside attribute values unescaped, so a later
|
|
205
|
+
// parser (MJML) could read them as markup.
|
|
206
|
+
function scanAltText(element: ScanElement, out: DocumentViolation[]): void {
|
|
207
|
+
const alt = element.getAttribute("alt");
|
|
208
|
+
if (alt !== null && /[<>]/.test(alt)) {
|
|
209
|
+
out.push(
|
|
210
|
+
violation(
|
|
211
|
+
"unsafe_attribute_value",
|
|
212
|
+
alt,
|
|
213
|
+
`Attribute alt must not contain "<" or ">": "${alt}"`,
|
|
214
|
+
),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function scanElement(element: ScanElement, out: DocumentViolation[]): void {
|
|
220
|
+
const tag = element.tagName.toLowerCase();
|
|
221
|
+
scanEmbedding(tag, out);
|
|
222
|
+
scanUrlAttributes(tag, element, out);
|
|
223
|
+
scanAltText(element, out);
|
|
224
|
+
scanScriptAndHandlers(tag, element, out);
|
|
225
|
+
if (tag === "a") scanAnchor(element, out);
|
|
226
|
+
if (tag === "img") scanImage(element, out);
|
|
227
|
+
scanReferences(tag, element, out);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Parses `html` in the hardened scan window and always closes it again, also
|
|
231
|
+
// when `read` throws. Shared by the sanitizer and the placeholder scan so there
|
|
232
|
+
// is exactly one DOM setup.
|
|
233
|
+
export function withParsedDocument<T>(html: string, read: (body: ScanElement) => T): T {
|
|
234
|
+
const window = createScanWindow();
|
|
235
|
+
try {
|
|
236
|
+
const parsed = new window.DOMParser().parseFromString(
|
|
237
|
+
["<!DOCTYPE html><html><body>", html, "</body></html>"].join(""),
|
|
238
|
+
"text/html",
|
|
239
|
+
);
|
|
240
|
+
return read(parsed.body);
|
|
241
|
+
} finally {
|
|
242
|
+
window.happyDOM.abort();
|
|
243
|
+
window.happyDOM.close();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function scanDocumentHtml(html: string): DocumentViolation[] {
|
|
248
|
+
const violations: DocumentViolation[] = [];
|
|
249
|
+
withParsedDocument(html, (body) => {
|
|
250
|
+
// A second `<body ...>` in the input merges its attributes into this body.
|
|
251
|
+
for (const element of [body, ...Array.from(body.querySelectorAll("*"))]) {
|
|
252
|
+
scanElement(element, violations);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
return violations;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Rejects hostile constructs, then rebuilds the body from the document model.
|
|
259
|
+
// The rebuilt HTML is always what gets returned (never the input), so anything
|
|
260
|
+
// the model cannot express is dropped silently.
|
|
261
|
+
export function sanitizeDocumentHtml(html: string): SanitizeHtmlResult {
|
|
262
|
+
const violations = scanDocumentHtml(html);
|
|
263
|
+
if (violations.length > 0) return { ok: false, violations };
|
|
264
|
+
return {
|
|
265
|
+
ok: true,
|
|
266
|
+
html: generateHTML(generateJSON(html, documentExtensions), documentExtensions),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Snippets accept any format, but their html is as untrusted as a mail body.
|
|
271
|
+
function isSanitizedHtmlKind(kind: string): boolean {
|
|
272
|
+
return kind === "snippet" || isOneOf(DOCUMENT_HTML_KINDS, kind);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const HTML_ONLY_KINDS: ReadonlySet<string> = new Set(DOCUMENT_HTML_KINDS);
|
|
276
|
+
|
|
277
|
+
function requiredFormatViolation(
|
|
278
|
+
kind: string,
|
|
279
|
+
contentFormat: ContentFormat,
|
|
280
|
+
): DocumentViolation | null {
|
|
281
|
+
if (HTML_ONLY_KINDS.has(kind) && contentFormat !== "html") {
|
|
282
|
+
return violation(
|
|
283
|
+
"document_requires_html_format",
|
|
284
|
+
contentFormat,
|
|
285
|
+
`Templates of kind ${kind} require contentFormat html, got "${contentFormat}"`,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function sanitizeTemplateBody(input: {
|
|
292
|
+
readonly kind: string;
|
|
293
|
+
readonly contentFormat: ContentFormat;
|
|
294
|
+
readonly content: string | null;
|
|
295
|
+
}): SanitizeBodyResult {
|
|
296
|
+
const formatViolation = requiredFormatViolation(input.kind, input.contentFormat);
|
|
297
|
+
if (formatViolation !== null) return { ok: false, violations: [formatViolation] };
|
|
298
|
+
if (
|
|
299
|
+
!isSanitizedHtmlKind(input.kind) ||
|
|
300
|
+
input.contentFormat !== "html" ||
|
|
301
|
+
input.content === null
|
|
302
|
+
) {
|
|
303
|
+
return { ok: true, content: input.content };
|
|
304
|
+
}
|
|
305
|
+
const result = sanitizeDocumentHtml(input.content);
|
|
306
|
+
return result.ok ? { ok: true, content: result.html } : result;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const VIOLATION_I18N_KEYS: Readonly<Record<DocumentViolationCode, string>> = {
|
|
310
|
+
script_element: "templateResolver.errors.document.scriptElement",
|
|
311
|
+
event_handler_attribute: "templateResolver.errors.document.eventHandlerAttribute",
|
|
312
|
+
disallowed_link_href: "templateResolver.errors.document.disallowedLinkHref",
|
|
313
|
+
external_image_source: "templateResolver.errors.document.externalImageSource",
|
|
314
|
+
image_without_resource: "templateResolver.errors.document.imageWithoutResource",
|
|
315
|
+
invalid_reference_name: "templateResolver.errors.document.invalidReferenceName",
|
|
316
|
+
embedded_content: "templateResolver.errors.document.embeddedContent",
|
|
317
|
+
unsafe_attribute_value: "templateResolver.errors.document.unsafeAttributeValue",
|
|
318
|
+
button_href_not_placeholder: "templateResolver.errors.document.buttonHrefNotPlaceholder",
|
|
319
|
+
document_requires_html_format: "templateResolver.errors.document.documentRequiresHtmlFormat",
|
|
320
|
+
layout_requires_mjml_format: "templateResolver.errors.document.layoutRequiresMjmlFormat",
|
|
321
|
+
layout_invalid_structure: "templateResolver.errors.document.layoutInvalidStructure",
|
|
322
|
+
layout_missing_content_slot: "templateResolver.errors.document.layoutMissingContentSlot",
|
|
323
|
+
layout_source_system_only: "templateResolver.errors.document.layoutSourceSystemOnly",
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
export function documentViolationsToFieldIssues(
|
|
327
|
+
violations: readonly DocumentViolation[],
|
|
328
|
+
): FieldIssue[] {
|
|
329
|
+
return violations.map((item) => ({
|
|
330
|
+
path: "content",
|
|
331
|
+
code: item.code,
|
|
332
|
+
i18nKey: VIOLATION_I18N_KEYS[item.code],
|
|
333
|
+
params: { detail: item.detail },
|
|
334
|
+
}));
|
|
335
|
+
}
|