@cosmicdrift/kumiko-samples 0.362.0 → 0.364.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/bundled-features/package.json +23 -6
- package/packages/bundled-features/src/admin-shell/changes.json +6 -0
- package/packages/bundled-features/src/admin-shell/feature.ts +8 -1
- package/packages/bundled-features/src/agent-tools/agent-manifest.ts +3 -4
- package/packages/bundled-features/src/agent-tools/filter-ops.ts +1 -3
- package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -3
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +2 -1
- package/packages/bundled-features/src/audit/changes.json +6 -0
- package/packages/bundled-features/src/audit/handlers/list.query.ts +10 -5
- package/packages/bundled-features/src/auth-email-password/bootstrap.ts +29 -8
- package/packages/bundled-features/src/auth-email-password/changes.json +51 -0
- package/packages/bundled-features/src/auth-email-password/email-rate-limits.ts +16 -0
- package/packages/bundled-features/src/auth-email-password/email-templates.ts +12 -138
- package/packages/bundled-features/src/auth-email-password/feature.ts +30 -9
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +6 -2
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -5
- package/packages/bundled-features/src/auth-email-password/handlers/logout.write.ts +7 -2
- package/packages/bundled-features/src/auth-email-password/handlers/request-account-unlock.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/request-email-verification.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/request-password-reset.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +3 -4
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +2 -1
- package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +5 -13
- package/packages/bundled-features/src/auth-email-password/index.ts +2 -17
- package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +30 -22
- package/packages/bundled-features/src/auth-email-password/mail-notifications.ts +158 -0
- package/packages/bundled-features/src/auth-email-password/signup-activation.ts +2 -3
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +24 -5
- package/packages/bundled-features/src/auth-foundation/types.ts +8 -18
- package/packages/bundled-features/src/auth-mfa/feature.ts +8 -10
- package/packages/bundled-features/src/auth-mfa/hmac-token-codec.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/mfa-challenge-token.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/mfa-preauth-setup-token.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/mfa-setup-token.ts +8 -8
- package/packages/bundled-features/src/billing-foundation/changes.json +43 -0
- package/packages/bundled-features/src/billing-foundation/constants.ts +5 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +90 -44
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +22 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +118 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +11 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +39 -33
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-notify.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-pages.ts +1 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +41 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +10 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +36 -20
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +9 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +35 -0
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +11 -3
- package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +4 -9
- package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +12 -6
- package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/i18n.ts +3 -0
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +20 -11
- package/packages/bundled-features/src/billing-foundation/types.ts +7 -2
- package/packages/bundled-features/src/cap-counter/calendar-cap-usage-reservation.ts +2 -0
- package/packages/bundled-features/src/cap-counter/changes.json +6 -0
- package/packages/bundled-features/src/cap-counter/enforce-cap.ts +75 -5
- package/packages/bundled-features/src/cap-counter/index.ts +1 -0
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -0
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +2 -1
- package/packages/bundled-features/src/cap-overview/web/cap-usage-cell.tsx +6 -4
- package/packages/bundled-features/src/channel-email/changes.json +19 -0
- package/packages/bundled-features/src/channel-email/email-channel.ts +135 -29
- package/packages/bundled-features/src/channel-email/feature.ts +6 -3
- package/packages/bundled-features/src/channel-email/index.ts +7 -1
- package/packages/bundled-features/src/channel-email/pii-guard.ts +13 -0
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +15 -1
- package/packages/bundled-features/src/channel-email/types.ts +23 -0
- package/packages/bundled-features/src/channel-in-app/handlers/mark-read.write.ts +2 -2
- package/packages/bundled-features/src/config/changes.json +6 -0
- package/packages/bundled-features/src/config/handlers/set.write.ts +3 -7
- package/packages/bundled-features/src/config/resolver.ts +15 -12
- package/packages/bundled-features/src/config/write-helpers.ts +23 -0
- package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +48 -27
- package/packages/bundled-features/src/custom-fields/changes.json +6 -0
- package/packages/bundled-features/src/custom-fields/db/queries/user-data-rights.ts +2 -2
- package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +0 -1
- package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -11
- package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +7 -6
- package/packages/bundled-features/src/data-retention/changes.json +6 -0
- package/packages/bundled-features/src/data-retention/index.ts +1 -0
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +8 -6
- package/packages/bundled-features/src/delivery/attachment-ref.ts +105 -0
- package/packages/bundled-features/src/delivery/changes.json +66 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +152 -4
- package/packages/bundled-features/src/delivery/constants.ts +2 -0
- package/packages/bundled-features/src/delivery/delivery-service.ts +104 -26
- package/packages/bundled-features/src/delivery/feature.ts +1 -1
- package/packages/bundled-features/src/delivery/index.ts +19 -2
- package/packages/bundled-features/src/delivery/jobs.ts +97 -17
- package/packages/bundled-features/src/delivery/notify-factory.ts +14 -9
- package/packages/bundled-features/src/delivery/testing.ts +9 -1
- package/packages/bundled-features/src/delivery/types.ts +34 -18
- package/packages/bundled-features/src/delivery/unsubscribe.ts +5 -2
- package/packages/bundled-features/src/derivatives-sharp/render.ts +1 -22
- package/packages/bundled-features/src/document-ingest-foundation/pages.ts +6 -3
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +4 -4
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/packages/bundled-features/src/file-foundation/changes.json +6 -0
- package/packages/bundled-features/src/file-foundation/index.ts +7 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +89 -0
- package/packages/bundled-features/src/files-tenant-data/changes.json +6 -0
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +4 -0
- package/packages/bundled-features/src/folders/web/folder-manager.tsx +5 -2
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +11 -11
- package/packages/bundled-features/src/form-draft/db/queries/draft-count.ts +2 -2
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +2 -2
- package/packages/bundled-features/src/form-draft/release-file-refs.ts +3 -2
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +6 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +6 -6
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +2 -2
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-access-token.ts +13 -8
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +7 -7
- package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +2 -6
- package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +5 -13
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +4 -6
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +42 -25
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +4 -3
- package/packages/bundled-features/src/jobs/changes.json +19 -0
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +12 -4
- package/packages/bundled-features/src/jobs/feature.ts +7 -4
- package/packages/bundled-features/src/jobs/handlers/catalog.query.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +3 -1
- package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +5 -2
- package/packages/bundled-features/src/jobs/job-run-logger.ts +52 -17
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +7 -2
- package/packages/bundled-features/src/ledger/changes.json +7 -0
- package/packages/bundled-features/src/ledger/entity.ts +3 -1
- package/packages/bundled-features/src/ledger/recurring.ts +1 -3
- package/packages/bundled-features/src/managed-pages/branding.ts +33 -9
- package/packages/bundled-features/src/managed-pages/changes.json +6 -0
- package/packages/bundled-features/src/managed-pages/feature.ts +1 -0
- package/packages/bundled-features/src/managed-pages/i18n.ts +1 -0
- package/packages/bundled-features/src/managed-pages/screens/branding-screen.ts +8 -0
- package/packages/bundled-features/src/page-render/branding.ts +3 -0
- package/packages/bundled-features/src/page-render/markdown.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +2 -4
- package/packages/bundled-features/src/personal-access-tokens/handlers/list.query.ts +2 -1
- package/packages/bundled-features/src/personal-access-tokens/index.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/revoke-for-user.ts +2 -1
- package/packages/bundled-features/src/renderer-foundation/README.md +11 -10
- package/packages/bundled-features/src/renderer-foundation/api.ts +14 -14
- package/packages/bundled-features/src/renderer-foundation/changes.json +39 -0
- package/packages/bundled-features/src/renderer-foundation/constants.ts +6 -8
- package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
- package/packages/bundled-features/src/renderer-foundation/index.ts +13 -2
- package/packages/bundled-features/src/renderer-foundation/types.ts +134 -35
- package/packages/bundled-features/src/renderer-pdf/changes.json +12 -0
- package/packages/bundled-features/src/renderer-pdf/feature.ts +7 -1
- package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +162 -82
- package/packages/bundled-features/src/secrets/changes.json +6 -0
- package/packages/bundled-features/src/secrets/feature.ts +4 -3
- package/packages/bundled-features/src/secrets/secrets-context.ts +23 -3
- package/packages/bundled-features/src/sessions/db/queries/cleanup.ts +2 -2
- package/packages/bundled-features/src/sessions/feature.ts +2 -4
- package/packages/bundled-features/src/sessions/handlers/list.query.ts +2 -1
- package/packages/bundled-features/src/shared/attachment-file-name.ts +39 -0
- package/packages/bundled-features/src/shared/changes.json +6 -0
- package/packages/bundled-features/src/shared/chunked-entity-migration.ts +4 -1
- package/packages/bundled-features/src/shared/index.ts +6 -0
- package/packages/bundled-features/src/shared/is-tenant-db.ts +1 -1
- package/packages/bundled-features/src/shared/mail-template-body.ts +42 -0
- package/packages/bundled-features/src/shared/sharp-input-limits.ts +26 -0
- package/packages/bundled-features/src/shared/signup-handover.ts +4 -4
- package/packages/bundled-features/src/subscription-stripe/changes.json +6 -0
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +19 -11
- package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +3 -1
- package/packages/bundled-features/src/tags/handlers/assign-tag.write.ts +5 -1
- package/packages/bundled-features/src/tags/web/tag-chip.tsx +2 -1
- package/packages/bundled-features/src/template-resolver/README.md +66 -8
- package/packages/bundled-features/src/template-resolver/api.ts +85 -9
- package/packages/bundled-features/src/template-resolver/changes.json +186 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +278 -0
- package/packages/bundled-features/src/template-resolver/constants.ts +49 -11
- package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +335 -0
- package/packages/bundled-features/src/template-resolver/document-schema.ts +345 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +59 -6
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +188 -0
- package/packages/bundled-features/src/template-resolver/handlers/collection-set.write.ts +30 -2
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +160 -0
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +33 -0
- package/packages/bundled-features/src/template-resolver/handlers/discard-draft.write.ts +52 -0
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +138 -0
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +9 -6
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle-system.write.ts +42 -0
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle.write.ts +22 -0
- package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +151 -0
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +19 -18
- package/packages/bundled-features/src/template-resolver/handlers/override-diff.ts +49 -0
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +323 -0
- package/packages/bundled-features/src/template-resolver/handlers/preview.query.ts +25 -0
- package/packages/bundled-features/src/template-resolver/handlers/reset-to-system.write.ts +58 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-link-pdf.write.ts +34 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-remove.write.ts +50 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-shared.ts +24 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-upload.write.ts +135 -0
- package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +135 -0
- package/packages/bundled-features/src/template-resolver/handlers/set.write.ts +32 -3
- package/packages/bundled-features/src/template-resolver/handlers/shared.ts +7 -2
- package/packages/bundled-features/src/template-resolver/handlers/template-gates.ts +196 -0
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +233 -0
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +137 -0
- package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +91 -17
- package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +19 -71
- package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +16 -77
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +31 -0
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +116 -0
- package/packages/bundled-features/src/template-resolver/index.ts +111 -0
- package/packages/bundled-features/src/template-resolver/layout-appearance.ts +40 -0
- package/packages/bundled-features/src/template-resolver/layout-regions.ts +206 -0
- package/packages/bundled-features/src/template-resolver/linked-resources.ts +102 -0
- package/packages/bundled-features/src/template-resolver/pdf-constants.ts +12 -0
- package/packages/bundled-features/src/template-resolver/preview-display-html.ts +36 -0
- package/packages/bundled-features/src/template-resolver/print-document.ts +58 -0
- package/packages/bundled-features/src/template-resolver/qualified-names.ts +34 -0
- package/packages/bundled-features/src/template-resolver/record-mapping.ts +26 -0
- package/packages/bundled-features/src/template-resolver/resource-image.ts +47 -0
- package/packages/bundled-features/src/template-resolver/seeding.ts +171 -20
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +416 -0
- package/packages/bundled-features/src/template-resolver/table.ts +27 -1
- package/packages/bundled-features/src/template-resolver/template-bundle.ts +79 -0
- package/packages/bundled-features/src/template-resolver/template-diff.ts +78 -0
- package/packages/bundled-features/src/template-resolver/template-draft.ts +106 -0
- package/packages/bundled-features/src/template-resolver/template-meta.ts +192 -0
- package/packages/bundled-features/src/template-resolver/template-preview.ts +676 -0
- package/packages/bundled-features/src/template-resolver/template-resources.ts +114 -0
- package/packages/bundled-features/src/template-resolver/template-usage.ts +68 -0
- package/packages/bundled-features/src/template-resolver/template-validation.ts +230 -0
- package/packages/bundled-features/src/template-resolver/testing.ts +5 -5
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +20 -13
- package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +215 -0
- package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +37 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +1065 -0
- package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +72 -0
- package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +26 -0
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +121 -0
- package/packages/bundled-features/src/template-resolver/web/index.ts +26 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor-lazy.tsx +36 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +176 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-text.ts +37 -0
- package/packages/bundled-features/src/template-resolver/web/use-stable-by-key.ts +14 -0
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +3 -2
- package/packages/bundled-features/src/tenant/seeding.ts +41 -7
- package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +2 -1
- package/packages/bundled-features/src/tier-engine/changes.json +6 -0
- package/packages/bundled-features/src/tier-engine/feature.ts +11 -3
- package/packages/bundled-features/src/user/changes.json +6 -0
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +4 -4
- package/packages/bundled-features/src/user/handlers/detail.query.ts +3 -5
- package/packages/bundled-features/src/user/resolve-display-names.ts +8 -15
- package/packages/bundled-features/src/user/roles-input-schema.ts +2 -2
- package/packages/bundled-features/src/user/seeding.ts +19 -5
- package/packages/bundled-features/src/user-data-rights/changes.json +13 -0
- package/packages/bundled-features/src/user-data-rights/db/queries/forget-cleanup.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/email-templates.ts +5 -149
- package/packages/bundled-features/src/user-data-rights/feature.ts +72 -35
- package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +2 -1
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +5 -11
- package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +134 -53
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/mail-notifications.ts +157 -0
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +2 -1
- package/packages/bundled-features/src/user-profile/changes.json +1 -1
- package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +2 -1
- package/packages/bundled-features/src/waitlist/changes.json +7 -0
- package/packages/bundled-features/src/waitlist/feature.ts +17 -0
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +4 -5
- package/packages/bundled-features/src/waitlist/mail.ts +69 -18
- package/packages/bundled-features/src/waitlist/options.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +3 -3
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +7 -1
- package/samples/apps/ui-walkthrough/package.json +11 -0
- package/samples/apps/ui-walkthrough/playwright.config.ts +40 -7
- package/samples/apps/ui-walkthrough/src/app/auth-constants.ts +9 -0
- package/samples/apps/ui-walkthrough/src/app/client.tsx +6 -0
- package/samples/apps/ui-walkthrough/src/app/seed-templates.ts +239 -0
- package/samples/apps/ui-walkthrough/src/app/server.ts +5 -1
- package/samples/apps/ui-walkthrough/src/app/shell.tsx +12 -1
- package/samples/apps/ui-walkthrough/src/app/template-seed-data.ts +570 -0
- package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +72 -0
- package/samples/apps/ui-walkthrough/src/features/billing/i18n.ts +18 -0
- package/samples/apps/ui-walkthrough/src/features/billing/index.ts +2 -0
- package/samples/apps/ui-walkthrough/src/features/billing/web/index.ts +7 -0
- package/samples/apps/ui-walkthrough/src/run-config.ts +41 -1
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +0 -2
- package/samples/apps/use-all-bundled/feature-manifest.json +118 -21
- package/samples/apps/use-all-bundled/kumiko/migrations/0045_template-resource-meta.rebuild.json +4 -0
- package/samples/apps/use-all-bundled/kumiko/migrations/0046_pdf-rendered.rebuild.json +4 -0
- package/samples/apps/use-all-bundled/package.json +2 -0
- package/samples/apps/use-all-bundled/playwright.config.ts +38 -3
- package/samples/apps/use-all-bundled/schema/generate.ts +13 -3
- package/samples/apps/use-all-bundled/schema/schema.generated.ts +3 -0
- package/samples/apps/use-all-bundled/scripts/check-coverage.ts +3 -0
- package/samples/apps/use-all-bundled/src/app/auth-constants.ts +3 -0
- package/samples/apps/use-all-bundled/src/app/client.tsx +4 -0
- package/samples/apps/use-all-bundled/src/app/seed-documents.ts +128 -0
- package/samples/apps/use-all-bundled/src/app/seed.ts +4 -0
- package/samples/apps/use-all-bundled/src/run-config.ts +11 -11
- package/samples/recipes/delivery-notifications/README.md +26 -25
- package/samples/recipes/delivery-notifications/package.json +2 -1
- package/samples/recipes/delivery-notifications/src/feature.ts +64 -58
- package/samples/recipes/seed-migration/README.md +1 -1
- package/packages/bundled-features/src/renderer-simple/changes.json +0 -27
- package/packages/bundled-features/src/renderer-simple/feature.ts +0 -66
- package/packages/bundled-features/src/renderer-simple/index.ts +0 -8
- package/packages/bundled-features/src/renderer-simple/mail-blocks.ts +0 -138
- package/packages/bundled-features/src/renderer-simple/resolve-variables.ts +0 -57
- package/packages/bundled-features/src/renderer-simple/simple-renderer.ts +0 -304
|
@@ -13,7 +13,10 @@ import { markStaleJobRunsFailed } from "../db/queries/stale-run-sweep.js";
|
|
|
13
13
|
// Configurable per-app via JobsFeatureOptions.staleRunTimeoutHours.
|
|
14
14
|
export const DEFAULT_JOB_RUN_STALE_TIMEOUT_HOURS = 24;
|
|
15
15
|
|
|
16
|
-
export function createStaleRunSweepJob(
|
|
16
|
+
export function createStaleRunSweepJob(
|
|
17
|
+
timeoutHours: number,
|
|
18
|
+
queuedRetentionDays: number,
|
|
19
|
+
): JobHandlerFn {
|
|
17
20
|
return async (_payload, ctx) => {
|
|
18
21
|
if (!ctx.systemDb) {
|
|
19
22
|
throw new InternalError({
|
|
@@ -24,7 +27,7 @@ export function createStaleRunSweepJob(timeoutHours: number): JobHandlerFn {
|
|
|
24
27
|
const db = ctx.systemDb.unsafeRaw(
|
|
25
28
|
"stale-run sweep marks stuck runs of every tenant as failed",
|
|
26
29
|
) as DbConnection; // @cast-boundary db-operator — DbRunner narrows to DbConnection, jobs never run inside a DbTx
|
|
27
|
-
const result = await markStaleJobRunsFailed(db, timeoutHours);
|
|
30
|
+
const result = await markStaleJobRunsFailed(db, timeoutHours, queuedRetentionDays);
|
|
28
31
|
ctx.log?.info?.(`[jobs:stale-run-sweep] complete: ${JSON.stringify(result)}`);
|
|
29
32
|
};
|
|
30
33
|
}
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
import { acquireNamespacedAdvisoryLock, type DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
20
20
|
import { type Registry, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
21
21
|
import type {
|
|
22
|
+
JobDroppedMeta,
|
|
22
23
|
JobLogEntry,
|
|
23
24
|
JobMeta,
|
|
24
25
|
JobOutcomeMeta,
|
|
@@ -169,6 +170,7 @@ async function encryptStartedPayload(
|
|
|
169
170
|
const TENANT_JOB_FAILURE_LOCK_NAMESPACE = 0x746a666c;
|
|
170
171
|
// 'tjrl': serialises the finished-row write + prune per tenant run key.
|
|
171
172
|
const TENANT_JOB_RUN_LOCK_NAMESPACE = 0x746a726c;
|
|
173
|
+
const TENANT_RUN_CONFLICT_KEYS = ["tenantId", "jobName", "bullJobId"] as const;
|
|
172
174
|
|
|
173
175
|
// fw#3079 — which row the tenant-visible failure record lives in: one per
|
|
174
176
|
// (tenant, job, subject), so the next outcome of the same work replaces or
|
|
@@ -227,8 +229,11 @@ async function clearTenantJobFailure(
|
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
// fw#3616 — tenant-visible run state. One row per BullMQ job in
|
|
230
|
-
// store_tenant_job_runs
|
|
231
|
-
//
|
|
232
|
+
// store_tenant_job_runs, addressed by (tenant, job name, bullJobId): BullMQ
|
|
233
|
+
// ids are only unique per queue (api and worker lane both count from 1) and
|
|
234
|
+
// restart after a Redis reset, so the id alone must never pick a row. The
|
|
235
|
+
// writes below only touch rows of that triple, except pruning older finished
|
|
236
|
+
// rows of the same key.
|
|
232
237
|
type TenantRunKey = {
|
|
233
238
|
readonly tenantId: string;
|
|
234
239
|
readonly jobName: string;
|
|
@@ -255,14 +260,27 @@ async function recordTenantRunQueued(
|
|
|
255
260
|
// skip: system-tenant run, no tenant-scoped query reads it
|
|
256
261
|
if (!key) return;
|
|
257
262
|
const now = Temporal.Now.instant();
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
263
|
+
await transaction(db, async (tx) => {
|
|
264
|
+
// A finished row under the same id is left over from before a Redis reset
|
|
265
|
+
// (ids restart at 1), not this job. Only rows that went through this hook
|
|
266
|
+
// (queuedAt set) qualify: a fast worker can finish before the hook runs,
|
|
267
|
+
// and that row is this job's own and must stay.
|
|
268
|
+
await deleteMany(tx, tenantJobRunsTable, {
|
|
269
|
+
tenantId: key.tenantId,
|
|
270
|
+
jobName,
|
|
271
|
+
bullJobId,
|
|
272
|
+
status: { in: ["completed", "failed"] },
|
|
273
|
+
queuedAt: { gt: Temporal.Instant.fromEpochMilliseconds(0) },
|
|
274
|
+
});
|
|
275
|
+
// DO NOTHING: a fast worker can report the start before this enqueue hook
|
|
276
|
+
// returns, and a repeated jobId (debounce, fan-out dedup) is the same job.
|
|
277
|
+
await insertOnConflictDoNothing(
|
|
278
|
+
tx,
|
|
279
|
+
tenantJobRunsTable,
|
|
280
|
+
{ ...key, bullJobId, status: "queued", queuedAt: now, updatedAt: now },
|
|
281
|
+
{ conflictKeys: [...TENANT_RUN_CONFLICT_KEYS] },
|
|
282
|
+
);
|
|
283
|
+
});
|
|
266
284
|
}
|
|
267
285
|
|
|
268
286
|
async function recordTenantRunStarted(
|
|
@@ -282,7 +300,7 @@ async function recordTenantRunStarted(
|
|
|
282
300
|
tenantJobRunsTable,
|
|
283
301
|
{ ...key, bullJobId, status: "running", startedAt: now, updatedAt: now },
|
|
284
302
|
{
|
|
285
|
-
conflictKeys: [
|
|
303
|
+
conflictKeys: [...TENANT_RUN_CONFLICT_KEYS],
|
|
286
304
|
update: { status: "running", startedAt: now, finishedAt: null, updatedAt: now },
|
|
287
305
|
},
|
|
288
306
|
);
|
|
@@ -308,7 +326,7 @@ async function recordTenantRunOutcome(
|
|
|
308
326
|
db,
|
|
309
327
|
tenantJobRunsTable,
|
|
310
328
|
{ status: "queued", startedAt: null, finishedAt: null, updatedAt: now },
|
|
311
|
-
{ bullJobId },
|
|
329
|
+
{ tenantId: key.tenantId, jobName, bullJobId },
|
|
312
330
|
);
|
|
313
331
|
// skip: retry pending, the row stays active until the final attempt
|
|
314
332
|
return;
|
|
@@ -327,7 +345,10 @@ async function recordTenantRunOutcome(
|
|
|
327
345
|
tx,
|
|
328
346
|
tenantJobRunsTable,
|
|
329
347
|
{ ...key, bullJobId, status: result, finishedAt: now, updatedAt: now },
|
|
330
|
-
{
|
|
348
|
+
{
|
|
349
|
+
conflictKeys: [...TENANT_RUN_CONFLICT_KEYS],
|
|
350
|
+
update: { status: result, finishedAt: now, updatedAt: now },
|
|
351
|
+
},
|
|
331
352
|
);
|
|
332
353
|
await deleteMany(tx, tenantJobRunsTable, {
|
|
333
354
|
...key,
|
|
@@ -337,8 +358,18 @@ async function recordTenantRunOutcome(
|
|
|
337
358
|
});
|
|
338
359
|
}
|
|
339
360
|
|
|
340
|
-
async function dropTenantRunQueued(
|
|
341
|
-
|
|
361
|
+
async function dropTenantRunQueued(
|
|
362
|
+
db: DbConnection,
|
|
363
|
+
jobName: string,
|
|
364
|
+
bullJobId: string,
|
|
365
|
+
meta: JobDroppedMeta,
|
|
366
|
+
): Promise<void> {
|
|
367
|
+
await deleteMany(db, tenantJobRunsTable, {
|
|
368
|
+
tenantId: meta.tenantId,
|
|
369
|
+
jobName,
|
|
370
|
+
bullJobId,
|
|
371
|
+
status: "queued",
|
|
372
|
+
});
|
|
342
373
|
}
|
|
343
374
|
|
|
344
375
|
const log = createFallbackLogger("job-run-logger");
|
|
@@ -458,8 +489,12 @@ export function createJobRunLogger(opts: JobRunLoggerOptions): JobRunLoggerCallb
|
|
|
458
489
|
);
|
|
459
490
|
},
|
|
460
491
|
|
|
461
|
-
onJobDropped: async (jobName: string, bullJobId: string) => {
|
|
462
|
-
await isolateTenantRunWrite(
|
|
492
|
+
onJobDropped: async (jobName: string, bullJobId: string, meta: JobDroppedMeta) => {
|
|
493
|
+
await isolateTenantRunWrite(
|
|
494
|
+
() => dropTenantRunQueued(db, jobName, bullJobId, meta),
|
|
495
|
+
jobName,
|
|
496
|
+
bullJobId,
|
|
497
|
+
);
|
|
463
498
|
},
|
|
464
499
|
|
|
465
500
|
onJobStart: async (jobName: string, bullJobId: string, meta: JobMeta) => {
|
|
@@ -21,7 +21,7 @@ export type TenantJobRunStatus = "queued" | "running" | "completed" | "failed";
|
|
|
21
21
|
// job is invisible there. This table holds no payload, error text or logs by
|
|
22
22
|
// design — the tenant learns only that work is waiting, running or finished.
|
|
23
23
|
//
|
|
24
|
-
// One row per BullMQ job. Active rows (queued/running) are kept as they are;
|
|
24
|
+
// One row per BullMQ job and (tenant, job name). Active rows (queued/running) are kept as they are;
|
|
25
25
|
// a finished row replaces earlier finished rows of the same (tenant, job,
|
|
26
26
|
// subject), so only the latest outcome stays. `subject` is the canonical JSON
|
|
27
27
|
// of the job's declared subjectFields, or NULL for none — stored in clear, so
|
|
@@ -41,7 +41,12 @@ export const tenantJobRunsTable = pgTable(
|
|
|
41
41
|
updatedAt: instant("updated_at").default(sql`now()`).notNull(),
|
|
42
42
|
},
|
|
43
43
|
(t) => [
|
|
44
|
-
|
|
44
|
+
// BullMQ ids are unique per queue only; the id alone would let two tenants collide.
|
|
45
|
+
uniqueIndex("store_tenant_job_runs_tenant_job_bull_job_id_unique").on(
|
|
46
|
+
t.tenantId,
|
|
47
|
+
t.jobName,
|
|
48
|
+
t.bullJobId,
|
|
49
|
+
),
|
|
45
50
|
// Finishing a run prunes older outcomes by this key; the tenant query reads by it.
|
|
46
51
|
index("store_tenant_job_runs_tenant_job_subject_idx").on(t.tenantId, t.jobName, t.subject),
|
|
47
52
|
// Stale sweep and retention filter on age alone.
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.364.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "Document the duplicate check required before the (tenant_id, source_ref) unique index",
|
|
6
|
+
"detail": "The unique index on read_ledger_schedules (tenant_id, source_ref) is created in place and fails on existing duplicate pairs, and a projection rebuild over duplicate events fails the same way. Replay handling for duplicate events is not part of this change.",
|
|
7
|
+
"migration": "Before upgrading, check each database for duplicates and clean them up: SELECT tenant_id, source_ref, count(*) FROM read_ledger_schedules WHERE source_ref IS NOT NULL GROUP BY 1, 2 HAVING count(*) > 1; Any row returned makes the unique index migration abort."
|
|
8
|
+
},
|
|
2
9
|
{
|
|
3
10
|
"version": "0.354.0",
|
|
4
11
|
"type": "fix",
|
|
@@ -176,7 +176,9 @@ export const scheduleEntity = createEntity({
|
|
|
176
176
|
},
|
|
177
177
|
// Find-then-create by sourceRef races across tabs/devices; only the DB closes
|
|
178
178
|
// that gap. Partial because sourceRef is optional. A new name (not the old
|
|
179
|
-
// non-unique default) keeps the migration an in-place CREATE UNIQUE INDEX
|
|
179
|
+
// non-unique default) keeps the migration an in-place CREATE UNIQUE INDEX;
|
|
180
|
+
// that statement, like a projection rebuild, fails on existing duplicate
|
|
181
|
+
// (tenant_id, source_ref) pairs, so duplicates must be cleaned up beforehand.
|
|
180
182
|
indexes: [
|
|
181
183
|
{ columns: ["tenantId", "subjectType", "subjectId"] },
|
|
182
184
|
{
|
|
@@ -63,9 +63,7 @@ export function findReversedIds(
|
|
|
63
63
|
): Set<string> {
|
|
64
64
|
const ids = new Set(rows.map((r) => r.id));
|
|
65
65
|
return new Set(
|
|
66
|
-
rows
|
|
67
|
-
.filter((r) => r.reference !== null && ids.has(r.reference))
|
|
68
|
-
.map((r) => r.reference as string),
|
|
66
|
+
rows.flatMap((r) => (r.reference !== null && ids.has(r.reference) ? [r.reference] : [])),
|
|
69
67
|
);
|
|
70
68
|
}
|
|
71
69
|
|
|
@@ -50,6 +50,11 @@ const DESCRIPTION_PATTERN = { regex: "^[\\s\\S]{0,500}$" } as const;
|
|
|
50
50
|
// there.
|
|
51
51
|
const CUSTOM_CSS_PATTERN = { regex: "^[\\s\\S]{0,8000}$" } as const;
|
|
52
52
|
|
|
53
|
+
// Empty or a FileRef id (UUID); the upload UI that fills it ships separately.
|
|
54
|
+
const FILE_REF_ID_PATTERN = {
|
|
55
|
+
regex: "^$|^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
|
|
56
|
+
} as const;
|
|
57
|
+
|
|
53
58
|
export const LAYOUT_PRESETS = ["minimal", "centered", "wide"] as const;
|
|
54
59
|
|
|
55
60
|
// Companion toggle feature name. managed-pages reads ctx.hasFeature() against it
|
|
@@ -91,6 +96,12 @@ export const BRANDING_KEYS = {
|
|
|
91
96
|
pattern: HTTPS_PATTERN,
|
|
92
97
|
write: BRANDING_WRITE,
|
|
93
98
|
}),
|
|
99
|
+
// Ownership is checked where it is read (renderer-mail-html: FileRef must belong to the rendering tenant).
|
|
100
|
+
brandingLogoFile: createTenantConfig("text", {
|
|
101
|
+
default: "",
|
|
102
|
+
pattern: FILE_REF_ID_PATTERN,
|
|
103
|
+
write: BRANDING_WRITE,
|
|
104
|
+
}),
|
|
94
105
|
brandingLayoutPreset: createTenantConfig("select", {
|
|
95
106
|
default: "centered",
|
|
96
107
|
options: LAYOUT_PRESETS,
|
|
@@ -112,6 +123,7 @@ export const BRANDING_QN = {
|
|
|
112
123
|
siteUrl: "managed-pages:config:branding-site-url",
|
|
113
124
|
accentColor: "managed-pages:config:branding-accent-color",
|
|
114
125
|
logoUrl: "managed-pages:config:branding-logo-url",
|
|
126
|
+
logoFile: "managed-pages:config:branding-logo-file",
|
|
115
127
|
layoutPreset: "managed-pages:config:branding-layout-preset",
|
|
116
128
|
customCss: "managed-pages:config:branding-custom-css",
|
|
117
129
|
} as const;
|
|
@@ -129,17 +141,28 @@ async function readText(config: ConfigAccessor, qualifiedKey: string): Promise<s
|
|
|
129
141
|
// publicstatus' branding read), not a crash.
|
|
130
142
|
export async function readBranding(config: ConfigAccessor | undefined): Promise<BrandingTokens> {
|
|
131
143
|
if (!config) return EMPTY_BRANDING;
|
|
132
|
-
const [title, description, siteUrl, accentColor, logoUrl, layoutPreset] =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
144
|
+
const [title, description, siteUrl, accentColor, logoUrl, logoFile, layoutPreset] =
|
|
145
|
+
await Promise.all([
|
|
146
|
+
readText(config, BRANDING_QN.title),
|
|
147
|
+
readText(config, BRANDING_QN.description),
|
|
148
|
+
readText(config, BRANDING_QN.siteUrl),
|
|
149
|
+
readText(config, BRANDING_QN.accentColor),
|
|
150
|
+
readText(config, BRANDING_QN.logoUrl),
|
|
151
|
+
readText(config, BRANDING_QN.logoFile),
|
|
152
|
+
readText(config, BRANDING_QN.layoutPreset),
|
|
153
|
+
]);
|
|
140
154
|
// customCss stays "" here — it is gated (allowCustomCss + per-tenant toggle)
|
|
141
155
|
// and read separately by the branding query via readCustomCss.
|
|
142
|
-
return {
|
|
156
|
+
return {
|
|
157
|
+
title,
|
|
158
|
+
description,
|
|
159
|
+
siteUrl,
|
|
160
|
+
accentColor,
|
|
161
|
+
logoUrl,
|
|
162
|
+
logoFile,
|
|
163
|
+
layoutPreset,
|
|
164
|
+
customCss: "",
|
|
165
|
+
};
|
|
143
166
|
}
|
|
144
167
|
|
|
145
168
|
// Read the raw, untrusted custom CSS for the request's tenant. The caller (the
|
|
@@ -167,6 +190,7 @@ export function coerceBranding(value: unknown): BrandingTokens {
|
|
|
167
190
|
siteUrl: stringField(source, "siteUrl"),
|
|
168
191
|
accentColor: stringField(source, "accentColor"),
|
|
169
192
|
logoUrl: stringField(source, "logoUrl"),
|
|
193
|
+
logoFile: stringField(source, "logoFile"),
|
|
170
194
|
layoutPreset: stringField(source, "layoutPreset"),
|
|
171
195
|
customCss: stringField(source, "customCss"),
|
|
172
196
|
};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.363.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "Branding screen edits the logo file and readBranding returns logoFile",
|
|
6
|
+
"detail": "The branding settings screen has a logo upload for managed-pages:config:branding-logo-file, and readBranding and the branding query include logoFile next to logoUrl. The template resource errors template_resource_too_large and template_resource_unsupported_image have German and Spanish texts."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.353.0",
|
|
4
10
|
"type": "fix",
|
|
@@ -42,6 +42,7 @@ export const MANAGED_PAGES_I18N: Readonly<Record<string, LocalizedString>> = {
|
|
|
42
42
|
en: "Accent color",
|
|
43
43
|
},
|
|
44
44
|
"managed-pages:entity:__config-edit__:field:logoUrl": { en: "Logo URL" },
|
|
45
|
+
"managed-pages:entity:__config-edit__:field:logoFile": { en: "Logo file" },
|
|
45
46
|
"managed-pages:entity:__config-edit__:field:layoutPreset": { en: "Layout" },
|
|
46
47
|
|
|
47
48
|
// Section-Header
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ConfigEditScreenDefinition,
|
|
3
|
+
createImageField,
|
|
3
4
|
createSelectField,
|
|
4
5
|
createTextField,
|
|
5
6
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -35,6 +36,7 @@ export function createBrandingSettingsScreen(opts: {
|
|
|
35
36
|
siteUrl: BRANDING_QN.siteUrl,
|
|
36
37
|
accentColor: BRANDING_QN.accentColor,
|
|
37
38
|
logoUrl: BRANDING_QN.logoUrl,
|
|
39
|
+
logoFile: BRANDING_QN.logoFile,
|
|
38
40
|
layoutPreset: BRANDING_QN.layoutPreset,
|
|
39
41
|
},
|
|
40
42
|
fields: {
|
|
@@ -62,6 +64,11 @@ export function createBrandingSettingsScreen(opts: {
|
|
|
62
64
|
personal: false,
|
|
63
65
|
reason: "is_business_data",
|
|
64
66
|
}),
|
|
67
|
+
logoFile: createImageField({
|
|
68
|
+
accept: ["image/png", "image/jpeg", "image/webp"],
|
|
69
|
+
personal: false,
|
|
70
|
+
reason: "is_business_data",
|
|
71
|
+
}),
|
|
65
72
|
layoutPreset: createSelectField({ options: LAYOUT_PRESETS }),
|
|
66
73
|
},
|
|
67
74
|
layout: {
|
|
@@ -75,6 +82,7 @@ export function createBrandingSettingsScreen(opts: {
|
|
|
75
82
|
{ field: "description", span: 2 },
|
|
76
83
|
{ field: "siteUrl", span: 1 },
|
|
77
84
|
{ field: "logoUrl", span: 1 },
|
|
85
|
+
{ field: "logoFile", span: 1 },
|
|
78
86
|
{ field: "accentColor", span: 1 },
|
|
79
87
|
],
|
|
80
88
|
},
|
|
@@ -13,6 +13,8 @@ export type BrandingTokens = {
|
|
|
13
13
|
readonly siteUrl: string;
|
|
14
14
|
readonly accentColor: string;
|
|
15
15
|
readonly logoUrl: string;
|
|
16
|
+
// FileRef id of an uploaded logo ("" = none); the renderer resolves it, tenant ownership is checked there.
|
|
17
|
+
readonly logoFile: string;
|
|
16
18
|
readonly layoutPreset: string;
|
|
17
19
|
// RAW, untrusted tenant CSS — "" unless the custom-css capability is gated on
|
|
18
20
|
// (managed-pages allowCustomCss + the per-tenant toggle). It is NOT pre-
|
|
@@ -29,6 +31,7 @@ export const EMPTY_BRANDING: BrandingTokens = {
|
|
|
29
31
|
siteUrl: "",
|
|
30
32
|
accentColor: "",
|
|
31
33
|
logoUrl: "",
|
|
34
|
+
logoFile: "",
|
|
32
35
|
layoutPreset: "",
|
|
33
36
|
customCss: "",
|
|
34
37
|
};
|
|
@@ -25,7 +25,7 @@ safeRenderer.use({
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
export function renderSafeMarkdown(markdown: string): string {
|
|
28
|
-
// @cast-boundary marked.parse return-type ist `string | Promise<string>`;
|
|
28
|
+
// @cast-boundary third-party-type-gap marked.parse return-type ist `string | Promise<string>`;
|
|
29
29
|
// `{ async: false }` garantiert sync (string) — Cast nur API-Vertragsfix.
|
|
30
30
|
return safeRenderer.parse(markdown, { async: false }) as string;
|
|
31
31
|
}
|
|
@@ -42,11 +42,9 @@ export function bindPatAutoRevokeOnPasswordChangeFromFeature(
|
|
|
42
42
|
): BindPatAutoRevokeOnPasswordChange | undefined {
|
|
43
43
|
const exports = feature.exports;
|
|
44
44
|
if (exports && typeof exports === "object" && "bindAutoRevokeOnPasswordChange" in exports) {
|
|
45
|
-
const { bindAutoRevokeOnPasswordChange } = exports
|
|
46
|
-
bindAutoRevokeOnPasswordChange: unknown;
|
|
47
|
-
};
|
|
45
|
+
const { bindAutoRevokeOnPasswordChange } = exports;
|
|
48
46
|
if (typeof bindAutoRevokeOnPasswordChange === "function") {
|
|
49
|
-
// @cast-boundary exports
|
|
47
|
+
// @cast-boundary feature-exports — feature.exports is untyped by design
|
|
50
48
|
return bindAutoRevokeOnPasswordChange as BindPatAutoRevokeOnPasswordChange;
|
|
51
49
|
}
|
|
52
50
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { definePagedQueryHandler, MAX_LIST_LIMIT } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
3
4
|
import * as z from "zod";
|
|
4
5
|
import { decryptStoredPii } from "../../shared/index.js";
|
|
5
6
|
import { isExpiredAt } from "../expiry.js";
|
|
@@ -13,7 +14,7 @@ const SORTABLE_COLUMNS = ["id", "name", "createdAt", "expiresAt", "revokedAt"] a
|
|
|
13
14
|
type SortableColumn = (typeof SORTABLE_COLUMNS)[number];
|
|
14
15
|
|
|
15
16
|
function isSortableColumn(value: string): value is SortableColumn {
|
|
16
|
-
return (SORTABLE_COLUMNS
|
|
17
|
+
return isOneOf(SORTABLE_COLUMNS, value);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
type PatStatus = "active" | "revoked" | "expired";
|
|
@@ -29,7 +29,7 @@ export { expandScopes } from "./scopes.js";
|
|
|
29
29
|
export function patRateLimitFromFeature(feature: FeatureDefinition): PatRateLimit {
|
|
30
30
|
const exports = feature.exports;
|
|
31
31
|
if (exports && typeof exports === "object" && "rateLimit" in exports) {
|
|
32
|
-
const { rateLimit } = exports
|
|
32
|
+
const { rateLimit } = exports;
|
|
33
33
|
if (
|
|
34
34
|
rateLimit &&
|
|
35
35
|
typeof rateLimit === "object" &&
|
|
@@ -4,6 +4,7 @@ import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
|
4
4
|
import { append } from "@cosmicdrift/kumiko-framework/event-store";
|
|
5
5
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
6
6
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
7
|
+
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
7
8
|
import {
|
|
8
9
|
PAT_REVOKED_AGGREGATE_TYPE,
|
|
9
10
|
PAT_REVOKED_EVENT_QN,
|
|
@@ -13,7 +14,7 @@ import { apiTokenTable } from "./schema/api-token.js";
|
|
|
13
14
|
|
|
14
15
|
// A pool has begin(); a tx/savepoint handle does not and is already atomic.
|
|
15
16
|
function canBeginTransaction(db: DbRunner): boolean {
|
|
16
|
-
return typeof (db
|
|
17
|
+
return typeof propertyOf(db, "begin") === "function";
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
// Cross-tenant revoke: password-change and MFA-enable/disable are account-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# renderer-foundation
|
|
2
2
|
|
|
3
|
-
Plugin-Foundation für Renderer (
|
|
3
|
+
Plugin-Foundation für Renderer (HTML-Mail, PDF, Image). Plugins (`renderer-mail-html`, `renderer-puppeteer-client`) registrieren sich via `r.useExtension("renderer", "<name>", { kinds, render })`.
|
|
4
4
|
|
|
5
5
|
**Plan-Doc:** [`kumiko-platform/docs/plans/features/renderer-foundation.md`](../../../../../../kumiko-platform/docs/plans/features/renderer-foundation.md)
|
|
6
6
|
|
|
@@ -16,8 +16,7 @@ import {
|
|
|
16
16
|
const features = [
|
|
17
17
|
createTemplateResolverFeature(),
|
|
18
18
|
createRendererFoundationFeature(),
|
|
19
|
-
|
|
20
|
-
createRendererMailHtmlFeature(), // Plugin (enterprise)
|
|
19
|
+
createRendererMailHtmlFeature(), // Plugin: kind "mail"
|
|
21
20
|
// weitere Plugins...
|
|
22
21
|
];
|
|
23
22
|
|
|
@@ -38,23 +37,25 @@ import { requireRendererFoundation } from "@cosmicdrift/kumiko-bundled-features/
|
|
|
38
37
|
|
|
39
38
|
async function sendMail(ctx, tenantId) {
|
|
40
39
|
const foundation = requireRendererFoundation(ctx, "sendMail");
|
|
41
|
-
const renderer = foundation.createRendererForTenant({ tenantId, kind: "mail
|
|
40
|
+
const renderer = foundation.createRendererForTenant({ tenantId, kind: "mail" });
|
|
42
41
|
const result = await renderer.render(
|
|
43
42
|
{
|
|
44
|
-
kind: "mail
|
|
45
|
-
payload: {
|
|
43
|
+
kind: "mail",
|
|
44
|
+
payload: { slug: "billing:notify:invoice-ready", locale: "de", variables: { name: "Frau Schmidt" } },
|
|
46
45
|
},
|
|
47
|
-
{ db: ctx.db, registry: ctx.registry, tenantId },
|
|
46
|
+
{ db: ctx.db, registry: ctx.registry, tenantId, config: ctx.config },
|
|
48
47
|
);
|
|
49
|
-
// result.html, result.text
|
|
48
|
+
// result.subject, result.html, result.text, result.previewText
|
|
50
49
|
}
|
|
51
50
|
```
|
|
52
51
|
|
|
53
|
-
Plugins ohne Service-Deps
|
|
52
|
+
Plugins ohne Service-Deps ignorieren den zweiten `ctx`-Parameter.
|
|
53
|
+
|
|
54
|
+
Mails einer `r.notification` rendert die Delivery selbst: `template` + `meta` an der Notification werden beim Boot gesät (`seedNotificationTemplates`) und über kind `"mail"` gerendert. Ohne `template` + `meta` scheitert der Versand über den Email-Channel.
|
|
54
55
|
|
|
55
56
|
## Plugin-Auswahl-Reihenfolge
|
|
56
57
|
|
|
57
|
-
1. Tenant-Override (Config-Key `rendererPluginByKind`, z.B. `{ "mail
|
|
58
|
+
1. Tenant-Override (Config-Key `rendererPluginByKind`, z.B. `{ "mail": "mail-html" }`)
|
|
58
59
|
2. `DEFAULT_PLUGIN_BY_KIND` aus constants
|
|
59
60
|
3. Erstes Plugin im Pool das das kind bedient
|
|
60
61
|
4. `RendererError("no_plugin_for_kind")` wenn nichts passt
|
|
@@ -10,21 +10,21 @@ import { RendererError, type RendererPlugin } from "./types.js";
|
|
|
10
10
|
|
|
11
11
|
export type RendererFoundationApi = {
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Selects + returns a RendererPlugin for (tenantId × kind). Callers
|
|
14
|
+
* then invoke `plugin.render(req, ctx)`.
|
|
15
15
|
*
|
|
16
|
-
* **
|
|
17
|
-
* 1. Tenant-
|
|
18
|
-
* 2. DEFAULT_PLUGIN_BY_KIND
|
|
19
|
-
* 3.
|
|
20
|
-
* 4. `RendererError("no_plugin_for_kind")`
|
|
16
|
+
* **Selection order:**
|
|
17
|
+
* 1. Tenant-specific config (config bundle: `rendererPluginByKind`)
|
|
18
|
+
* 2. DEFAULT_PLUGIN_BY_KIND from constants
|
|
19
|
+
* 3. First plugin in the pool that serves the kind
|
|
20
|
+
* 4. `RendererError("no_plugin_for_kind")` if nothing matches
|
|
21
21
|
*
|
|
22
|
-
* **Caller
|
|
23
|
-
* `ctx.user.tenantId`). Plugins
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* Plugins
|
|
22
|
+
* **Caller pattern:** `tenantId` MUST come from the server (typically
|
|
23
|
+
* `ctx.user.tenantId`). Plugins that need service access (e.g.
|
|
24
|
+
* `@cosmicdrift/kumiko-renderer-mail-html` for layout resolution via `template-resolver`)
|
|
25
|
+
* receive `RendererContext` as the second argument to `render()` —
|
|
26
|
+
* matches the pattern of `DeliveryChannel.send(addr, msg, ctx)`.
|
|
27
|
+
* Plugins without service deps ignore ctx.
|
|
28
28
|
*/
|
|
29
29
|
readonly createRendererForTenant: (args: {
|
|
30
30
|
readonly tenantId: TenantId;
|
|
@@ -66,7 +66,7 @@ export function createRendererFoundationApi(
|
|
|
66
66
|
|
|
67
67
|
// 4. Kein Plugin → Error
|
|
68
68
|
throw new RendererError(
|
|
69
|
-
`[renderer-foundation] no plugin registered for kind="${kind}". Mount at least one plugin (renderer-
|
|
69
|
+
`[renderer-foundation] no plugin registered for kind="${kind}". Mount at least one plugin (renderer-mail-html, renderer-puppeteer-client) for this kind.`,
|
|
70
70
|
"no_plugin_for_kind",
|
|
71
71
|
);
|
|
72
72
|
},
|
|
@@ -1,4 +1,43 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.363.0",
|
|
4
|
+
"type": "breaking",
|
|
5
|
+
"title": "document-pdf requests name a template, the response carries fileName",
|
|
6
|
+
"detail": "The document-pdf payload is { slug, locale, variables } instead of ready HTML in content. The response has a fileName, RendererContext gets an optional filesForTenant (file access per tenant) and RendererError has the new code unavailable for a renderer backend that cannot be reached.",
|
|
7
|
+
"migration": "Callers that sent `payload: { content }` switch to a document-pdf template and pass slug, locale and variables (money-horse credit-schedule-pdf follows in #621); renderer-pdf callers pass slug and locale too. The enterprise package @cosmicdriftgamestudio/kumiko-renderer-puppeteer-client no longer builds against these types and is replaced by @cosmicdrift/kumiko-renderer-puppeteer-client."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"version": "0.363.0",
|
|
11
|
+
"type": "improvement",
|
|
12
|
+
"title": "renderer plugins can preview a document",
|
|
13
|
+
"detail": "RendererPlugin gets the optional capability previewDocument(req, ctx), which returns the filled-in body and layout regions without calling the print service. template-resolver:query:preview with kind document-pdf uses it, so the editor shows replaced placeholders and the resource and required-placeholder checks; without such a plugin the preview keeps returning the body and raw layout."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"version": "0.363.0",
|
|
17
|
+
"type": "improvement",
|
|
18
|
+
"title": "PdfOptions has landscape",
|
|
19
|
+
"detail": "The document-pdf options gain landscape, which the puppeteer-service passes to Chromium."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"version": "0.363.0",
|
|
23
|
+
"type": "breaking",
|
|
24
|
+
"title": "RenderRequest kind mail replaces mail-html",
|
|
25
|
+
"detail": "RenderRequest and RenderResponse use kind mail with a MailPayload ({ slug, locale, variables, unsubscribeUrl? }) and a response of { subject, html, text, previewText? }. The export MailHtmlPayload is removed; use MailPayload. The default plugin name for kind mail stays mail-html. RendererContext gains an optional config accessor that plugins use for tenant locale, currency and branding.",
|
|
26
|
+
"migration": "Replace kind mail-html with mail in createRendererForTenant and render calls, pass { slug, locale, variables } instead of content/contentFormat, and read the subject from the response instead of building it yourself. Pass ctx.config in the RendererContext to get tenant locale, currency and branding."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"version": "0.363.0",
|
|
30
|
+
"type": "breaking",
|
|
31
|
+
"title": "renderer-simple, MailBranding and renderer kind \"notification\" are removed",
|
|
32
|
+
"detail": "Every mail is a stored template rendered by renderer-mail-html now. renderer-simple (createRendererSimpleFeature, simpleRenderer, createSimpleRenderer, RendererSimpleOptions, mail blocks with LocalizedText, MailBadge, MailChip, MailStage, MailStageState and MailTone), the MailBranding type, the renderer kind \"notification\" and AuthMailContent are gone. NotificationRenderer and RendererInput are removed from delivery, and createChannelEmailFeature no longer takes a renderer option. The type NotificationPayload is removed from renderer-foundation. The template-resolver keeps \"notification\" only as a stored kind so legacy rows stay readable; RENDER_KINDS is mail, document-pdf and image-snapshot.",
|
|
33
|
+
"migration": "Remove createRendererSimpleFeature() and any renderer or mailBranding option from your feature list and createChannelEmailFeature call. An r.notification that is sent by email needs template + meta (or localizedTemplate) and variables; the sections/header/footer data it carried is replaced by template variables. Branding (logo, footer) now comes from the tenant branding config and the mail layout of renderer-mail-html. Mount template-resolver, renderer-foundation and @cosmicdrift/kumiko-renderer-mail-html (composeRendererStack() mounts all of them) and run seedNotificationTemplates at boot. Existing template rows with kind \"notification\" are no longer rendered; re-create them as kind \"mail\" or archive them."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"version": "0.363.0",
|
|
37
|
+
"type": "improvement",
|
|
38
|
+
"title": "MailPayload.draft renders an unsaved mail template; RendererContext.db also accepts a TenantDb",
|
|
39
|
+
"detail": "MailDraft (content, meta, variableSchema) replaces the stored body and subject of the template with the payload's slug and keeps its stored images; a slug without a stored template renders as a tenant draft. The renderer sanitizes the draft body and validates its meta again, and the response carries no template provenance. RendererContext.db is widened from DbConnection to DbRunner | TenantDb."
|
|
40
|
+
},
|
|
2
41
|
{
|
|
3
42
|
"version": "0.306.0",
|
|
4
43
|
"type": "breaking",
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
// Re-export
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// Re-export from template-resolver — same RenderKind domain. Both
|
|
2
|
+
// bundles share the enum so plugins (@cosmicdrift/kumiko-renderer-mail-html etc.)
|
|
3
|
+
// do not have to import two sources. Cross-feature imports go
|
|
4
|
+
// through the barrel (../template-resolver), not via deep import.
|
|
5
5
|
import type { RenderKind as RenderKindLocal } from "../template-resolver/index.js";
|
|
6
6
|
|
|
7
|
-
// Extension-point name for renderer plugins (renderer-
|
|
8
|
-
// renderer-mail-html, renderer-puppeteer-client, ...).
|
|
7
|
+
// Extension-point name for renderer plugins (@cosmicdrift/kumiko-renderer-mail-html, @cosmicdrift/kumiko-renderer-puppeteer-client, ...).
|
|
9
8
|
export const RENDERER_EXTENSION = "renderer" as const;
|
|
10
9
|
|
|
11
10
|
export {
|
|
@@ -18,8 +17,7 @@ export {
|
|
|
18
17
|
// Standard-Default-Plugin pro Kind, wenn Tenant keine explizite Config
|
|
19
18
|
// gesetzt hat. App-Bootstrap kann das via TenantConfigKey überschreiben.
|
|
20
19
|
export const DEFAULT_PLUGIN_BY_KIND: Readonly<Record<RenderKindLocal, string>> = {
|
|
21
|
-
|
|
22
|
-
"mail-html": "mail-html",
|
|
20
|
+
mail: "mail-html",
|
|
23
21
|
"document-pdf": "puppeteer",
|
|
24
22
|
"image-snapshot": "puppeteer",
|
|
25
23
|
};
|