@cosmicdrift/kumiko-bundled-features 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/dist/admin-shell/feature.d.ts +3 -0
- package/dist/admin-shell/feature.js +6 -1
- package/dist/agent-tools/agent-manifest.js +2 -1
- package/dist/agent-tools/filter-ops.d.ts +1 -1
- package/dist/agent-tools/filter-ops.js +1 -3
- package/dist/agent-tools/tool-dispatch.js +2 -1
- package/dist/audit/handlers/list.query.js +9 -5
- package/dist/auth-email-password/bootstrap.d.ts +8 -5
- package/dist/auth-email-password/bootstrap.js +12 -4
- package/dist/auth-email-password/email-rate-limits.d.ts +4 -0
- package/dist/auth-email-password/email-rate-limits.js +13 -0
- package/dist/auth-email-password/email-templates.d.ts +4 -26
- package/dist/auth-email-password/email-templates.js +9 -101
- package/dist/auth-email-password/feature.js +24 -6
- package/dist/auth-email-password/handlers/change-password.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/change-password.write.js +6 -2
- package/dist/auth-email-password/handlers/confirm-account-unlock.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-accept.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
- package/dist/auth-email-password/handlers/invite-create.write.js +4 -4
- package/dist/auth-email-password/handlers/invite-signup-complete.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/logout.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/logout.write.js +6 -2
- package/dist/auth-email-password/handlers/request-account-unlock.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-account-unlock.write.js +2 -4
- package/dist/auth-email-password/handlers/request-email-verification.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-email-verification.write.js +2 -4
- package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-password-reset.write.js +2 -4
- package/dist/auth-email-password/handlers/reset-password.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.js +3 -4
- package/dist/auth-email-password/handlers/signup-request.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-request.write.js +2 -1
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/token-request-handler.d.ts +2 -3
- package/dist/auth-email-password/handlers/token-request-handler.js +2 -2
- package/dist/auth-email-password/handlers/verify-email.write.d.ts +1 -1
- package/dist/auth-email-password/index.d.ts +2 -2
- package/dist/auth-email-password/index.js +1 -1
- package/dist/auth-email-password/magic-link-mail.d.ts +3 -2
- package/dist/auth-email-password/magic-link-mail.js +19 -14
- package/dist/auth-email-password/mail-notifications.d.ts +32 -0
- package/dist/auth-email-password/mail-notifications.js +116 -0
- package/dist/auth-email-password/signup-activation.d.ts +1 -1
- package/dist/auth-email-password/signup-activation.js +2 -3
- package/dist/auth-email-password/web/auth-gate.js +16 -4
- package/dist/auth-foundation/types.js +2 -8
- package/dist/auth-mfa/feature.js +4 -4
- package/dist/auth-mfa/handlers/disable.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-confirm.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-start-preauth.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-start.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/regenerate-recovery.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/verify.write.d.ts +1 -1
- package/dist/auth-mfa/hmac-token-codec.js +4 -4
- package/dist/auth-mfa/mfa-challenge-token.js +3 -3
- package/dist/auth-mfa/mfa-preauth-setup-token.js +3 -3
- package/dist/auth-mfa/mfa-setup-token.js +8 -8
- package/dist/billing-foundation/constants.d.ts +3 -3
- package/dist/billing-foundation/constants.js +5 -4
- package/dist/billing-foundation/consumer-protection/confirmation-mail.d.ts +32 -14
- package/dist/billing-foundation/consumer-protection/confirmation-mail.js +74 -32
- package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +3 -0
- package/dist/billing-foundation/consumer-protection/consent-gate.js +21 -2
- package/dist/billing-foundation/consumer-protection/issue-confirmation.js +3 -3
- package/dist/billing-foundation/consumer-protection/mail-notifications.d.ts +8 -0
- package/dist/billing-foundation/consumer-protection/mail-notifications.js +95 -0
- package/dist/billing-foundation/consumer-protection/register-consent.js +2 -0
- package/dist/billing-foundation/consumer-protection/register-termination.js +11 -1
- package/dist/billing-foundation/consumer-protection/termination-mail.d.ts +25 -13
- package/dist/billing-foundation/consumer-protection/termination-mail.js +11 -23
- package/dist/billing-foundation/consumer-protection/termination-notify.js +2 -2
- package/dist/billing-foundation/consumer-protection/termination-pages.js +1 -3
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.d.ts +6 -0
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +34 -0
- package/dist/billing-foundation/consumer-protection/termination-record.d.ts +2 -0
- package/dist/billing-foundation/consumer-protection/termination-record.js +7 -1
- package/dist/billing-foundation/consumer-protection/termination-request.js +31 -21
- package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +1 -1
- package/dist/billing-foundation/consumer-protection/termination-texts.js +6 -0
- package/dist/billing-foundation/consumer-protection/withdrawal-period.d.ts +10 -0
- package/dist/billing-foundation/consumer-protection/withdrawal-period.js +26 -0
- package/dist/billing-foundation/handlers/create-checkout-session.write.js +11 -5
- package/dist/billing-foundation/handlers/process-event.write.js +12 -8
- package/dist/billing-foundation/handlers/process-payment-event.write.js +1 -1
- package/dist/billing-foundation/i18n.js +3 -0
- package/dist/billing-foundation/subscription-tier-sync.d.ts +7 -1
- package/dist/billing-foundation/subscription-tier-sync.js +16 -9
- package/dist/billing-foundation/types.d.ts +7 -2
- package/dist/cap-counter/calendar-cap-usage-reservation.js +2 -0
- package/dist/cap-counter/enforce-cap.d.ts +18 -1
- package/dist/cap-counter/enforce-cap.js +49 -6
- package/dist/cap-counter/index.d.ts +1 -1
- package/dist/cap-counter/index.js +1 -1
- package/dist/cap-counter/with-cap-enforcement.d.ts +2 -0
- package/dist/cap-overview/handlers/tenant-caps-list.query.js +2 -1
- package/dist/cap-overview/web/cap-usage-cell.js +5 -4
- package/dist/channel-email/email-channel.d.ts +2 -2
- package/dist/channel-email/email-channel.js +101 -26
- package/dist/channel-email/feature.js +3 -3
- package/dist/channel-email/index.d.ts +1 -1
- package/dist/channel-email/index.js +1 -1
- package/dist/channel-email/pii-guard.js +10 -0
- package/dist/channel-email/smtp-transport.js +13 -0
- package/dist/channel-email/types.d.ts +8 -0
- package/dist/channel-in-app/handlers/mark-all-read.write.d.ts +1 -1
- package/dist/channel-in-app/handlers/mark-read.write.d.ts +1 -1
- package/dist/compliance-profiles/handlers/set-profile.write.d.ts +1 -1
- package/dist/config/handlers/reset.write.d.ts +1 -1
- package/dist/config/handlers/set.write.d.ts +1 -1
- package/dist/config/handlers/set.write.js +3 -4
- package/dist/config/resolver.d.ts +1 -0
- package/dist/config/resolver.js +11 -14
- package/dist/config/write-helpers.d.ts +2 -0
- package/dist/config/write-helpers.js +19 -0
- package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -1
- package/dist/crypto-shredding/handlers/forget-subject.write.js +29 -19
- package/dist/custom-fields/handlers/update-tenant-field.write.js +0 -1
- package/dist/custom-fields/lib/value-schema.js +10 -11
- package/dist/custom-fields/wire-user-data-rights.js +7 -6
- package/dist/data-retention/index.d.ts +1 -0
- package/dist/data-retention/index.js +1 -0
- package/dist/data-retention/run-retention-cleanup.js +6 -6
- package/dist/delivery/attachment-ref.d.ts +41 -0
- package/dist/delivery/attachment-ref.js +78 -0
- package/dist/delivery/channel-context.d.ts +8 -4
- package/dist/delivery/channel-context.js +94 -2
- package/dist/delivery/constants.d.ts +1 -0
- package/dist/delivery/constants.js +1 -0
- package/dist/delivery/delivery-service.d.ts +4 -1
- package/dist/delivery/delivery-service.js +38 -32
- package/dist/delivery/feature.js +1 -1
- package/dist/delivery/handlers/resubscribe-address.write.d.ts +1 -1
- package/dist/delivery/handlers/resubscribe-user.write.d.ts +1 -1
- package/dist/delivery/handlers/set-preference.write.d.ts +1 -1
- package/dist/delivery/handlers/unsubscribe-address.write.d.ts +1 -1
- package/dist/delivery/handlers/unsubscribe-user.write.d.ts +1 -1
- package/dist/delivery/index.d.ts +5 -2
- package/dist/delivery/index.js +4 -1
- package/dist/delivery/jobs.js +66 -14
- package/dist/delivery/notify-factory.js +4 -1
- package/dist/delivery/testing.d.ts +4 -1
- package/dist/delivery/types.d.ts +26 -14
- package/dist/delivery/unsubscribe.js +4 -1
- package/dist/derivatives-sharp/render.js +1 -18
- package/dist/document-ingest-foundation/pages.js +4 -4
- package/dist/feature-toggles/handlers/set.write.d.ts +1 -1
- package/dist/feature-toggles/toggle-sync-signal.js +4 -4
- package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
- package/dist/file-foundation/index.d.ts +1 -0
- package/dist/file-foundation/index.js +1 -0
- package/dist/file-foundation/read-file-ref-content.d.ts +27 -0
- package/dist/file-foundation/read-file-ref-content.js +51 -0
- package/dist/files-tenant-data/hooks.js +4 -0
- package/dist/folders/web/folder-manager.js +3 -2
- package/dist/form-draft/db/queries/cleanup.js +4 -4
- package/dist/form-draft/db/queries/draft-count.js +2 -2
- package/dist/form-draft/db/queries/owned-file-refs.js +2 -2
- package/dist/form-draft/handlers/discard.write.d.ts +1 -1
- package/dist/form-draft/handlers/save.write.d.ts +1 -1
- package/dist/form-draft/release-file-refs.js +3 -2
- package/dist/inbound-mail-foundation/handlers/connect-account.write.js +1 -1
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +6 -6
- package/dist/inbound-mail-foundation/handlers/scope-visibility.d.ts +1 -1
- package/dist/inbound-mail-foundation/oauth-access-token.js +8 -3
- package/dist/inbound-mail-foundation/oauth-state.js +7 -7
- package/dist/inbound-mail-foundation/types.js +4 -4
- package/dist/inbound-mail-foundation/watch-supervisor.js +24 -13
- package/dist/inbound-provider-imap/imap-client.js +4 -3
- package/dist/jobs/db/queries/stale-run-sweep.d.ts +1 -1
- package/dist/jobs/db/queries/stale-run-sweep.js +12 -5
- package/dist/jobs/feature.js +5 -3
- package/dist/jobs/handlers/list.query.js +3 -1
- package/dist/jobs/handlers/retry.write.d.ts +1 -1
- package/dist/jobs/handlers/stale-run-sweep.job.d.ts +1 -1
- package/dist/jobs/handlers/stale-run-sweep.job.js +2 -2
- package/dist/jobs/handlers/trigger.write.d.ts +1 -1
- package/dist/jobs/job-run-logger.js +32 -10
- package/dist/jobs/tenant-job-run-table.js +3 -2
- package/dist/ledger/entity.js +3 -1
- package/dist/ledger/recurring.js +1 -3
- package/dist/managed-pages/branding.d.ts +2 -0
- package/dist/managed-pages/branding.js +24 -2
- package/dist/managed-pages/feature.js +1 -0
- package/dist/managed-pages/handlers/set.write.d.ts +1 -1
- package/dist/managed-pages/i18n.js +1 -0
- package/dist/managed-pages/screens/branding-screen.js +8 -1
- package/dist/page-render/branding.d.ts +1 -0
- package/dist/page-render/branding.js +1 -0
- package/dist/page-render/markdown.js +1 -1
- package/dist/personal-access-tokens/feature.js +1 -1
- package/dist/personal-access-tokens/handlers/create.write.d.ts +1 -1
- package/dist/personal-access-tokens/handlers/list.query.js +2 -1
- package/dist/personal-access-tokens/handlers/revoke.write.d.ts +1 -1
- package/dist/personal-access-tokens/revoke-for-user.js +2 -1
- package/dist/renderer-foundation/api.d.ts +13 -13
- package/dist/renderer-foundation/api.js +1 -1
- package/dist/renderer-foundation/constants.js +2 -4
- package/dist/renderer-foundation/feature.js +7 -7
- package/dist/renderer-foundation/index.d.ts +2 -1
- package/dist/renderer-foundation/index.js +1 -0
- package/dist/renderer-foundation/types.d.ts +94 -32
- package/dist/renderer-pdf/feature.js +7 -1
- package/dist/renderer-pdf/render-table-pdf.d.ts +27 -33
- package/dist/renderer-pdf/render-table-pdf.js +95 -34
- package/dist/secrets/feature.js +4 -3
- package/dist/secrets/handlers/delete.write.d.ts +1 -1
- package/dist/secrets/handlers/set.write.d.ts +1 -1
- package/dist/secrets/secrets-context.js +15 -3
- package/dist/sessions/db/queries/cleanup.js +2 -2
- package/dist/sessions/feature.js +1 -1
- package/dist/sessions/handlers/list.query.js +2 -1
- package/dist/sessions/handlers/revoke-all-for-user.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke-all-others.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke.write.d.ts +1 -1
- package/dist/shared/attachment-file-name.d.ts +3 -0
- package/dist/shared/attachment-file-name.js +35 -0
- package/dist/shared/chunked-entity-migration.d.ts +1 -0
- package/dist/shared/chunked-entity-migration.js +1 -1
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.js +3 -0
- package/dist/shared/is-tenant-db.js +1 -1
- package/dist/shared/mail-template-body.d.ts +7 -0
- package/dist/shared/mail-template-body.js +27 -0
- package/dist/shared/sharp-input-limits.d.ts +6 -0
- package/dist/shared/sharp-input-limits.js +22 -0
- package/dist/shared/signup-handover.js +2 -2
- package/dist/subscription-stripe/plugin-methods.js +18 -8
- package/dist/subscription-stripe/verify-webhook.d.ts +1 -1
- package/dist/tags/handlers/assign-tag.write.js +3 -1
- package/dist/tags/web/tag-chip.js +2 -0
- package/dist/template-resolver/api.d.ts +30 -2
- package/dist/template-resolver/api.js +42 -6
- package/dist/template-resolver/compile-document-body.d.ts +12 -0
- package/dist/template-resolver/compile-document-body.js +205 -0
- package/dist/template-resolver/constants.d.ts +13 -4
- package/dist/template-resolver/constants.js +32 -10
- package/dist/template-resolver/document-sanitizer.d.ts +32 -0
- package/dist/template-resolver/document-sanitizer.js +201 -0
- package/dist/template-resolver/document-schema.d.ts +21 -0
- package/dist/template-resolver/document-schema.js +316 -0
- package/dist/template-resolver/feature.d.ts +20 -0
- package/dist/template-resolver/feature.js +53 -6
- package/dist/template-resolver/handlers/bundle-import.d.ts +20 -0
- package/dist/template-resolver/handlers/bundle-import.js +147 -0
- package/dist/template-resolver/handlers/collection-set.write.d.ts +1 -1
- package/dist/template-resolver/handlers/collection-set.write.js +25 -2
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +19 -0
- package/dist/template-resolver/handlers/copy-locale.write.js +134 -0
- package/dist/template-resolver/handlers/diff-to-system.query.d.ts +22 -0
- package/dist/template-resolver/handlers/diff-to-system.query.js +30 -0
- package/dist/template-resolver/handlers/discard-draft.write.d.ts +13 -0
- package/dist/template-resolver/handlers/discard-draft.write.js +37 -0
- package/dist/template-resolver/handlers/export-bundle.query.d.ts +35 -0
- package/dist/template-resolver/handlers/export-bundle.query.js +118 -0
- package/dist/template-resolver/handlers/find-by-id.query.d.ts +2 -0
- package/dist/template-resolver/handlers/find-by-id.query.js +9 -6
- package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +39 -0
- package/dist/template-resolver/handlers/import-bundle-system.write.js +36 -0
- package/dist/template-resolver/handlers/import-bundle.write.d.ts +40 -0
- package/dist/template-resolver/handlers/import-bundle.write.js +19 -0
- package/dist/template-resolver/handlers/layout-source.query.d.ts +31 -0
- package/dist/template-resolver/handlers/layout-source.query.js +87 -0
- package/dist/template-resolver/handlers/list.query.d.ts +4 -1
- package/dist/template-resolver/handlers/list.query.js +9 -8
- package/dist/template-resolver/handlers/override-diff.d.ts +20 -0
- package/dist/template-resolver/handlers/override-diff.js +41 -0
- package/dist/template-resolver/handlers/overview.query.d.ts +65 -0
- package/dist/template-resolver/handlers/overview.query.js +205 -0
- package/dist/template-resolver/handlers/preview.query.d.ts +12 -0
- package/dist/template-resolver/handlers/preview.query.js +24 -0
- package/dist/template-resolver/handlers/reset-to-system.write.d.ts +8 -0
- package/dist/template-resolver/handlers/reset-to-system.write.js +50 -0
- package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +8 -0
- package/dist/template-resolver/handlers/resource-link-pdf.write.js +32 -0
- package/dist/template-resolver/handlers/resource-remove.write.d.ts +9 -0
- package/dist/template-resolver/handlers/resource-remove.write.js +44 -0
- package/dist/template-resolver/handlers/resource-shared.d.ts +9 -0
- package/dist/template-resolver/handlers/resource-shared.js +14 -0
- package/dist/template-resolver/handlers/resource-upload.write.d.ts +13 -0
- package/dist/template-resolver/handlers/resource-upload.write.js +98 -0
- package/dist/template-resolver/handlers/send-test.write.d.ts +13 -0
- package/dist/template-resolver/handlers/send-test.write.js +108 -0
- package/dist/template-resolver/handlers/set.write.d.ts +5 -2
- package/dist/template-resolver/handlers/set.write.js +25 -3
- package/dist/template-resolver/handlers/shared.d.ts +9 -5
- package/dist/template-resolver/handlers/shared.js +6 -2
- package/dist/template-resolver/handlers/template-gates.d.ts +36 -0
- package/dist/template-resolver/handlers/template-gates.js +142 -0
- package/dist/template-resolver/handlers/template-write.d.ts +45 -0
- package/dist/template-resolver/handlers/template-write.js +141 -0
- package/dist/template-resolver/handlers/tenant-overrides.query.d.ts +49 -0
- package/dist/template-resolver/handlers/tenant-overrides.query.js +112 -0
- package/dist/template-resolver/handlers/toggle-status.write.d.ts +27 -2
- package/dist/template-resolver/handlers/toggle-status.write.js +47 -15
- package/dist/template-resolver/handlers/upsert-system.write.d.ts +5 -8
- package/dist/template-resolver/handlers/upsert-system.write.js +14 -59
- package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +5 -8
- package/dist/template-resolver/handlers/upsert-tenant.write.js +11 -62
- package/dist/template-resolver/handlers/usage.query.d.ts +17 -0
- package/dist/template-resolver/handlers/usage.query.js +24 -0
- package/dist/template-resolver/handlers/validate.query.d.ts +74 -0
- package/dist/template-resolver/handlers/validate.query.js +80 -0
- package/dist/template-resolver/index.d.ts +19 -1
- package/dist/template-resolver/index.js +17 -1
- package/dist/template-resolver/layout-appearance.d.ts +10 -0
- package/dist/template-resolver/layout-appearance.js +26 -0
- package/dist/template-resolver/layout-regions.d.ts +23 -0
- package/dist/template-resolver/layout-regions.js +125 -0
- package/dist/template-resolver/linked-resources.d.ts +17 -0
- package/dist/template-resolver/linked-resources.js +75 -0
- package/dist/template-resolver/pdf-constants.d.ts +7 -0
- package/dist/template-resolver/pdf-constants.js +8 -0
- package/dist/template-resolver/preview-display-html.d.ts +7 -0
- package/dist/template-resolver/preview-display-html.js +27 -0
- package/dist/template-resolver/print-document.d.ts +1 -0
- package/dist/template-resolver/print-document.js +55 -0
- package/dist/template-resolver/qualified-names.d.ts +34 -0
- package/dist/template-resolver/qualified-names.js +34 -0
- package/dist/template-resolver/record-mapping.d.ts +2 -0
- package/dist/template-resolver/record-mapping.js +21 -0
- package/dist/template-resolver/resource-image.d.ts +15 -0
- package/dist/template-resolver/resource-image.js +34 -0
- package/dist/template-resolver/seeding.d.ts +14 -1
- package/dist/template-resolver/seeding.js +125 -18
- package/dist/template-resolver/store-template-resource.d.ts +64 -0
- package/dist/template-resolver/store-template-resource.js +264 -0
- package/dist/template-resolver/table.d.ts +150 -8
- package/dist/template-resolver/table.js +20 -2
- package/dist/template-resolver/template-bundle.d.ts +36 -0
- package/dist/template-resolver/template-bundle.js +60 -0
- package/dist/template-resolver/template-diff.d.ts +7 -0
- package/dist/template-resolver/template-diff.js +68 -0
- package/dist/template-resolver/template-draft.d.ts +51 -0
- package/dist/template-resolver/template-draft.js +72 -0
- package/dist/template-resolver/template-meta.d.ts +85 -0
- package/dist/template-resolver/template-meta.js +145 -0
- package/dist/template-resolver/template-preview.d.ts +103 -0
- package/dist/template-resolver/template-preview.js +418 -0
- package/dist/template-resolver/template-resources.d.ts +19 -0
- package/dist/template-resolver/template-resources.js +73 -0
- package/dist/template-resolver/template-usage.d.ts +16 -0
- package/dist/template-resolver/template-usage.js +35 -0
- package/dist/template-resolver/template-validation.d.ts +22 -0
- package/dist/template-resolver/template-validation.js +173 -0
- package/dist/template-resolver/testing.js +5 -5
- package/dist/template-resolver/user-content-table.d.ts +14 -8
- package/dist/template-resolver/web/client-plugin.d.ts +1 -0
- package/dist/template-resolver/web/client-plugin.js +12 -7
- package/dist/template-resolver/web/document-editor-extensions.d.ts +18 -0
- package/dist/template-resolver/web/document-editor-extensions.js +145 -0
- package/dist/template-resolver/web/document-editor.d.ts +4 -0
- package/dist/template-resolver/web/document-editor.js +17 -0
- package/dist/template-resolver/web/document-tiptap-editor.d.ts +3 -0
- package/dist/template-resolver/web/document-tiptap-editor.js +492 -0
- package/dist/template-resolver/web/document-toolbar-blocks.d.ts +41 -0
- package/dist/template-resolver/web/document-toolbar-blocks.js +19 -0
- package/dist/template-resolver/web/editor-handle.d.ts +3 -0
- package/dist/template-resolver/web/editor-handle.js +24 -0
- package/dist/template-resolver/web/i18n.js +91 -0
- package/dist/template-resolver/web/index.d.ts +5 -0
- package/dist/template-resolver/web/index.js +5 -0
- package/dist/template-resolver/web/placeholder-line-editor-lazy.d.ts +4 -0
- package/dist/template-resolver/web/placeholder-line-editor-lazy.js +16 -0
- package/dist/template-resolver/web/placeholder-line-editor.d.ts +17 -0
- package/dist/template-resolver/web/placeholder-line-editor.js +94 -0
- package/dist/template-resolver/web/placeholder-text.d.ts +4 -0
- package/dist/template-resolver/web/placeholder-text.js +34 -0
- package/dist/template-resolver/web/use-stable-by-key.d.ts +1 -0
- package/dist/template-resolver/web/use-stable-by-key.js +13 -0
- package/dist/tenant/handlers/add-member.write.d.ts +1 -1
- package/dist/tenant/handlers/cancel-invitation.write.d.ts +1 -1
- package/dist/tenant/handlers/create.write.d.ts +1 -1
- package/dist/tenant/handlers/remove-member.write.d.ts +1 -1
- package/dist/tenant/handlers/team-list.query.js +3 -2
- package/dist/tenant/handlers/toggle-enabled.write.d.ts +2 -2
- package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
- package/dist/tenant/handlers/update.write.d.ts +1 -1
- package/dist/tenant/seeding.js +31 -6
- package/dist/tenant-handover/handlers/claim.write.d.ts +1 -1
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +2 -2
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.js +2 -1
- package/dist/tenant-lifecycle/handlers/request-destruction.write.d.ts +1 -1
- package/dist/tier-engine/feature.js +9 -3
- package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +1 -1
- package/dist/user/db/queries/stream-tenant-backfill.js +4 -4
- package/dist/user/handlers/create.write.d.ts +1 -1
- package/dist/user/handlers/detail.query.js +2 -1
- package/dist/user/handlers/update.write.d.ts +1 -1
- package/dist/user/resolve-display-names.js +5 -9
- package/dist/user/seeding.js +20 -1
- package/dist/user-data-rights/db/queries/forget-cleanup.d.ts +2 -2
- package/dist/user-data-rights/email-templates.d.ts +1 -29
- package/dist/user-data-rights/email-templates.js +5 -106
- package/dist/user-data-rights/feature.js +53 -35
- package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +2 -2
- package/dist/user-data-rights/handlers/cancel-deletion.write.js +2 -1
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/lift-restriction.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion.write.js +5 -7
- package/dist/user-data-rights/handlers/request-export.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/restrict-account.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/run-forget-cleanup.write.d.ts +1 -1
- package/dist/user-data-rights/lib/default-mailers.d.ts +15 -7
- package/dist/user-data-rights/lib/default-mailers.js +100 -48
- package/dist/user-data-rights/lib/update-user-lifecycle.js +1 -1
- package/dist/user-data-rights/mail-notifications.d.ts +13 -0
- package/dist/user-data-rights/mail-notifications.js +118 -0
- package/dist/user-data-rights/web/privacy-center-screen.js +2 -1
- package/dist/user-profile/handlers/change-email.write.d.ts +1 -1
- package/dist/user-profile/web/profile-screen.js +2 -1
- package/dist/waitlist/feature.js +10 -0
- package/dist/waitlist/handlers/invite.write.d.ts +1 -1
- package/dist/waitlist/handlers/reject.write.d.ts +1 -1
- package/dist/waitlist/handlers/submit.write.d.ts +1 -1
- package/dist/waitlist/handlers/submit.write.js +3 -4
- package/dist/waitlist/mail.d.ts +19 -6
- package/dist/waitlist/mail.js +50 -14
- package/dist/waitlist/options.js +1 -1
- package/dist/workflow-runner/event-trigger.js +2 -2
- package/dist/workflow-runner/handlers/resume-run.write.js +7 -2
- package/package.json +37 -17
- package/src/admin-shell/changes.json +6 -0
- package/src/audit/changes.json +6 -0
- package/src/auth-email-password/changes.json +51 -0
- package/src/billing-foundation/changes.json +43 -0
- package/src/cap-counter/changes.json +6 -0
- package/src/channel-email/changes.json +19 -0
- package/src/config/changes.json +6 -0
- package/src/crypto-shredding/changes.json +6 -0
- package/src/custom-fields/changes.json +6 -0
- package/src/data-retention/changes.json +6 -0
- package/src/delivery/changes.json +66 -0
- package/src/file-foundation/changes.json +6 -0
- package/src/files-tenant-data/changes.json +6 -0
- package/src/inbound-mail-foundation/changes.json +6 -0
- package/src/jobs/changes.json +19 -0
- package/src/ledger/changes.json +7 -0
- package/src/managed-pages/changes.json +6 -0
- package/src/renderer-foundation/changes.json +39 -0
- package/src/renderer-pdf/changes.json +12 -0
- package/src/secrets/changes.json +6 -0
- package/src/shared/changes.json +6 -0
- package/src/subscription-stripe/changes.json +6 -0
- package/src/template-resolver/changes.json +186 -0
- package/src/tier-engine/changes.json +6 -0
- package/src/user/changes.json +6 -0
- package/src/user-data-rights/changes.json +13 -0
- package/src/user-profile/changes.json +1 -1
- package/src/waitlist/changes.json +7 -0
- package/src/workflow-runner/changes.json +6 -0
- package/dist/renderer-simple/feature.d.ts +0 -10
- package/dist/renderer-simple/feature.js +0 -42
- package/dist/renderer-simple/index.d.ts +0 -3
- package/dist/renderer-simple/index.js +0 -2
- package/dist/renderer-simple/mail-blocks.d.ts +0 -21
- package/dist/renderer-simple/mail-blocks.js +0 -107
- package/dist/renderer-simple/resolve-variables.d.ts +0 -7
- package/dist/renderer-simple/resolve-variables.js +0 -47
- package/dist/renderer-simple/simple-renderer.d.ts +0 -24
- package/dist/renderer-simple/simple-renderer.js +0 -200
- package/src/renderer-simple/changes.json +0 -27
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
const MAX_FILE_NAME_LENGTH = 255;
|
|
3
|
+
function hasControlCharacter(value) {
|
|
4
|
+
return value.split("").some((char) => {
|
|
5
|
+
const code = char.charCodeAt(0);
|
|
6
|
+
return code <= 0x1f || code === 0x7f;
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export const attachmentFileNameSchema = z
|
|
10
|
+
.string()
|
|
11
|
+
.min(1)
|
|
12
|
+
.max(MAX_FILE_NAME_LENGTH)
|
|
13
|
+
.refine((name) => !name.includes("/") && !name.includes("\\") && !hasControlCharacter(name), {
|
|
14
|
+
message: "file name must not contain path separators or control characters",
|
|
15
|
+
});
|
|
16
|
+
const PDF_FILE_NAME_MAX_LENGTH = 200;
|
|
17
|
+
const PDF_EXTENSION = ".pdf";
|
|
18
|
+
// Shared by everything that names a rendered PDF, so a mail attachment and a download of the same
|
|
19
|
+
// template get the same name. The cut leaves room for the extension: the attachment schema caps
|
|
20
|
+
// rendered-pdf names, and a name that only fits without ".pdf" would fail it.
|
|
21
|
+
export function sanitizePdfAttachmentFileName(raw, templateSlug) {
|
|
22
|
+
const cleaned = raw
|
|
23
|
+
.split("")
|
|
24
|
+
.filter((char) => !hasControlCharacter(char))
|
|
25
|
+
.join("")
|
|
26
|
+
.replace(/[/\\";]/g, "-")
|
|
27
|
+
.replaceAll("..", "-")
|
|
28
|
+
.trim()
|
|
29
|
+
.slice(0, PDF_FILE_NAME_MAX_LENGTH - PDF_EXTENSION.length)
|
|
30
|
+
.trim();
|
|
31
|
+
const lastSegment = templateSlug.split(":").pop() ?? templateSlug;
|
|
32
|
+
if (cleaned === "")
|
|
33
|
+
return `${lastSegment}${PDF_EXTENSION}`;
|
|
34
|
+
return cleaned.toLowerCase().endsWith(PDF_EXTENSION) ? cleaned : `${cleaned}${PDF_EXTENSION}`;
|
|
35
|
+
}
|
|
@@ -12,6 +12,7 @@ export type ChunkedMigrationOptions<Row> = {
|
|
|
12
12
|
readonly migrateRow: (row: Row) => Promise<MigrationRowOutcome>;
|
|
13
13
|
readonly maxFailures: number;
|
|
14
14
|
readonly deadlineAt: number;
|
|
15
|
+
readonly minBatches?: number;
|
|
15
16
|
readonly signal?: AbortSignal | undefined;
|
|
16
17
|
readonly onRowError?: (row: Row, err: unknown) => void;
|
|
17
18
|
};
|
|
@@ -25,7 +25,7 @@ export async function runChunkedMigration(opts) {
|
|
|
25
25
|
stoppedReason = "signal";
|
|
26
26
|
break;
|
|
27
27
|
}
|
|
28
|
-
if (Date.now() >= opts.deadlineAt) {
|
|
28
|
+
if (batchesProcessed >= (opts.minBatches ?? 0) && Date.now() >= opts.deadlineAt) {
|
|
29
29
|
stoppedReason = "timeout";
|
|
30
30
|
break;
|
|
31
31
|
}
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { collectErasureFailure, throwIfErasureFailed } from "./assert-erased.js";
|
|
2
2
|
export { assertNoWhereRuleInOwnershipWrite } from "./assert-no-where-rule-in-ownership-write.js";
|
|
3
|
+
export { attachmentFileNameSchema, sanitizePdfAttachmentFileName, } from "./attachment-file-name.js";
|
|
3
4
|
export { type ChunkedMigrationOptions, type ChunkedMigrationResult, type ChunkedMigrationStopReason, type MigrationRowOutcome, runChunkedMigration, } from "./chunked-entity-migration.js";
|
|
4
5
|
export { classifyStoredEnvelope, type StoredEnvelopeClassification, } from "./classify-stored-envelope.js";
|
|
5
6
|
export { decryptStoredPii } from "./decrypt-stored-pii.js";
|
|
@@ -9,6 +10,7 @@ export { isWithinGracePeriod } from "./grace-period.js";
|
|
|
9
10
|
export { isIdentityV3Hash, verifyIdentityV3Hash } from "./identity-v3-hash.js";
|
|
10
11
|
export { isTenantDb } from "./is-tenant-db.js";
|
|
11
12
|
export { createLockoutCounter, type LockoutCounterState } from "./lockout-counter.js";
|
|
13
|
+
export { mailHeading, mailParagraph, mailRepeatedRows } from "./mail-template-body.js";
|
|
12
14
|
export { mapWithConcurrency } from "./map-with-concurrency.js";
|
|
13
15
|
export { denyUnlessJoinRowParentVisible, hostRowIsGone, joinRowParentIsVisible, parentRowIsVisible, } from "./parent-visibility.js";
|
|
14
16
|
export { hashPassword, verifyDummyPassword, verifyPassword } from "./password-hashing.js";
|
|
@@ -20,6 +22,7 @@ export { type RowBoundGrantResult, redeemRowBoundGrant, signRowBoundGrant, } fro
|
|
|
20
22
|
export { runInSubTransaction } from "./run-in-sub-transaction.js";
|
|
21
23
|
export { registrationLocaleFromRequest, sessionLocaleField } from "./session-locale-field.js";
|
|
22
24
|
export { sessionTimezoneField } from "./session-timezone-field.js";
|
|
25
|
+
export { assertNotSvg, MAX_INPUT_PIXELS, SHARP_INPUT_OPTIONS } from "./sharp-input-limits.js";
|
|
23
26
|
export { peekTokenSubject, signToken, TokenPurpose, type VerifyResult, verifyToken, } from "./signed-token.js";
|
|
24
27
|
export { EXT_SIGNUP_HANDOVER, findSignupHandoverProvider, isSignupHandoverProvider, SIGNUP_HANDOVER_BENIGN_CLAIM_REJECTION_CODE, type SignupHandoverBinding, type SignupHandoverProvider, } from "./signup-handover.js";
|
|
25
28
|
export { createSingleUseTokenStore, hashSingleUseToken } from "./single-use-token-store.js";
|
package/dist/shared/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { collectErasureFailure, throwIfErasureFailed } from "./assert-erased.js";
|
|
2
2
|
export { assertNoWhereRuleInOwnershipWrite } from "./assert-no-where-rule-in-ownership-write.js";
|
|
3
|
+
export { attachmentFileNameSchema, sanitizePdfAttachmentFileName, } from "./attachment-file-name.js";
|
|
3
4
|
export { runChunkedMigration, } from "./chunked-entity-migration.js";
|
|
4
5
|
export { classifyStoredEnvelope, } from "./classify-stored-envelope.js";
|
|
5
6
|
export { decryptStoredPii } from "./decrypt-stored-pii.js";
|
|
@@ -9,6 +10,7 @@ export { isWithinGracePeriod } from "./grace-period.js";
|
|
|
9
10
|
export { isIdentityV3Hash, verifyIdentityV3Hash } from "./identity-v3-hash.js";
|
|
10
11
|
export { isTenantDb } from "./is-tenant-db.js";
|
|
11
12
|
export { createLockoutCounter } from "./lockout-counter.js";
|
|
13
|
+
export { mailHeading, mailParagraph, mailRepeatedRows } from "./mail-template-body.js";
|
|
12
14
|
export { mapWithConcurrency } from "./map-with-concurrency.js";
|
|
13
15
|
export { denyUnlessJoinRowParentVisible, hostRowIsGone, joinRowParentIsVisible, parentRowIsVisible, } from "./parent-visibility.js";
|
|
14
16
|
export { hashPassword, verifyDummyPassword, verifyPassword } from "./password-hashing.js";
|
|
@@ -20,6 +22,7 @@ export { redeemRowBoundGrant, signRowBoundGrant, } from "./row-bound-grant.js";
|
|
|
20
22
|
export { runInSubTransaction } from "./run-in-sub-transaction.js";
|
|
21
23
|
export { registrationLocaleFromRequest, sessionLocaleField } from "./session-locale-field.js";
|
|
22
24
|
export { sessionTimezoneField } from "./session-timezone-field.js";
|
|
25
|
+
export { assertNotSvg, MAX_INPUT_PIXELS, SHARP_INPUT_OPTIONS } from "./sharp-input-limits.js";
|
|
23
26
|
export { peekTokenSubject, signToken, TokenPurpose, verifyToken, } from "./signed-token.js";
|
|
24
27
|
export { EXT_SIGNUP_HANDOVER, findSignupHandoverProvider, isSignupHandoverProvider, SIGNUP_HANDOVER_BENIGN_CLAIM_REJECTION_CODE, } from "./signup-handover.js";
|
|
25
28
|
export { createSingleUseTokenStore, hashSingleUseToken } from "./single-use-token-store.js";
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
// Shared so retention/compliance-profile resolvers that accept either (bulk cron
|
|
4
4
|
// vs. per-hook TenantDb) branch through one predicate instead of duplicating it.
|
|
5
5
|
export function isTenantDb(db) {
|
|
6
|
-
return typeof db.unsafeRaw === "function";
|
|
6
|
+
return "unsafeRaw" in db && typeof db.unsafeRaw === "function";
|
|
7
7
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function mailTextWithSlotsHtml(text: string): string;
|
|
2
|
+
export declare function mailParagraph(text: string): string;
|
|
3
|
+
export declare function mailHeading(text: string): string;
|
|
4
|
+
export declare function mailButton(label: string, linkVariable: string): string;
|
|
5
|
+
export declare function mailParagraphWithLink(text: string, linkLabel: string, linkVariable: string): string;
|
|
6
|
+
export declare function mailRepeatedRows(listName: string, field: string): string;
|
|
7
|
+
export declare function mailSubject(text: string): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
|
|
2
|
+
const SLOT_PATTERN = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
3
|
+
// Author text uses `{name}` slots. The text is escaped first and the slots are
|
|
4
|
+
// swapped for variable nodes afterwards, so a slot can never smuggle markup.
|
|
5
|
+
export function mailTextWithSlotsHtml(text) {
|
|
6
|
+
return escapeHtml(text).replace(SLOT_PATTERN, (_, name) => `<span data-variable="${escapeHtmlAttr(name)}"></span>`);
|
|
7
|
+
}
|
|
8
|
+
export function mailParagraph(text) {
|
|
9
|
+
return `<p>${mailTextWithSlotsHtml(text)}</p>`;
|
|
10
|
+
}
|
|
11
|
+
export function mailHeading(text) {
|
|
12
|
+
return `<h2>${mailTextWithSlotsHtml(text)}</h2>`;
|
|
13
|
+
}
|
|
14
|
+
export function mailButton(label, linkVariable) {
|
|
15
|
+
return `<p><a data-button href="{{${escapeHtmlAttr(linkVariable)}}}">${escapeHtml(label)}</a></p>`;
|
|
16
|
+
}
|
|
17
|
+
export function mailParagraphWithLink(text, linkLabel, linkVariable) {
|
|
18
|
+
return `<p>${mailTextWithSlotsHtml(text)} <a href="{{${escapeHtmlAttr(linkVariable)}}}">${escapeHtml(linkLabel)}</a></p>`;
|
|
19
|
+
}
|
|
20
|
+
// One table row per list entry; the cell shows the entry's `field`.
|
|
21
|
+
export function mailRepeatedRows(listName, field) {
|
|
22
|
+
return `<table><tbody><tr data-each="${escapeHtmlAttr(listName)}"><td><p><span data-variable="${escapeHtmlAttr(field)}"></span></p></td></tr></tbody></table>`;
|
|
23
|
+
}
|
|
24
|
+
// Subjects are Handlebars text, so the same `{name}` slots become `{{name}}`.
|
|
25
|
+
export function mailSubject(text) {
|
|
26
|
+
return text.replace(SLOT_PATTERN, "{{$1}}");
|
|
27
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Sharp-free so callers can import the limits without loading the native binding.
|
|
2
|
+
// Above this, a decoded pixel buffer runs into hundreds of MB before any
|
|
3
|
+
// resize even happens — sharp's own default (~268MP) is still large enough
|
|
4
|
+
// to be an OOM primitive for a single request.
|
|
5
|
+
export const MAX_INPUT_PIXELS = 100_000_000;
|
|
6
|
+
// sequentialRead trades random pixel access for lower peak memory on
|
|
7
|
+
// streamed formats — applied at every decode entry point below since none
|
|
8
|
+
// of them need non-sequential access to the *original* bytes (extract runs
|
|
9
|
+
// against a single already-buffered region per call).
|
|
10
|
+
export const SHARP_INPUT_OPTIONS = {
|
|
11
|
+
limitInputPixels: MAX_INPUT_PIXELS,
|
|
12
|
+
sequentialRead: true,
|
|
13
|
+
};
|
|
14
|
+
// Shared trust boundary for every image byte source this renderer decodes —
|
|
15
|
+
// the original upload (renderImage) and an overlay layer's bytes (applyOverlays)
|
|
16
|
+
// alike: a base64 string in a field declaration isn't more trustworthy than
|
|
17
|
+
// an upload just because it came from app code.
|
|
18
|
+
export function assertNotSvg(format) {
|
|
19
|
+
if (format === "svg") {
|
|
20
|
+
throw new Error("derivatives-sharp: SVG is not supported — rendering untrusted SVG through libvips is a trust boundary this renderer doesn't need to cross.");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// The grant itself never crosses this boundary: verifyGrant returns only
|
|
11
11
|
// the { entityType, rowId, sourceTenantId } binding, and mintClaim returns a
|
|
12
12
|
// freshly signed short-lived token rather than handing back the original.
|
|
13
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
13
14
|
export const EXT_SIGNUP_HANDOVER = "signupHandover";
|
|
14
15
|
// Part of the contract: a provider's minted claim write may reject two
|
|
15
16
|
// different ways. Benign — this code, nothing was written (forged,
|
|
@@ -20,8 +21,7 @@ export const EXT_SIGNUP_HANDOVER = "signupHandover";
|
|
|
20
21
|
// together with it, never treat that as a skippable no-op.
|
|
21
22
|
export const SIGNUP_HANDOVER_BENIGN_CLAIM_REJECTION_CODE = "invalid_or_expired_grant";
|
|
22
23
|
export function isSignupHandoverProvider(options) {
|
|
23
|
-
return (
|
|
24
|
-
options !== null &&
|
|
24
|
+
return (isPlainObject(options) &&
|
|
25
25
|
typeof options["verifyGrant"] === "function" &&
|
|
26
26
|
typeof options["mintClaim"] === "function");
|
|
27
27
|
}
|
|
@@ -75,9 +75,7 @@ export function mapAppLocaleToStripeLocale(locale) {
|
|
|
75
75
|
return knownLocales.find((known) => known === language) ?? "auto";
|
|
76
76
|
}
|
|
77
77
|
function isUnknownCustomerStripeError(error) {
|
|
78
|
-
return
|
|
79
|
-
error instanceof Stripe.errors.StripeInvalidRequestError &&
|
|
80
|
-
error.param === "customer");
|
|
78
|
+
return isResourceMissingStripeError(error) && error.param === "customer";
|
|
81
79
|
}
|
|
82
80
|
export function createStripeCheckoutSession(runtime, { paymentInvoiceCreation = true } = {}) {
|
|
83
81
|
return async (ctx, options) => {
|
|
@@ -147,11 +145,23 @@ export function createStripeCheckoutSession(runtime, { paymentInvoiceCreation =
|
|
|
147
145
|
export function createStripePortalSession(runtime) {
|
|
148
146
|
return async (ctx, options) => {
|
|
149
147
|
const stripe = await runtime.clientForCtx(ctx);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
148
|
+
try {
|
|
149
|
+
const session = await stripe.billingPortal.sessions.create({
|
|
150
|
+
customer: options.providerCustomerId,
|
|
151
|
+
return_url: options.returnUrl,
|
|
152
|
+
});
|
|
153
|
+
return { url: session.url };
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
// A portal needs an existing customer, so unlike checkout there is no retry without one.
|
|
157
|
+
if (!isUnknownCustomerStripeError(error))
|
|
158
|
+
throw error;
|
|
159
|
+
throw new UnprocessableError("provider_customer_unknown", {
|
|
160
|
+
i18nKey: "billing-foundation.errors.providerCustomerUnknown",
|
|
161
|
+
message: `subscription-stripe: Stripe does not know customer "${options.providerCustomerId}" (account switch or customer deleted).`,
|
|
162
|
+
cause: error,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
155
165
|
};
|
|
156
166
|
}
|
|
157
167
|
export function createStripeCancelSubscription(runtime) {
|
|
@@ -3,7 +3,7 @@ import { type SubscriptionEventType, type SubscriptionStatus } from "@cosmicdrif
|
|
|
3
3
|
import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
|
|
4
4
|
import Stripe from "stripe";
|
|
5
5
|
import type { StripeWebhookRuntime } from "./runtime.js";
|
|
6
|
-
export declare function isResourceMissingStripeError(error: unknown):
|
|
6
|
+
export declare function isResourceMissingStripeError(error: unknown): error is Stripe.errors.StripeInvalidRequestError;
|
|
7
7
|
export type StripeWebhookOptions = {
|
|
8
8
|
/** Price-to-tier-Map. Plugin liest die price-id aus dem event und
|
|
9
9
|
* mapped auf tier-name. Fehlt die price-id im Mapping → null. App-
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NotFoundError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
2
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
3
|
import { denyUnlessJoinRowParentVisible } from "../../shared/index.js";
|
|
3
4
|
import { tagAssignmentAggregateId } from "../aggregate-id.js";
|
|
4
5
|
import { DEFAULT_TAG_ACCESS } from "../constants.js";
|
|
@@ -49,7 +50,8 @@ export function createAssignTagHandler(access = DEFAULT_TAG_ACCESS, assignmentEx
|
|
|
49
50
|
// above and this restore() call, so restore() correctly refuses with
|
|
50
51
|
// "not_deleted" — but the desired end state is already true. Converge
|
|
51
52
|
// like the create() race below instead of surfacing a spurious error.
|
|
52
|
-
if (restored.error.details
|
|
53
|
+
if (isPlainObject(restored.error.details) &&
|
|
54
|
+
restored.error.details["reason"] === "not_deleted") {
|
|
53
55
|
return { isSuccess: true, data: { id } };
|
|
54
56
|
}
|
|
55
57
|
if (restored.error.code !== "not_found")
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import type { DbRunner } from "@cosmicdrift/kumiko-framework/db";
|
|
1
|
+
import type { DbRunner, TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
2
2
|
import type { SessionUser, TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
|
+
import type { FileAttachmentRef } from "../renderer-foundation/index.js";
|
|
4
5
|
import type { ContentFormat, TemplateKind } from "./constants.js";
|
|
6
|
+
import { type TemplatePdfAttachmentLink } from "./linked-resources.js";
|
|
7
|
+
import { type TemplateMeta } from "./template-meta.js";
|
|
5
8
|
export type TemplateResource = {
|
|
6
9
|
readonly id: string;
|
|
7
10
|
readonly version: number;
|
|
@@ -14,7 +17,15 @@ export type TemplateResource = {
|
|
|
14
17
|
readonly content: string;
|
|
15
18
|
readonly contentFormat: ContentFormat;
|
|
16
19
|
readonly variableSchema: Record<string, unknown>;
|
|
20
|
+
/** Embeddable images: resource key -> fileRef id. */
|
|
17
21
|
readonly linkedResources: Record<string, string>;
|
|
22
|
+
/** Files attached to every mail rendered from this template, ordered by key. */
|
|
23
|
+
readonly fileAttachments?: readonly FileAttachmentRef[];
|
|
24
|
+
/** PDFs rendered from document templates and attached at send time, ordered by key. */
|
|
25
|
+
readonly pdfAttachmentLinks?: readonly TemplatePdfAttachmentLink[];
|
|
26
|
+
/** Parsed per kind; null when absent or when the stored JSON no longer
|
|
27
|
+
* matches the kind's schema (the renderer then fails with a clear error). */
|
|
28
|
+
readonly meta: TemplateMeta | null;
|
|
18
29
|
readonly scope: "system" | "tenant";
|
|
19
30
|
readonly parentTemplateId: string | null;
|
|
20
31
|
readonly status: "draft" | "active" | "archived";
|
|
@@ -26,6 +37,13 @@ export type ResolveRequest = {
|
|
|
26
37
|
readonly kind: TemplateKind;
|
|
27
38
|
readonly locale: string;
|
|
28
39
|
};
|
|
40
|
+
export type LayoutKind = "mail-layout" | "pdf-layout";
|
|
41
|
+
export type ResolveLayoutRequest = {
|
|
42
|
+
readonly tenantId: TenantId;
|
|
43
|
+
readonly slug: string;
|
|
44
|
+
readonly kind: LayoutKind;
|
|
45
|
+
readonly locale: string;
|
|
46
|
+
};
|
|
29
47
|
export type TemplateResolverApi = {
|
|
30
48
|
/**
|
|
31
49
|
* Findet ein konkretes Template by (tenantId, slug, kind, locale).
|
|
@@ -60,8 +78,18 @@ export type TemplateResolverApi = {
|
|
|
60
78
|
* User cross-tenant Templates abfragen (Tenant-Isolation gebrochen).
|
|
61
79
|
*/
|
|
62
80
|
readonly resolveTemplate: (args: ResolveRequest) => Promise<TemplateResource>;
|
|
81
|
+
/**
|
|
82
|
+
* Layouts are locale-neutral in the product (the layout editor saves under the locale of the
|
|
83
|
+
* row it loaded), so the locale chain is wider than resolveTemplate: tenant (requested,
|
|
84
|
+
* FALLBACK_LOCALE, any active locale), then system (same). Only active rows count; among
|
|
85
|
+
* "any" the highest version wins, then the alphabetically first locale, so the pick is stable.
|
|
86
|
+
* Throws TemplateNotFoundError when neither scope has an active row.
|
|
87
|
+
*
|
|
88
|
+
* **Caller invariant:** `tenantId` must come from the server, as for `resolveTemplate`.
|
|
89
|
+
*/
|
|
90
|
+
readonly resolveLayout: (args: ResolveLayoutRequest) => Promise<TemplateResource>;
|
|
63
91
|
};
|
|
64
|
-
export declare function createTemplateResolverApi(db: DbRunner): TemplateResolverApi;
|
|
92
|
+
export declare function createTemplateResolverApi(db: DbRunner | TenantDb): TemplateResolverApi;
|
|
65
93
|
export declare class TemplateNotFoundError extends Error {
|
|
66
94
|
readonly query: {
|
|
67
95
|
slug: string;
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
5
5
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
6
6
|
import { FALLBACK_LOCALE, SYSTEM_TENANT_ID } from "./constants.js";
|
|
7
|
+
import { fileAttachmentsOf, imageLinksOf, parseLinkedResourceEntries, pdfAttachmentLinksOf, } from "./linked-resources.js";
|
|
7
8
|
import { templateResourcesTable } from "./table.js";
|
|
9
|
+
import { activeVersionOf, servedVersionOf } from "./template-draft.js";
|
|
10
|
+
import { parseTemplateMeta } from "./template-meta.js";
|
|
8
11
|
export function createTemplateResolverApi(db) {
|
|
9
12
|
return {
|
|
10
13
|
findExact: async ({ tenantId, slug, kind, locale, scope }) => {
|
|
@@ -31,8 +34,30 @@ export function createTemplateResolverApi(db) {
|
|
|
31
34
|
}
|
|
32
35
|
throw new TemplateNotFoundError({ slug, kind, locale });
|
|
33
36
|
},
|
|
37
|
+
resolveLayout: async ({ tenantId, slug, kind, locale }) => {
|
|
38
|
+
for (const tid of [tenantId, SYSTEM_TENANT_ID]) {
|
|
39
|
+
const rows = await selectMany(db, templateResourcesTable, {
|
|
40
|
+
tenantId: tid,
|
|
41
|
+
slug,
|
|
42
|
+
kind,
|
|
43
|
+
});
|
|
44
|
+
const row = pickActiveLayoutRow(rows, locale);
|
|
45
|
+
if (row !== null)
|
|
46
|
+
return toPublic(row);
|
|
47
|
+
}
|
|
48
|
+
throw new TemplateNotFoundError({ slug, kind, locale });
|
|
49
|
+
},
|
|
34
50
|
};
|
|
35
51
|
}
|
|
52
|
+
function pickActiveLayoutRow(rows, locale) {
|
|
53
|
+
const active = rows
|
|
54
|
+
.filter((row) => row.status === "active")
|
|
55
|
+
.sort((a, b) => activeVersionOf(b) - activeVersionOf(a) || a.locale.localeCompare(b.locale));
|
|
56
|
+
return (active.find((row) => row.locale === locale) ??
|
|
57
|
+
active.find((row) => row.locale === FALLBACK_LOCALE) ??
|
|
58
|
+
active[0] ??
|
|
59
|
+
null);
|
|
60
|
+
}
|
|
36
61
|
async function fetchTemplate(db, tenantId, slug, kind, locale) {
|
|
37
62
|
const rows = await selectMany(db, templateResourcesTable, { tenantId, slug, kind, locale }, { limit: 1 });
|
|
38
63
|
// @cast-boundary db-row — db.select returnt unbenanntes unknown[],
|
|
@@ -43,7 +68,6 @@ function toPublic(row) {
|
|
|
43
68
|
// @cast-boundary db-row — Drizzle-Schema typisiert kind/contentFormat/
|
|
44
69
|
// scope/status als generic text. CHECK-Constraints in der DB schränken
|
|
45
70
|
// sie auf die Union-Types ein; Cast assertet das Schema-Wissen.
|
|
46
|
-
// linkedResources ist ein text-column mit JSON-payload (string→string map).
|
|
47
71
|
const kind = row.kind;
|
|
48
72
|
// @cast-boundary db-row — siehe kind.
|
|
49
73
|
const contentFormat = row.contentFormat;
|
|
@@ -51,12 +75,10 @@ function toPublic(row) {
|
|
|
51
75
|
const scope = row.scope;
|
|
52
76
|
// @cast-boundary db-row — siehe kind.
|
|
53
77
|
const status = row.status;
|
|
54
|
-
|
|
55
|
-
// linkedResources-Spalte enthält per Schema {key: signedUrl}-Map.
|
|
56
|
-
const linkedResources = parseJson(row.linkedResources);
|
|
78
|
+
const linkedEntries = parseLinkedResourceEntries(row.linkedResources);
|
|
57
79
|
return {
|
|
58
80
|
id: String(row.id),
|
|
59
|
-
version: row
|
|
81
|
+
version: servedVersionOf(row),
|
|
60
82
|
tenantId: row.tenantId,
|
|
61
83
|
slug: row.slug,
|
|
62
84
|
kind,
|
|
@@ -66,13 +88,27 @@ function toPublic(row) {
|
|
|
66
88
|
content: row.content ?? "",
|
|
67
89
|
contentFormat,
|
|
68
90
|
variableSchema: parseJson(row.variableSchema),
|
|
69
|
-
linkedResources,
|
|
91
|
+
linkedResources: imageLinksOf(linkedEntries),
|
|
92
|
+
fileAttachments: fileAttachmentsOf(linkedEntries),
|
|
93
|
+
pdfAttachmentLinks: pdfAttachmentLinksOf(linkedEntries),
|
|
94
|
+
meta: parseStoredMeta(kind, row.meta),
|
|
70
95
|
scope,
|
|
71
96
|
parentTemplateId: row.parentTemplateId,
|
|
72
97
|
status,
|
|
73
98
|
modifiedAt: row.modifiedAt,
|
|
74
99
|
};
|
|
75
100
|
}
|
|
101
|
+
function parseStoredMeta(kind, raw) {
|
|
102
|
+
if (!raw)
|
|
103
|
+
return null;
|
|
104
|
+
try {
|
|
105
|
+
const result = parseTemplateMeta(kind, JSON.parse(raw));
|
|
106
|
+
return result.ok ? result.meta : null;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
76
112
|
function parseJson(raw) {
|
|
77
113
|
if (!raw)
|
|
78
114
|
return {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TemplateVariables } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
export type CompileDocumentBodyOptions = {
|
|
3
|
+
readonly resourceSrc?: (key: string) => string | undefined;
|
|
4
|
+
readonly resourceImageMaxWidthPx?: number;
|
|
5
|
+
readonly alignmentAsInlineStyle?: boolean;
|
|
6
|
+
readonly buttonAsInlineStyle?: boolean;
|
|
7
|
+
readonly tableAsInlineStyle?: boolean;
|
|
8
|
+
readonly buttonInlineStyle?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function collectDocumentResourceKeys(html: string): readonly string[];
|
|
11
|
+
export declare function toResourceDataUri(contentType: string, bytes: Uint8Array): string;
|
|
12
|
+
export declare function compileDocumentBodyToHandlebars(html: string, variables: TemplateVariables, options?: CompileDocumentBodyOptions): string;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
|
+
import { withParsedDocument } from "./document-sanitizer.js";
|
|
3
|
+
import { DOCUMENT_TEXT_ALIGNMENTS, isAllowedDocumentLinkHref, isDocumentPlaceholderHref, isDocumentReferenceName, isDocumentResourceKey, } from "./document-schema.js";
|
|
4
|
+
// Private-use characters mark where a Handlebars expression goes. Author text
|
|
5
|
+
// is scrubbed of them first and every `{`/`}` is entity-encoded before the
|
|
6
|
+
// markers are swapped for real expressions, so the only Handlebars syntax in
|
|
7
|
+
// the output is the syntax this module wrote.
|
|
8
|
+
const EXPRESSION_OPEN = "\uE000";
|
|
9
|
+
const EXPRESSION_CLOSE = "\uE001";
|
|
10
|
+
const MARKER_PATTERN = /\uE000(\d+)\uE001/g;
|
|
11
|
+
function marker(state, expression) {
|
|
12
|
+
state.expressions.push(expression);
|
|
13
|
+
return `${EXPRESSION_OPEN}${state.expressions.length - 1}${EXPRESSION_CLOSE}`;
|
|
14
|
+
}
|
|
15
|
+
function ownVariable(variables, name) {
|
|
16
|
+
return Object.hasOwn(variables, name) ? variables[name] : undefined;
|
|
17
|
+
}
|
|
18
|
+
function itemFieldsOf(variables, listName) {
|
|
19
|
+
const def = ownVariable(variables, listName);
|
|
20
|
+
return def?.type === "list" ? def.item : null;
|
|
21
|
+
}
|
|
22
|
+
function rowScopeOf(element, state) {
|
|
23
|
+
const listName = element.closest("tr[data-each]")?.getAttribute("data-each") ?? null;
|
|
24
|
+
if (listName === null)
|
|
25
|
+
return null;
|
|
26
|
+
const itemFields = itemFieldsOf(state.variables, listName);
|
|
27
|
+
return itemFields === null ? null : { listName, itemFields };
|
|
28
|
+
}
|
|
29
|
+
function resolveReference(name, scope, variables) {
|
|
30
|
+
if (scope !== null && ownVariable(scope.itemFields, name) !== undefined) {
|
|
31
|
+
return { path: name, def: ownVariable(scope.itemFields, name) };
|
|
32
|
+
}
|
|
33
|
+
return { path: scope === null ? name : `@root.${name}`, def: ownVariable(variables, name) };
|
|
34
|
+
}
|
|
35
|
+
function valueExpression(path, def) {
|
|
36
|
+
if (def?.type === "money")
|
|
37
|
+
return `{{money ${path}}}`;
|
|
38
|
+
if (def?.type === "date")
|
|
39
|
+
return `{{date ${path}}}`;
|
|
40
|
+
if (def?.type === "richText")
|
|
41
|
+
return `{{richText ${path}}}`;
|
|
42
|
+
return `{{${path}}}`;
|
|
43
|
+
}
|
|
44
|
+
function rewriteVariable(element, state) {
|
|
45
|
+
const name = element.getAttribute("data-variable") ?? "";
|
|
46
|
+
const replacement = element.ownerDocument.createTextNode(isDocumentReferenceName(name)
|
|
47
|
+
? marker(state, valueExpression(...pathAndDef(name, element, state)))
|
|
48
|
+
: "");
|
|
49
|
+
element.replaceWith(replacement);
|
|
50
|
+
}
|
|
51
|
+
function pathAndDef(name, element, state) {
|
|
52
|
+
const { path, def } = resolveReference(name, rowScopeOf(element, state), state.variables);
|
|
53
|
+
return [path, def];
|
|
54
|
+
}
|
|
55
|
+
function rewriteLink(element, state) {
|
|
56
|
+
const href = element.getAttribute("href") ?? "";
|
|
57
|
+
if (isDocumentPlaceholderHref(href)) {
|
|
58
|
+
const name = href.slice(2, -2);
|
|
59
|
+
if (isDocumentReferenceName(name)) {
|
|
60
|
+
const [path] = pathAndDef(name, element, state);
|
|
61
|
+
element.setAttribute("href", marker(state, `{{link ${path}}}`));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
element.setAttribute("href", "#");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else if (!isAllowedDocumentLinkHref(href)) {
|
|
68
|
+
element.setAttribute("href", "#");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function rewriteRepeatRow(row, state) {
|
|
72
|
+
const listName = row.getAttribute("data-each") ?? "";
|
|
73
|
+
const document = row.ownerDocument;
|
|
74
|
+
if (itemFieldsOf(state.variables, listName) === null || !isDocumentReferenceName(listName)) {
|
|
75
|
+
row.remove();
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
row.before(document.createTextNode(marker(state, `{{#each ${listName}}}`)));
|
|
79
|
+
row.after(document.createTextNode(marker(state, "{{/each}}")));
|
|
80
|
+
row.removeAttribute("data-each");
|
|
81
|
+
row.removeAttribute("data-repeat-header");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function stripActiveContent(element) {
|
|
85
|
+
for (const attribute of Array.from(element.attributes)) {
|
|
86
|
+
if (/^on/i.test(attribute.name))
|
|
87
|
+
element.removeAttribute(attribute.name);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function neutralizeBraces(html) {
|
|
91
|
+
return html.replace(/[{}]/g, (brace) => (brace === "{" ? "{" : "}"));
|
|
92
|
+
}
|
|
93
|
+
const MAIL_BUTTON_STYLE = "display:inline-block;padding:10px 20px;border-radius:6px;background-color:#1f5468;color:#ffffff;font-weight:600;text-decoration:none";
|
|
94
|
+
const MAIL_TABLE_STYLE = "width:100%;border-collapse:collapse";
|
|
95
|
+
const MAIL_TABLE_CELL_STYLE = "padding:6px 12px;vertical-align:top;border-bottom:1px solid #e5e5e5";
|
|
96
|
+
const MAIL_TABLE_HEAD_STYLE = "font-size:12px;font-weight:600;color:#6b7280;text-align:left";
|
|
97
|
+
const MAIL_SMALL_STYLE = "font-size:12px;color:#6b7280";
|
|
98
|
+
// Appended after an existing style (data-align) so the alignment survives.
|
|
99
|
+
function appendStyle(element, style) {
|
|
100
|
+
const existing = element.getAttribute("style");
|
|
101
|
+
element.setAttribute("style", existing === null ? style : `${existing};${style}`);
|
|
102
|
+
}
|
|
103
|
+
const RESOURCE_IMAGE_CONTENT_TYPES = new Set([
|
|
104
|
+
"image/png",
|
|
105
|
+
"image/jpeg",
|
|
106
|
+
"image/gif",
|
|
107
|
+
"image/webp",
|
|
108
|
+
]);
|
|
109
|
+
// Unique valid resource keys in document order.
|
|
110
|
+
export function collectDocumentResourceKeys(html) {
|
|
111
|
+
return withParsedDocument(html, (root) => {
|
|
112
|
+
const keys = new Set();
|
|
113
|
+
for (const element of Array.from(root.querySelectorAll("img[data-resource]"))) {
|
|
114
|
+
const key = element.getAttribute("data-resource") ?? "";
|
|
115
|
+
if (isDocumentResourceKey(key))
|
|
116
|
+
keys.add(key);
|
|
117
|
+
}
|
|
118
|
+
return [...keys];
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
export function toResourceDataUri(contentType, bytes) {
|
|
122
|
+
if (!RESOURCE_IMAGE_CONTENT_TYPES.has(contentType)) {
|
|
123
|
+
throw new Error(`resource content type "${contentType}" cannot be embedded as an image`);
|
|
124
|
+
}
|
|
125
|
+
return `data:${contentType};base64,${Buffer.from(bytes).toString("base64")}`;
|
|
126
|
+
}
|
|
127
|
+
function rewriteAlignment(element) {
|
|
128
|
+
const align = element.getAttribute("data-align");
|
|
129
|
+
element.removeAttribute("data-align");
|
|
130
|
+
if (isOneOf(DOCUMENT_TEXT_ALIGNMENTS, align))
|
|
131
|
+
element.setAttribute("style", `text-align:${align}`);
|
|
132
|
+
}
|
|
133
|
+
function rewriteResourceImage(element, resourceSrc, maxWidthPx) {
|
|
134
|
+
const key = element.getAttribute("data-resource") ?? "";
|
|
135
|
+
const src = isDocumentResourceKey(key) ? resourceSrc?.(key) : undefined;
|
|
136
|
+
if (src === undefined) {
|
|
137
|
+
element.remove();
|
|
138
|
+
// skip: an unresolved resource key drops the image
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
element.setAttribute("src", src);
|
|
142
|
+
element.removeAttribute("data-resource");
|
|
143
|
+
if (maxWidthPx !== undefined)
|
|
144
|
+
element.setAttribute("style", `max-width:${maxWidthPx}px;height:auto`);
|
|
145
|
+
}
|
|
146
|
+
function stripScriptsAndActiveContent(root, options) {
|
|
147
|
+
for (const element of Array.from(root.querySelectorAll("script")))
|
|
148
|
+
element.remove();
|
|
149
|
+
for (const element of Array.from(root.querySelectorAll("img[data-resource]"))) {
|
|
150
|
+
rewriteResourceImage(element, options.resourceSrc, options.resourceImageMaxWidthPx);
|
|
151
|
+
}
|
|
152
|
+
for (const element of Array.from(root.querySelectorAll("*")))
|
|
153
|
+
stripActiveContent(element);
|
|
154
|
+
}
|
|
155
|
+
function applyAlignmentInlineStyles(root) {
|
|
156
|
+
for (const element of Array.from(root.querySelectorAll("[data-align]"))) {
|
|
157
|
+
rewriteAlignment(element);
|
|
158
|
+
}
|
|
159
|
+
for (const element of Array.from(root.querySelectorAll("[data-size]"))) {
|
|
160
|
+
element.removeAttribute("data-size");
|
|
161
|
+
appendStyle(element, MAIL_SMALL_STYLE);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function applyTableInlineStyles(root) {
|
|
165
|
+
for (const table of Array.from(root.querySelectorAll("table"))) {
|
|
166
|
+
appendStyle(table, MAIL_TABLE_STYLE);
|
|
167
|
+
}
|
|
168
|
+
for (const cell of Array.from(root.querySelectorAll("td, th"))) {
|
|
169
|
+
appendStyle(cell, MAIL_TABLE_CELL_STYLE);
|
|
170
|
+
}
|
|
171
|
+
for (const head of Array.from(root.querySelectorAll("th"))) {
|
|
172
|
+
appendStyle(head, MAIL_TABLE_HEAD_STYLE);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function rewriteLinks(root, state, options) {
|
|
176
|
+
for (const element of Array.from(root.querySelectorAll("a[href]"))) {
|
|
177
|
+
rewriteLink(element, state);
|
|
178
|
+
if (options.buttonAsInlineStyle === true && element.hasAttribute("data-button")) {
|
|
179
|
+
element.setAttribute("style", options.buttonInlineStyle ?? MAIL_BUTTON_STYLE);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Turns a sanitized document body into Handlebars source. Rows and links are
|
|
184
|
+
// rewritten in document order; list rows last so their placeholders still see
|
|
185
|
+
// the `tr[data-each]` ancestor.
|
|
186
|
+
export function compileDocumentBodyToHandlebars(html, variables, options = {}) {
|
|
187
|
+
const state = { variables, expressions: [] };
|
|
188
|
+
const scrubbed = html.replaceAll(EXPRESSION_OPEN, "").replaceAll(EXPRESSION_CLOSE, "");
|
|
189
|
+
const compiled = withParsedDocument(scrubbed, (root) => {
|
|
190
|
+
stripScriptsAndActiveContent(root, options);
|
|
191
|
+
if (options.alignmentAsInlineStyle === true)
|
|
192
|
+
applyAlignmentInlineStyles(root);
|
|
193
|
+
if (options.tableAsInlineStyle === true)
|
|
194
|
+
applyTableInlineStyles(root);
|
|
195
|
+
for (const element of Array.from(root.querySelectorAll("span[data-variable]"))) {
|
|
196
|
+
rewriteVariable(element, state);
|
|
197
|
+
}
|
|
198
|
+
rewriteLinks(root, state, options);
|
|
199
|
+
for (const element of Array.from(root.querySelectorAll("tr[data-each]"))) {
|
|
200
|
+
rewriteRepeatRow(element, state);
|
|
201
|
+
}
|
|
202
|
+
return root.innerHTML;
|
|
203
|
+
});
|
|
204
|
+
return neutralizeBraces(compiled).replace(MARKER_PATTERN, (_match, index) => state.expressions[Number(index)] ?? "");
|
|
205
|
+
}
|