@cosmicdrift/kumiko-bundled-features 0.363.0 → 0.365.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/audit/feature.js +1 -1
- package/dist/audit/handlers/list.query.js +9 -5
- package/dist/audit/run-escape-hatch-retention.d.ts +1 -0
- package/dist/audit/run-escape-hatch-retention.js +39 -12
- package/dist/auth-email-password/bootstrap.d.ts +9 -8
- 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/feature.js +2 -0
- 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-with-login.write.js +2 -3
- package/dist/auth-email-password/handlers/invite-accept.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-create.write.d.ts +4 -5
- package/dist/auth-email-password/handlers/invite-create.write.js +4 -1
- 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-email-verification.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -1
- 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-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 +2 -1
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +1 -1
- package/dist/auth-email-password/handlers/token-request-handler.d.ts +1 -1
- package/dist/auth-email-password/handlers/token-request-handler.js +2 -1
- package/dist/auth-email-password/handlers/verify-email.write.d.ts +1 -1
- package/dist/auth-email-password/magic-link-mail.js +6 -3
- package/dist/auth-email-password/mail-i18n.d.ts +3 -0
- package/dist/auth-email-password/mail-i18n.js +10 -0
- package/dist/auth-email-password/web/auth-gate.js +16 -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/billing-foundation/consumer-protection/confirmation-mail.js +52 -5
- 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/mail-notifications.js +1 -1
- package/dist/billing-foundation/consumer-protection/register-termination.js +11 -1
- package/dist/billing-foundation/consumer-protection/termination-mail.d.ts +5 -3
- package/dist/billing-foundation/consumer-protection/termination-mail.js +1 -2
- 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 +15 -2
- package/dist/billing-foundation/consumer-protection/termination-request.js +30 -20
- 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 +10 -6
- package/dist/billing-foundation/i18n.js +9 -0
- package/dist/billing-foundation/subscription-tier-sync.d.ts +7 -1
- package/dist/billing-foundation/subscription-tier-sync.js +21 -7
- package/dist/billing-foundation/types.d.ts +7 -2
- package/dist/cap-counter/book-cap-usage.js +8 -0
- package/dist/cap-counter/calendar-cap-usage-reservation.js +2 -0
- package/dist/cap-counter/cap-reservation.js +7 -3
- package/dist/cap-counter/enforce-cap.d.ts +18 -1
- package/dist/cap-counter/enforce-cap.js +49 -6
- package/dist/cap-counter/handlers/increment-rolling.write.d.ts +1 -0
- package/dist/cap-counter/handlers/increment-rolling.write.js +2 -0
- 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 +3 -1
- package/dist/cap-overview/handlers/tenant-options.query.js +3 -1
- package/dist/channel-discord/discord-channel.js +3 -2
- package/dist/channel-email/email-channel.js +40 -23
- package/dist/channel-email/smtp-transport.js +6 -1
- 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/channel-slack/slack-channel.js +3 -2
- package/dist/channel-teams/teams-channel.js +3 -2
- 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 +2 -2
- 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 +30 -20
- package/dist/custom-fields/db/queries/retention.d.ts +1 -1
- package/dist/custom-fields/db/queries/retention.js +2 -1
- package/dist/custom-fields/handlers/update-tenant-field.write.js +0 -1
- package/dist/data-retention/run-retention-cleanup.js +4 -5
- package/dist/delivery/attachment-ref.d.ts +2 -0
- package/dist/delivery/attachment-ref.js +30 -1
- package/dist/delivery/channel-context.js +19 -1
- package/dist/delivery/delivery-service.js +71 -82
- 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 +1 -1
- package/dist/delivery/index.js +1 -1
- package/dist/delivery/jobs.js +43 -11
- package/dist/delivery/types.d.ts +7 -1
- package/dist/feature-toggles/handlers/set.write.d.ts +1 -1
- package/dist/feature-toggles/toggle-runtime.d.ts +5 -3
- package/dist/feature-toggles/toggle-runtime.js +25 -12
- package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
- package/dist/file-foundation/read-file-ref-content.d.ts +1 -1
- package/dist/file-foundation/read-file-ref-content.js +6 -4
- package/dist/files-tenant-data/hooks.js +4 -0
- package/dist/files-tenant-data/index.d.ts +1 -0
- package/dist/files-tenant-data/index.js +2 -1
- package/dist/form-draft/db/queries/owned-file-refs.d.ts +1 -1
- package/dist/form-draft/db/queries/owned-file-refs.js +9 -2
- package/dist/form-draft/handlers/discard.write.d.ts +1 -1
- package/dist/form-draft/handlers/discard.write.js +1 -1
- package/dist/form-draft/handlers/save.write.d.ts +1 -1
- package/dist/inbound-mail-foundation/oauth-access-token.js +8 -3
- package/dist/inbound-mail-foundation/watch-supervisor.js +3 -1
- 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 +35 -10
- package/dist/jobs/tenant-job-run-table.js +3 -2
- package/dist/ledger/entity.js +3 -1
- package/dist/managed-pages/handlers/set.write.d.ts +1 -1
- package/dist/metrics/filters.d.ts +16 -1
- package/dist/metrics/filters.js +27 -11
- package/dist/personal-access-tokens/handlers/create.write.d.ts +1 -1
- package/dist/personal-access-tokens/handlers/revoke.write.d.ts +1 -1
- package/dist/personal-access-tokens/revoke-for-user.js +3 -7
- package/dist/renderer-foundation/types.d.ts +2 -0
- 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/delete.write.js +3 -1
- package/dist/secrets/handlers/set.write.d.ts +1 -1
- package/dist/secrets/secrets-context.js +15 -3
- package/dist/secrets/write-gate.d.ts +3 -1
- package/dist/secrets/write-gate.js +6 -3
- 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/chunked-entity-migration.d.ts +1 -0
- package/dist/shared/chunked-entity-migration.js +1 -1
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/mail-template-body.d.ts +3 -0
- package/dist/shared/mail-template-body.js +10 -0
- package/dist/shared/resource-image-formats.d.ts +23 -0
- package/dist/shared/resource-image-formats.js +21 -0
- package/dist/shared/sharp-input-limits.js +7 -8
- package/dist/step-dispatcher/webhook-runner.js +2 -2
- package/dist/subscription-stripe/plugin-methods.js +18 -8
- package/dist/subscription-stripe/verify-webhook.d.ts +1 -1
- package/dist/template-resolver/api.js +3 -2
- package/dist/template-resolver/compile-document-body.d.ts +7 -0
- package/dist/template-resolver/compile-document-body.js +114 -21
- package/dist/template-resolver/compile-steps-block.d.ts +10 -0
- package/dist/template-resolver/compile-steps-block.js +45 -0
- package/dist/template-resolver/constants.d.ts +6 -3
- package/dist/template-resolver/constants.js +13 -2
- package/dist/template-resolver/document-sanitizer.js +3 -0
- package/dist/template-resolver/document-schema.d.ts +3 -2
- package/dist/template-resolver/document-schema.js +29 -4
- package/dist/template-resolver/document-steps.d.ts +17 -0
- package/dist/template-resolver/document-steps.js +93 -0
- package/dist/template-resolver/feature.d.ts +2 -0
- package/dist/template-resolver/feature.js +13 -1
- package/dist/template-resolver/handlers/bundle-import.d.ts +7 -1
- package/dist/template-resolver/handlers/bundle-import.js +63 -38
- package/dist/template-resolver/handlers/collection-set.write.d.ts +1 -1
- package/dist/template-resolver/handlers/collection-set.write.js +4 -3
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +1 -1
- package/dist/template-resolver/handlers/copy-locale.write.js +95 -49
- package/dist/template-resolver/handlers/diff-to-system.query.js +5 -38
- 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.js +51 -1
- package/dist/template-resolver/handlers/find-by-id.query.d.ts +1 -0
- package/dist/template-resolver/handlers/find-by-id.query.js +6 -3
- package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +1 -1
- package/dist/template-resolver/handlers/import-bundle-system.write.js +16 -5
- package/dist/template-resolver/handlers/import-bundle.write.d.ts +1 -1
- package/dist/template-resolver/handlers/import-bundle.write.js +11 -1
- package/dist/template-resolver/handlers/layout-source.query.d.ts +5 -0
- package/dist/template-resolver/handlers/layout-source.query.js +46 -23
- package/dist/template-resolver/handlers/list.query.js +4 -1
- 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 +9 -0
- package/dist/template-resolver/handlers/overview.query.js +60 -12
- package/dist/template-resolver/handlers/reset-to-system.write.d.ts +1 -1
- package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +1 -1
- package/dist/template-resolver/handlers/resource-remove.write.d.ts +1 -1
- package/dist/template-resolver/handlers/resource-upload.write.d.ts +2 -2
- package/dist/template-resolver/handlers/resource-upload.write.js +36 -6
- package/dist/template-resolver/handlers/send-test.write.d.ts +3 -1
- package/dist/template-resolver/handlers/send-test.write.js +37 -10
- package/dist/template-resolver/handlers/set.write.d.ts +1 -1
- package/dist/template-resolver/handlers/template-gates.js +13 -2
- package/dist/template-resolver/handlers/template-write.js +36 -4
- package/dist/template-resolver/handlers/tenant-overrides.query.d.ts +32 -0
- package/dist/template-resolver/handlers/tenant-overrides.query.js +80 -16
- package/dist/template-resolver/handlers/toggle-status.write.d.ts +17 -4
- package/dist/template-resolver/handlers/toggle-status.write.js +35 -19
- package/dist/template-resolver/handlers/upsert-system.write.d.ts +1 -1
- package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +1 -1
- package/dist/template-resolver/handlers/upsert-tenant.write.js +1 -1
- package/dist/template-resolver/handlers/usage.query.js +11 -2
- package/dist/template-resolver/handlers/validate.query.js +4 -1
- package/dist/template-resolver/index.d.ts +5 -4
- package/dist/template-resolver/index.js +5 -4
- package/dist/template-resolver/layout-appearance.d.ts +10 -0
- package/dist/template-resolver/layout-appearance.js +36 -0
- package/dist/template-resolver/mail-constants.d.ts +2 -0
- package/dist/template-resolver/mail-constants.js +3 -0
- package/dist/template-resolver/pdf-constants.d.ts +7 -0
- package/dist/template-resolver/pdf-constants.js +8 -0
- package/dist/template-resolver/qualified-names.d.ts +4 -0
- package/dist/template-resolver/qualified-names.js +4 -0
- package/dist/template-resolver/resource-image.d.ts +2 -9
- package/dist/template-resolver/resource-image.js +2 -11
- package/dist/template-resolver/seeding.js +23 -4
- package/dist/template-resolver/store-template-resource.d.ts +53 -9
- package/dist/template-resolver/store-template-resource.js +250 -97
- package/dist/template-resolver/table.d.ts +103 -0
- package/dist/template-resolver/table.js +9 -2
- package/dist/template-resolver/template-bundle.js +4 -0
- package/dist/template-resolver/template-draft.d.ts +54 -0
- package/dist/template-resolver/template-draft.js +72 -0
- package/dist/template-resolver/template-meta.d.ts +35 -6
- package/dist/template-resolver/template-meta.js +67 -16
- package/dist/template-resolver/template-preview.d.ts +19 -2
- package/dist/template-resolver/template-preview.js +98 -55
- package/dist/template-resolver/template-resources.js +3 -4
- package/dist/template-resolver/template-usage.d.ts +5 -1
- package/dist/template-resolver/template-usage.js +14 -9
- package/dist/template-resolver/template-validation.js +13 -9
- package/dist/template-resolver/web/client-plugin.js +4 -2
- package/dist/template-resolver/web/document-editor-extensions.d.ts +5 -1
- package/dist/template-resolver/web/document-editor-extensions.js +76 -13
- package/dist/template-resolver/web/document-editor.d.ts +2 -1
- package/dist/template-resolver/web/document-editor.js +12 -2
- package/dist/template-resolver/web/document-tiptap-editor.d.ts +1 -1
- package/dist/template-resolver/web/document-tiptap-editor.js +173 -34
- package/dist/template-resolver/web/document-toolbar-blocks.d.ts +13 -5
- package/dist/template-resolver/web/document-toolbar-blocks.js +6 -0
- package/dist/template-resolver/web/i18n.js +24 -4
- package/dist/template-resolver/web/index.d.ts +2 -0
- package/dist/template-resolver/web/index.js +2 -0
- package/dist/template-resolver/web/placeholder-line-editor.d.ts +6 -2
- package/dist/template-resolver/web/placeholder-line-editor.js +9 -7
- 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/toggle-enabled.write.d.ts +2 -2
- package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
- package/dist/tenant/handlers/update-member-roles.write.js +3 -6
- package/dist/tenant/handlers/update.write.d.ts +1 -1
- package/dist/tenant/seeding.js +23 -24
- package/dist/tenant-handover/handlers/claim.write.d.ts +1 -1
- package/dist/tenant-handover/move-entity-graph.js +14 -4
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +1 -1
- package/dist/tenant-lifecycle/handlers/request-destruction.write.d.ts +1 -1
- package/dist/tenant-lifecycle/run-tenant-destroy.d.ts +1 -0
- package/dist/tenant-lifecycle/run-tenant-destroy.js +8 -1
- package/dist/tenant-lifecycle/stages.js +3 -2
- package/dist/tier-engine/constants.d.ts +2 -0
- package/dist/tier-engine/constants.js +4 -0
- package/dist/tier-engine/entity.d.ts +15 -14
- package/dist/tier-engine/entity.js +4 -3
- package/dist/tier-engine/feature.js +9 -3
- package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +6 -1
- package/dist/tier-engine/handlers/set-tenant-tier.write.js +7 -4
- package/dist/tier-engine/index.d.ts +1 -1
- package/dist/tier-engine/index.js +1 -1
- package/dist/tier-engine/tier-resolver.d.ts +13 -13
- package/dist/user/constants.d.ts +1 -0
- package/dist/user/handlers/create.write.d.ts +1 -1
- package/dist/user/handlers/update.write.d.ts +1 -1
- package/dist/user/invited-global-roles.d.ts +2 -4
- package/dist/user/resolve-display-names.js +5 -9
- package/dist/user/seeding.js +20 -1
- package/dist/user-data-rights/email-templates.js +0 -1
- package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +1 -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-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/i18n.js +5 -0
- package/dist/user-data-rights/lib/update-user-lifecycle.d.ts +2 -0
- package/dist/user-data-rights/lib/update-user-lifecycle.js +6 -2
- package/dist/user-data-rights/run-forget-cleanup.js +2 -2
- package/dist/user-profile/handlers/change-email.write.d.ts +1 -1
- package/dist/waitlist/feature.js +1 -1
- package/dist/waitlist/handlers/invite.write.d.ts +1 -1
- package/dist/waitlist/handlers/invite.write.js +21 -3
- package/dist/waitlist/handlers/reject.write.d.ts +1 -1
- package/dist/waitlist/handlers/reject.write.js +3 -16
- package/dist/waitlist/handlers/submit.write.d.ts +1 -1
- package/dist/waitlist/handlers/submit.write.js +29 -7
- package/dist/waitlist/i18n.js +9 -0
- package/dist/waitlist/lib.d.ts +2 -1
- package/dist/waitlist/lib.js +15 -0
- package/dist/waitlist/options.js +1 -1
- package/dist/workflow-runner/event-trigger.js +2 -2
- package/dist/workflow-runner/feature.js +1 -1
- package/dist/workflow-runner/handlers/resume-run.write.js +7 -2
- package/package.json +19 -10
- package/src/audit/changes.json +18 -0
- package/src/auth-email-password/changes.json +55 -0
- package/src/billing-foundation/changes.json +48 -0
- package/src/cap-counter/changes.json +19 -0
- package/src/cap-overview/changes.json +6 -0
- package/src/channel-email/changes.json +18 -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/delivery/changes.json +30 -0
- package/src/document-ingest-foundation/changes.json +6 -0
- package/src/feature-toggles/changes.json +7 -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 +31 -0
- package/src/ledger/changes.json +7 -0
- package/src/metrics/changes.json +6 -0
- package/src/personal-access-tokens/changes.json +6 -0
- package/src/renderer-pdf/changes.json +12 -0
- package/src/secrets/changes.json +12 -0
- package/src/shared/changes.json +6 -0
- package/src/subscription-stripe/changes.json +6 -0
- package/src/template-resolver/changes.json +149 -0
- package/src/tenant/changes.json +6 -0
- package/src/tenant-lifecycle/changes.json +6 -0
- package/src/tier-engine/changes.json +18 -0
- package/src/user/changes.json +6 -0
- package/src/user-data-rights/changes.json +12 -0
- package/src/user-profile/changes.json +1 -1
- package/src/waitlist/changes.json +18 -0
- package/src/workflow-runner/changes.json +6 -0
|
@@ -1,22 +1,44 @@
|
|
|
1
1
|
import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
import { RendererError } from "../renderer-foundation/index.js";
|
|
4
|
-
|
|
4
|
+
// Upper bounds keep a hostile or runaway payload from exhausting memory and CPU in layout.
|
|
5
|
+
const PDF_TABLE_MAX_ROWS = 10_000;
|
|
6
|
+
const PDF_TABLE_MAX_COLUMNS = 30;
|
|
7
|
+
const PDF_TABLE_MAX_CELL_LENGTH = 5_000;
|
|
8
|
+
const pdfTableColumnSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
header: z.string(),
|
|
11
|
+
weight: z.number().positive().optional(),
|
|
12
|
+
align: z.enum(["left", "right"]).optional(),
|
|
13
|
+
})
|
|
14
|
+
.readonly();
|
|
15
|
+
export const pdfTableDocumentSchema = z
|
|
16
|
+
.object({
|
|
5
17
|
title: z.string(),
|
|
6
18
|
subtitle: z.string().optional(),
|
|
7
|
-
meta: z
|
|
8
|
-
|
|
9
|
-
.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
.
|
|
15
|
-
|
|
19
|
+
meta: z
|
|
20
|
+
.array(z.object({ label: z.string(), value: z.string() }).readonly())
|
|
21
|
+
.readonly()
|
|
22
|
+
.optional(),
|
|
23
|
+
columns: z.array(pdfTableColumnSchema).min(1).max(PDF_TABLE_MAX_COLUMNS).readonly(),
|
|
24
|
+
rows: z
|
|
25
|
+
.array(z.array(z.string().max(PDF_TABLE_MAX_CELL_LENGTH)).readonly())
|
|
26
|
+
.max(PDF_TABLE_MAX_ROWS)
|
|
27
|
+
.readonly(),
|
|
16
28
|
emptyText: z.string().optional(),
|
|
17
29
|
footerText: z.string().optional(),
|
|
30
|
+
// Template with `{page}` and `{pages}`; default `"{page} / {pages}"`.
|
|
18
31
|
pageLabel: z.string().optional(),
|
|
19
|
-
})
|
|
32
|
+
})
|
|
33
|
+
.readonly();
|
|
34
|
+
const marginMmSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
top: z.number().nonnegative().optional(),
|
|
37
|
+
right: z.number().nonnegative().optional(),
|
|
38
|
+
bottom: z.number().nonnegative().optional(),
|
|
39
|
+
left: z.number().nonnegative().optional(),
|
|
40
|
+
})
|
|
41
|
+
.readonly();
|
|
20
42
|
const POINTS_PER_MM = 72 / 25.4;
|
|
21
43
|
const PAGE_SIZE_POINTS = {
|
|
22
44
|
A4: { width: 595.28, height: 841.89 },
|
|
@@ -31,6 +53,7 @@ const LINE_HEIGHT_FACTOR = 1.3;
|
|
|
31
53
|
const CELL_PADDING_X = 4;
|
|
32
54
|
const CELL_PADDING_Y = 3;
|
|
33
55
|
const FOOTER_RESERVE = 16;
|
|
56
|
+
const MIN_COLUMN_WIDTH = 20;
|
|
34
57
|
const META_LABEL_GAP = 10;
|
|
35
58
|
const BLOCK_GAP = 10;
|
|
36
59
|
const REPLACEMENT_CHAR = "?";
|
|
@@ -46,7 +69,7 @@ function createEncodableText(font) {
|
|
|
46
69
|
const supported = new Set(font.getCharacterSet());
|
|
47
70
|
return (text) => {
|
|
48
71
|
let result = "";
|
|
49
|
-
for (const char of text.replace(/\t/g, " ")) {
|
|
72
|
+
for (const char of text.replace(/\r\n?/g, "\n").replace(/\t/g, " ")) {
|
|
50
73
|
const codePoint = char.codePointAt(0) ?? 0;
|
|
51
74
|
result += codePoint === 0x0a || supported.has(codePoint) ? char : REPLACEMENT_CHAR;
|
|
52
75
|
}
|
|
@@ -111,25 +134,22 @@ function assertRowShape(doc) {
|
|
|
111
134
|
}
|
|
112
135
|
});
|
|
113
136
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
137
|
+
function resolveTableLayout(doc, options, fonts, clean) {
|
|
138
|
+
const marginMm = marginMmSchema.safeParse(options?.marginMm ?? {});
|
|
139
|
+
if (!marginMm.success) {
|
|
140
|
+
throw new RendererError("margins must be non-negative numbers", "invalid_payload");
|
|
117
141
|
}
|
|
118
|
-
assertRowShape(doc);
|
|
119
|
-
const pdf = await PDFDocument.create({ updateMetadata: false });
|
|
120
|
-
const fonts = {
|
|
121
|
-
regular: await pdf.embedFont(StandardFonts.Helvetica),
|
|
122
|
-
bold: await pdf.embedFont(StandardFonts.HelveticaBold),
|
|
123
|
-
};
|
|
124
|
-
const clean = createEncodableText(fonts.regular);
|
|
125
142
|
const pageSize = PAGE_SIZE_POINTS[options?.format ?? "A4"];
|
|
126
143
|
const margin = {
|
|
127
|
-
top: (
|
|
128
|
-
right: (
|
|
129
|
-
bottom: (
|
|
130
|
-
left: (
|
|
144
|
+
top: (marginMm.data.top ?? DEFAULT_MARGIN_MM.top) * POINTS_PER_MM,
|
|
145
|
+
right: (marginMm.data.right ?? DEFAULT_MARGIN_MM.right) * POINTS_PER_MM,
|
|
146
|
+
bottom: (marginMm.data.bottom ?? DEFAULT_MARGIN_MM.bottom) * POINTS_PER_MM,
|
|
147
|
+
left: (marginMm.data.left ?? DEFAULT_MARGIN_MM.left) * POINTS_PER_MM,
|
|
131
148
|
};
|
|
132
149
|
const contentWidth = pageSize.width - margin.left - margin.right;
|
|
150
|
+
if (contentWidth < MIN_COLUMN_WIDTH * doc.columns.length) {
|
|
151
|
+
throw new RendererError("margins leave no room for table columns", "invalid_payload");
|
|
152
|
+
}
|
|
133
153
|
const contentBottom = margin.bottom + FOOTER_RESERVE;
|
|
134
154
|
const columnWidths = resolveColumnWidths(doc.columns, contentWidth);
|
|
135
155
|
const bodyLineHeight = lineHeightFor(BODY_SIZE);
|
|
@@ -139,6 +159,33 @@ export async function renderTablePdf(doc, options) {
|
|
|
139
159
|
}
|
|
140
160
|
const headerLines = doc.columns.map((column, index) => wrapText(clean(column.header), fonts.bold, BODY_SIZE, (columnWidths[index] ?? contentWidth) - 2 * CELL_PADDING_X));
|
|
141
161
|
const headerHeight = Math.max(...headerLines.map((lines) => lines.length)) * bodyLineHeight + 2 * CELL_PADDING_Y;
|
|
162
|
+
// A header this tall would leave every continuation page without room for a row.
|
|
163
|
+
if (rowSpaceOnEmptyPage - headerHeight < bodyLineHeight + 2 * CELL_PADDING_Y) {
|
|
164
|
+
throw new RendererError("table header leaves no room for rows", "invalid_payload");
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
pageSize,
|
|
168
|
+
margin,
|
|
169
|
+
contentWidth,
|
|
170
|
+
contentBottom,
|
|
171
|
+
columnWidths,
|
|
172
|
+
bodyLineHeight,
|
|
173
|
+
headerLines,
|
|
174
|
+
headerHeight,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
export async function renderTablePdf(doc, options) {
|
|
178
|
+
if (doc.columns.length === 0) {
|
|
179
|
+
throw new RendererError("table needs at least one column", "invalid_payload");
|
|
180
|
+
}
|
|
181
|
+
assertRowShape(doc);
|
|
182
|
+
const pdf = await PDFDocument.create({ updateMetadata: false });
|
|
183
|
+
const fonts = {
|
|
184
|
+
regular: await pdf.embedFont(StandardFonts.Helvetica),
|
|
185
|
+
bold: await pdf.embedFont(StandardFonts.HelveticaBold),
|
|
186
|
+
};
|
|
187
|
+
const clean = createEncodableText(fonts.regular);
|
|
188
|
+
const { pageSize, margin, contentWidth, contentBottom, columnWidths, bodyLineHeight, headerLines, headerHeight, } = resolveTableLayout(doc, options, fonts, clean);
|
|
142
189
|
let page = pdf.addPage([pageSize.width, pageSize.height]);
|
|
143
190
|
let cursorY = pageSize.height - margin.top;
|
|
144
191
|
function drawLines(lines, font, size, left, width, topY, align, color = TEXT_COLOR) {
|
|
@@ -154,14 +201,27 @@ export async function renderTablePdf(doc, options) {
|
|
|
154
201
|
});
|
|
155
202
|
});
|
|
156
203
|
}
|
|
204
|
+
function startBlankPage() {
|
|
205
|
+
page = pdf.addPage([pageSize.width, pageSize.height]);
|
|
206
|
+
cursorY = pageSize.height - margin.top;
|
|
207
|
+
}
|
|
208
|
+
function ensureRoom(height) {
|
|
209
|
+
if (cursorY - height < contentBottom)
|
|
210
|
+
startBlankPage();
|
|
211
|
+
}
|
|
212
|
+
// Line by line, so a block taller than one page continues on the next.
|
|
213
|
+
function flowLines(lines, font, size, left, width, color = TEXT_COLOR) {
|
|
214
|
+
const lineHeight = lineHeightFor(size);
|
|
215
|
+
for (const line of lines) {
|
|
216
|
+
ensureRoom(lineHeight);
|
|
217
|
+
drawLines([line], font, size, left, width, cursorY, "left", color);
|
|
218
|
+
cursorY -= lineHeight;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
157
221
|
function drawIntroBlock() {
|
|
158
|
-
|
|
159
|
-
drawLines(titleLines, fonts.bold, TITLE_SIZE, margin.left, contentWidth, cursorY, "left");
|
|
160
|
-
cursorY -= titleLines.length * lineHeightFor(TITLE_SIZE);
|
|
222
|
+
flowLines(wrapText(clean(doc.title), fonts.bold, TITLE_SIZE, contentWidth), fonts.bold, TITLE_SIZE, margin.left, contentWidth);
|
|
161
223
|
if (doc.subtitle !== undefined) {
|
|
162
|
-
|
|
163
|
-
drawLines(subtitleLines, fonts.regular, SUBTITLE_SIZE, margin.left, contentWidth, cursorY, "left", MUTED_COLOR);
|
|
164
|
-
cursorY -= subtitleLines.length * lineHeightFor(SUBTITLE_SIZE);
|
|
224
|
+
flowLines(wrapText(clean(doc.subtitle), fonts.regular, SUBTITLE_SIZE, contentWidth), fonts.regular, SUBTITLE_SIZE, margin.left, contentWidth, MUTED_COLOR);
|
|
165
225
|
}
|
|
166
226
|
const meta = doc.meta ?? [];
|
|
167
227
|
if (meta.length > 0) {
|
|
@@ -171,9 +231,9 @@ export async function renderTablePdf(doc, options) {
|
|
|
171
231
|
META_LABEL_GAP;
|
|
172
232
|
meta.forEach((entry, index) => {
|
|
173
233
|
const valueLines = wrapText(clean(entry.value), fonts.regular, BODY_SIZE, contentWidth - labelWidth);
|
|
234
|
+
ensureRoom(bodyLineHeight);
|
|
174
235
|
drawLines([labels[index] ?? ""], fonts.bold, BODY_SIZE, margin.left, labelWidth, cursorY, "left");
|
|
175
|
-
|
|
176
|
-
cursorY -= valueLines.length * bodyLineHeight;
|
|
236
|
+
flowLines(valueLines, fonts.regular, BODY_SIZE, margin.left + labelWidth, contentWidth - labelWidth);
|
|
177
237
|
});
|
|
178
238
|
}
|
|
179
239
|
cursorY -= BLOCK_GAP;
|
|
@@ -235,6 +295,7 @@ export async function renderTablePdf(doc, options) {
|
|
|
235
295
|
}
|
|
236
296
|
}
|
|
237
297
|
drawIntroBlock();
|
|
298
|
+
ensureRoom(headerHeight + bodyLineHeight + 2 * CELL_PADDING_Y);
|
|
238
299
|
drawTableHeader();
|
|
239
300
|
if (doc.rows.length === 0 && doc.emptyText !== undefined) {
|
|
240
301
|
drawRow([
|
package/dist/secrets/feature.js
CHANGED
|
@@ -60,20 +60,21 @@ export function requireSecretsContext(ctx, handlerName) {
|
|
|
60
60
|
"createSecretsContext({db, masterKeyProvider, registry}) via extraContext.secrets at boot.",
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
const raw = ctx.secrets;
|
|
64
63
|
const userId = ctx._userId;
|
|
65
64
|
if (!userId) {
|
|
66
65
|
throw new InternalError({
|
|
67
66
|
message: `[${handlerName}] ctx._userId missing — cannot audit secret reads without a caller identity.`,
|
|
68
67
|
});
|
|
69
68
|
}
|
|
70
|
-
|
|
69
|
+
const withAudit = (raw) => ({
|
|
70
|
+
withRunner: (runner) => withAudit(raw.withRunner(runner)),
|
|
71
71
|
get: (tenantId, key, overrideAudit) => raw.get(tenantId, key, overrideAudit ?? { userId, handlerName }),
|
|
72
72
|
// No audit injection: has() is metadata-only and never logs a read.
|
|
73
73
|
has: raw.has.bind(raw),
|
|
74
74
|
set: raw.set.bind(raw),
|
|
75
75
|
delete: raw.delete.bind(raw),
|
|
76
|
-
};
|
|
76
|
+
});
|
|
77
|
+
return withAudit(ctx.secrets);
|
|
77
78
|
}
|
|
78
79
|
function resolveSecretsAccess(opts) {
|
|
79
80
|
if (opts.access !== undefined && opts.roles !== undefined) {
|
|
@@ -4,4 +4,4 @@ export declare function createDeleteHandler(access?: AccessRule): import("@cosmi
|
|
|
4
4
|
key: z.ZodString;
|
|
5
5
|
}, z.core.$strip>, {
|
|
6
6
|
key: string;
|
|
7
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
7
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -16,7 +16,9 @@ export function createDeleteHandler(access = DEFAULT_SECRETS_ACCESS) {
|
|
|
16
16
|
access,
|
|
17
17
|
handler: async (event, ctx) => {
|
|
18
18
|
const secrets = requireSecretsContext(ctx, "secrets:write:delete");
|
|
19
|
-
const gate = checkSecretKeyWrite(ctx.registry, event.user.roles, event.payload.key
|
|
19
|
+
const gate = checkSecretKeyWrite(ctx.registry, event.user.roles, event.payload.key, {
|
|
20
|
+
allowUndeclared: true,
|
|
21
|
+
});
|
|
20
22
|
if (!gate.ok)
|
|
21
23
|
return gate.failure;
|
|
22
24
|
const removed = await secrets.delete(event.user.tenantId, event.payload.key, {
|
|
@@ -8,4 +8,4 @@ export declare function createSetHandler(access?: AccessRule): import("@cosmicdr
|
|
|
8
8
|
}, z.core.$strip>, {
|
|
9
9
|
key: string;
|
|
10
10
|
redactedPreview: string;
|
|
11
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
11
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -44,12 +44,21 @@ function resolveKey(keyOrHandle) {
|
|
|
44
44
|
return typeof keyOrHandle === "string" ? keyOrHandle : keyOrHandle.name;
|
|
45
45
|
}
|
|
46
46
|
export function createSecretsContext(opts) {
|
|
47
|
-
const {
|
|
47
|
+
const { masterKeyProvider } = opts;
|
|
48
48
|
const provider = withDekCache(masterKeyProvider, opts.dekCache ?? createDekCache());
|
|
49
|
+
return buildSecretsContext(opts, provider, opts.db, false);
|
|
50
|
+
}
|
|
51
|
+
// `runnerIsTransaction`: the runner is a caller-owned transaction, so audited reads join it
|
|
52
|
+
// instead of opening a second connection (which would deadlock a small pool).
|
|
53
|
+
function buildSecretsContext(opts, provider, db, runnerIsTransaction) {
|
|
54
|
+
const { masterKeyProvider } = opts;
|
|
49
55
|
async function lookup(tenantId, key) {
|
|
50
56
|
return fetchOne(db, tenantSecretsTable, { tenantId, key });
|
|
51
57
|
}
|
|
52
58
|
return {
|
|
59
|
+
withRunner(runner) {
|
|
60
|
+
return buildSecretsContext(opts, provider, runner, true);
|
|
61
|
+
},
|
|
53
62
|
async get(tenantId, keyOrHandle, auditCtx) {
|
|
54
63
|
const key = resolveKey(keyOrHandle);
|
|
55
64
|
// Atomic audit + read: a decrypt that "escaped" the audit trail
|
|
@@ -65,7 +74,7 @@ export function createSecretsContext(opts) {
|
|
|
65
74
|
const plaintext = await decryptValue(decodeStoredEnvelope(existing.envelope), provider);
|
|
66
75
|
return createSecret(plaintext);
|
|
67
76
|
}
|
|
68
|
-
const
|
|
77
|
+
const readAndAudit = async (tx) => {
|
|
69
78
|
// Inline select inside the TX via raw client — fetchOne's connection
|
|
70
79
|
// type doesn't widen to the transaction object cleanly.
|
|
71
80
|
const envelope = await selectTenantSecretEnvelope(tx, tenantId, key);
|
|
@@ -99,7 +108,10 @@ export function createSecretsContext(opts) {
|
|
|
99
108
|
metadata,
|
|
100
109
|
});
|
|
101
110
|
return pt;
|
|
102
|
-
}
|
|
111
|
+
};
|
|
112
|
+
const plaintext = runnerIsTransaction
|
|
113
|
+
? await readAndAudit(db)
|
|
114
|
+
: await transaction(db, readAndAudit);
|
|
103
115
|
if (plaintext === undefined)
|
|
104
116
|
return undefined;
|
|
105
117
|
// Brand the plaintext only after audit committed. The response
|
|
@@ -7,7 +7,9 @@ export type SecretKeyWriteCheck = {
|
|
|
7
7
|
readonly ok: false;
|
|
8
8
|
readonly failure: WriteFailure;
|
|
9
9
|
};
|
|
10
|
-
export declare function checkSecretKeyWrite(registry: Registry, userRoles: readonly string[], key: string
|
|
10
|
+
export declare function checkSecretKeyWrite(registry: Registry, userRoles: readonly string[], key: string, options?: {
|
|
11
|
+
readonly allowUndeclared?: boolean;
|
|
12
|
+
}): SecretKeyWriteCheck;
|
|
11
13
|
export type SecretValueSchemaSource = Pick<Registry, "getSecretKey" | "findSecretNamespace">;
|
|
12
14
|
export declare function invalidSecretValueError(): ValidationError;
|
|
13
15
|
export declare function isInvalidSecretValueError(err: unknown): err is ValidationError;
|
|
@@ -15,11 +15,14 @@ function namespaceAcceptsKey(namespace, key) {
|
|
|
15
15
|
// Only keys declared via r.secret() or under a declared r.secretNamespace()
|
|
16
16
|
// are writable through the API, so a tenant admin cannot plant arbitrary rows
|
|
17
17
|
// next to the feature-owned ones. writeRoles narrows the handler access
|
|
18
|
-
// further for a key or a whole namespace.
|
|
19
|
-
|
|
18
|
+
// further for a key or a whole namespace. Delete passes allowUndeclared: it
|
|
19
|
+
// creates nothing, and legacy rows under since-undeclared keys must stay removable.
|
|
20
|
+
export function checkSecretKeyWrite(registry, userRoles, key, options = {}) {
|
|
20
21
|
const keyDef = registry.getSecretKey(key);
|
|
21
22
|
const namespace = keyDef ? undefined : registry.findSecretNamespace(key);
|
|
22
|
-
if (!keyDef &&
|
|
23
|
+
if (!keyDef &&
|
|
24
|
+
!(namespace && namespaceAcceptsKey(namespace, key)) &&
|
|
25
|
+
options.allowUndeclared !== true) {
|
|
23
26
|
return unknownKeyFailure(key);
|
|
24
27
|
}
|
|
25
28
|
const writeRoles = keyDef?.writeRoles ?? namespace?.writeRoles;
|
|
@@ -6,4 +6,4 @@ export declare const revokeAllForUserWrite: import("@cosmicdrift/kumiko-types/de
|
|
|
6
6
|
}, z.core.$strip>, {
|
|
7
7
|
count: number;
|
|
8
8
|
userId: string;
|
|
9
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
9
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
export declare const revokeAllOthersWrite: import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"user-session:revoke-all-others", z.ZodObject<{}, z.core.$strip>, {
|
|
3
3
|
count: 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 revokeWrite: import("@cosmicdrift/kumiko-types/define-handl
|
|
|
3
3
|
id: z.ZodUUID;
|
|
4
4
|
}, z.core.$strip>, {
|
|
5
5
|
id: string;
|
|
6
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
6
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -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
|
@@ -18,6 +18,7 @@ export { rateLimitedTextResponse } from "./rate-limited-text-response.js";
|
|
|
18
18
|
export { redactBotTokens, redactEmailAddresses, redactErrorText, redactUrls, } from "./redact.js";
|
|
19
19
|
export { REDACTED_PII_PLACEHOLDER, redactPiiCiphertext } from "./redact-pii-ciphertext.js";
|
|
20
20
|
export { requireForTenant } from "./require-for-tenant.js";
|
|
21
|
+
export { extensionForResourceContentType, isResourceImageContentType, isResourceImageFormat, RESOURCE_IMAGE_FORMATS, type ResourceImageContentType, type ResourceImageFormat, } from "./resource-image-formats.js";
|
|
21
22
|
export { type RowBoundGrantResult, redeemRowBoundGrant, signRowBoundGrant, } from "./row-bound-grant.js";
|
|
22
23
|
export { runInSubTransaction } from "./run-in-sub-transaction.js";
|
|
23
24
|
export { registrationLocaleFromRequest, sessionLocaleField } from "./session-locale-field.js";
|
package/dist/shared/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export { rateLimitedTextResponse } from "./rate-limited-text-response.js";
|
|
|
18
18
|
export { redactBotTokens, redactEmailAddresses, redactErrorText, redactUrls, } from "./redact.js";
|
|
19
19
|
export { REDACTED_PII_PLACEHOLDER, redactPiiCiphertext } from "./redact-pii-ciphertext.js";
|
|
20
20
|
export { requireForTenant } from "./require-for-tenant.js";
|
|
21
|
+
export { extensionForResourceContentType, isResourceImageContentType, isResourceImageFormat, RESOURCE_IMAGE_FORMATS, } from "./resource-image-formats.js";
|
|
21
22
|
export { redeemRowBoundGrant, signRowBoundGrant, } from "./row-bound-grant.js";
|
|
22
23
|
export { runInSubTransaction } from "./run-in-sub-transaction.js";
|
|
23
24
|
export { registrationLocaleFromRequest, sessionLocaleField } from "./session-locale-field.js";
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export declare function mailTextWithSlotsHtml(text: string): string;
|
|
2
2
|
export declare function mailParagraph(text: string): string;
|
|
3
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;
|
|
4
6
|
export declare function mailRepeatedRows(listName: string, field: string): string;
|
|
7
|
+
export declare function mailSubject(text: string): string;
|
|
@@ -11,7 +11,17 @@ export function mailParagraph(text) {
|
|
|
11
11
|
export function mailHeading(text) {
|
|
12
12
|
return `<h2>${mailTextWithSlotsHtml(text)}</h2>`;
|
|
13
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
|
+
}
|
|
14
20
|
// One table row per list entry; the cell shows the entry's `field`.
|
|
15
21
|
export function mailRepeatedRows(listName, field) {
|
|
16
22
|
return `<table><tbody><tr data-each="${escapeHtmlAttr(listName)}"><td><p><span data-variable="${escapeHtmlAttr(field)}"></span></p></td></tr></tbody></table>`;
|
|
17
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,23 @@
|
|
|
1
|
+
export declare const RESOURCE_IMAGE_FORMATS: {
|
|
2
|
+
readonly png: {
|
|
3
|
+
readonly contentType: "image/png";
|
|
4
|
+
readonly extension: "png";
|
|
5
|
+
};
|
|
6
|
+
readonly jpeg: {
|
|
7
|
+
readonly contentType: "image/jpeg";
|
|
8
|
+
readonly extension: "jpg";
|
|
9
|
+
};
|
|
10
|
+
readonly gif: {
|
|
11
|
+
readonly contentType: "image/gif";
|
|
12
|
+
readonly extension: "gif";
|
|
13
|
+
};
|
|
14
|
+
readonly webp: {
|
|
15
|
+
readonly contentType: "image/webp";
|
|
16
|
+
readonly extension: "webp";
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export type ResourceImageFormat = keyof typeof RESOURCE_IMAGE_FORMATS;
|
|
20
|
+
export type ResourceImageContentType = (typeof RESOURCE_IMAGE_FORMATS)[ResourceImageFormat]["contentType"];
|
|
21
|
+
export declare function isResourceImageFormat(format: string | undefined): format is ResourceImageFormat;
|
|
22
|
+
export declare function isResourceImageContentType(contentType: string): contentType is ResourceImageContentType;
|
|
23
|
+
export declare function extensionForResourceContentType(contentType: string): string | undefined;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Single source for the raster formats a template resource image may have: the normalizer, the
|
|
2
|
+
// stored file extension, the data-URI embedding and the inline mail attachment name all derive
|
|
3
|
+
// from it, so a new format cannot be accepted in one place and dropped in another.
|
|
4
|
+
export const RESOURCE_IMAGE_FORMATS = {
|
|
5
|
+
png: { contentType: "image/png", extension: "png" },
|
|
6
|
+
jpeg: { contentType: "image/jpeg", extension: "jpg" },
|
|
7
|
+
gif: { contentType: "image/gif", extension: "gif" },
|
|
8
|
+
webp: { contentType: "image/webp", extension: "webp" },
|
|
9
|
+
};
|
|
10
|
+
export function isResourceImageFormat(format) {
|
|
11
|
+
return format !== undefined && Object.hasOwn(RESOURCE_IMAGE_FORMATS, format);
|
|
12
|
+
}
|
|
13
|
+
function formatForContentType(contentType) {
|
|
14
|
+
return Object.values(RESOURCE_IMAGE_FORMATS).find((entry) => entry.contentType === contentType);
|
|
15
|
+
}
|
|
16
|
+
export function isResourceImageContentType(contentType) {
|
|
17
|
+
return formatForContentType(contentType) !== undefined;
|
|
18
|
+
}
|
|
19
|
+
export function extensionForResourceContentType(contentType) {
|
|
20
|
+
return formatForContentType(contentType)?.extension;
|
|
21
|
+
}
|
|
@@ -4,19 +4,18 @@
|
|
|
4
4
|
// to be an OOM primitive for a single request.
|
|
5
5
|
export const MAX_INPUT_PIXELS = 100_000_000;
|
|
6
6
|
// sequentialRead trades random pixel access for lower peak memory on
|
|
7
|
-
// streamed formats
|
|
8
|
-
//
|
|
9
|
-
// against a single already-buffered region per call).
|
|
7
|
+
// streamed formats. Shared by every sharp caller since none of them need
|
|
8
|
+
// non-sequential access to the original bytes.
|
|
10
9
|
export const SHARP_INPUT_OPTIONS = {
|
|
11
10
|
limitInputPixels: MAX_INPUT_PIXELS,
|
|
12
11
|
sequentialRead: true,
|
|
13
12
|
};
|
|
14
|
-
// Shared trust boundary for every image byte source
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
13
|
+
// Shared trust boundary for every image byte source decoded through sharp:
|
|
14
|
+
// uploads, overlay layers and template resources alike. A base64 string in a
|
|
15
|
+
// field declaration isn't more trustworthy than an upload just because it
|
|
16
|
+
// came from app code.
|
|
18
17
|
export function assertNotSvg(format) {
|
|
19
18
|
if (format === "svg") {
|
|
20
|
-
throw new Error("
|
|
19
|
+
throw new Error("SVG is not supported — rendering untrusted SVG through libvips is a trust boundary this code doesn't need to cross.");
|
|
21
20
|
}
|
|
22
21
|
}
|
|
@@ -79,8 +79,8 @@ let fetchImpl = globalThis.fetch.bind(globalThis);
|
|
|
79
79
|
export function setWebhookFetch(fn) {
|
|
80
80
|
fetchImpl = fn;
|
|
81
81
|
}
|
|
82
|
-
//
|
|
83
|
-
//
|
|
82
|
+
// Bounds a single hanging tenant-controlled receiver to this long. The consumer
|
|
83
|
+
// is sequential and shared, so N hanging requests still delay other tenants by N times this.
|
|
84
84
|
export const WEBHOOK_REQUEST_TIMEOUT_MS = 10_000;
|
|
85
85
|
// Never includes the secret name or value — spec.auth.secret is a
|
|
86
86
|
// tenant-chosen name, but the error still reaches the tenant via the
|
|
@@ -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-
|
|
@@ -6,6 +6,7 @@ import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
|
6
6
|
import { FALLBACK_LOCALE, SYSTEM_TENANT_ID } from "./constants.js";
|
|
7
7
|
import { fileAttachmentsOf, imageLinksOf, parseLinkedResourceEntries, pdfAttachmentLinksOf, } from "./linked-resources.js";
|
|
8
8
|
import { templateResourcesTable } from "./table.js";
|
|
9
|
+
import { activeVersionOf, servedVersionOf } from "./template-draft.js";
|
|
9
10
|
import { parseTemplateMeta } from "./template-meta.js";
|
|
10
11
|
export function createTemplateResolverApi(db) {
|
|
11
12
|
return {
|
|
@@ -51,7 +52,7 @@ export function createTemplateResolverApi(db) {
|
|
|
51
52
|
function pickActiveLayoutRow(rows, locale) {
|
|
52
53
|
const active = rows
|
|
53
54
|
.filter((row) => row.status === "active")
|
|
54
|
-
.sort((a, b) => b
|
|
55
|
+
.sort((a, b) => activeVersionOf(b) - activeVersionOf(a) || a.locale.localeCompare(b.locale));
|
|
55
56
|
return (active.find((row) => row.locale === locale) ??
|
|
56
57
|
active.find((row) => row.locale === FALLBACK_LOCALE) ??
|
|
57
58
|
active[0] ??
|
|
@@ -77,7 +78,7 @@ function toPublic(row) {
|
|
|
77
78
|
const linkedEntries = parseLinkedResourceEntries(row.linkedResources);
|
|
78
79
|
return {
|
|
79
80
|
id: String(row.id),
|
|
80
|
-
version: row
|
|
81
|
+
version: servedVersionOf(row),
|
|
81
82
|
tenantId: row.tenantId,
|
|
82
83
|
slug: row.slug,
|
|
83
84
|
kind,
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { TemplateVariables } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
export type CompileDocumentBodyOptions = {
|
|
3
3
|
readonly resourceSrc?: (key: string) => string | undefined;
|
|
4
|
+
readonly resourceImageMaxWidthPx?: number;
|
|
5
|
+
readonly resourceImageMaxHeightPx?: number;
|
|
4
6
|
readonly alignmentAsInlineStyle?: boolean;
|
|
7
|
+
readonly buttonAsInlineStyle?: boolean;
|
|
8
|
+
readonly tableAsInlineStyle?: boolean;
|
|
9
|
+
readonly buttonInlineStyle?: string;
|
|
10
|
+
readonly headingAsInlineStyle?: boolean;
|
|
11
|
+
readonly stepsColor?: string;
|
|
5
12
|
};
|
|
6
13
|
export declare function collectDocumentResourceKeys(html: string): readonly string[];
|
|
7
14
|
export declare function toResourceDataUri(contentType: string, bytes: Uint8Array): string;
|