@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
|
@@ -57,6 +57,9 @@ export declare function assertBelowHardCap(valueSeenByLastUnit: number, cap: {
|
|
|
57
57
|
readonly capName: string;
|
|
58
58
|
readonly limit: number;
|
|
59
59
|
readonly profile: CapToleranceProfileName;
|
|
60
|
+
readonly periodStartIso?: string;
|
|
61
|
+
readonly periodEndIso?: string;
|
|
62
|
+
readonly i18nKey?: string;
|
|
60
63
|
}): void;
|
|
61
64
|
/**
|
|
62
65
|
* Synchronous read-and-check of the calling tenant's counter for
|
|
@@ -85,6 +88,8 @@ export declare function enforceCap(ctx: CapCounterContext, options: {
|
|
|
85
88
|
readonly limit: number;
|
|
86
89
|
readonly profile: CapToleranceProfileName;
|
|
87
90
|
readonly amount?: number;
|
|
91
|
+
readonly periodEndIso?: string;
|
|
92
|
+
readonly i18nKey?: string;
|
|
88
93
|
}): Promise<EnforceCapResult>;
|
|
89
94
|
/**
|
|
90
95
|
* Synchronous read-and-check of the calling tenant's Rolling-Window-
|
|
@@ -120,6 +125,9 @@ export declare function enforceRollingCap(ctx: CapCounterContext, options: {
|
|
|
120
125
|
* Thrown by enforceCap/enforceRollingCap when value ≥ hard-threshold.
|
|
121
126
|
* Extends `KumikoError` — the dispatcher auto-maps this to HTTP 429
|
|
122
127
|
* with `code: "cap_exceeded"`, no caller-side catch needed.
|
|
128
|
+
*
|
|
129
|
+
* Calendar caps carry `resetAt` (end of the period) and `retryAfterSeconds`, which the API
|
|
130
|
+
* answers as Retry-After. Rolling caps have no fixed reset instant, so both stay undefined.
|
|
123
131
|
*/
|
|
124
132
|
export declare class CapExceededError extends KumikoError {
|
|
125
133
|
readonly capName: string;
|
|
@@ -128,7 +136,13 @@ export declare class CapExceededError extends KumikoError {
|
|
|
128
136
|
readonly tolerance: CapToleranceProfile;
|
|
129
137
|
readonly code: "cap_exceeded";
|
|
130
138
|
readonly httpStatus = 429;
|
|
131
|
-
|
|
139
|
+
readonly resetAt: string | undefined;
|
|
140
|
+
constructor(capName: string, limit: number, currentValue: number, tolerance: CapToleranceProfile, options?: {
|
|
141
|
+
readonly resetAt?: string;
|
|
142
|
+
readonly i18nKey?: string;
|
|
143
|
+
readonly cause?: Error;
|
|
144
|
+
readonly now?: Temporal.Instant;
|
|
145
|
+
});
|
|
132
146
|
}
|
|
133
147
|
/**
|
|
134
148
|
* Calendar-month period start in UTC. Use this for monthly caps
|
|
@@ -137,6 +151,7 @@ export declare class CapExceededError extends KumikoError {
|
|
|
137
151
|
* Returns ISO string for the 1st of the current month at 00:00 UTC.
|
|
138
152
|
*/
|
|
139
153
|
export declare function currentCalendarMonthStartIso(now?: Temporal.Instant): string;
|
|
154
|
+
export declare function calendarMonthPeriodEndIso(periodStartIso: string): string;
|
|
140
155
|
/**
|
|
141
156
|
* Soft-hit-notifier callback. Caller liefert die Funktion die ein
|
|
142
157
|
* delivery-event emittet (z.B. `delivery.send({to, template, payload})`).
|
|
@@ -169,6 +184,8 @@ export declare function enforceCapAndMaybeNotify(ctx: CapCounterContext, options
|
|
|
169
184
|
readonly profile: CapToleranceProfileName;
|
|
170
185
|
readonly notify: SoftHitNotifier;
|
|
171
186
|
readonly amount?: number;
|
|
187
|
+
readonly periodEndIso?: string;
|
|
188
|
+
readonly i18nKey?: string;
|
|
172
189
|
readonly markSoftWarnedOutsideTransaction?: boolean;
|
|
173
190
|
}): Promise<EnforceCapResult>;
|
|
174
191
|
/**
|
|
@@ -17,8 +17,24 @@ export const CAP_TOLERANCES = {
|
|
|
17
17
|
export function assertBelowHardCap(valueSeenByLastUnit, cap) {
|
|
18
18
|
const tolerance = CAP_TOLERANCES[cap.profile];
|
|
19
19
|
if (valueSeenByLastUnit >= cap.limit * tolerance.hard) {
|
|
20
|
-
|
|
20
|
+
const resetAt = resolvePeriodEndIso(cap.periodStartIso, cap.periodEndIso);
|
|
21
|
+
throw new CapExceededError(cap.capName, cap.limit, valueSeenByLastUnit, tolerance, {
|
|
22
|
+
...(resetAt !== undefined && { resetAt }),
|
|
23
|
+
...(cap.i18nKey !== undefined && { i18nKey: cap.i18nKey }),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function resolvePeriodEndIso(periodStartIso, periodEndIso) {
|
|
28
|
+
if (periodStartIso === undefined)
|
|
29
|
+
return periodEndIso;
|
|
30
|
+
if (periodEndIso === undefined)
|
|
31
|
+
return calendarMonthPeriodEndIso(periodStartIso);
|
|
32
|
+
const start = Temporal.Instant.from(periodStartIso);
|
|
33
|
+
const end = Temporal.Instant.from(periodEndIso);
|
|
34
|
+
if (Temporal.Instant.compare(end, start) <= 0) {
|
|
35
|
+
throw new Error(`cap-counter: periodEndIso (${periodEndIso}) must be after periodStartIso (${periodStartIso})`);
|
|
21
36
|
}
|
|
37
|
+
return end.toString();
|
|
22
38
|
}
|
|
23
39
|
// =============================================================================
|
|
24
40
|
// Enforce-Cap helper
|
|
@@ -115,6 +131,9 @@ export async function enforceRollingCap(ctx, options) {
|
|
|
115
131
|
* Thrown by enforceCap/enforceRollingCap when value ≥ hard-threshold.
|
|
116
132
|
* Extends `KumikoError` — the dispatcher auto-maps this to HTTP 429
|
|
117
133
|
* with `code: "cap_exceeded"`, no caller-side catch needed.
|
|
134
|
+
*
|
|
135
|
+
* Calendar caps carry `resetAt` (end of the period) and `retryAfterSeconds`, which the API
|
|
136
|
+
* answers as Retry-After. Rolling caps have no fixed reset instant, so both stay undefined.
|
|
118
137
|
*/
|
|
119
138
|
export class CapExceededError extends KumikoError {
|
|
120
139
|
capName;
|
|
@@ -123,17 +142,32 @@ export class CapExceededError extends KumikoError {
|
|
|
123
142
|
tolerance;
|
|
124
143
|
code = "cap_exceeded";
|
|
125
144
|
httpStatus = 429;
|
|
126
|
-
|
|
145
|
+
resetAt;
|
|
146
|
+
constructor(capName, limit, currentValue, tolerance, options) {
|
|
147
|
+
const resetAt = options?.resetAt;
|
|
148
|
+
const retryAfterSeconds = resetAt === undefined
|
|
149
|
+
? undefined
|
|
150
|
+
: Math.max(0, Math.ceil(Temporal.Instant.from(resetAt).epochMilliseconds / 1000 -
|
|
151
|
+
(options?.now ?? Temporal.Now.instant()).epochMilliseconds / 1000));
|
|
127
152
|
super({
|
|
128
|
-
message: `Cap "${capName}" exceeded: current=${currentValue}, limit=${limit}, hard-threshold=${limit * tolerance.hard}. Upgrade tier or wait for next period reset
|
|
129
|
-
i18nKey: "errors.cap.exceeded",
|
|
130
|
-
i18nParams: { capName, limit, currentValue },
|
|
131
|
-
details: {
|
|
153
|
+
message: `Cap "${capName}" exceeded: current=${currentValue}, limit=${limit}, hard-threshold=${limit * tolerance.hard}. Upgrade tier or wait for next period reset.${resetAt === undefined ? "" : ` Resets at ${resetAt}.`}`,
|
|
154
|
+
i18nKey: options?.i18nKey ?? "errors.cap.exceeded",
|
|
155
|
+
i18nParams: { capName, limit, currentValue, ...(resetAt !== undefined && { resetAt }) },
|
|
156
|
+
details: {
|
|
157
|
+
capName,
|
|
158
|
+
limit,
|
|
159
|
+
currentValue,
|
|
160
|
+
tolerance,
|
|
161
|
+
...(resetAt !== undefined && { resetAt, retryAfterSeconds }),
|
|
162
|
+
},
|
|
163
|
+
...(retryAfterSeconds !== undefined && { retryAfterSeconds }),
|
|
164
|
+
...(options?.cause && { cause: options.cause }),
|
|
132
165
|
});
|
|
133
166
|
this.capName = capName;
|
|
134
167
|
this.limit = limit;
|
|
135
168
|
this.currentValue = currentValue;
|
|
136
169
|
this.tolerance = tolerance;
|
|
170
|
+
this.resetAt = resetAt;
|
|
137
171
|
}
|
|
138
172
|
}
|
|
139
173
|
// =============================================================================
|
|
@@ -158,6 +192,13 @@ export function currentCalendarMonthStartIso(now = Temporal.Now.instant()) {
|
|
|
158
192
|
});
|
|
159
193
|
return start.toInstant().toString();
|
|
160
194
|
}
|
|
195
|
+
export function calendarMonthPeriodEndIso(periodStartIso) {
|
|
196
|
+
return Temporal.Instant.from(periodStartIso)
|
|
197
|
+
.toZonedDateTimeISO("UTC")
|
|
198
|
+
.add({ months: 1 })
|
|
199
|
+
.toInstant()
|
|
200
|
+
.toString();
|
|
201
|
+
}
|
|
161
202
|
/**
|
|
162
203
|
* Calendar-Period-enforcement + automatische soft-hit-Notification.
|
|
163
204
|
* Ruft `enforceCap`, bei `crossed: true` den Notifier UND
|
|
@@ -175,6 +216,8 @@ export async function enforceCapAndMaybeNotify(ctx, options) {
|
|
|
175
216
|
limit: options.limit,
|
|
176
217
|
profile: options.profile,
|
|
177
218
|
...(options.amount !== undefined && { amount: options.amount }),
|
|
219
|
+
...(options.periodEndIso !== undefined && { periodEndIso: options.periodEndIso }),
|
|
220
|
+
...(options.i18nKey !== undefined && { i18nKey: options.i18nKey }),
|
|
178
221
|
});
|
|
179
222
|
if (result.state === "soft-hit" && result.crossed) {
|
|
180
223
|
await options.notify({
|
|
@@ -2,7 +2,7 @@ export { capCounterAggregateId, rollingCapAggregateId } from "./aggregate-id.js"
|
|
|
2
2
|
export { type BookCapUsageOptions, bookCapUsage, type MarkCapSoftWarnedOptions, markCapSoftWarned, type ReadRollingCapUsageOptions, readRollingCapUsage, } from "./book-cap-usage.js";
|
|
3
3
|
export { type CalendarCapUsageReservation, type CalendarCapUsageReservationSpec, reserveCalendarCapUsage, } from "./calendar-cap-usage-reservation.js";
|
|
4
4
|
export { CAP_COUNTER_FEATURE, CAP_COUNTER_ROLLING_AGGREGATE_TYPE, CapCounterHandlers, CapCounterQueries, ROLLING_INCREMENTED_EVENT_QN, ROLLING_INCREMENTED_EVENT_SHORT, ROLLING_RELEASED_EVENT_QN, ROLLING_RELEASED_EVENT_SHORT, } from "./constants.js";
|
|
5
|
-
export { CAP_TOLERANCES, CapExceededError, type CapToleranceProfile, type CapToleranceProfileName, currentCalendarMonthStartIso, type EnforceCapResult, enforceCap, enforceCapAndMaybeNotify, enforceRollingCap, enforceRollingCapAndMaybeNotify, enforceStockCap, type SoftHitNotifier, type StockCapResult, } from "./enforce-cap.js";
|
|
5
|
+
export { CAP_TOLERANCES, CapExceededError, type CapToleranceProfile, type CapToleranceProfileName, calendarMonthPeriodEndIso, currentCalendarMonthStartIso, type EnforceCapResult, enforceCap, enforceCapAndMaybeNotify, enforceRollingCap, enforceRollingCapAndMaybeNotify, enforceStockCap, type SoftHitNotifier, type StockCapResult, } from "./enforce-cap.js";
|
|
6
6
|
export { capCounterEntity } from "./entity.js";
|
|
7
7
|
export { capCounterFeature } from "./feature.js";
|
|
8
8
|
export { readPayloadField } from "./payload-field.js";
|
|
@@ -3,7 +3,7 @@ export { capCounterAggregateId, rollingCapAggregateId } from "./aggregate-id.js"
|
|
|
3
3
|
export { bookCapUsage, markCapSoftWarned, readRollingCapUsage, } from "./book-cap-usage.js";
|
|
4
4
|
export { reserveCalendarCapUsage, } from "./calendar-cap-usage-reservation.js";
|
|
5
5
|
export { CAP_COUNTER_FEATURE, CAP_COUNTER_ROLLING_AGGREGATE_TYPE, CapCounterHandlers, CapCounterQueries, ROLLING_INCREMENTED_EVENT_QN, ROLLING_INCREMENTED_EVENT_SHORT, ROLLING_RELEASED_EVENT_QN, ROLLING_RELEASED_EVENT_SHORT, } from "./constants.js";
|
|
6
|
-
export { CAP_TOLERANCES, CapExceededError, currentCalendarMonthStartIso, enforceCap, enforceCapAndMaybeNotify, enforceRollingCap, enforceRollingCapAndMaybeNotify, enforceStockCap, } from "./enforce-cap.js";
|
|
6
|
+
export { CAP_TOLERANCES, CapExceededError, calendarMonthPeriodEndIso, currentCalendarMonthStartIso, enforceCap, enforceCapAndMaybeNotify, enforceRollingCap, enforceRollingCapAndMaybeNotify, enforceStockCap, } from "./enforce-cap.js";
|
|
7
7
|
export { capCounterEntity } from "./entity.js";
|
|
8
8
|
export { capCounterFeature } from "./feature.js";
|
|
9
9
|
export { readPayloadField } from "./payload-field.js";
|
|
@@ -10,6 +10,8 @@ import { type CapToleranceProfileName, type SoftHitNotifier } from "./enforce-ca
|
|
|
10
10
|
export type CalendarCapDef = {
|
|
11
11
|
readonly capName: string;
|
|
12
12
|
readonly periodStartIso: string;
|
|
13
|
+
readonly periodEndIso?: string;
|
|
14
|
+
readonly i18nKey?: string;
|
|
13
15
|
readonly limit: number;
|
|
14
16
|
readonly profile: CapToleranceProfileName;
|
|
15
17
|
/** Increment-amount post-success. Default 1. */
|
|
@@ -2,6 +2,7 @@ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
2
2
|
import { buildEntityTable, decodeCursor, encodeCursor } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import { definePagedQueryHandler, MAX_LIST_LIMIT, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import { InternalError, ValidationError } from "@cosmicdrift/kumiko-framework/errors";
|
|
5
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
5
6
|
import * as z from "zod";
|
|
6
7
|
import { subscriptionsProjectionTable } from "../../billing-foundation/index.js";
|
|
7
8
|
import { decryptTenantNames, tenantTable } from "../../tenant/index.js";
|
|
@@ -34,7 +35,7 @@ async function resolveLimitsByTenantAndCap(caps, tenants, configFor) {
|
|
|
34
35
|
return new Map(entries);
|
|
35
36
|
}
|
|
36
37
|
function isSortableField(field) {
|
|
37
|
-
return SORTABLE_FIELDS
|
|
38
|
+
return isOneOf(SORTABLE_FIELDS, field);
|
|
38
39
|
}
|
|
39
40
|
// tier is a plain string with no defined ordering — lt/gt have no meaning
|
|
40
41
|
// on it and are rejected rather than silently treated as "no filter".
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
3
|
import { CapUsageBar } from "./cap-usage-bar.js";
|
|
3
4
|
function isCapUsage(value) {
|
|
4
|
-
if (
|
|
5
|
+
if (!isPlainObject(value))
|
|
5
6
|
return false;
|
|
6
|
-
const used = value
|
|
7
|
-
const limit = value
|
|
7
|
+
const used = value["used"];
|
|
8
|
+
const limit = value["limit"];
|
|
8
9
|
return ((typeof used === "number" || used === null) &&
|
|
9
10
|
(typeof limit === "number" || limit === null) &&
|
|
10
|
-
typeof value
|
|
11
|
+
typeof value["fraction"] === "number");
|
|
11
12
|
}
|
|
12
13
|
export function CapUsageCell({ value }) {
|
|
13
14
|
if (!isCapUsage(value))
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
-
import { type DeliveryChannel, type
|
|
2
|
+
import { type DeliveryChannel, type EmailAttachmentRef } from "../delivery/index.js";
|
|
3
3
|
import type { EmailTransport } from "./types.js";
|
|
4
|
+
export declare function fileAttachmentsFrom(data: Readonly<Record<string, unknown>> | undefined, messageLocale?: string): readonly EmailAttachmentRef[];
|
|
4
5
|
export type EmailChannelOptions = {
|
|
5
6
|
readonly transport: EmailTransport;
|
|
6
|
-
readonly renderer: NotificationRenderer;
|
|
7
7
|
readonly resolveEmail: (userId: string, ctx: {
|
|
8
8
|
db: unknown;
|
|
9
9
|
tenantId: TenantId;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PII_ERASED_SENTINEL } from "@cosmicdrift/kumiko-framework/crypto";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { DELIVERY_UNSUBSCRIBE_ONE_CLICK_HEADER_VALUE, DELIVERY_UNSUBSCRIBE_PATH, EMAIL_CHANNEL_NAME, fileAttachmentRefSchema, MAX_EMAIL_ATTACHMENTS, renderedPdfAttachmentRefSchema, } from "../delivery/index.js";
|
|
3
4
|
import { decryptStoredPii } from "../shared/index.js";
|
|
4
5
|
import { guardEmailMessage } from "./pii-guard.js";
|
|
5
6
|
// Envelope (From / Reply-To / threading headers) rides on the channel data —
|
|
@@ -65,31 +66,95 @@ function emailEnvelopeFrom(data) {
|
|
|
65
66
|
...(headers && { headers }),
|
|
66
67
|
};
|
|
67
68
|
}
|
|
69
|
+
const fileAttachmentsSchema = z
|
|
70
|
+
.array(z.discriminatedUnion("kind", [fileAttachmentRefSchema, renderedPdfAttachmentRefSchema]))
|
|
71
|
+
.max(MAX_EMAIL_ATTACHMENTS);
|
|
72
|
+
// Only kinds "file" and "rendered-pdf" may come from notify data; inline images are created by the renderer alone.
|
|
73
|
+
// Invalid input throws instead of dropping the attachment silently.
|
|
74
|
+
export function fileAttachmentsFrom(data, messageLocale) {
|
|
75
|
+
const raw = data?.["attachments"];
|
|
76
|
+
if (raw === undefined)
|
|
77
|
+
return [];
|
|
78
|
+
return fileAttachmentsSchema
|
|
79
|
+
.parse(raw)
|
|
80
|
+
.map((ref) => ref.kind === "rendered-pdf" && ref.locale === undefined && messageLocale !== undefined
|
|
81
|
+
? { ...ref, locale: messageLocale }
|
|
82
|
+
: ref);
|
|
83
|
+
}
|
|
84
|
+
const INLINE_EXTENSION_BY_CONTENT_TYPE = {
|
|
85
|
+
"image/png": "png",
|
|
86
|
+
"image/jpeg": "jpg",
|
|
87
|
+
"image/gif": "gif",
|
|
88
|
+
"image/webp": "webp",
|
|
89
|
+
};
|
|
90
|
+
async function resolveAttachments(sources, ctx) {
|
|
91
|
+
if (sources.length === 0)
|
|
92
|
+
return [];
|
|
93
|
+
if (ctx.readAttachment === undefined) {
|
|
94
|
+
throw new Error("email channel: the message has attachments but the channel context cannot read files");
|
|
95
|
+
}
|
|
96
|
+
const resolved = [];
|
|
97
|
+
for (const { ref, origin } of sources) {
|
|
98
|
+
const found = await ctx.readAttachment(ref, origin);
|
|
99
|
+
if (ref.kind === "rendered-pdf") {
|
|
100
|
+
if (found === null) {
|
|
101
|
+
throw new Error(`email channel: rendering attachment "${ref.fileName}" returned nothing`);
|
|
102
|
+
}
|
|
103
|
+
resolved.push({
|
|
104
|
+
kind: "rendered-pdf",
|
|
105
|
+
templateSlug: ref.templateSlug,
|
|
106
|
+
fileName: ref.fileName,
|
|
107
|
+
content: found.content,
|
|
108
|
+
contentType: found.contentType,
|
|
109
|
+
});
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (found === null) {
|
|
113
|
+
// A missing inline image only degrades the look; a missing file is content the recipient expects.
|
|
114
|
+
if (ref.kind === "inline")
|
|
115
|
+
continue;
|
|
116
|
+
throw new Error(`email channel: attachment file ${ref.fileRefId} was not found`);
|
|
117
|
+
}
|
|
118
|
+
const fileName = ref.kind === "file"
|
|
119
|
+
? ref.fileName
|
|
120
|
+
: INLINE_EXTENSION_BY_CONTENT_TYPE[found.contentType]
|
|
121
|
+
? `${ref.cid}.${INLINE_EXTENSION_BY_CONTENT_TYPE[found.contentType]}`
|
|
122
|
+
: ref.cid;
|
|
123
|
+
resolved.push({ ...ref, fileName, content: found.content, contentType: found.contentType });
|
|
124
|
+
}
|
|
125
|
+
return resolved;
|
|
126
|
+
}
|
|
68
127
|
export function createEmailChannel(options) {
|
|
69
|
-
const { transport,
|
|
70
|
-
// Render
|
|
71
|
-
//
|
|
72
|
-
// can retry independently. Extracted so the inline fallback (no job runner)
|
|
128
|
+
const { transport, resolveEmail } = options;
|
|
129
|
+
// Render runs in the delivery.render job, decoupled from the SMTP send so
|
|
130
|
+
// each can retry independently. Extracted so the inline path (no job runner)
|
|
73
131
|
// can reuse it without going through the channel's own render() indirection.
|
|
74
|
-
async function renderMessage(message) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
132
|
+
async function renderMessage(message, ctx) {
|
|
133
|
+
const notificationType = message.notificationType;
|
|
134
|
+
if (ctx.registry.getAllNotifications().get(notificationType)?.template === undefined) {
|
|
135
|
+
throw new Error(`email channel: notification "${notificationType}" has no mail template, declare template + meta on r.notification`);
|
|
136
|
+
}
|
|
137
|
+
if (ctx.renderMail === undefined) {
|
|
138
|
+
throw new Error(`email channel: notification "${notificationType}" has a mail template but the channel context carries no renderMail`);
|
|
139
|
+
}
|
|
140
|
+
const unsubscribeUrl = message.data?.["unsubscribeUrl"];
|
|
141
|
+
const { subject, html, text, attachments, fromName, replyTo } = await ctx.renderMail({
|
|
142
|
+
slug: notificationType,
|
|
143
|
+
locale: message.locale ?? "en",
|
|
144
|
+
variables: message.data ?? {},
|
|
145
|
+
...(typeof unsubscribeUrl === "string" && { unsubscribeUrl }),
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
html,
|
|
149
|
+
subject,
|
|
150
|
+
text,
|
|
151
|
+
...(fromName !== undefined && { fromName }),
|
|
152
|
+
...(replyTo !== undefined && { replyTo }),
|
|
153
|
+
...(attachments !== undefined && { attachments }),
|
|
85
154
|
};
|
|
86
|
-
const html = await renderer.render(rendererInput);
|
|
87
|
-
const text = renderer.renderText ? await renderer.renderText(rendererInput) : undefined;
|
|
88
|
-
const subject = variables["subject"] ?? message.title; // @cast-boundary dynamic-key
|
|
89
|
-
return { html, subject, ...(text !== undefined && { text }) };
|
|
90
155
|
}
|
|
91
156
|
return {
|
|
92
|
-
name:
|
|
157
|
+
name: EMAIL_CHANNEL_NAME,
|
|
93
158
|
mode: "queued",
|
|
94
159
|
// resolveEmail typically returns the raw stored user.email, which is ciphertext under an active KMS.
|
|
95
160
|
async resolve(userId, ctx) {
|
|
@@ -99,18 +164,28 @@ export function createEmailChannel(options) {
|
|
|
99
164
|
const address = await decryptStoredPii(stored, "email", "channel-email:resolve");
|
|
100
165
|
return address === PII_ERASED_SENTINEL ? null : address;
|
|
101
166
|
},
|
|
102
|
-
render(message,
|
|
103
|
-
return renderMessage(message);
|
|
167
|
+
render(message, ctx) {
|
|
168
|
+
return renderMessage(message, ctx);
|
|
104
169
|
},
|
|
105
|
-
async send(address, message,
|
|
106
|
-
const { html, subject, text } = rendered ?? (await renderMessage(message));
|
|
107
|
-
|
|
170
|
+
async send(address, message, ctx, rendered) {
|
|
171
|
+
const { html, subject, text, attachments, fromName, replyTo } = rendered ?? (await renderMessage(message, ctx));
|
|
172
|
+
// Template-level sender defaults; notify data wins.
|
|
173
|
+
const envelope = {
|
|
174
|
+
...(fromName && { fromName }),
|
|
175
|
+
...(replyTo && { replyTo }),
|
|
176
|
+
...emailEnvelopeFrom(message.data),
|
|
177
|
+
};
|
|
178
|
+
const resolvedAttachments = await resolveAttachments([
|
|
179
|
+
...(attachments ?? []).map((ref) => ({ ref, origin: "template" })),
|
|
180
|
+
...fileAttachmentsFrom(message.data, message.locale).map((ref) => ({ ref, origin: "message" })),
|
|
181
|
+
], ctx);
|
|
108
182
|
await transport.send(guardEmailMessage({
|
|
109
183
|
to: address,
|
|
110
184
|
subject,
|
|
111
185
|
html,
|
|
112
186
|
...(text !== undefined && { text }),
|
|
113
187
|
...envelope,
|
|
188
|
+
...(resolvedAttachments.length > 0 && { attachments: resolvedAttachments }),
|
|
114
189
|
}));
|
|
115
190
|
return { status: "sent", address };
|
|
116
191
|
},
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { DELIVERY_CHANNEL_EXTENSION } from "@cosmicdrift/kumiko-bundled-features/delivery";
|
|
1
|
+
import { DELIVERY_CHANNEL_EXTENSION, EMAIL_CHANNEL_NAME, } from "@cosmicdrift/kumiko-bundled-features/delivery";
|
|
2
2
|
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { createEmailChannel } from "./email-channel.js";
|
|
4
4
|
export function createChannelEmailFeature(options) {
|
|
5
5
|
const channel = createEmailChannel(options);
|
|
6
6
|
return defineFeature("channel-email", (r) => {
|
|
7
|
-
r.describe("Wires an `EmailTransport` (typically `mail-transport-smtp` in production, `createInMemoryTransport()` in tests) into the delivery system as the `email` channel. Requires `delivery`; pass an `EmailChannelOptions` with a `transport
|
|
7
|
+
r.describe("Wires an `EmailTransport` (typically `mail-transport-smtp` in production, `createInMemoryTransport()` in tests) into the delivery system as the `email` channel. Requires `delivery`; pass an `EmailChannelOptions` with a `transport` and a `resolveEmail` function that maps a user ID to their email address. Mails render from stored templates (`renderer-mail-html`): a notification routed to this channel needs `template` + `meta` on `r.notification`, otherwise its delivery fails.");
|
|
8
8
|
r.uiHints({
|
|
9
9
|
displayLabel: "Email Channel",
|
|
10
10
|
category: "notifications",
|
|
@@ -12,7 +12,7 @@ export function createChannelEmailFeature(options) {
|
|
|
12
12
|
});
|
|
13
13
|
r.requires("delivery");
|
|
14
14
|
r.translations({ keys: { "delivery.channel.email": { en: "Email" } } });
|
|
15
|
-
r.useExtension(DELIVERY_CHANNEL_EXTENSION,
|
|
15
|
+
r.useExtension(DELIVERY_CHANNEL_EXTENSION, EMAIL_CHANNEL_NAME, {
|
|
16
16
|
mode: channel.mode,
|
|
17
17
|
resolve: channel.resolve,
|
|
18
18
|
render: channel.render,
|
|
@@ -2,4 +2,4 @@ export { createEmailChannel, type EmailChannelOptions } from "./email-channel.js
|
|
|
2
2
|
export { createChannelEmailFeature } from "./feature.js";
|
|
3
3
|
export { guardEmailMessage, withPiiCiphertextGuard } from "./pii-guard.js";
|
|
4
4
|
export { createSmtpTransport, createSmtpTransportFromEnv, type SmtpEnv, type SmtpTransportOptions, } from "./smtp-transport.js";
|
|
5
|
-
export { createInMemoryTransport, type EmailMessage, type EmailTransport } from "./types.js";
|
|
5
|
+
export { createInMemoryTransport, type EmailAttachment, type EmailAttachmentRef, type EmailMessage, type EmailTransport, } from "./types.js";
|
|
@@ -2,4 +2,4 @@ export { createEmailChannel } from "./email-channel.js";
|
|
|
2
2
|
export { createChannelEmailFeature } from "./feature.js";
|
|
3
3
|
export { guardEmailMessage, withPiiCiphertextGuard } from "./pii-guard.js";
|
|
4
4
|
export { createSmtpTransport, createSmtpTransportFromEnv, } from "./smtp-transport.js";
|
|
5
|
-
export { createInMemoryTransport } from "./types.js";
|
|
5
|
+
export { createInMemoryTransport, } from "./types.js";
|
|
@@ -8,6 +8,15 @@ const CIPHERTEXT_RE = /kumiko-pii:v\d+:[^"\s<>\\]*/g;
|
|
|
8
8
|
// is always refused (the address is garbage — better no mail than a leaked
|
|
9
9
|
// blob); ciphertext in subject/body fails loud in dev and is redacted+logged
|
|
10
10
|
// in prod.
|
|
11
|
+
function assertAttachmentNamesArePlain(message) {
|
|
12
|
+
for (const attachment of message.attachments ?? []) {
|
|
13
|
+
const fields = [attachment.fileName, attachment.kind === "inline" ? attachment.cid : undefined];
|
|
14
|
+
if (fields.some((field) => field?.includes(CIPHERTEXT_MARKER))) {
|
|
15
|
+
throw new Error(`[channel-email] refusing to send: attachment file name is a PII ciphertext ` +
|
|
16
|
+
`("${PII_CIPHERTEXT_PREFIX}…") — decrypt the stored value before mailing (decryptStoredPii).`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
11
20
|
export function guardEmailMessage(message) {
|
|
12
21
|
// Every envelope address, not just `to`: a ciphertext From, From-Name or Reply-To is
|
|
13
22
|
// the same garbage as a ciphertext recipient — refuse rather than mail it.
|
|
@@ -28,6 +37,7 @@ export function guardEmailMessage(message) {
|
|
|
28
37
|
`("${PII_CIPHERTEXT_PREFIX}…") — decrypt the stored value before mailing (decryptStoredPii).`);
|
|
29
38
|
}
|
|
30
39
|
}
|
|
40
|
+
assertAttachmentNamesArePlain(message);
|
|
31
41
|
const leaking = message.subject.includes(CIPHERTEXT_MARKER) ||
|
|
32
42
|
message.html.includes(CIPHERTEXT_MARKER) ||
|
|
33
43
|
message.text?.includes(CIPHERTEXT_MARKER) === true;
|
|
@@ -37,6 +37,16 @@ function fromHeader(address, fromName) {
|
|
|
37
37
|
const [parsed] = addressparser(address, { flatten: true });
|
|
38
38
|
return parsed?.address ? { name, address: parsed.address } : address;
|
|
39
39
|
}
|
|
40
|
+
function toNodemailerAttachment(attachment) {
|
|
41
|
+
const base = {
|
|
42
|
+
filename: attachment.fileName,
|
|
43
|
+
content: Buffer.from(attachment.content),
|
|
44
|
+
contentType: attachment.contentType,
|
|
45
|
+
};
|
|
46
|
+
return attachment.kind === "inline"
|
|
47
|
+
? { ...base, cid: attachment.cid, contentDisposition: "inline" }
|
|
48
|
+
: base;
|
|
49
|
+
}
|
|
40
50
|
export function createSmtpTransport(options) {
|
|
41
51
|
const transporter = createTransport({
|
|
42
52
|
host: options.host,
|
|
@@ -57,6 +67,9 @@ export function createSmtpTransport(options) {
|
|
|
57
67
|
...(message.text !== undefined && { text: message.text }),
|
|
58
68
|
...(message.replyTo && { replyTo: message.replyTo }),
|
|
59
69
|
...(message.headers && { headers: message.headers }),
|
|
70
|
+
...(message.attachments && {
|
|
71
|
+
attachments: message.attachments.map(toNodemailerAttachment),
|
|
72
|
+
}),
|
|
60
73
|
});
|
|
61
74
|
},
|
|
62
75
|
};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import type { EmailAttachmentRef, FileAttachmentRef, InlineAttachmentRef, RenderedPdfAttachmentRef } from "../delivery/index.js";
|
|
2
|
+
export type { EmailAttachmentRef };
|
|
3
|
+
export type EmailAttachment = (InlineAttachmentRef | FileAttachmentRef | Pick<RenderedPdfAttachmentRef, "kind" | "templateSlug">) & {
|
|
4
|
+
readonly content: Uint8Array;
|
|
5
|
+
readonly contentType: string;
|
|
6
|
+
readonly fileName: string;
|
|
7
|
+
};
|
|
1
8
|
export type EmailMessage = {
|
|
2
9
|
readonly to: string;
|
|
3
10
|
readonly subject: string;
|
|
@@ -7,6 +14,7 @@ export type EmailMessage = {
|
|
|
7
14
|
readonly fromName?: string;
|
|
8
15
|
readonly replyTo?: string;
|
|
9
16
|
readonly headers?: Readonly<Record<string, string>>;
|
|
17
|
+
readonly attachments?: readonly EmailAttachment[];
|
|
10
18
|
};
|
|
11
19
|
export type EmailTransport = {
|
|
12
20
|
send(message: EmailMessage): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
export declare const markAllReadWrite: import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"markAllRead", z.ZodObject<{}, z.core.$strip>, {
|
|
3
3
|
marked: number;
|
|
4
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
4
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -3,4 +3,4 @@ export declare const markReadWrite: import("@cosmicdrift/kumiko-types/define-han
|
|
|
3
3
|
id: z.ZodNumber;
|
|
4
4
|
}, z.core.$strip>, {
|
|
5
5
|
id: number | undefined;
|
|
6
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
6
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -10,4 +10,4 @@ export declare const setProfileWrite: import("@cosmicdrift/kumiko-types/define-h
|
|
|
10
10
|
}, z.core.$strip>, {
|
|
11
11
|
profileKey: "de-hr-dsgvo-hgb" | "eu-dsgvo" | "swiss-dsg";
|
|
12
12
|
isNew: boolean;
|
|
13
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
13
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -9,4 +9,4 @@ export declare const resetWrite: import("@cosmicdrift/kumiko-types/define-handle
|
|
|
9
9
|
}, z.core.$strip>, {
|
|
10
10
|
key: string;
|
|
11
11
|
scope: import("@cosmicdrift/kumiko-types/config-scope").ConfigScope;
|
|
12
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
12
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -11,4 +11,4 @@ export declare const setWrite: import("@cosmicdrift/kumiko-types/define-handler"
|
|
|
11
11
|
key: string;
|
|
12
12
|
value: string | number | boolean;
|
|
13
13
|
scope: import("@cosmicdrift/kumiko-types/config-scope").ConfigScope;
|
|
14
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
14
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { requestContext } from "@cosmicdrift/kumiko-framework/api";
|
|
2
2
|
import { configuredPiiSubjectKms, encryptPiiValueForSubject, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
3
|
-
import { createEventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
+
import { createEventStoreExecutor, scopeTenantDbToTenant } from "@cosmicdrift/kumiko-framework/db";
|
|
4
4
|
import { ConfigScopes, defineWriteHandler, isEncryptedAtRest, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
5
|
import { InternalError, isKumikoError, UnprocessableError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
|
|
6
6
|
import * as z from "zod";
|
|
@@ -74,7 +74,7 @@ export const setWrite = defineWriteHandler({
|
|
|
74
74
|
scope,
|
|
75
75
|
tenantId,
|
|
76
76
|
userId,
|
|
77
|
-
db,
|
|
77
|
+
db: scopeTenantDbToTenant(db, tenantId),
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
catch (error) {
|
|
@@ -120,8 +120,7 @@ export const setWrite = defineWriteHandler({
|
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
// scope="system" was already rejected above — narrow accordingly.
|
|
123
|
-
const subject = resolvePiiSubject(scope,
|
|
124
|
-
tenantId, userId);
|
|
123
|
+
const subject = resolvePiiSubject(scope, tenantId, userId);
|
|
125
124
|
const kmsCtx = {
|
|
126
125
|
requestId: requestContext.get()?.requestId ?? "config:write:set",
|
|
127
126
|
tenantId,
|
|
@@ -7,6 +7,7 @@ export type ConfigResolverOptions = {
|
|
|
7
7
|
cipher?: EnvelopeCipher;
|
|
8
8
|
appOverrides?: AppConfigOverrides;
|
|
9
9
|
};
|
|
10
|
+
export declare function decryptStoredValue(raw: string, keyDef: ConfigKeyDefinition, cipher: EnvelopeCipher | undefined, qualifiedKey: string): Promise<string>;
|
|
10
11
|
export declare function createConfigResolver(options?: ConfigResolverOptions): ConfigResolver;
|
|
11
12
|
export declare function validateAppOverrides(registry: {
|
|
12
13
|
getConfigKey: (key: string) => import("@cosmicdrift/kumiko-framework/engine").ConfigKeyDefinition | undefined;
|
package/dist/config/resolver.js
CHANGED
|
@@ -57,6 +57,15 @@ async function decryptPiiEncrypted(raw, qualifiedKey) {
|
|
|
57
57
|
const kmsCtx = { requestId: requestContext.get()?.requestId ?? "config:resolver" };
|
|
58
58
|
return decryptPiiValueForSubject(kms, raw, kmsCtx, qualifiedKey);
|
|
59
59
|
}
|
|
60
|
+
// Plaintext form of a stored config_values.value for every read path (resolver,
|
|
61
|
+
// bootstrap idempotency check).
|
|
62
|
+
export async function decryptStoredValue(raw, keyDef, cipher, qualifiedKey) {
|
|
63
|
+
if (keyDef.encrypted)
|
|
64
|
+
return decryptEncrypted(cipher, raw, qualifiedKey);
|
|
65
|
+
if (keyDef.piiEncrypted)
|
|
66
|
+
return decryptPiiEncrypted(raw, qualifiedKey);
|
|
67
|
+
return raw;
|
|
68
|
+
}
|
|
60
69
|
// backing="secrets" keys store their value in the secrets store (flat per
|
|
61
70
|
// (tenant,key) at SYSTEM_TENANT_ID), not in config_values. Both read paths
|
|
62
71
|
// (getWithSource + buildCascade) route the system rung here. Missing reader =
|
|
@@ -142,13 +151,7 @@ async function buildCascade(qualifiedKey, keyDef, tenantId, userId, db, fetchRow
|
|
|
142
151
|
}
|
|
143
152
|
const row = await fetchRow(lookup.tenantId, lookup.userId);
|
|
144
153
|
if (row?.value !== null && row?.value !== undefined) {
|
|
145
|
-
|
|
146
|
-
if (keyDef.encrypted) {
|
|
147
|
-
raw = await decryptEncrypted(cipher, raw, qualifiedKey);
|
|
148
|
-
}
|
|
149
|
-
else if (keyDef.piiEncrypted) {
|
|
150
|
-
raw = await decryptPiiEncrypted(raw, qualifiedKey);
|
|
151
|
-
}
|
|
154
|
+
const raw = await decryptStoredValue(row.value, keyDef, cipher, qualifiedKey);
|
|
152
155
|
if (activeIndex === -1)
|
|
153
156
|
activeIndex = levels.length;
|
|
154
157
|
levels.push({
|
|
@@ -296,13 +299,7 @@ export function createConfigResolver(options = {}) {
|
|
|
296
299
|
for (const lookup of lookups) {
|
|
297
300
|
const row = await findRow(qualifiedKey, lookup.tenantId, lookup.userId, db);
|
|
298
301
|
if (row?.value !== null && row?.value !== undefined) {
|
|
299
|
-
|
|
300
|
-
if (keyDef.encrypted) {
|
|
301
|
-
raw = await decryptEncrypted(cipher, raw, qualifiedKey);
|
|
302
|
-
}
|
|
303
|
-
else if (keyDef.piiEncrypted) {
|
|
304
|
-
raw = await decryptPiiEncrypted(raw, qualifiedKey);
|
|
305
|
-
}
|
|
302
|
+
const raw = await decryptStoredValue(row.value, keyDef, cipher, qualifiedKey);
|
|
306
303
|
return { value: deserializeValue(raw, keyDef.type), source: lookup.source };
|
|
307
304
|
}
|
|
308
305
|
}
|