@cosmicdrift/kumiko-samples 0.362.0 → 0.363.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 +17 -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/auth-email-password/changes.json +14 -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/invite-create.write.ts +4 -5
- 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/token-request-handler.ts +3 -12
- 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-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 +7 -0
- package/packages/bundled-features/src/billing-foundation/constants.ts +5 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +38 -44
- 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 +115 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +30 -30
- 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-request.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +2 -2
- 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 +2 -2
- package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -6
- 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 +122 -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/handlers/set.write.ts +1 -5
- package/packages/bundled-features/src/custom-fields/db/queries/user-data-rights.ts +2 -2
- 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 +84 -0
- package/packages/bundled-features/src/delivery/changes.json +48 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +129 -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 +17 -2
- package/packages/bundled-features/src/delivery/jobs.ts +48 -5
- 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 +28 -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-foundation/index.ts +7 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +86 -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/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-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 +40 -25
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +4 -3
- package/packages/bundled-features/src/jobs/handlers/catalog.query.ts +1 -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 +132 -35
- package/packages/bundled-features/src/renderer-pdf/feature.ts +7 -1
- 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/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 +25 -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/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 +63 -7
- package/packages/bundled-features/src/template-resolver/api.ts +83 -8
- package/packages/bundled-features/src/template-resolver/changes.json +118 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +214 -0
- package/packages/bundled-features/src/template-resolver/constants.ts +39 -11
- package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +335 -0
- package/packages/bundled-features/src/template-resolver/document-schema.ts +323 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +56 -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 +142 -0
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +72 -0
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +83 -0
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +3 -3
- 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 +108 -0
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +5 -7
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +250 -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 +106 -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 +184 -0
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +183 -0
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +55 -0
- package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +57 -10
- 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 +15 -76
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +17 -0
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +116 -0
- package/packages/bundled-features/src/template-resolver/index.ts +100 -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/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 +30 -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 +147 -17
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +416 -0
- package/packages/bundled-features/src/template-resolver/table.ts +14 -0
- 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-meta.ts +129 -0
- package/packages/bundled-features/src/template-resolver/template-preview.ts +573 -0
- package/packages/bundled-features/src/template-resolver/template-resources.ts +74 -0
- package/packages/bundled-features/src/template-resolver/template-usage.ts +48 -0
- package/packages/bundled-features/src/template-resolver/template-validation.ts +229 -0
- package/packages/bundled-features/src/template-resolver/testing.ts +5 -5
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +9 -6
- package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +162 -0
- package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +28 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +850 -0
- package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +61 -0
- package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +26 -0
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +100 -0
- package/packages/bundled-features/src/template-resolver/web/index.ts +19 -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 +169 -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/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/roles-input-schema.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/changes.json +7 -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/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/workflow-runner/event-trigger.ts +1 -2
- package/samples/apps/ui-walkthrough/package.json +2 -0
- 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 +154 -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 +354 -0
- package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +68 -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 +37 -1
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +0 -2
- package/samples/apps/use-all-bundled/feature-manifest.json +115 -20
- 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
|
@@ -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
|
};
|
|
@@ -2,18 +2,18 @@ import { defineFeature, type Registry } from "@cosmicdrift/kumiko-framework/engi
|
|
|
2
2
|
import { RENDERER_EXTENSION } from "./constants.js";
|
|
3
3
|
import { isRendererRegistrationPlugin, type RendererPlugin } from "./types.js";
|
|
4
4
|
|
|
5
|
-
// renderer-foundation —
|
|
6
|
-
//
|
|
5
|
+
// renderer-foundation — plugin foundation for renderers (HTML mail, PDF,
|
|
6
|
+
// image). Plan doc:
|
|
7
7
|
// kumiko-platform/docs/plans/features/renderer-foundation.md
|
|
8
8
|
//
|
|
9
|
-
// Pattern
|
|
10
|
-
//
|
|
11
|
-
// renderer-puppeteer-client)
|
|
12
|
-
//
|
|
9
|
+
// Pattern symmetric to ai-foundation: the foundation defines the
|
|
10
|
+
// extension point `renderer`, plugins (@cosmicdrift/kumiko-renderer-mail-html,
|
|
11
|
+
// @cosmicdrift/kumiko-renderer-puppeteer-client) register via `r.useExtension`.
|
|
12
|
+
// Consumers get a plugin at runtime via createRendererForTenant.
|
|
13
13
|
export function createRendererFoundationFeature() {
|
|
14
14
|
return defineFeature("renderer-foundation", (r) => {
|
|
15
15
|
r.describe(
|
|
16
|
-
'Plugin registry for content rendering (
|
|
16
|
+
'Plugin registry for content rendering (mail HTML, PDF, images): call `foundation.createRendererForTenant({ tenantId, kind })` at render time to get the right renderer plugin selected by kind, with tenant-level overrides via the `rendererPluginByKind` config key. Requires `template-resolver` (declared via `r.requires`). Low-level building block \u2014 add `renderer-mail-html` (or write a custom plugin via `r.useExtension("renderer", name, { kinds, render })`) rather than using this feature alone.',
|
|
17
17
|
);
|
|
18
18
|
r.uiHints({
|
|
19
19
|
displayLabel: "Renderer Foundation",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { attachmentFileNameSchema } from "../shared/index.js";
|
|
1
2
|
export {
|
|
2
3
|
createRendererFoundationApi,
|
|
3
4
|
type RendererFoundationApi,
|
|
@@ -14,15 +15,25 @@ export {
|
|
|
14
15
|
export { collectRendererPlugins, createRendererFoundationFeature } from "./feature.js";
|
|
15
16
|
export {
|
|
16
17
|
type DocumentPayload,
|
|
18
|
+
type DocumentPdfDraft,
|
|
19
|
+
type DocumentPdfPayload,
|
|
20
|
+
type DocumentPreview,
|
|
21
|
+
type DocumentPreviewRequest,
|
|
22
|
+
type FileAttachmentRef,
|
|
17
23
|
type ImageOptions,
|
|
24
|
+
type InlineAttachmentRef,
|
|
18
25
|
isRendererRegistrationPlugin,
|
|
19
|
-
type
|
|
20
|
-
type
|
|
26
|
+
type MailAttachmentRef,
|
|
27
|
+
type MailDraft,
|
|
28
|
+
type MailPayload,
|
|
21
29
|
type PdfOptions,
|
|
30
|
+
type RenderedPdfAttachmentRef,
|
|
31
|
+
type RenderedTemplateRef,
|
|
22
32
|
type RendererContext,
|
|
23
33
|
RendererError,
|
|
24
34
|
type RendererPlugin,
|
|
25
35
|
type RendererRegistrationPlugin,
|
|
36
|
+
type RenderProvenance,
|
|
26
37
|
type RenderRequest,
|
|
27
38
|
type RenderResponse,
|
|
28
39
|
} from "./types.js";
|
|
@@ -1,43 +1,101 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { Registry, TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
1
|
+
import type { DbRunner, TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
+
import type { ConfigAccessor, Registry, TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import type { FileContext } from "@cosmicdrift/kumiko-framework/files";
|
|
4
|
+
import type {
|
|
5
|
+
LinkedResourceEntry,
|
|
6
|
+
MailTemplateMeta,
|
|
7
|
+
PdfPageFormat,
|
|
8
|
+
PdfTemplateMeta,
|
|
9
|
+
TemplateScope,
|
|
10
|
+
} from "../template-resolver/index.js";
|
|
3
11
|
import { type ContentFormat, RENDERER_EXTENSION, type RenderKind } from "./constants.js";
|
|
4
12
|
|
|
5
13
|
// RenderRequest — Plugins erhalten das. Resource-Mode wählt der Caller
|
|
6
14
|
// (renderer-foundation api) je nach kind oder explizit. Plugin selbst
|
|
7
15
|
// entscheidet nicht — es bekommt eine schon aufgelöste Form.
|
|
8
16
|
export type RenderRequest =
|
|
9
|
-
| { kind: "
|
|
10
|
-
| { kind: "
|
|
11
|
-
| { kind: "document-pdf"; payload: DocumentPayload; options?: PdfOptions }
|
|
17
|
+
| { kind: "mail"; payload: MailPayload }
|
|
18
|
+
| { kind: "document-pdf"; payload: DocumentPdfPayload; options?: PdfOptions }
|
|
12
19
|
| { kind: "image-snapshot"; payload: DocumentPayload; options?: ImageOptions };
|
|
13
20
|
|
|
14
|
-
export type
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
export type RenderedTemplateRef = {
|
|
22
|
+
readonly templateId: string;
|
|
23
|
+
readonly templateVersion: number;
|
|
24
|
+
readonly scope: TemplateScope;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type RenderProvenance = {
|
|
28
|
+
readonly template?: RenderedTemplateRef;
|
|
29
|
+
readonly layout?: RenderedTemplateRef;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Image a rendered mail embeds by Content-ID; the send job reads the bytes.
|
|
33
|
+
export type InlineAttachmentRef = {
|
|
34
|
+
readonly kind: "inline";
|
|
35
|
+
readonly cid: string;
|
|
36
|
+
readonly fileRefId: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// A stored file attached as-is; the send job reads the bytes.
|
|
40
|
+
export type FileAttachmentRef = {
|
|
41
|
+
readonly kind: "file";
|
|
42
|
+
readonly fileRefId: string;
|
|
43
|
+
readonly fileName: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// A PDF rendered from a stored document template when the mail is sent, so the job
|
|
47
|
+
// payload carries the variables instead of the bytes.
|
|
48
|
+
export type RenderedPdfAttachmentRef = {
|
|
49
|
+
readonly kind: "rendered-pdf";
|
|
50
|
+
readonly templateSlug: string;
|
|
51
|
+
readonly variables: Readonly<Record<string, unknown>>;
|
|
52
|
+
readonly fileName: string;
|
|
53
|
+
readonly locale?: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type MailAttachmentRef = InlineAttachmentRef | FileAttachmentRef | RenderedPdfAttachmentRef;
|
|
57
|
+
|
|
58
|
+
export type RenderResponse = (
|
|
59
|
+
| {
|
|
60
|
+
kind: "mail";
|
|
61
|
+
subject: string;
|
|
62
|
+
html: string;
|
|
63
|
+
text: string;
|
|
64
|
+
previewText?: string;
|
|
65
|
+
attachments?: readonly MailAttachmentRef[];
|
|
66
|
+
}
|
|
67
|
+
| {
|
|
68
|
+
kind: "document-pdf";
|
|
69
|
+
pdfBytes: Uint8Array;
|
|
70
|
+
pageCount: number;
|
|
71
|
+
sizeBytes: number;
|
|
72
|
+
fileName: string;
|
|
73
|
+
}
|
|
18
74
|
| {
|
|
19
75
|
kind: "image-snapshot";
|
|
20
76
|
imageBytes: Uint8Array;
|
|
21
77
|
format: "png" | "jpg";
|
|
22
78
|
width: number;
|
|
23
79
|
height: number;
|
|
24
|
-
}
|
|
80
|
+
}
|
|
81
|
+
) &
|
|
82
|
+
RenderProvenance;
|
|
25
83
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
readonly content
|
|
29
|
-
readonly
|
|
30
|
-
readonly
|
|
31
|
-
|
|
84
|
+
// An unsaved version of the mail template; rendering it never reads its stored body or subject.
|
|
85
|
+
export type MailDraft = {
|
|
86
|
+
readonly content: string;
|
|
87
|
+
readonly meta: MailTemplateMeta;
|
|
88
|
+
readonly variableSchema?: Readonly<Record<string, unknown>>;
|
|
89
|
+
/** Images and attachments of the row being edited; absent means those of the stored template. */
|
|
90
|
+
readonly linkedResources?: Readonly<Record<string, LinkedResourceEntry>>;
|
|
32
91
|
};
|
|
33
92
|
|
|
34
|
-
export type
|
|
35
|
-
readonly
|
|
36
|
-
readonly
|
|
37
|
-
readonly
|
|
38
|
-
readonly
|
|
39
|
-
readonly
|
|
40
|
-
readonly subject?: string;
|
|
93
|
+
export type MailPayload = {
|
|
94
|
+
readonly slug: string;
|
|
95
|
+
readonly locale: string;
|
|
96
|
+
readonly variables: Readonly<Record<string, unknown>>;
|
|
97
|
+
readonly unsubscribeUrl?: string;
|
|
98
|
+
readonly draft?: MailDraft;
|
|
41
99
|
};
|
|
42
100
|
|
|
43
101
|
export type DocumentPayload = {
|
|
@@ -48,13 +106,46 @@ export type DocumentPayload = {
|
|
|
48
106
|
readonly locale?: string;
|
|
49
107
|
};
|
|
50
108
|
|
|
109
|
+
export type DocumentPdfPayload = {
|
|
110
|
+
readonly slug: string;
|
|
111
|
+
readonly locale: string;
|
|
112
|
+
readonly variables: Readonly<Record<string, unknown>>;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// An unsaved version of the document template, like MailDraft.
|
|
116
|
+
export type DocumentPdfDraft = {
|
|
117
|
+
readonly content: string;
|
|
118
|
+
readonly meta: PdfTemplateMeta | null;
|
|
119
|
+
readonly variableSchema?: Readonly<Record<string, unknown>>;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export type DocumentPreviewRequest = DocumentPdfPayload & { readonly draft?: DocumentPdfDraft };
|
|
123
|
+
|
|
124
|
+
// What an editor shows for a document: the printable HTML with the values filled in,
|
|
125
|
+
// without calling any print service.
|
|
126
|
+
export type DocumentPreview = {
|
|
127
|
+
readonly title: string;
|
|
128
|
+
readonly fileName: string;
|
|
129
|
+
readonly bodyHtml: string;
|
|
130
|
+
readonly layout: {
|
|
131
|
+
readonly slug: string;
|
|
132
|
+
readonly scope: TemplateScope;
|
|
133
|
+
readonly headerHtml: string;
|
|
134
|
+
readonly signatureHtml: string;
|
|
135
|
+
readonly footerHtml: string;
|
|
136
|
+
} | null;
|
|
137
|
+
/** Image keys the body or the layout asks for that no file is linked to. */
|
|
138
|
+
readonly missingResourceKeys: readonly string[];
|
|
139
|
+
};
|
|
140
|
+
|
|
51
141
|
export type PdfOptions = {
|
|
52
|
-
readonly format?:
|
|
142
|
+
readonly format?: PdfPageFormat;
|
|
53
143
|
readonly marginMm?: { top?: number; right?: number; bottom?: number; left?: number };
|
|
54
144
|
readonly headerTemplate?: string;
|
|
55
145
|
readonly footerTemplate?: string;
|
|
56
146
|
readonly printBackground?: boolean;
|
|
57
147
|
readonly displayHeaderFooter?: boolean;
|
|
148
|
+
readonly landscape?: boolean;
|
|
58
149
|
};
|
|
59
150
|
|
|
60
151
|
export type ImageOptions = {
|
|
@@ -75,33 +166,39 @@ export type ImageOptions = {
|
|
|
75
166
|
// const tplApi = createTemplateResolverApi(ctx.db);
|
|
76
167
|
// const layout = await tplApi.resolveTemplate({ tenantId: ctx.tenantId, slug, kind });
|
|
77
168
|
export type RendererContext = {
|
|
78
|
-
readonly db:
|
|
169
|
+
readonly db: DbRunner | TenantDb;
|
|
79
170
|
readonly registry: Registry;
|
|
80
171
|
readonly tenantId: TenantId;
|
|
172
|
+
/** Tenant-scoped config for plugins that read the tenant currency or
|
|
173
|
+
* branding; plugins fall back to request defaults when absent. */
|
|
174
|
+
readonly config?: ConfigAccessor;
|
|
175
|
+
/** Only plugins that embed resource bytes (PDF) need it; without it images are dropped. */
|
|
176
|
+
readonly filesForTenant?: (tenantId: string) => FileContext | undefined;
|
|
81
177
|
};
|
|
82
178
|
|
|
83
|
-
// RendererPlugin-Contract — pro Plugin (renderer-
|
|
179
|
+
// RendererPlugin-Contract — pro Plugin (renderer-mail-html,
|
|
84
180
|
// renderer-puppeteer-client). Plugin deklariert welche kinds es bedienen
|
|
85
181
|
// kann; Foundation-Resolver wählt basierend auf Tenant-Config + kind.
|
|
86
182
|
//
|
|
87
|
-
//
|
|
88
|
-
// - `kinds`
|
|
89
|
-
// - `render(req, ctx)
|
|
90
|
-
// -
|
|
91
|
-
// -
|
|
92
|
-
// - `ctx`
|
|
93
|
-
//
|
|
94
|
-
// Implementations weiterhin (Implementation-Args ≤ Contract-Args ist OK).
|
|
183
|
+
// Plugin invariants:
|
|
184
|
+
// - `kinds` has at least one entry, otherwise the plugin is never picked.
|
|
185
|
+
// - The response kind of `render(req, ctx)` matches req.kind.
|
|
186
|
+
// - Plugins are stateless between render calls.
|
|
187
|
+
// - Domain failures throw `RendererError`, not a bare Error.
|
|
188
|
+
// - `ctx` is required; plugins without service deps ignore it. Function-arg
|
|
189
|
+
// variance still lets `(req) => ...` implementations satisfy the contract.
|
|
95
190
|
export type RendererPlugin = {
|
|
96
191
|
readonly name: string;
|
|
97
192
|
readonly kinds: ReadonlyArray<RenderKind>;
|
|
98
193
|
render(req: RenderRequest, ctx: RendererContext): Promise<RenderResponse>;
|
|
194
|
+
/** Optional: plugins that print documents offer a preview that never reaches the print service. */
|
|
195
|
+
previewDocument?(req: DocumentPreviewRequest, ctx: RendererContext): Promise<DocumentPreview>;
|
|
99
196
|
};
|
|
100
197
|
|
|
101
198
|
export class RendererError extends Error {
|
|
102
199
|
constructor(
|
|
103
200
|
message: string,
|
|
104
|
-
public readonly code: "no_plugin_for_kind" | "invalid_payload" | "other",
|
|
201
|
+
public readonly code: "no_plugin_for_kind" | "invalid_payload" | "unavailable" | "other",
|
|
105
202
|
) {
|
|
106
203
|
super(message);
|
|
107
204
|
this.name = "RendererError";
|
|
@@ -25,7 +25,13 @@ export async function renderTablePdfRequest(req: RenderRequest): Promise<RenderR
|
|
|
25
25
|
...(req.options?.format && { format: req.options.format }),
|
|
26
26
|
...(req.options?.marginMm && { marginMm: req.options.marginMm }),
|
|
27
27
|
});
|
|
28
|
-
return {
|
|
28
|
+
return {
|
|
29
|
+
kind: "document-pdf",
|
|
30
|
+
pdfBytes,
|
|
31
|
+
pageCount,
|
|
32
|
+
sizeBytes: pdfBytes.byteLength,
|
|
33
|
+
fileName: `${req.payload.slug}.pdf`,
|
|
34
|
+
};
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
export function createRendererPdfFeature(): FeatureDefinition {
|
|
@@ -6,7 +6,7 @@ export async function deleteStaleSessionsBatch(
|
|
|
6
6
|
olderThanDays: number,
|
|
7
7
|
batchSize: number,
|
|
8
8
|
): Promise<number> {
|
|
9
|
-
const rows =
|
|
9
|
+
const rows = await asRawClient(db).unsafe<{ id: string }>(
|
|
10
10
|
`DELETE FROM "store_user_sessions"
|
|
11
11
|
WHERE "id" IN (
|
|
12
12
|
SELECT "id" FROM "store_user_sessions"
|
|
@@ -16,6 +16,6 @@ export async function deleteStaleSessionsBatch(
|
|
|
16
16
|
)
|
|
17
17
|
RETURNING "id"`,
|
|
18
18
|
[olderThanDays, batchSize],
|
|
19
|
-
)
|
|
19
|
+
);
|
|
20
20
|
return rows.length;
|
|
21
21
|
}
|
|
@@ -71,11 +71,9 @@ export function bindAutoRevokeFromFeature(
|
|
|
71
71
|
): BindAutoRevokeOnPasswordChange | undefined {
|
|
72
72
|
const exports = feature.exports;
|
|
73
73
|
if (exports && typeof exports === "object" && "bindAutoRevokeOnPasswordChange" in exports) {
|
|
74
|
-
const { bindAutoRevokeOnPasswordChange } = exports
|
|
75
|
-
bindAutoRevokeOnPasswordChange: unknown;
|
|
76
|
-
};
|
|
74
|
+
const { bindAutoRevokeOnPasswordChange } = exports;
|
|
77
75
|
if (typeof bindAutoRevokeOnPasswordChange === "function") {
|
|
78
|
-
// @cast-boundary exports
|
|
76
|
+
// @cast-boundary feature-exports — feature.exports is untyped by design
|
|
79
77
|
return bindAutoRevokeOnPasswordChange as BindAutoRevokeOnPasswordChange;
|
|
80
78
|
}
|
|
81
79
|
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
definePagedQueryHandler,
|
|
5
5
|
MAX_LIST_LIMIT,
|
|
6
6
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
7
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
7
8
|
import * as z from "zod";
|
|
8
9
|
import { decryptStoredPii } from "../../shared/index.js";
|
|
9
10
|
import { userSessionTable } from "../schema/user-session.js";
|
|
@@ -17,7 +18,7 @@ const SORTABLE_COLUMNS = ["id", "userId", "createdAt", "expiresAt", "revokedAt"]
|
|
|
17
18
|
type SortableColumn = (typeof SORTABLE_COLUMNS)[number];
|
|
18
19
|
|
|
19
20
|
function isSortableColumn(value: string): value is SortableColumn {
|
|
20
|
-
return (SORTABLE_COLUMNS
|
|
21
|
+
return isOneOf(SORTABLE_COLUMNS, value);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
// Admin view of every session in the active tenant. ctx.db (TenantDb)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
|
|
3
|
+
const MAX_FILE_NAME_LENGTH = 255;
|
|
4
|
+
|
|
5
|
+
function hasControlCharacter(value: string): boolean {
|
|
6
|
+
return value.split("").some((char) => {
|
|
7
|
+
const code = char.charCodeAt(0);
|
|
8
|
+
return code <= 0x1f || code === 0x7f;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const attachmentFileNameSchema = z
|
|
13
|
+
.string()
|
|
14
|
+
.min(1)
|
|
15
|
+
.max(MAX_FILE_NAME_LENGTH)
|
|
16
|
+
.refine((name) => !name.includes("/") && !name.includes("\\") && !hasControlCharacter(name), {
|
|
17
|
+
message: "file name must not contain path separators or control characters",
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const PDF_FILE_NAME_MAX_LENGTH = 200;
|
|
21
|
+
const PDF_EXTENSION = ".pdf";
|
|
22
|
+
|
|
23
|
+
// Shared by everything that names a rendered PDF, so a mail attachment and a download of the same
|
|
24
|
+
// template get the same name. The cut leaves room for the extension: the attachment schema caps
|
|
25
|
+
// rendered-pdf names, and a name that only fits without ".pdf" would fail it.
|
|
26
|
+
export function sanitizePdfAttachmentFileName(raw: string, templateSlug: string): string {
|
|
27
|
+
const cleaned = raw
|
|
28
|
+
.split("")
|
|
29
|
+
.filter((char) => !hasControlCharacter(char))
|
|
30
|
+
.join("")
|
|
31
|
+
.replace(/[/\\";]/g, "-")
|
|
32
|
+
.replaceAll("..", "-")
|
|
33
|
+
.trim()
|
|
34
|
+
.slice(0, PDF_FILE_NAME_MAX_LENGTH - PDF_EXTENSION.length)
|
|
35
|
+
.trim();
|
|
36
|
+
const lastSegment = templateSlug.split(":").pop() ?? templateSlug;
|
|
37
|
+
if (cleaned === "") return `${lastSegment}${PDF_EXTENSION}`;
|
|
38
|
+
return cleaned.toLowerCase().endsWith(PDF_EXTENSION) ? cleaned : `${cleaned}${PDF_EXTENSION}`;
|
|
39
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export { collectErasureFailure, throwIfErasureFailed } from "./assert-erased.js";
|
|
2
2
|
export { assertNoWhereRuleInOwnershipWrite } from "./assert-no-where-rule-in-ownership-write.js";
|
|
3
|
+
export {
|
|
4
|
+
attachmentFileNameSchema,
|
|
5
|
+
sanitizePdfAttachmentFileName,
|
|
6
|
+
} from "./attachment-file-name.js";
|
|
3
7
|
export {
|
|
4
8
|
type ChunkedMigrationOptions,
|
|
5
9
|
type ChunkedMigrationResult,
|
|
@@ -18,6 +22,7 @@ export { isWithinGracePeriod } from "./grace-period.js";
|
|
|
18
22
|
export { isIdentityV3Hash, verifyIdentityV3Hash } from "./identity-v3-hash.js";
|
|
19
23
|
export { isTenantDb } from "./is-tenant-db.js";
|
|
20
24
|
export { createLockoutCounter, type LockoutCounterState } from "./lockout-counter.js";
|
|
25
|
+
export { mailHeading, mailParagraph, mailRepeatedRows } from "./mail-template-body.js";
|
|
21
26
|
export { mapWithConcurrency } from "./map-with-concurrency.js";
|
|
22
27
|
export {
|
|
23
28
|
denyUnlessJoinRowParentVisible,
|
|
@@ -43,6 +48,7 @@ export {
|
|
|
43
48
|
export { runInSubTransaction } from "./run-in-sub-transaction.js";
|
|
44
49
|
export { registrationLocaleFromRequest, sessionLocaleField } from "./session-locale-field.js";
|
|
45
50
|
export { sessionTimezoneField } from "./session-timezone-field.js";
|
|
51
|
+
export { assertNotSvg, MAX_INPUT_PIXELS, SHARP_INPUT_OPTIONS } from "./sharp-input-limits.js";
|
|
46
52
|
export {
|
|
47
53
|
peekTokenSubject,
|
|
48
54
|
signToken,
|
|
@@ -5,5 +5,5 @@ import type { DbRunner, TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
|
5
5
|
// Shared so retention/compliance-profile resolvers that accept either (bulk cron
|
|
6
6
|
// vs. per-hook TenantDb) branch through one predicate instead of duplicating it.
|
|
7
7
|
export function isTenantDb(db: DbRunner | TenantDb): db is TenantDb {
|
|
8
|
-
return
|
|
8
|
+
return "unsafeRaw" in db && typeof db.unsafeRaw === "function";
|
|
9
9
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
|
|
2
|
+
|
|
3
|
+
const SLOT_PATTERN = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
4
|
+
|
|
5
|
+
// Author text uses `{name}` slots. The text is escaped first and the slots are
|
|
6
|
+
// swapped for variable nodes afterwards, so a slot can never smuggle markup.
|
|
7
|
+
export function mailTextWithSlotsHtml(text: string): string {
|
|
8
|
+
return escapeHtml(text).replace(
|
|
9
|
+
SLOT_PATTERN,
|
|
10
|
+
(_, name: string) => `<span data-variable="${escapeHtmlAttr(name)}"></span>`,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function mailParagraph(text: string): string {
|
|
15
|
+
return `<p>${mailTextWithSlotsHtml(text)}</p>`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function mailHeading(text: string): string {
|
|
19
|
+
return `<h2>${mailTextWithSlotsHtml(text)}</h2>`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// One table row per list entry; the cell shows the entry's `field`.
|
|
23
|
+
export function mailRepeatedRows(listName: string, field: string): string {
|
|
24
|
+
return `<table><tbody><tr data-each="${escapeHtmlAttr(listName)}"><td><p><span data-variable="${escapeHtmlAttr(field)}"></span></p></td></tr></tbody></table>`;
|
|
25
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Sharp-free so callers can import the limits without loading the native binding.
|
|
2
|
+
|
|
3
|
+
// Above this, a decoded pixel buffer runs into hundreds of MB before any
|
|
4
|
+
// resize even happens — sharp's own default (~268MP) is still large enough
|
|
5
|
+
// to be an OOM primitive for a single request.
|
|
6
|
+
export const MAX_INPUT_PIXELS = 100_000_000;
|
|
7
|
+
// sequentialRead trades random pixel access for lower peak memory on
|
|
8
|
+
// streamed formats — applied at every decode entry point below since none
|
|
9
|
+
// of them need non-sequential access to the *original* bytes (extract runs
|
|
10
|
+
// against a single already-buffered region per call).
|
|
11
|
+
export const SHARP_INPUT_OPTIONS = {
|
|
12
|
+
limitInputPixels: MAX_INPUT_PIXELS,
|
|
13
|
+
sequentialRead: true,
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
// Shared trust boundary for every image byte source this renderer decodes —
|
|
17
|
+
// the original upload (renderImage) and an overlay layer's bytes (applyOverlays)
|
|
18
|
+
// alike: a base64 string in a field declaration isn't more trustworthy than
|
|
19
|
+
// an upload just because it came from app code.
|
|
20
|
+
export function assertNotSvg(format: string | undefined): void {
|
|
21
|
+
if (format === "svg") {
|
|
22
|
+
throw new Error(
|
|
23
|
+
"derivatives-sharp: SVG is not supported — rendering untrusted SVG through libvips is a trust boundary this renderer doesn't need to cross.",
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import type { DbRunner } from "@cosmicdrift/kumiko-framework/db";
|
|
15
15
|
import type { Registry } from "@cosmicdrift/kumiko-framework/engine";
|
|
16
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
16
17
|
|
|
17
18
|
export const EXT_SIGNUP_HANDOVER = "signupHandover" as const;
|
|
18
19
|
|
|
@@ -46,10 +47,9 @@ export type SignupHandoverProvider = {
|
|
|
46
47
|
|
|
47
48
|
export function isSignupHandoverProvider(options: unknown): options is SignupHandoverProvider {
|
|
48
49
|
return (
|
|
49
|
-
|
|
50
|
-
options
|
|
51
|
-
typeof
|
|
52
|
-
typeof (options as Record<string, unknown>)["mintClaim"] === "function"
|
|
50
|
+
isPlainObject(options) &&
|
|
51
|
+
typeof options["verifyGrant"] === "function" &&
|
|
52
|
+
typeof options["mintClaim"] === "function"
|
|
53
53
|
);
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AccessRule, WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { NotFoundError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
3
4
|
import { denyUnlessJoinRowParentVisible } from "../../shared/index.js";
|
|
4
5
|
import { tagAssignmentAggregateId } from "../aggregate-id.js";
|
|
5
6
|
import { DEFAULT_TAG_ACCESS } from "../constants.js";
|
|
@@ -67,7 +68,10 @@ export function createAssignTagHandler(
|
|
|
67
68
|
// above and this restore() call, so restore() correctly refuses with
|
|
68
69
|
// "not_deleted" — but the desired end state is already true. Converge
|
|
69
70
|
// like the create() race below instead of surfacing a spurious error.
|
|
70
|
-
if (
|
|
71
|
+
if (
|
|
72
|
+
isPlainObject(restored.error.details) &&
|
|
73
|
+
restored.error.details["reason"] === "not_deleted"
|
|
74
|
+
) {
|
|
71
75
|
return { isSuccess: true as const, data: { id } };
|
|
72
76
|
}
|
|
73
77
|
if (restored.error.code !== "not_found") return restored;
|
|
@@ -21,7 +21,8 @@ export function contrastText(color: string): "#000000" | "#ffffff" | null {
|
|
|
21
21
|
function hexToRgb(color: string): { r: number; g: number; b: number } | null {
|
|
22
22
|
const match = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.exec(color.trim());
|
|
23
23
|
if (match === null) return null;
|
|
24
|
-
const short = match[1]
|
|
24
|
+
const short = match[1];
|
|
25
|
+
if (short === undefined) return null;
|
|
25
26
|
const hex =
|
|
26
27
|
short.length === 3
|
|
27
28
|
? `${short[0]}${short[0]}${short[1]}${short[1]}${short[2]}${short[2]}`
|