@cosmicdrift/kumiko-bundled-features 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/dist/admin-shell/feature.d.ts +3 -0
- package/dist/admin-shell/feature.js +6 -1
- package/dist/agent-tools/agent-manifest.js +2 -1
- package/dist/agent-tools/filter-ops.d.ts +1 -1
- package/dist/agent-tools/filter-ops.js +1 -3
- package/dist/agent-tools/tool-dispatch.js +2 -1
- package/dist/auth-email-password/email-templates.d.ts +4 -26
- package/dist/auth-email-password/email-templates.js +9 -101
- package/dist/auth-email-password/feature.js +24 -6
- package/dist/auth-email-password/handlers/invite-create.write.js +4 -4
- package/dist/auth-email-password/handlers/request-account-unlock.write.js +2 -4
- package/dist/auth-email-password/handlers/request-email-verification.write.js +2 -4
- package/dist/auth-email-password/handlers/request-password-reset.write.js +2 -4
- package/dist/auth-email-password/handlers/signup-confirm.write.js +3 -4
- package/dist/auth-email-password/handlers/token-request-handler.d.ts +1 -2
- package/dist/auth-email-password/handlers/token-request-handler.js +0 -1
- package/dist/auth-email-password/index.d.ts +2 -2
- package/dist/auth-email-password/index.js +1 -1
- package/dist/auth-email-password/magic-link-mail.d.ts +3 -2
- package/dist/auth-email-password/magic-link-mail.js +19 -14
- package/dist/auth-email-password/mail-notifications.d.ts +32 -0
- package/dist/auth-email-password/mail-notifications.js +116 -0
- package/dist/auth-email-password/signup-activation.d.ts +1 -1
- package/dist/auth-email-password/signup-activation.js +2 -3
- package/dist/auth-foundation/types.js +2 -8
- package/dist/auth-mfa/feature.js +4 -4
- package/dist/auth-mfa/hmac-token-codec.js +4 -4
- package/dist/auth-mfa/mfa-challenge-token.js +3 -3
- package/dist/auth-mfa/mfa-preauth-setup-token.js +3 -3
- package/dist/auth-mfa/mfa-setup-token.js +8 -8
- package/dist/billing-foundation/constants.d.ts +3 -3
- package/dist/billing-foundation/constants.js +5 -4
- package/dist/billing-foundation/consumer-protection/confirmation-mail.d.ts +32 -14
- package/dist/billing-foundation/consumer-protection/confirmation-mail.js +27 -32
- package/dist/billing-foundation/consumer-protection/issue-confirmation.js +3 -3
- package/dist/billing-foundation/consumer-protection/mail-notifications.d.ts +8 -0
- package/dist/billing-foundation/consumer-protection/mail-notifications.js +95 -0
- package/dist/billing-foundation/consumer-protection/register-consent.js +2 -0
- package/dist/billing-foundation/consumer-protection/termination-mail.d.ts +20 -10
- package/dist/billing-foundation/consumer-protection/termination-mail.js +10 -21
- package/dist/billing-foundation/consumer-protection/termination-notify.js +2 -2
- package/dist/billing-foundation/consumer-protection/termination-pages.js +1 -3
- package/dist/billing-foundation/consumer-protection/termination-request.js +2 -2
- package/dist/billing-foundation/handlers/process-event.write.js +2 -2
- package/dist/billing-foundation/handlers/process-payment-event.write.js +1 -1
- package/dist/billing-foundation/subscription-tier-sync.js +3 -4
- package/dist/cap-overview/handlers/tenant-caps-list.query.js +2 -1
- package/dist/cap-overview/web/cap-usage-cell.js +5 -4
- package/dist/channel-email/email-channel.d.ts +2 -2
- package/dist/channel-email/email-channel.js +89 -26
- package/dist/channel-email/feature.js +3 -3
- package/dist/channel-email/index.d.ts +1 -1
- package/dist/channel-email/index.js +1 -1
- package/dist/channel-email/pii-guard.js +10 -0
- package/dist/channel-email/smtp-transport.js +13 -0
- package/dist/channel-email/types.d.ts +8 -0
- package/dist/config/handlers/set.write.js +1 -2
- package/dist/custom-fields/lib/value-schema.js +10 -11
- package/dist/custom-fields/wire-user-data-rights.js +7 -6
- package/dist/data-retention/index.d.ts +1 -0
- package/dist/data-retention/index.js +1 -0
- package/dist/data-retention/run-retention-cleanup.js +6 -6
- package/dist/delivery/attachment-ref.d.ts +39 -0
- package/dist/delivery/attachment-ref.js +64 -0
- package/dist/delivery/channel-context.d.ts +8 -4
- package/dist/delivery/channel-context.js +76 -2
- package/dist/delivery/constants.d.ts +1 -0
- package/dist/delivery/constants.js +1 -0
- package/dist/delivery/delivery-service.d.ts +4 -1
- package/dist/delivery/delivery-service.js +38 -32
- package/dist/delivery/feature.js +1 -1
- package/dist/delivery/index.d.ts +5 -2
- package/dist/delivery/index.js +4 -1
- package/dist/delivery/jobs.js +25 -5
- package/dist/delivery/notify-factory.js +4 -1
- package/dist/delivery/testing.d.ts +4 -1
- package/dist/delivery/types.d.ts +20 -14
- package/dist/delivery/unsubscribe.js +4 -1
- package/dist/derivatives-sharp/render.js +1 -18
- package/dist/document-ingest-foundation/pages.js +4 -4
- package/dist/feature-toggles/toggle-sync-signal.js +4 -4
- package/dist/file-foundation/index.d.ts +1 -0
- package/dist/file-foundation/index.js +1 -0
- package/dist/file-foundation/read-file-ref-content.d.ts +27 -0
- package/dist/file-foundation/read-file-ref-content.js +49 -0
- package/dist/folders/web/folder-manager.js +3 -2
- package/dist/form-draft/db/queries/cleanup.js +4 -4
- package/dist/form-draft/db/queries/draft-count.js +2 -2
- package/dist/form-draft/db/queries/owned-file-refs.js +2 -2
- package/dist/form-draft/release-file-refs.js +3 -2
- package/dist/inbound-mail-foundation/handlers/connect-account.write.js +1 -1
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +6 -6
- package/dist/inbound-mail-foundation/handlers/scope-visibility.d.ts +1 -1
- package/dist/inbound-mail-foundation/oauth-state.js +7 -7
- package/dist/inbound-mail-foundation/types.js +4 -4
- package/dist/inbound-mail-foundation/watch-supervisor.js +22 -13
- package/dist/inbound-provider-imap/imap-client.js +4 -3
- package/dist/ledger/recurring.js +1 -3
- package/dist/managed-pages/branding.d.ts +2 -0
- package/dist/managed-pages/branding.js +24 -2
- package/dist/managed-pages/feature.js +1 -0
- package/dist/managed-pages/i18n.js +1 -0
- package/dist/managed-pages/screens/branding-screen.js +8 -1
- package/dist/page-render/branding.d.ts +1 -0
- package/dist/page-render/branding.js +1 -0
- package/dist/page-render/markdown.js +1 -1
- package/dist/personal-access-tokens/feature.js +1 -1
- package/dist/personal-access-tokens/handlers/list.query.js +2 -1
- package/dist/personal-access-tokens/revoke-for-user.js +2 -1
- package/dist/renderer-foundation/api.d.ts +13 -13
- package/dist/renderer-foundation/api.js +1 -1
- package/dist/renderer-foundation/constants.js +2 -4
- package/dist/renderer-foundation/feature.js +7 -7
- package/dist/renderer-foundation/index.d.ts +2 -1
- package/dist/renderer-foundation/index.js +1 -0
- package/dist/renderer-foundation/types.d.ts +92 -32
- package/dist/renderer-pdf/feature.js +7 -1
- package/dist/sessions/db/queries/cleanup.js +2 -2
- package/dist/sessions/feature.js +1 -1
- package/dist/sessions/handlers/list.query.js +2 -1
- package/dist/shared/attachment-file-name.d.ts +3 -0
- package/dist/shared/attachment-file-name.js +35 -0
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.js +3 -0
- package/dist/shared/is-tenant-db.js +1 -1
- package/dist/shared/mail-template-body.d.ts +4 -0
- package/dist/shared/mail-template-body.js +17 -0
- package/dist/shared/sharp-input-limits.d.ts +6 -0
- package/dist/shared/sharp-input-limits.js +22 -0
- package/dist/shared/signup-handover.js +2 -2
- package/dist/tags/handlers/assign-tag.write.js +3 -1
- package/dist/tags/web/tag-chip.js +2 -0
- package/dist/template-resolver/api.d.ts +30 -2
- package/dist/template-resolver/api.js +40 -5
- package/dist/template-resolver/compile-document-body.d.ts +8 -0
- package/dist/template-resolver/compile-document-body.js +163 -0
- package/dist/template-resolver/constants.d.ts +13 -4
- package/dist/template-resolver/constants.js +30 -10
- package/dist/template-resolver/document-sanitizer.d.ts +32 -0
- package/dist/template-resolver/document-sanitizer.js +201 -0
- package/dist/template-resolver/document-schema.d.ts +21 -0
- package/dist/template-resolver/document-schema.js +296 -0
- package/dist/template-resolver/feature.d.ts +18 -0
- package/dist/template-resolver/feature.js +50 -6
- package/dist/template-resolver/handlers/bundle-import.d.ts +20 -0
- package/dist/template-resolver/handlers/bundle-import.js +147 -0
- package/dist/template-resolver/handlers/collection-set.write.js +25 -2
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +19 -0
- package/dist/template-resolver/handlers/copy-locale.write.js +116 -0
- package/dist/template-resolver/handlers/diff-to-system.query.d.ts +22 -0
- package/dist/template-resolver/handlers/diff-to-system.query.js +64 -0
- package/dist/template-resolver/handlers/export-bundle.query.d.ts +35 -0
- package/dist/template-resolver/handlers/export-bundle.query.js +68 -0
- package/dist/template-resolver/handlers/find-by-id.query.d.ts +1 -0
- package/dist/template-resolver/handlers/find-by-id.query.js +3 -3
- package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +39 -0
- package/dist/template-resolver/handlers/import-bundle-system.write.js +36 -0
- package/dist/template-resolver/handlers/import-bundle.write.d.ts +40 -0
- package/dist/template-resolver/handlers/import-bundle.write.js +19 -0
- package/dist/template-resolver/handlers/layout-source.query.d.ts +26 -0
- package/dist/template-resolver/handlers/layout-source.query.js +64 -0
- package/dist/template-resolver/handlers/list.query.d.ts +4 -1
- package/dist/template-resolver/handlers/list.query.js +5 -7
- package/dist/template-resolver/handlers/overview.query.d.ts +59 -0
- package/dist/template-resolver/handlers/overview.query.js +165 -0
- package/dist/template-resolver/handlers/preview.query.d.ts +12 -0
- package/dist/template-resolver/handlers/preview.query.js +24 -0
- package/dist/template-resolver/handlers/reset-to-system.write.d.ts +8 -0
- package/dist/template-resolver/handlers/reset-to-system.write.js +50 -0
- package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +8 -0
- package/dist/template-resolver/handlers/resource-link-pdf.write.js +32 -0
- package/dist/template-resolver/handlers/resource-remove.write.d.ts +9 -0
- package/dist/template-resolver/handlers/resource-remove.write.js +44 -0
- package/dist/template-resolver/handlers/resource-shared.d.ts +9 -0
- package/dist/template-resolver/handlers/resource-shared.js +14 -0
- package/dist/template-resolver/handlers/resource-upload.write.d.ts +13 -0
- package/dist/template-resolver/handlers/resource-upload.write.js +98 -0
- package/dist/template-resolver/handlers/send-test.write.d.ts +11 -0
- package/dist/template-resolver/handlers/send-test.write.js +81 -0
- package/dist/template-resolver/handlers/set.write.d.ts +4 -1
- package/dist/template-resolver/handlers/set.write.js +25 -3
- package/dist/template-resolver/handlers/shared.d.ts +9 -5
- package/dist/template-resolver/handlers/shared.js +6 -2
- package/dist/template-resolver/handlers/template-gates.d.ts +36 -0
- package/dist/template-resolver/handlers/template-gates.js +131 -0
- package/dist/template-resolver/handlers/template-write.d.ts +45 -0
- package/dist/template-resolver/handlers/template-write.js +109 -0
- package/dist/template-resolver/handlers/tenant-overrides.query.d.ts +17 -0
- package/dist/template-resolver/handlers/tenant-overrides.query.js +48 -0
- package/dist/template-resolver/handlers/toggle-status.write.d.ts +12 -0
- package/dist/template-resolver/handlers/toggle-status.write.js +27 -11
- package/dist/template-resolver/handlers/upsert-system.write.d.ts +5 -8
- package/dist/template-resolver/handlers/upsert-system.write.js +14 -59
- package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +5 -8
- package/dist/template-resolver/handlers/upsert-tenant.write.js +10 -61
- package/dist/template-resolver/handlers/usage.query.d.ts +17 -0
- package/dist/template-resolver/handlers/usage.query.js +15 -0
- package/dist/template-resolver/handlers/validate.query.d.ts +74 -0
- package/dist/template-resolver/handlers/validate.query.js +80 -0
- package/dist/template-resolver/index.d.ts +18 -1
- package/dist/template-resolver/index.js +16 -1
- package/dist/template-resolver/layout-regions.d.ts +23 -0
- package/dist/template-resolver/layout-regions.js +125 -0
- package/dist/template-resolver/linked-resources.d.ts +17 -0
- package/dist/template-resolver/linked-resources.js +75 -0
- package/dist/template-resolver/preview-display-html.d.ts +7 -0
- package/dist/template-resolver/preview-display-html.js +27 -0
- package/dist/template-resolver/print-document.d.ts +1 -0
- package/dist/template-resolver/print-document.js +55 -0
- package/dist/template-resolver/qualified-names.d.ts +30 -0
- package/dist/template-resolver/qualified-names.js +30 -0
- package/dist/template-resolver/record-mapping.d.ts +2 -0
- package/dist/template-resolver/record-mapping.js +21 -0
- package/dist/template-resolver/resource-image.d.ts +15 -0
- package/dist/template-resolver/resource-image.js +34 -0
- package/dist/template-resolver/seeding.d.ts +14 -1
- package/dist/template-resolver/seeding.js +105 -15
- package/dist/template-resolver/store-template-resource.d.ts +64 -0
- package/dist/template-resolver/store-template-resource.js +264 -0
- package/dist/template-resolver/table.d.ts +47 -8
- package/dist/template-resolver/table.js +11 -0
- package/dist/template-resolver/template-bundle.d.ts +36 -0
- package/dist/template-resolver/template-bundle.js +60 -0
- package/dist/template-resolver/template-diff.d.ts +7 -0
- package/dist/template-resolver/template-diff.js +68 -0
- package/dist/template-resolver/template-meta.d.ts +60 -0
- package/dist/template-resolver/template-meta.js +101 -0
- package/dist/template-resolver/template-preview.d.ts +86 -0
- package/dist/template-resolver/template-preview.js +374 -0
- package/dist/template-resolver/template-resources.d.ts +14 -0
- package/dist/template-resolver/template-resources.js +42 -0
- package/dist/template-resolver/template-usage.d.ts +12 -0
- package/dist/template-resolver/template-usage.js +30 -0
- package/dist/template-resolver/template-validation.d.ts +22 -0
- package/dist/template-resolver/template-validation.js +172 -0
- package/dist/template-resolver/testing.js +5 -5
- package/dist/template-resolver/user-content-table.d.ts +14 -8
- package/dist/template-resolver/web/client-plugin.d.ts +1 -0
- package/dist/template-resolver/web/client-plugin.js +9 -6
- package/dist/template-resolver/web/document-editor-extensions.d.ts +15 -0
- package/dist/template-resolver/web/document-editor-extensions.js +107 -0
- package/dist/template-resolver/web/document-editor.d.ts +3 -0
- package/dist/template-resolver/web/document-editor.js +11 -0
- package/dist/template-resolver/web/document-tiptap-editor.d.ts +3 -0
- package/dist/template-resolver/web/document-tiptap-editor.js +376 -0
- package/dist/template-resolver/web/document-toolbar-blocks.d.ts +33 -0
- package/dist/template-resolver/web/document-toolbar-blocks.js +15 -0
- package/dist/template-resolver/web/editor-handle.d.ts +3 -0
- package/dist/template-resolver/web/editor-handle.js +24 -0
- package/dist/template-resolver/web/i18n.js +76 -0
- package/dist/template-resolver/web/index.d.ts +4 -0
- package/dist/template-resolver/web/index.js +4 -0
- package/dist/template-resolver/web/placeholder-line-editor-lazy.d.ts +4 -0
- package/dist/template-resolver/web/placeholder-line-editor-lazy.js +16 -0
- package/dist/template-resolver/web/placeholder-line-editor.d.ts +15 -0
- package/dist/template-resolver/web/placeholder-line-editor.js +94 -0
- package/dist/template-resolver/web/placeholder-text.d.ts +4 -0
- package/dist/template-resolver/web/placeholder-text.js +34 -0
- package/dist/template-resolver/web/use-stable-by-key.d.ts +1 -0
- package/dist/template-resolver/web/use-stable-by-key.js +13 -0
- package/dist/tenant/handlers/team-list.query.js +3 -2
- package/dist/tenant/seeding.js +31 -6
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +1 -1
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.js +2 -1
- package/dist/user/db/queries/stream-tenant-backfill.js +4 -4
- package/dist/user/handlers/detail.query.js +2 -1
- package/dist/user-data-rights/db/queries/forget-cleanup.d.ts +2 -2
- package/dist/user-data-rights/email-templates.d.ts +1 -29
- package/dist/user-data-rights/email-templates.js +5 -106
- package/dist/user-data-rights/feature.js +53 -35
- package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/cancel-deletion.write.js +2 -1
- package/dist/user-data-rights/handlers/request-deletion.write.js +5 -7
- package/dist/user-data-rights/lib/default-mailers.d.ts +15 -7
- package/dist/user-data-rights/lib/default-mailers.js +100 -48
- package/dist/user-data-rights/lib/update-user-lifecycle.js +1 -1
- package/dist/user-data-rights/mail-notifications.d.ts +13 -0
- package/dist/user-data-rights/mail-notifications.js +118 -0
- package/dist/user-data-rights/web/privacy-center-screen.js +2 -1
- package/dist/user-profile/web/profile-screen.js +2 -1
- package/dist/waitlist/feature.js +10 -0
- package/dist/waitlist/handlers/submit.write.js +3 -4
- package/dist/waitlist/mail.d.ts +19 -6
- package/dist/waitlist/mail.js +50 -14
- package/package.json +28 -17
- package/src/admin-shell/changes.json +6 -0
- package/src/auth-email-password/changes.json +14 -0
- package/src/billing-foundation/changes.json +7 -0
- package/src/channel-email/changes.json +19 -0
- package/src/data-retention/changes.json +6 -0
- package/src/delivery/changes.json +48 -0
- package/src/managed-pages/changes.json +6 -0
- package/src/renderer-foundation/changes.json +39 -0
- package/src/template-resolver/changes.json +118 -0
- package/src/user-data-rights/changes.json +7 -0
- package/src/waitlist/changes.json +7 -0
- package/dist/renderer-simple/feature.d.ts +0 -10
- package/dist/renderer-simple/feature.js +0 -42
- package/dist/renderer-simple/index.d.ts +0 -3
- package/dist/renderer-simple/index.js +0 -2
- package/dist/renderer-simple/mail-blocks.d.ts +0 -21
- package/dist/renderer-simple/mail-blocks.js +0 -107
- package/dist/renderer-simple/resolve-variables.d.ts +0 -7
- package/dist/renderer-simple/resolve-variables.js +0 -47
- package/dist/renderer-simple/simple-renderer.d.ts +0 -24
- package/dist/renderer-simple/simple-renderer.js +0 -200
- package/src/renderer-simple/changes.json +0 -27
package/dist/ledger/recurring.js
CHANGED
|
@@ -23,9 +23,7 @@ export function scheduleReference(scheduleId, period) {
|
|
|
23
23
|
// can't land in one call-site and drift from the other.
|
|
24
24
|
export function findReversedIds(rows) {
|
|
25
25
|
const ids = new Set(rows.map((r) => r.id));
|
|
26
|
-
return new Set(rows
|
|
27
|
-
.filter((r) => r.reference !== null && ids.has(r.reference))
|
|
28
|
-
.map((r) => r.reference));
|
|
26
|
+
return new Set(rows.flatMap((r) => (r.reference !== null && ids.has(r.reference) ? [r.reference] : [])));
|
|
29
27
|
}
|
|
30
28
|
// "YYYY-MM" ⇄ a month index (year*12 + monthOfYear), so window math is pure
|
|
31
29
|
// integer arithmetic — no Date object (no-date-api guard) and no DST/timezone
|
|
@@ -8,6 +8,7 @@ export declare const BRANDING_KEYS: {
|
|
|
8
8
|
brandingSiteUrl: ConfigKeyDefinition<"text">;
|
|
9
9
|
brandingAccentColor: ConfigKeyDefinition<"text">;
|
|
10
10
|
brandingLogoUrl: ConfigKeyDefinition<"text">;
|
|
11
|
+
brandingLogoFile: ConfigKeyDefinition<"text">;
|
|
11
12
|
brandingLayoutPreset: ConfigKeyDefinition<"select">;
|
|
12
13
|
};
|
|
13
14
|
export declare const CUSTOM_CSS_KEY: {
|
|
@@ -19,6 +20,7 @@ export declare const BRANDING_QN: {
|
|
|
19
20
|
readonly siteUrl: "managed-pages:config:branding-site-url";
|
|
20
21
|
readonly accentColor: "managed-pages:config:branding-accent-color";
|
|
21
22
|
readonly logoUrl: "managed-pages:config:branding-logo-url";
|
|
23
|
+
readonly logoFile: "managed-pages:config:branding-logo-file";
|
|
22
24
|
readonly layoutPreset: "managed-pages:config:branding-layout-preset";
|
|
23
25
|
readonly customCss: "managed-pages:config:branding-custom-css";
|
|
24
26
|
};
|
|
@@ -42,6 +42,10 @@ const DESCRIPTION_PATTERN = { regex: "^[\\s\\S]{0,500}$" };
|
|
|
42
42
|
// render (page-render/css-sanitize.ts). Keep this cap in sync with MAX_CSS_LENGTH
|
|
43
43
|
// there.
|
|
44
44
|
const CUSTOM_CSS_PATTERN = { regex: "^[\\s\\S]{0,8000}$" };
|
|
45
|
+
// Empty or a FileRef id (UUID); the upload UI that fills it ships separately.
|
|
46
|
+
const FILE_REF_ID_PATTERN = {
|
|
47
|
+
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}$",
|
|
48
|
+
};
|
|
45
49
|
export const LAYOUT_PRESETS = ["minimal", "centered", "wide"];
|
|
46
50
|
// Companion toggle feature name. managed-pages reads ctx.hasFeature() against it
|
|
47
51
|
// in the branding query to per-tenant-gate custom-CSS emission. Declared as a
|
|
@@ -80,6 +84,12 @@ export const BRANDING_KEYS = {
|
|
|
80
84
|
pattern: HTTPS_PATTERN,
|
|
81
85
|
write: BRANDING_WRITE,
|
|
82
86
|
}),
|
|
87
|
+
// Ownership is checked where it is read (renderer-mail-html: FileRef must belong to the rendering tenant).
|
|
88
|
+
brandingLogoFile: createTenantConfig("text", {
|
|
89
|
+
default: "",
|
|
90
|
+
pattern: FILE_REF_ID_PATTERN,
|
|
91
|
+
write: BRANDING_WRITE,
|
|
92
|
+
}),
|
|
83
93
|
brandingLayoutPreset: createTenantConfig("select", {
|
|
84
94
|
default: "centered",
|
|
85
95
|
options: LAYOUT_PRESETS,
|
|
@@ -99,6 +109,7 @@ export const BRANDING_QN = {
|
|
|
99
109
|
siteUrl: "managed-pages:config:branding-site-url",
|
|
100
110
|
accentColor: "managed-pages:config:branding-accent-color",
|
|
101
111
|
logoUrl: "managed-pages:config:branding-logo-url",
|
|
112
|
+
logoFile: "managed-pages:config:branding-logo-file",
|
|
102
113
|
layoutPreset: "managed-pages:config:branding-layout-preset",
|
|
103
114
|
customCss: "managed-pages:config:branding-custom-css",
|
|
104
115
|
};
|
|
@@ -114,17 +125,27 @@ async function readText(config, qualifiedKey) {
|
|
|
114
125
|
export async function readBranding(config) {
|
|
115
126
|
if (!config)
|
|
116
127
|
return EMPTY_BRANDING;
|
|
117
|
-
const [title, description, siteUrl, accentColor, logoUrl, layoutPreset] = await Promise.all([
|
|
128
|
+
const [title, description, siteUrl, accentColor, logoUrl, logoFile, layoutPreset] = await Promise.all([
|
|
118
129
|
readText(config, BRANDING_QN.title),
|
|
119
130
|
readText(config, BRANDING_QN.description),
|
|
120
131
|
readText(config, BRANDING_QN.siteUrl),
|
|
121
132
|
readText(config, BRANDING_QN.accentColor),
|
|
122
133
|
readText(config, BRANDING_QN.logoUrl),
|
|
134
|
+
readText(config, BRANDING_QN.logoFile),
|
|
123
135
|
readText(config, BRANDING_QN.layoutPreset),
|
|
124
136
|
]);
|
|
125
137
|
// customCss stays "" here — it is gated (allowCustomCss + per-tenant toggle)
|
|
126
138
|
// and read separately by the branding query via readCustomCss.
|
|
127
|
-
return {
|
|
139
|
+
return {
|
|
140
|
+
title,
|
|
141
|
+
description,
|
|
142
|
+
siteUrl,
|
|
143
|
+
accentColor,
|
|
144
|
+
logoUrl,
|
|
145
|
+
logoFile,
|
|
146
|
+
layoutPreset,
|
|
147
|
+
customCss: "",
|
|
148
|
+
};
|
|
128
149
|
}
|
|
129
150
|
// Read the raw, untrusted custom CSS for the request's tenant. The caller (the
|
|
130
151
|
// branding query) only invokes this once the gate has passed; the value is
|
|
@@ -150,6 +171,7 @@ export function coerceBranding(value) {
|
|
|
150
171
|
siteUrl: stringField(source, "siteUrl"),
|
|
151
172
|
accentColor: stringField(source, "accentColor"),
|
|
152
173
|
logoUrl: stringField(source, "logoUrl"),
|
|
174
|
+
logoFile: stringField(source, "logoFile"),
|
|
153
175
|
layoutPreset: stringField(source, "layoutPreset"),
|
|
154
176
|
customCss: stringField(source, "customCss"),
|
|
155
177
|
};
|
|
@@ -37,6 +37,7 @@ export const MANAGED_PAGES_I18N = {
|
|
|
37
37
|
en: "Accent color",
|
|
38
38
|
},
|
|
39
39
|
"managed-pages:entity:__config-edit__:field:logoUrl": { en: "Logo URL" },
|
|
40
|
+
"managed-pages:entity:__config-edit__:field:logoFile": { en: "Logo file" },
|
|
40
41
|
"managed-pages:entity:__config-edit__:field:layoutPreset": { en: "Layout" },
|
|
41
42
|
// Section-Header
|
|
42
43
|
"managed-pages:section.meta": { en: "Metadata" },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createSelectField, createTextField, } from "@cosmicdrift/kumiko-framework/engine";
|
|
1
|
+
import { createImageField, createSelectField, createTextField, } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { BRANDING_QN, LAYOUT_PRESETS } from "../branding.js";
|
|
3
3
|
// Tenant self-service branding editor. A `configEdit` screen (no entity
|
|
4
4
|
// table) — the renderer loads config:query:values, maps the form fields via
|
|
@@ -27,6 +27,7 @@ export function createBrandingSettingsScreen(opts) {
|
|
|
27
27
|
siteUrl: BRANDING_QN.siteUrl,
|
|
28
28
|
accentColor: BRANDING_QN.accentColor,
|
|
29
29
|
logoUrl: BRANDING_QN.logoUrl,
|
|
30
|
+
logoFile: BRANDING_QN.logoFile,
|
|
30
31
|
layoutPreset: BRANDING_QN.layoutPreset,
|
|
31
32
|
},
|
|
32
33
|
fields: {
|
|
@@ -54,6 +55,11 @@ export function createBrandingSettingsScreen(opts) {
|
|
|
54
55
|
personal: false,
|
|
55
56
|
reason: "is_business_data",
|
|
56
57
|
}),
|
|
58
|
+
logoFile: createImageField({
|
|
59
|
+
accept: ["image/png", "image/jpeg", "image/webp"],
|
|
60
|
+
personal: false,
|
|
61
|
+
reason: "is_business_data",
|
|
62
|
+
}),
|
|
57
63
|
layoutPreset: createSelectField({ options: LAYOUT_PRESETS }),
|
|
58
64
|
},
|
|
59
65
|
layout: {
|
|
@@ -67,6 +73,7 @@ export function createBrandingSettingsScreen(opts) {
|
|
|
67
73
|
{ field: "description", span: 2 },
|
|
68
74
|
{ field: "siteUrl", span: 1 },
|
|
69
75
|
{ field: "logoUrl", span: 1 },
|
|
76
|
+
{ field: "logoFile", span: 1 },
|
|
70
77
|
{ field: "accentColor", span: 1 },
|
|
71
78
|
],
|
|
72
79
|
},
|
|
@@ -23,7 +23,7 @@ safeRenderer.use({
|
|
|
23
23
|
},
|
|
24
24
|
});
|
|
25
25
|
export function renderSafeMarkdown(markdown) {
|
|
26
|
-
// @cast-boundary marked.parse return-type ist `string | Promise<string>`;
|
|
26
|
+
// @cast-boundary third-party-type-gap marked.parse return-type ist `string | Promise<string>`;
|
|
27
27
|
// `{ async: false }` garantiert sync (string) — Cast nur API-Vertragsfix.
|
|
28
28
|
return safeRenderer.parse(markdown, { async: false });
|
|
29
29
|
}
|
|
@@ -27,7 +27,7 @@ export function bindPatAutoRevokeOnPasswordChangeFromFeature(feature) {
|
|
|
27
27
|
if (exports && typeof exports === "object" && "bindAutoRevokeOnPasswordChange" in exports) {
|
|
28
28
|
const { bindAutoRevokeOnPasswordChange } = exports;
|
|
29
29
|
if (typeof bindAutoRevokeOnPasswordChange === "function") {
|
|
30
|
-
// @cast-boundary exports
|
|
30
|
+
// @cast-boundary feature-exports — feature.exports is untyped by design
|
|
31
31
|
return bindAutoRevokeOnPasswordChange;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -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";
|
|
@@ -10,7 +11,7 @@ import { apiTokenTable } from "../schema/api-token.js";
|
|
|
10
11
|
// client sorting by tokenHash or any other non-exposed column.
|
|
11
12
|
const SORTABLE_COLUMNS = ["id", "name", "createdAt", "expiresAt", "revokedAt"];
|
|
12
13
|
function isSortableColumn(value) {
|
|
13
|
-
return SORTABLE_COLUMNS
|
|
14
|
+
return isOneOf(SORTABLE_COLUMNS, value);
|
|
14
15
|
}
|
|
15
16
|
function tokenStatus(row) {
|
|
16
17
|
if (row.revokedAt !== null)
|
|
@@ -3,11 +3,12 @@ import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
|
3
3
|
import { append } from "@cosmicdrift/kumiko-framework/event-store";
|
|
4
4
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
5
5
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
6
|
+
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
6
7
|
import { PAT_REVOKED_AGGREGATE_TYPE, PAT_REVOKED_EVENT_QN, patRevokedSchema, } from "./pat-revoked-event.js";
|
|
7
8
|
import { apiTokenTable } from "./schema/api-token.js";
|
|
8
9
|
// A pool has begin(); a tx/savepoint handle does not and is already atomic.
|
|
9
10
|
function canBeginTransaction(db) {
|
|
10
|
-
return typeof db
|
|
11
|
+
return typeof propertyOf(db, "begin") === "function";
|
|
11
12
|
}
|
|
12
13
|
// Cross-tenant revoke: password-change and MFA-enable/disable are account-
|
|
13
14
|
// level security events, not scoped to one tenant. Mirrors sessions'
|
|
@@ -3,21 +3,21 @@ import { type RenderKind } from "./constants.js";
|
|
|
3
3
|
import { type RendererPlugin } from "./types.js";
|
|
4
4
|
export type RendererFoundationApi = {
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Selects + returns a RendererPlugin for (tenantId × kind). Callers
|
|
7
|
+
* then invoke `plugin.render(req, ctx)`.
|
|
8
8
|
*
|
|
9
|
-
* **
|
|
10
|
-
* 1. Tenant-
|
|
11
|
-
* 2. DEFAULT_PLUGIN_BY_KIND
|
|
12
|
-
* 3.
|
|
13
|
-
* 4. `RendererError("no_plugin_for_kind")`
|
|
9
|
+
* **Selection order:**
|
|
10
|
+
* 1. Tenant-specific config (config bundle: `rendererPluginByKind`)
|
|
11
|
+
* 2. DEFAULT_PLUGIN_BY_KIND from constants
|
|
12
|
+
* 3. First plugin in the pool that serves the kind
|
|
13
|
+
* 4. `RendererError("no_plugin_for_kind")` if nothing matches
|
|
14
14
|
*
|
|
15
|
-
* **Caller
|
|
16
|
-
* `ctx.user.tenantId`). Plugins
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* Plugins
|
|
15
|
+
* **Caller pattern:** `tenantId` MUST come from the server (typically
|
|
16
|
+
* `ctx.user.tenantId`). Plugins that need service access (e.g.
|
|
17
|
+
* `@cosmicdrift/kumiko-renderer-mail-html` for layout resolution via `template-resolver`)
|
|
18
|
+
* receive `RendererContext` as the second argument to `render()` —
|
|
19
|
+
* matches the pattern of `DeliveryChannel.send(addr, msg, ctx)`.
|
|
20
|
+
* Plugins without service deps ignore ctx.
|
|
21
21
|
*/
|
|
22
22
|
readonly createRendererForTenant: (args: {
|
|
23
23
|
readonly tenantId: TenantId;
|
|
@@ -34,7 +34,7 @@ export function createRendererFoundationApi(plugins, tenantConfigLookup = () =>
|
|
|
34
34
|
if (first)
|
|
35
35
|
return first;
|
|
36
36
|
// 4. Kein Plugin → Error
|
|
37
|
-
throw new RendererError(`[renderer-foundation] no plugin registered for kind="${kind}". Mount at least one plugin (renderer-
|
|
37
|
+
throw new RendererError(`[renderer-foundation] no plugin registered for kind="${kind}". Mount at least one plugin (renderer-mail-html, renderer-puppeteer-client) for this kind.`, "no_plugin_for_kind");
|
|
38
38
|
},
|
|
39
39
|
};
|
|
40
40
|
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
// Extension-point name for renderer plugins (renderer-
|
|
2
|
-
// renderer-mail-html, renderer-puppeteer-client, ...).
|
|
1
|
+
// Extension-point name for renderer plugins (@cosmicdrift/kumiko-renderer-mail-html, @cosmicdrift/kumiko-renderer-puppeteer-client, ...).
|
|
3
2
|
export const RENDERER_EXTENSION = "renderer";
|
|
4
3
|
export { CONTENT_FORMATS, RENDER_KINDS, } from "../template-resolver/index.js";
|
|
5
4
|
// Standard-Default-Plugin pro Kind, wenn Tenant keine explizite Config
|
|
6
5
|
// gesetzt hat. App-Bootstrap kann das via TenantConfigKey überschreiben.
|
|
7
6
|
export const DEFAULT_PLUGIN_BY_KIND = {
|
|
8
|
-
|
|
9
|
-
"mail-html": "mail-html",
|
|
7
|
+
mail: "mail-html",
|
|
10
8
|
"document-pdf": "puppeteer",
|
|
11
9
|
"image-snapshot": "puppeteer",
|
|
12
10
|
};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { RENDERER_EXTENSION } from "./constants.js";
|
|
3
3
|
import { isRendererRegistrationPlugin } from "./types.js";
|
|
4
|
-
// renderer-foundation —
|
|
5
|
-
//
|
|
4
|
+
// renderer-foundation — plugin foundation for renderers (HTML mail, PDF,
|
|
5
|
+
// image). Plan doc:
|
|
6
6
|
// kumiko-platform/docs/plans/features/renderer-foundation.md
|
|
7
7
|
//
|
|
8
|
-
// Pattern
|
|
9
|
-
//
|
|
10
|
-
// renderer-puppeteer-client)
|
|
11
|
-
//
|
|
8
|
+
// Pattern symmetric to ai-foundation: the foundation defines the
|
|
9
|
+
// extension point `renderer`, plugins (@cosmicdrift/kumiko-renderer-mail-html,
|
|
10
|
+
// @cosmicdrift/kumiko-renderer-puppeteer-client) register via `r.useExtension`.
|
|
11
|
+
// Consumers get a plugin at runtime via createRendererForTenant.
|
|
12
12
|
export function createRendererFoundationFeature() {
|
|
13
13
|
return defineFeature("renderer-foundation", (r) => {
|
|
14
|
-
r.describe('Plugin registry for content rendering (
|
|
14
|
+
r.describe('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.');
|
|
15
15
|
r.uiHints({
|
|
16
16
|
displayLabel: "Renderer Foundation",
|
|
17
17
|
category: "notifications",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
export { attachmentFileNameSchema } from "../shared/index.js";
|
|
1
2
|
export { createRendererFoundationApi, type RendererFoundationApi, requireRendererFoundation, } from "./api.js";
|
|
2
3
|
export { CONTENT_FORMATS, type ContentFormat, DEFAULT_PLUGIN_BY_KIND, RENDER_KINDS, RENDERER_EXTENSION, type RenderKind, } from "./constants.js";
|
|
3
4
|
export { collectRendererPlugins, createRendererFoundationFeature } from "./feature.js";
|
|
4
|
-
export { type DocumentPayload, type ImageOptions, isRendererRegistrationPlugin, type
|
|
5
|
+
export { type DocumentPayload, type DocumentPdfDraft, type DocumentPdfPayload, type DocumentPreview, type DocumentPreviewRequest, type FileAttachmentRef, type ImageOptions, type InlineAttachmentRef, isRendererRegistrationPlugin, type MailAttachmentRef, type MailDraft, type MailPayload, type PdfOptions, type RenderedPdfAttachmentRef, type RenderedTemplateRef, type RendererContext, RendererError, type RendererPlugin, type RendererRegistrationPlugin, type RenderProvenance, type RenderRequest, type RenderResponse, } from "./types.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { attachmentFileNameSchema } from "../shared/index.js";
|
|
1
2
|
export { createRendererFoundationApi, requireRendererFoundation, } from "./api.js";
|
|
2
3
|
export { CONTENT_FORMATS, DEFAULT_PLUGIN_BY_KIND, RENDER_KINDS, RENDERER_EXTENSION, } from "./constants.js";
|
|
3
4
|
export { collectRendererPlugins, createRendererFoundationFeature } from "./feature.js";
|
|
@@ -1,55 +1,80 @@
|
|
|
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 { LinkedResourceEntry, MailTemplateMeta, PdfPageFormat, PdfTemplateMeta, TemplateScope } from "../template-resolver/index.js";
|
|
3
5
|
import { type ContentFormat, RENDERER_EXTENSION, type RenderKind } from "./constants.js";
|
|
4
6
|
export type RenderRequest = {
|
|
5
|
-
kind: "
|
|
6
|
-
payload:
|
|
7
|
-
} | {
|
|
8
|
-
kind: "mail-html";
|
|
9
|
-
payload: MailHtmlPayload;
|
|
7
|
+
kind: "mail";
|
|
8
|
+
payload: MailPayload;
|
|
10
9
|
} | {
|
|
11
10
|
kind: "document-pdf";
|
|
12
|
-
payload:
|
|
11
|
+
payload: DocumentPdfPayload;
|
|
13
12
|
options?: PdfOptions;
|
|
14
13
|
} | {
|
|
15
14
|
kind: "image-snapshot";
|
|
16
15
|
payload: DocumentPayload;
|
|
17
16
|
options?: ImageOptions;
|
|
18
17
|
};
|
|
19
|
-
export type
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
18
|
+
export type RenderedTemplateRef = {
|
|
19
|
+
readonly templateId: string;
|
|
20
|
+
readonly templateVersion: number;
|
|
21
|
+
readonly scope: TemplateScope;
|
|
22
|
+
};
|
|
23
|
+
export type RenderProvenance = {
|
|
24
|
+
readonly template?: RenderedTemplateRef;
|
|
25
|
+
readonly layout?: RenderedTemplateRef;
|
|
26
|
+
};
|
|
27
|
+
export type InlineAttachmentRef = {
|
|
28
|
+
readonly kind: "inline";
|
|
29
|
+
readonly cid: string;
|
|
30
|
+
readonly fileRefId: string;
|
|
31
|
+
};
|
|
32
|
+
export type FileAttachmentRef = {
|
|
33
|
+
readonly kind: "file";
|
|
34
|
+
readonly fileRefId: string;
|
|
35
|
+
readonly fileName: string;
|
|
36
|
+
};
|
|
37
|
+
export type RenderedPdfAttachmentRef = {
|
|
38
|
+
readonly kind: "rendered-pdf";
|
|
39
|
+
readonly templateSlug: string;
|
|
40
|
+
readonly variables: Readonly<Record<string, unknown>>;
|
|
41
|
+
readonly fileName: string;
|
|
42
|
+
readonly locale?: string;
|
|
43
|
+
};
|
|
44
|
+
export type MailAttachmentRef = InlineAttachmentRef | FileAttachmentRef | RenderedPdfAttachmentRef;
|
|
45
|
+
export type RenderResponse = ({
|
|
46
|
+
kind: "mail";
|
|
47
|
+
subject: string;
|
|
25
48
|
html: string;
|
|
26
49
|
text: string;
|
|
50
|
+
previewText?: string;
|
|
51
|
+
attachments?: readonly MailAttachmentRef[];
|
|
27
52
|
} | {
|
|
28
53
|
kind: "document-pdf";
|
|
29
54
|
pdfBytes: Uint8Array;
|
|
30
55
|
pageCount: number;
|
|
31
56
|
sizeBytes: number;
|
|
57
|
+
fileName: string;
|
|
32
58
|
} | {
|
|
33
59
|
kind: "image-snapshot";
|
|
34
60
|
imageBytes: Uint8Array;
|
|
35
61
|
format: "png" | "jpg";
|
|
36
62
|
width: number;
|
|
37
63
|
height: number;
|
|
64
|
+
}) & RenderProvenance;
|
|
65
|
+
export type MailDraft = {
|
|
66
|
+
readonly content: string;
|
|
67
|
+
readonly meta: MailTemplateMeta;
|
|
68
|
+
readonly variableSchema?: Readonly<Record<string, unknown>>;
|
|
69
|
+
/** Images and attachments of the row being edited; absent means those of the stored template. */
|
|
70
|
+
readonly linkedResources?: Readonly<Record<string, LinkedResourceEntry>>;
|
|
38
71
|
};
|
|
39
|
-
export type
|
|
40
|
-
readonly
|
|
41
|
-
readonly
|
|
42
|
-
readonly
|
|
43
|
-
readonly
|
|
44
|
-
readonly
|
|
45
|
-
};
|
|
46
|
-
export type MailHtmlPayload = {
|
|
47
|
-
readonly template?: string;
|
|
48
|
-
readonly content?: string;
|
|
49
|
-
readonly contentFormat?: ContentFormat;
|
|
50
|
-
readonly variables?: Readonly<Record<string, unknown>>;
|
|
51
|
-
readonly locale?: string;
|
|
52
|
-
readonly subject?: string;
|
|
72
|
+
export type MailPayload = {
|
|
73
|
+
readonly slug: string;
|
|
74
|
+
readonly locale: string;
|
|
75
|
+
readonly variables: Readonly<Record<string, unknown>>;
|
|
76
|
+
readonly unsubscribeUrl?: string;
|
|
77
|
+
readonly draft?: MailDraft;
|
|
53
78
|
};
|
|
54
79
|
export type DocumentPayload = {
|
|
55
80
|
readonly template?: string;
|
|
@@ -58,8 +83,35 @@ export type DocumentPayload = {
|
|
|
58
83
|
readonly variables?: Readonly<Record<string, unknown>>;
|
|
59
84
|
readonly locale?: string;
|
|
60
85
|
};
|
|
86
|
+
export type DocumentPdfPayload = {
|
|
87
|
+
readonly slug: string;
|
|
88
|
+
readonly locale: string;
|
|
89
|
+
readonly variables: Readonly<Record<string, unknown>>;
|
|
90
|
+
};
|
|
91
|
+
export type DocumentPdfDraft = {
|
|
92
|
+
readonly content: string;
|
|
93
|
+
readonly meta: PdfTemplateMeta | null;
|
|
94
|
+
readonly variableSchema?: Readonly<Record<string, unknown>>;
|
|
95
|
+
};
|
|
96
|
+
export type DocumentPreviewRequest = DocumentPdfPayload & {
|
|
97
|
+
readonly draft?: DocumentPdfDraft;
|
|
98
|
+
};
|
|
99
|
+
export type DocumentPreview = {
|
|
100
|
+
readonly title: string;
|
|
101
|
+
readonly fileName: string;
|
|
102
|
+
readonly bodyHtml: string;
|
|
103
|
+
readonly layout: {
|
|
104
|
+
readonly slug: string;
|
|
105
|
+
readonly scope: TemplateScope;
|
|
106
|
+
readonly headerHtml: string;
|
|
107
|
+
readonly signatureHtml: string;
|
|
108
|
+
readonly footerHtml: string;
|
|
109
|
+
} | null;
|
|
110
|
+
/** Image keys the body or the layout asks for that no file is linked to. */
|
|
111
|
+
readonly missingResourceKeys: readonly string[];
|
|
112
|
+
};
|
|
61
113
|
export type PdfOptions = {
|
|
62
|
-
readonly format?:
|
|
114
|
+
readonly format?: PdfPageFormat;
|
|
63
115
|
readonly marginMm?: {
|
|
64
116
|
top?: number;
|
|
65
117
|
right?: number;
|
|
@@ -70,6 +122,7 @@ export type PdfOptions = {
|
|
|
70
122
|
readonly footerTemplate?: string;
|
|
71
123
|
readonly printBackground?: boolean;
|
|
72
124
|
readonly displayHeaderFooter?: boolean;
|
|
125
|
+
readonly landscape?: boolean;
|
|
73
126
|
};
|
|
74
127
|
export type ImageOptions = {
|
|
75
128
|
readonly width?: number;
|
|
@@ -78,18 +131,25 @@ export type ImageOptions = {
|
|
|
78
131
|
readonly quality?: number;
|
|
79
132
|
};
|
|
80
133
|
export type RendererContext = {
|
|
81
|
-
readonly db:
|
|
134
|
+
readonly db: DbRunner | TenantDb;
|
|
82
135
|
readonly registry: Registry;
|
|
83
136
|
readonly tenantId: TenantId;
|
|
137
|
+
/** Tenant-scoped config for plugins that read the tenant currency or
|
|
138
|
+
* branding; plugins fall back to request defaults when absent. */
|
|
139
|
+
readonly config?: ConfigAccessor;
|
|
140
|
+
/** Only plugins that embed resource bytes (PDF) need it; without it images are dropped. */
|
|
141
|
+
readonly filesForTenant?: (tenantId: string) => FileContext | undefined;
|
|
84
142
|
};
|
|
85
143
|
export type RendererPlugin = {
|
|
86
144
|
readonly name: string;
|
|
87
145
|
readonly kinds: ReadonlyArray<RenderKind>;
|
|
88
146
|
render(req: RenderRequest, ctx: RendererContext): Promise<RenderResponse>;
|
|
147
|
+
/** Optional: plugins that print documents offer a preview that never reaches the print service. */
|
|
148
|
+
previewDocument?(req: DocumentPreviewRequest, ctx: RendererContext): Promise<DocumentPreview>;
|
|
89
149
|
};
|
|
90
150
|
export declare class RendererError extends Error {
|
|
91
|
-
readonly code: "no_plugin_for_kind" | "invalid_payload" | "other";
|
|
92
|
-
constructor(message: string, code: "no_plugin_for_kind" | "invalid_payload" | "other");
|
|
151
|
+
readonly code: "no_plugin_for_kind" | "invalid_payload" | "unavailable" | "other";
|
|
152
|
+
constructor(message: string, code: "no_plugin_for_kind" | "invalid_payload" | "unavailable" | "other");
|
|
93
153
|
}
|
|
94
154
|
export type RendererRegistrationPlugin = Omit<RendererPlugin, "name">;
|
|
95
155
|
export declare function isRendererRegistrationPlugin(o: unknown): o is RendererRegistrationPlugin;
|
|
@@ -13,7 +13,13 @@ export async function renderTablePdfRequest(req) {
|
|
|
13
13
|
...(req.options?.format && { format: req.options.format }),
|
|
14
14
|
...(req.options?.marginMm && { marginMm: req.options.marginMm }),
|
|
15
15
|
});
|
|
16
|
-
return {
|
|
16
|
+
return {
|
|
17
|
+
kind: "document-pdf",
|
|
18
|
+
pdfBytes,
|
|
19
|
+
pageCount,
|
|
20
|
+
sizeBytes: pdfBytes.byteLength,
|
|
21
|
+
fileName: `${req.payload.slug}.pdf`,
|
|
22
|
+
};
|
|
17
23
|
}
|
|
18
24
|
export function createRendererPdfFeature() {
|
|
19
25
|
return defineFeature("renderer-pdf", (r) => {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
export async function deleteStaleSessionsBatch(db, olderThanDays, batchSize) {
|
|
3
|
-
const rows =
|
|
3
|
+
const rows = await asRawClient(db).unsafe(`DELETE FROM "store_user_sessions"
|
|
4
4
|
WHERE "id" IN (
|
|
5
5
|
SELECT "id" FROM "store_user_sessions"
|
|
6
6
|
WHERE "expires_at" < now() - ($1::int * interval '1 day')
|
|
7
7
|
OR "revoked_at" < now() - ($1::int * interval '1 day')
|
|
8
8
|
LIMIT $2
|
|
9
9
|
)
|
|
10
|
-
RETURNING "id"`, [olderThanDays, batchSize])
|
|
10
|
+
RETURNING "id"`, [olderThanDays, batchSize]);
|
|
11
11
|
return rows.length;
|
|
12
12
|
}
|
package/dist/sessions/feature.js
CHANGED
|
@@ -24,7 +24,7 @@ export function bindAutoRevokeFromFeature(feature) {
|
|
|
24
24
|
if (exports && typeof exports === "object" && "bindAutoRevokeOnPasswordChange" in exports) {
|
|
25
25
|
const { bindAutoRevokeOnPasswordChange } = exports;
|
|
26
26
|
if (typeof bindAutoRevokeOnPasswordChange === "function") {
|
|
27
|
-
// @cast-boundary exports
|
|
27
|
+
// @cast-boundary feature-exports — feature.exports is untyped by design
|
|
28
28
|
return bindAutoRevokeOnPasswordChange;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { access, 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 { userSessionTable } from "../schema/user-session.js";
|
|
@@ -10,7 +11,7 @@ import { userSessionTable } from "../schema/user-session.js";
|
|
|
10
11
|
// non-exposed column.
|
|
11
12
|
const SORTABLE_COLUMNS = ["id", "userId", "createdAt", "expiresAt", "revokedAt"];
|
|
12
13
|
function isSortableColumn(value) {
|
|
13
|
-
return SORTABLE_COLUMNS
|
|
14
|
+
return isOneOf(SORTABLE_COLUMNS, value);
|
|
14
15
|
}
|
|
15
16
|
// Admin view of every session in the active tenant. ctx.db (TenantDb)
|
|
16
17
|
// applies tenant-scoping automatically on selects from tables with a
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
const MAX_FILE_NAME_LENGTH = 255;
|
|
3
|
+
function hasControlCharacter(value) {
|
|
4
|
+
return value.split("").some((char) => {
|
|
5
|
+
const code = char.charCodeAt(0);
|
|
6
|
+
return code <= 0x1f || code === 0x7f;
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export const attachmentFileNameSchema = z
|
|
10
|
+
.string()
|
|
11
|
+
.min(1)
|
|
12
|
+
.max(MAX_FILE_NAME_LENGTH)
|
|
13
|
+
.refine((name) => !name.includes("/") && !name.includes("\\") && !hasControlCharacter(name), {
|
|
14
|
+
message: "file name must not contain path separators or control characters",
|
|
15
|
+
});
|
|
16
|
+
const PDF_FILE_NAME_MAX_LENGTH = 200;
|
|
17
|
+
const PDF_EXTENSION = ".pdf";
|
|
18
|
+
// Shared by everything that names a rendered PDF, so a mail attachment and a download of the same
|
|
19
|
+
// template get the same name. The cut leaves room for the extension: the attachment schema caps
|
|
20
|
+
// rendered-pdf names, and a name that only fits without ".pdf" would fail it.
|
|
21
|
+
export function sanitizePdfAttachmentFileName(raw, templateSlug) {
|
|
22
|
+
const cleaned = raw
|
|
23
|
+
.split("")
|
|
24
|
+
.filter((char) => !hasControlCharacter(char))
|
|
25
|
+
.join("")
|
|
26
|
+
.replace(/[/\\";]/g, "-")
|
|
27
|
+
.replaceAll("..", "-")
|
|
28
|
+
.trim()
|
|
29
|
+
.slice(0, PDF_FILE_NAME_MAX_LENGTH - PDF_EXTENSION.length)
|
|
30
|
+
.trim();
|
|
31
|
+
const lastSegment = templateSlug.split(":").pop() ?? templateSlug;
|
|
32
|
+
if (cleaned === "")
|
|
33
|
+
return `${lastSegment}${PDF_EXTENSION}`;
|
|
34
|
+
return cleaned.toLowerCase().endsWith(PDF_EXTENSION) ? cleaned : `${cleaned}${PDF_EXTENSION}`;
|
|
35
|
+
}
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { collectErasureFailure, throwIfErasureFailed } from "./assert-erased.js";
|
|
2
2
|
export { assertNoWhereRuleInOwnershipWrite } from "./assert-no-where-rule-in-ownership-write.js";
|
|
3
|
+
export { attachmentFileNameSchema, sanitizePdfAttachmentFileName, } from "./attachment-file-name.js";
|
|
3
4
|
export { type ChunkedMigrationOptions, type ChunkedMigrationResult, type ChunkedMigrationStopReason, type MigrationRowOutcome, runChunkedMigration, } from "./chunked-entity-migration.js";
|
|
4
5
|
export { classifyStoredEnvelope, type StoredEnvelopeClassification, } from "./classify-stored-envelope.js";
|
|
5
6
|
export { decryptStoredPii } from "./decrypt-stored-pii.js";
|
|
@@ -9,6 +10,7 @@ export { isWithinGracePeriod } from "./grace-period.js";
|
|
|
9
10
|
export { isIdentityV3Hash, verifyIdentityV3Hash } from "./identity-v3-hash.js";
|
|
10
11
|
export { isTenantDb } from "./is-tenant-db.js";
|
|
11
12
|
export { createLockoutCounter, type LockoutCounterState } from "./lockout-counter.js";
|
|
13
|
+
export { mailHeading, mailParagraph, mailRepeatedRows } from "./mail-template-body.js";
|
|
12
14
|
export { mapWithConcurrency } from "./map-with-concurrency.js";
|
|
13
15
|
export { denyUnlessJoinRowParentVisible, hostRowIsGone, joinRowParentIsVisible, parentRowIsVisible, } from "./parent-visibility.js";
|
|
14
16
|
export { hashPassword, verifyDummyPassword, verifyPassword } from "./password-hashing.js";
|
|
@@ -20,6 +22,7 @@ export { type RowBoundGrantResult, redeemRowBoundGrant, signRowBoundGrant, } fro
|
|
|
20
22
|
export { runInSubTransaction } from "./run-in-sub-transaction.js";
|
|
21
23
|
export { registrationLocaleFromRequest, sessionLocaleField } from "./session-locale-field.js";
|
|
22
24
|
export { sessionTimezoneField } from "./session-timezone-field.js";
|
|
25
|
+
export { assertNotSvg, MAX_INPUT_PIXELS, SHARP_INPUT_OPTIONS } from "./sharp-input-limits.js";
|
|
23
26
|
export { peekTokenSubject, signToken, TokenPurpose, type VerifyResult, verifyToken, } from "./signed-token.js";
|
|
24
27
|
export { EXT_SIGNUP_HANDOVER, findSignupHandoverProvider, isSignupHandoverProvider, SIGNUP_HANDOVER_BENIGN_CLAIM_REJECTION_CODE, type SignupHandoverBinding, type SignupHandoverProvider, } from "./signup-handover.js";
|
|
25
28
|
export { createSingleUseTokenStore, hashSingleUseToken } from "./single-use-token-store.js";
|