@cosmicdrift/kumiko-bundled-features 0.374.0 → 0.378.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/account-security/constants.d.ts +1 -0
- package/dist/account-security/constants.js +1 -0
- package/dist/account-security/index.d.ts +1 -1
- package/dist/account-security/index.js +1 -1
- package/dist/admin-shell/web/client-plugin.d.ts +1 -1
- package/dist/audit/web/client-plugin.d.ts +1 -1
- package/dist/auth-email-password/mail-notifications.js +1 -1
- package/dist/auth-email-password/web/auth-client.js +1 -1
- package/dist/auth-email-password/web/auth-form-primitives.js +1 -1
- package/dist/auth-email-password/web/default-topbar-actions.js +1 -1
- package/dist/auth-email-password/web/index.js +1 -1
- package/dist/auth-email-password/web/session.js +1 -1
- package/dist/auth-email-password/web/tenant-menu-items.js +1 -1
- package/dist/auth-email-password/web/tenant-switcher.js +1 -1
- package/dist/auth-email-password/web/user-menu.js +1 -1
- package/dist/auth-foundation/env.d.ts +9 -0
- package/dist/auth-foundation/env.js +21 -0
- package/dist/auth-foundation/index.d.ts +1 -0
- package/dist/auth-foundation/index.js +1 -0
- package/dist/auth-mfa/schema/user-mfa.d.ts +6 -6
- package/dist/billing-foundation/consumer-protection/confirmation-mail.js +1 -1
- package/dist/billing-foundation/consumer-protection/termination-pages.js +1 -1
- package/dist/billing-foundation/contact-card.d.ts +2 -0
- package/dist/billing-foundation/contact-card.js +16 -0
- package/dist/billing-foundation/entities.d.ts +8 -8
- package/dist/billing-foundation/handlers/billing-plans.query.js +1 -1
- package/dist/billing-foundation/index.d.ts +1 -1
- package/dist/billing-foundation/plan-catalog.js +1 -0
- package/dist/billing-foundation/projection.d.ts +8 -8
- package/dist/billing-foundation/types.d.ts +11 -0
- package/dist/billing-foundation/validate-options.js +13 -0
- package/dist/billing-foundation/web/billing-contact-plan.d.ts +5 -0
- package/dist/billing-foundation/web/billing-contact-plan.js +17 -0
- package/dist/billing-foundation/web/billing-plans-panel.js +5 -13
- package/dist/billing-foundation/web/cancel-contract-dialog.js +2 -2
- package/dist/billing-foundation/web/checkout-consent-dialog.d.ts +1 -1
- package/dist/billing-foundation/web/checkout-consent-dialog.js +1 -0
- package/dist/billing-foundation/web/client-plugin.d.ts +1 -1
- package/dist/billing-foundation/web/localized-params.d.ts +1 -0
- package/dist/billing-foundation/web/localized-params.js +11 -0
- package/dist/cap-counter/entity.d.ts +1 -1
- package/dist/cap-counter/stock-cap-guard.js +8 -0
- package/dist/cap-overview/web/cap-cards-panel.js +3 -3
- package/dist/cap-overview/web/client-plugin.d.ts +1 -1
- package/dist/channel-email/email-channel.d.ts +2 -1
- package/dist/compliance-profiles/web/client-plugin.d.ts +1 -1
- package/dist/config/feature.js +2 -2
- package/dist/config/table.d.ts +6 -6
- package/dist/config/web/i18n.js +1 -0
- package/dist/custom-fields/entity.d.ts +4 -4
- package/dist/custom-fields/web/client-plugin.d.ts +1 -1
- package/dist/data-retention/schema/tenant-retention-override.d.ts +4 -4
- package/dist/delivery/tables.d.ts +6 -6
- package/dist/delivery/unsubscribe-page.js +1 -1
- package/dist/delivery/web/client-plugin.d.ts +1 -1
- package/dist/delivery/web/delivery-status-cell.js +1 -1
- package/dist/document-ingest-foundation/entity.d.ts +4 -4
- package/dist/feature-toggles/web/client-plugin.d.ts +1 -1
- package/dist/file-provider-s3/env-named-feature.d.ts +17 -0
- package/dist/file-provider-s3/env-named-feature.js +23 -0
- package/dist/file-provider-s3/env-schema.d.ts +7 -0
- package/dist/file-provider-s3/env-schema.js +15 -10
- package/dist/file-provider-s3/index.d.ts +1 -0
- package/dist/file-provider-s3/index.js +1 -0
- package/dist/folders/entity.d.ts +8 -8
- package/dist/folders/web/client-plugin.d.ts +1 -1
- package/dist/form-draft/entity.d.ts +2 -2
- package/dist/host-routing/constants.d.ts +2 -0
- package/dist/host-routing/constants.js +3 -0
- package/dist/host-routing/dispatch.d.ts +1 -1
- package/dist/host-routing/dispatch.js +35 -25
- package/dist/host-routing/feature.js +100 -27
- package/dist/host-routing/host.d.ts +9 -0
- package/dist/host-routing/host.js +24 -2
- package/dist/host-routing/index.d.ts +2 -1
- package/dist/host-routing/index.js +1 -0
- package/dist/host-routing/options.d.ts +14 -0
- package/dist/host-routing/redirects.d.ts +6 -0
- package/dist/host-routing/redirects.js +27 -0
- package/dist/host-routing/tenant-lookup.d.ts +7 -0
- package/dist/host-routing/tenant-lookup.js +39 -0
- package/dist/host-routing/validate-options.js +34 -1
- package/dist/inbound-mail-foundation/entities.d.ts +25 -25
- package/dist/inbound-mail-foundation/feature.js +7 -0
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.d.ts +1 -1
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +11 -3
- package/dist/inbound-mail-foundation/i18n.d.ts +3 -0
- package/dist/inbound-mail-foundation/i18n.js +35 -0
- package/dist/inbound-mail-foundation/index.d.ts +1 -0
- package/dist/inbound-mail-foundation/index.js +1 -0
- package/dist/inbound-mail-foundation/projection.d.ts +20 -20
- package/dist/inbound-mail-foundation/screens.d.ts +7 -0
- package/dist/inbound-mail-foundation/screens.js +86 -0
- package/dist/inbound-mail-foundation/types.d.ts +2 -2
- package/dist/jobs/job-run-table.d.ts +6 -6
- package/dist/jobs/web/client-plugin.d.ts +1 -1
- package/dist/ledger/entity.d.ts +13 -13
- package/dist/legal-pages/feature.js +11 -1
- package/dist/legal-pages/layout.d.ts +2 -2
- package/dist/legal-pages/layout.js +4 -4
- package/dist/legal-pages/web/client-plugin.d.ts +1 -1
- package/dist/mail-foundation/feature.d.ts +20 -21
- package/dist/mail-foundation/feature.js +1 -1
- package/dist/mail-foundation/i18n.d.ts +5 -2
- package/dist/mail-foundation/i18n.js +3 -2
- package/dist/mail-transport-smtp/i18n.d.ts +1 -1
- package/dist/mail-transport-smtp/i18n.js +1 -1
- package/dist/managed-pages/table.d.ts +12 -12
- package/dist/managed-pages/web/client-plugin.d.ts +1 -1
- package/dist/notes-history/entity.d.ts +12 -12
- package/dist/notes-history/web/client-plugin.d.ts +1 -1
- package/dist/page-render/branding.js +1 -1
- package/dist/page-render/layout.d.ts +1 -1
- package/dist/page-render/layout.js +2 -2
- package/dist/page-render/markdown.js +1 -1
- package/dist/personal-access-tokens/i18n.js +1 -1
- package/dist/personal-access-tokens/schema/api-token.d.ts +6 -6
- package/dist/personal-access-tokens/screens.js +1 -1
- package/dist/secrets/feature.d.ts +1 -1
- package/dist/secrets/feature.js +3 -3
- package/dist/secrets/table.d.ts +1 -1
- package/dist/seo/sitemap.js +1 -1
- package/dist/sessions/schema/user-session.d.ts +4 -4
- package/dist/shared/mail-template-body.js +1 -1
- package/dist/shared/purge-files-reaching-subject.js +98 -3
- package/dist/tags/entity.d.ts +9 -9
- package/dist/tags/web/client-plugin.d.ts +1 -1
- package/dist/template-resolver/compile-document-body.d.ts +3 -0
- package/dist/template-resolver/compile-document-body.js +50 -3
- package/dist/template-resolver/constants.d.ts +3 -0
- package/dist/template-resolver/constants.js +2 -0
- package/dist/template-resolver/document-blocks.d.ts +5 -0
- package/dist/template-resolver/document-blocks.js +22 -0
- package/dist/template-resolver/document-images.d.ts +2 -0
- package/dist/template-resolver/document-images.js +28 -0
- package/dist/template-resolver/document-ops.d.ts +167 -0
- package/dist/template-resolver/document-ops.js +390 -0
- package/dist/template-resolver/document-sanitizer.d.ts +4 -1
- package/dist/template-resolver/document-sanitizer.js +26 -0
- package/dist/template-resolver/document-schema.d.ts +2 -1
- package/dist/template-resolver/document-schema.js +4 -0
- package/dist/template-resolver/feature.d.ts +11 -0
- package/dist/template-resolver/feature.js +60 -31
- package/dist/template-resolver/handlers/bundle-import.js +1 -0
- package/dist/template-resolver/handlers/collection-item.query.js +2 -1
- package/dist/template-resolver/handlers/collection-list.query.js +2 -1
- package/dist/template-resolver/handlers/collection-set.write.js +2 -2
- package/dist/template-resolver/handlers/collection-shared.d.ts +1 -2
- package/dist/template-resolver/handlers/collection-shared.js +0 -4
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +2 -1
- package/dist/template-resolver/handlers/copy-locale.write.js +67 -63
- package/dist/template-resolver/handlers/discard-draft.write.d.ts +3 -2
- package/dist/template-resolver/handlers/discard-draft.write.js +18 -6
- package/dist/template-resolver/handlers/document-base.d.ts +23 -0
- package/dist/template-resolver/handlers/document-base.js +55 -0
- package/dist/template-resolver/handlers/document-blocks.query.d.ts +22 -0
- package/dist/template-resolver/handlers/document-blocks.query.js +46 -0
- package/dist/template-resolver/handlers/export-bundle.query.d.ts +1 -0
- package/dist/template-resolver/handlers/export-bundle.query.js +9 -2
- package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +1 -0
- package/dist/template-resolver/handlers/import-bundle.write.d.ts +3 -1
- package/dist/template-resolver/handlers/import-bundle.write.js +30 -25
- package/dist/template-resolver/handlers/overview.query.d.ts +2 -1
- package/dist/template-resolver/handlers/overview.query.js +46 -42
- package/dist/template-resolver/handlers/patch-draft.write.d.ts +67 -0
- package/dist/template-resolver/handlers/patch-draft.write.js +111 -0
- package/dist/template-resolver/handlers/preview.query.d.ts +1 -0
- package/dist/template-resolver/handlers/preview.query.js +2 -1
- package/dist/template-resolver/handlers/reset-to-system.write.d.ts +2 -1
- package/dist/template-resolver/handlers/reset-to-system.write.js +51 -44
- package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +2 -1
- package/dist/template-resolver/handlers/resource-link-pdf.write.js +34 -28
- package/dist/template-resolver/handlers/resource-remove.write.d.ts +2 -1
- package/dist/template-resolver/handlers/resource-remove.write.js +39 -33
- package/dist/template-resolver/handlers/resource-upload.write.d.ts +2 -1
- package/dist/template-resolver/handlers/resource-upload.write.js +104 -96
- package/dist/template-resolver/handlers/send-test-shared.d.ts +5 -0
- package/dist/template-resolver/handlers/send-test-shared.js +19 -0
- package/dist/template-resolver/handlers/send-test-status.query.d.ts +9 -0
- package/dist/template-resolver/handlers/send-test-status.query.js +18 -0
- package/dist/template-resolver/handlers/send-test.write.d.ts +3 -1
- package/dist/template-resolver/handlers/send-test.write.js +87 -88
- package/dist/template-resolver/handlers/set.write.d.ts +2 -1
- package/dist/template-resolver/handlers/set.write.js +94 -90
- package/dist/template-resolver/handlers/template-write.d.ts +2 -0
- package/dist/template-resolver/handlers/template-write.js +14 -2
- package/dist/template-resolver/handlers/toggle-status.write.d.ts +6 -7
- package/dist/template-resolver/handlers/toggle-status.write.js +28 -10
- package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +2 -1
- package/dist/template-resolver/handlers/upsert-tenant.write.js +30 -24
- package/dist/template-resolver/handlers/validate.query.d.ts +10 -4
- package/dist/template-resolver/handlers/validate.query.js +12 -7
- package/dist/template-resolver/index.d.ts +4 -2
- package/dist/template-resolver/index.js +3 -1
- package/dist/template-resolver/{web/placeholder-text.js → placeholder-text.js} +1 -1
- package/dist/template-resolver/qualified-names.d.ts +4 -0
- package/dist/template-resolver/qualified-names.js +4 -0
- package/dist/template-resolver/seeding.d.ts +1 -0
- package/dist/template-resolver/seeding.js +2 -1
- package/dist/template-resolver/table.d.ts +10 -10
- package/dist/template-resolver/template-bundle.d.ts +1 -0
- package/dist/template-resolver/template-bundle.js +6 -1
- package/dist/template-resolver/template-edit-policy.d.ts +22 -0
- package/dist/template-resolver/template-edit-policy.js +45 -0
- package/dist/template-resolver/template-preview.d.ts +4 -1
- package/dist/template-resolver/template-preview.js +23 -5
- package/dist/template-resolver/template-validation.d.ts +2 -1
- package/dist/template-resolver/template-validation.js +34 -14
- package/dist/template-resolver/user-content-table.d.ts +10 -10
- package/dist/template-resolver/web/client-plugin.d.ts +1 -1
- package/dist/template-resolver/web/client-plugin.js +3 -3
- package/dist/template-resolver/web/document-editor-extensions.d.ts +1 -0
- package/dist/template-resolver/web/document-editor-extensions.js +21 -1
- package/dist/template-resolver/web/document-tiptap-editor.d.ts +1 -1
- package/dist/template-resolver/web/document-tiptap-editor.js +23 -7
- package/dist/template-resolver/web/document-toolbar-blocks.d.ts +4 -5
- package/dist/template-resolver/web/document-toolbar-blocks.js +1 -21
- package/dist/template-resolver/web/i18n.js +18 -0
- package/dist/template-resolver/web/placeholder-line-editor.js +2 -2
- package/dist/tenant/invitation-table.d.ts +6 -6
- package/dist/tenant/membership-table.d.ts +4 -4
- package/dist/tenant/schema/tenant.d.ts +6 -6
- package/dist/tenant/web/client-plugin.d.ts +1 -1
- package/dist/tenant/web/member-status-cell.js +1 -1
- package/dist/tier-engine/entity.d.ts +1 -1
- package/dist/tier-engine/tier-resolver.d.ts +1 -1
- package/dist/tier-engine/web/client-plugin.d.ts +1 -1
- package/dist/user/schema/user.d.ts +12 -12
- package/dist/user-data-rights/env.d.ts +5 -0
- package/dist/user-data-rights/env.js +11 -0
- package/dist/user-data-rights/feature.js +4 -2
- package/dist/user-data-rights/index.d.ts +1 -0
- package/dist/user-data-rights/index.js +1 -0
- package/dist/user-data-rights/lib/mail-transport-resolver.js +1 -1
- package/dist/user-data-rights/lib/storage-provider-resolver.js +1 -1
- package/dist/user-data-rights/mail-notifications.js +1 -1
- package/dist/user-data-rights/schema/download-attempt.d.ts +14 -14
- package/dist/user-data-rights/schema/download-token.d.ts +8 -8
- package/dist/user-data-rights/schema/export-job.d.ts +6 -6
- package/dist/user-data-rights/web/client-plugin.d.ts +1 -1
- package/dist/user-data-rights/web/privacy-center-screen.js +2 -2
- package/dist/waitlist/entity.d.ts +14 -14
- package/dist/waitlist/legacy-migrate.d.ts +2 -0
- package/dist/waitlist/legacy-migrate.js +38 -3
- package/package.json +16 -11
- package/src/account-security/changes.json +6 -0
- package/src/auth-foundation/changes.json +6 -0
- package/src/billing-foundation/changes.json +6 -0
- package/src/cap-counter/changes.json +12 -0
- package/src/channel-email/changes.json +6 -0
- package/src/crypto-shredding/changes.json +6 -0
- package/src/file-provider-s3/changes.json +6 -0
- package/src/host-routing/changes.json +6 -0
- package/src/inbound-mail-foundation/changes.json +13 -0
- package/src/legal-pages/changes.json +6 -0
- package/src/mail-foundation/changes.json +6 -0
- package/src/template-resolver/changes.json +38 -0
- package/src/user-data-rights/changes.json +6 -0
- package/src/waitlist/changes.json +6 -0
- /package/dist/template-resolver/{web/placeholder-text.d.ts → placeholder-text.d.ts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ACTION_FORM_ENTITY } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
2
|
-
import { fieldLabelKey, fieldOptionLabelKey } from "@cosmicdrift/kumiko-headless";
|
|
2
|
+
import { fieldLabelKey, fieldOptionLabelKey } from "@cosmicdrift/kumiko-renderer/headless";
|
|
3
3
|
import { PAT_FEATURE } from "./constants.js";
|
|
4
4
|
const mintFieldKey = (field) => fieldLabelKey(PAT_FEATURE, ACTION_FORM_ENTITY, field);
|
|
5
5
|
const scopeOptionKey = (grant) => fieldOptionLabelKey(PAT_FEATURE, ACTION_FORM_ENTITY, "scopes", grant);
|
|
@@ -13,7 +13,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
13
13
|
readonly sensitive?: boolean;
|
|
14
14
|
readonly writeOnly?: boolean;
|
|
15
15
|
readonly readAsInstruction?: boolean;
|
|
16
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
16
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
17
17
|
readonly default?: string;
|
|
18
18
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
19
19
|
readonly multiline?: boolean | {
|
|
@@ -36,7 +36,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
36
36
|
readonly sensitive?: boolean;
|
|
37
37
|
readonly writeOnly?: boolean;
|
|
38
38
|
readonly readAsInstruction?: boolean;
|
|
39
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
39
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
40
40
|
readonly default?: string;
|
|
41
41
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
42
42
|
readonly multiline?: boolean | {
|
|
@@ -59,7 +59,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
59
59
|
readonly sensitive?: boolean;
|
|
60
60
|
readonly writeOnly?: boolean;
|
|
61
61
|
readonly readAsInstruction?: boolean;
|
|
62
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
62
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
63
63
|
readonly default?: string;
|
|
64
64
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
65
65
|
readonly multiline?: boolean | {
|
|
@@ -82,7 +82,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
82
82
|
readonly sensitive?: boolean;
|
|
83
83
|
readonly writeOnly?: boolean;
|
|
84
84
|
readonly readAsInstruction?: boolean;
|
|
85
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
85
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
86
86
|
readonly default?: string;
|
|
87
87
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
88
88
|
readonly multiline?: boolean | {
|
|
@@ -105,7 +105,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
105
105
|
readonly sensitive?: boolean;
|
|
106
106
|
readonly writeOnly?: boolean;
|
|
107
107
|
readonly readAsInstruction?: boolean;
|
|
108
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
108
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
109
109
|
readonly default?: string;
|
|
110
110
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
111
111
|
readonly multiline?: boolean | {
|
|
@@ -128,7 +128,7 @@ export declare const apiTokenEntity: import("@cosmicdrift/kumiko-types/fields").
|
|
|
128
128
|
readonly sensitive?: boolean;
|
|
129
129
|
readonly writeOnly?: boolean;
|
|
130
130
|
readonly readAsInstruction?: boolean;
|
|
131
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
131
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
132
132
|
readonly default?: string;
|
|
133
133
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
134
134
|
readonly multiline?: boolean | {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i18nKey, MAX_LIST_LIMIT, } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { ACTION_FORM_ENTITY } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
3
|
-
import { fieldOptionLabelKeyPrefix } from "@cosmicdrift/kumiko-headless";
|
|
3
|
+
import { fieldOptionLabelKeyPrefix } from "@cosmicdrift/kumiko-renderer/headless";
|
|
4
4
|
import { PAT_FEATURE, PAT_MINT_SCREEN_ID, PAT_SCREEN_ID, PatHandlers, PatQueries, } from "./constants.js";
|
|
5
5
|
const PAT_STATUS_OPTION_KEY_PREFIX = "pat.list.status.";
|
|
6
6
|
// Same keys the mint form's `scopes` multiSelect options resolve through
|
|
@@ -26,7 +26,7 @@ export { type StoredEnvelope, type StoredMetadata, tenantSecretsTable } from "./
|
|
|
26
26
|
export declare const JOIN_REQUEST_TRANSACTION_REASON = "the secret write runs on the request transaction so a failing batch rolls it back together with the config writes";
|
|
27
27
|
export declare function requireSecretsContext(ctx: {
|
|
28
28
|
readonly secrets?: SecretsContext;
|
|
29
|
-
readonly
|
|
29
|
+
readonly auditUserId?: string | undefined;
|
|
30
30
|
readonly user?: {
|
|
31
31
|
readonly id: string;
|
|
32
32
|
};
|
package/dist/secrets/feature.js
CHANGED
|
@@ -57,7 +57,7 @@ export const JOIN_REQUEST_TRANSACTION_REASON = "the secret write runs on the req
|
|
|
57
57
|
// FileProviderContext-Pfade (S3-Plugin im Worker) liefern dieselben
|
|
58
58
|
// zwei Felder, also reicht die schmale ctx-shape — kein voller
|
|
59
59
|
// HandlerContext-Import noetig.
|
|
60
|
-
// Handler contexts carry the caller as ctx.user, provider contexts set
|
|
60
|
+
// Handler contexts carry the caller as ctx.user, provider contexts set auditUserId (takes precedence).
|
|
61
61
|
export function requireSecretsContext(ctx, handlerName) {
|
|
62
62
|
if (!ctx.secrets) {
|
|
63
63
|
throw new InternalError({
|
|
@@ -65,10 +65,10 @@ export function requireSecretsContext(ctx, handlerName) {
|
|
|
65
65
|
"createSecretsContext({db, masterKeyProvider, registry}) via extraContext.secrets at boot.",
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
|
-
const userId = ctx.
|
|
68
|
+
const userId = ctx.auditUserId ?? ctx.user?.id;
|
|
69
69
|
if (!userId) {
|
|
70
70
|
throw new InternalError({
|
|
71
|
-
message: `[${handlerName}] ctx.
|
|
71
|
+
message: `[${handlerName}] ctx.auditUserId missing (and no ctx.user) — cannot audit secret reads without a caller identity.`,
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
74
|
const withAudit = (raw) => ({
|
package/dist/secrets/table.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare const tenantSecretEntity: import("@cosmicdrift/kumiko-types/field
|
|
|
18
18
|
readonly sensitive?: boolean;
|
|
19
19
|
readonly writeOnly?: boolean;
|
|
20
20
|
readonly readAsInstruction?: boolean;
|
|
21
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
21
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
22
22
|
readonly default?: string;
|
|
23
23
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
24
24
|
readonly multiline?: boolean | {
|
package/dist/seo/sitemap.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { escapeXml } from "@cosmicdrift/kumiko-headless";
|
|
1
|
+
import { escapeXml } from "@cosmicdrift/kumiko-renderer/headless";
|
|
2
2
|
// Pure XML builder — sitemaps.org urlset + xhtml:link alternates. No
|
|
3
3
|
// dedup/sort: callers control ordering, entries are trusted app/feature data
|
|
4
4
|
// (see feature.ts's trust boundary — same posture as ApexCta.href).
|
|
@@ -13,7 +13,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
13
13
|
readonly sensitive?: boolean;
|
|
14
14
|
readonly writeOnly?: boolean;
|
|
15
15
|
readonly readAsInstruction?: boolean;
|
|
16
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
16
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
17
17
|
readonly default?: string;
|
|
18
18
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
19
19
|
readonly multiline?: boolean | {
|
|
@@ -36,7 +36,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
36
36
|
readonly sensitive?: boolean;
|
|
37
37
|
readonly writeOnly?: boolean;
|
|
38
38
|
readonly readAsInstruction?: boolean;
|
|
39
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
39
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
40
40
|
readonly default?: string;
|
|
41
41
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
42
42
|
readonly multiline?: boolean | {
|
|
@@ -119,7 +119,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
119
119
|
readonly sensitive?: boolean;
|
|
120
120
|
readonly writeOnly?: boolean;
|
|
121
121
|
readonly readAsInstruction?: boolean;
|
|
122
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
122
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
123
123
|
readonly default?: string;
|
|
124
124
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
125
125
|
readonly multiline?: boolean | {
|
|
@@ -142,7 +142,7 @@ export declare const userSessionEntity: import("@cosmicdrift/kumiko-types/fields
|
|
|
142
142
|
readonly sensitive?: boolean;
|
|
143
143
|
readonly writeOnly?: boolean;
|
|
144
144
|
readonly readAsInstruction?: boolean;
|
|
145
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
145
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
146
146
|
readonly default?: string;
|
|
147
147
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
148
148
|
readonly multiline?: boolean | {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
|
|
1
|
+
import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-renderer/headless";
|
|
2
2
|
const SLOT_PATTERN = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
3
3
|
// Author text uses `{name}` slots. The text is escaped first and the slots are
|
|
4
4
|
// swapped for variable nodes afterwards, so a slot can never smuggle markup.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolveSubjectForField, SubjectResolutionError, subjectIdToKey, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
2
2
|
import { createEventStoreExecutor, createTenantDb, selectMany, } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import { createSystemUser, parseTenantId, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
|
-
import { fileRefEntity, fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
|
|
3
|
+
import { createSystemUser, parseTenantId, SYSTEM_USER_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
|
+
import { FILE_REF_ENTITY_NAME, fileRefEntity, fileRefsTable, } from "@cosmicdrift/kumiko-framework/files";
|
|
5
5
|
import { collectSingleFileHolders, planFileFields, purgeRowFileRefs, } from "./purge-row-file-refs.js";
|
|
6
6
|
// Callers' error labels read err.code, so audit events name the failure class.
|
|
7
7
|
export class FileSweepError extends Error {
|
|
@@ -95,7 +95,8 @@ export async function purgeFilesReachingSubject(args) {
|
|
|
95
95
|
const fileHolders = collectSingleFileHolders(registry);
|
|
96
96
|
const storageByTenant = new Map();
|
|
97
97
|
let firstFailure;
|
|
98
|
-
|
|
98
|
+
const fileEntities = collectFileEntities(registry);
|
|
99
|
+
for (const fileEntity of fileEntities) {
|
|
99
100
|
const seenRowIds = new Set();
|
|
100
101
|
for (const where of candidateWheres(subject, recordTenantId, fileEntity)) {
|
|
101
102
|
const rows = await selectMany(db, fileEntity.table, where);
|
|
@@ -141,6 +142,100 @@ export async function purgeFilesReachingSubject(args) {
|
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
}
|
|
145
|
+
// Runs even after an entity-sweep failure: one failed row must not leave the pending uploads.
|
|
146
|
+
const pendingFailure = await purgePendingUploadsOfUser({
|
|
147
|
+
db,
|
|
148
|
+
registry,
|
|
149
|
+
subject,
|
|
150
|
+
fileFieldKeys: pendingFieldKeys(fileEntities),
|
|
151
|
+
fileHolders,
|
|
152
|
+
storageByTenant,
|
|
153
|
+
resolveDeleteBytes,
|
|
154
|
+
missingStorage,
|
|
155
|
+
});
|
|
156
|
+
firstFailure ??= pendingFailure;
|
|
144
157
|
if (firstFailure)
|
|
145
158
|
throw firstFailure;
|
|
146
159
|
}
|
|
160
|
+
const pendingFieldKey = (entityType, fieldName) => `${entityType}\u0000${fieldName}`;
|
|
161
|
+
function pendingFieldKeys(fileEntities) {
|
|
162
|
+
const keys = new Set();
|
|
163
|
+
for (const { entityName, fileFields } of fileEntities) {
|
|
164
|
+
for (const fieldName of fileFields)
|
|
165
|
+
keys.add(pendingFieldKey(entityName, fieldName));
|
|
166
|
+
}
|
|
167
|
+
return keys;
|
|
168
|
+
}
|
|
169
|
+
async function isFileRefHeldByRecord(db, holders, tenantId, fileRefId) {
|
|
170
|
+
for (const holder of holders) {
|
|
171
|
+
for (const field of holder.singleFields) {
|
|
172
|
+
const where = { [field]: fileRefId };
|
|
173
|
+
if (holder.tableHasTenantId)
|
|
174
|
+
where["tenantId"] = tenantId;
|
|
175
|
+
if ((await selectMany(db, holder.table, where, { limit: 1 })).length > 0)
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
// An upload the user made but never bound to a record has no entity row to find it through, so
|
|
182
|
+
// the uploader id is the only link. Only refs that originate from a registered entity file
|
|
183
|
+
// field count as pending uploads: config saves (a tenant logo) and server-made files never
|
|
184
|
+
// bind and must outlive the uploader. A legacy unbound ref a record's single file column still
|
|
185
|
+
// names is not pending but in use and stays with that record.
|
|
186
|
+
async function purgePendingUploadsOfUser(args) {
|
|
187
|
+
const { db, registry, subject, fileFieldKeys } = args;
|
|
188
|
+
// skip: only a user uploads files without binding them to a record
|
|
189
|
+
if (subject.kind !== "user")
|
|
190
|
+
return undefined;
|
|
191
|
+
// skip: the system user is not a person; its server-made files are not pending uploads
|
|
192
|
+
if (subject.userId === SYSTEM_USER_ID)
|
|
193
|
+
return undefined;
|
|
194
|
+
// skip: without the files feature there is no file_refs table to sweep
|
|
195
|
+
if (registry.getEntity(FILE_REF_ENTITY_NAME) === undefined)
|
|
196
|
+
return undefined;
|
|
197
|
+
const pending = await selectMany(db, fileRefsTable, {
|
|
198
|
+
insertedById: subject.userId,
|
|
199
|
+
entityId: null,
|
|
200
|
+
});
|
|
201
|
+
let firstFailure;
|
|
202
|
+
for (const fileRef of pending) {
|
|
203
|
+
// skip: not uploaded for a registered entity file field (config, server-made, unknown origin)
|
|
204
|
+
if (!fileFieldKeys.has(pendingFieldKey(fileRef["entityType"], fileRef["fieldName"])))
|
|
205
|
+
continue;
|
|
206
|
+
firstFailure ??= await purgePendingFileRef(args, fileRef);
|
|
207
|
+
}
|
|
208
|
+
return firstFailure;
|
|
209
|
+
}
|
|
210
|
+
async function purgePendingFileRef(args, fileRef) {
|
|
211
|
+
const { db, fileHolders, storageByTenant, resolveDeleteBytes, missingStorage } = args;
|
|
212
|
+
const tenantId = parseTenantId(fileRef["tenantId"]);
|
|
213
|
+
const fileRefId = String(fileRef["id"]);
|
|
214
|
+
const storageKey = fileRef["storageKey"];
|
|
215
|
+
// skip: a ref without tenant or key has no bytes this sweep could address
|
|
216
|
+
if (tenantId === null || typeof storageKey !== "string" || storageKey === "")
|
|
217
|
+
return undefined;
|
|
218
|
+
// skip: a record still uses this file
|
|
219
|
+
if (await isFileRefHeldByRecord(db, fileHolders, tenantId, fileRefId))
|
|
220
|
+
return undefined;
|
|
221
|
+
let storagePromise = storageByTenant.get(tenantId);
|
|
222
|
+
if (storagePromise === undefined) {
|
|
223
|
+
storagePromise = resolveDeleteBytes(tenantId);
|
|
224
|
+
storageByTenant.set(tenantId, storagePromise);
|
|
225
|
+
}
|
|
226
|
+
const deleteBytes = await storagePromise;
|
|
227
|
+
if (deleteBytes === undefined) {
|
|
228
|
+
if (missingStorage.mode === "skip") {
|
|
229
|
+
missingStorage.warn(tenantId);
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
return new FileSweepError("missing_file_storage", FILE_REF_ENTITY_NAME);
|
|
233
|
+
}
|
|
234
|
+
if ((await deleteBytesFailingClosed(deleteBytes, storageKey)).length > 0) {
|
|
235
|
+
return new FileSweepError("file_delete_failed", FILE_REF_ENTITY_NAME);
|
|
236
|
+
}
|
|
237
|
+
const forgotten = await fileRefExecutor.forget({ id: fileRefId }, createSystemUser(tenantId), createTenantDb({ db, tenantId, mode: "system" }));
|
|
238
|
+
return forgotten.isSuccess
|
|
239
|
+
? undefined
|
|
240
|
+
: new FileSweepError("file_delete_failed", FILE_REF_ENTITY_NAME);
|
|
241
|
+
}
|
package/dist/tags/entity.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare const tagEntity: EntityDefinition<{
|
|
|
14
14
|
readonly sensitive?: boolean;
|
|
15
15
|
readonly writeOnly?: boolean;
|
|
16
16
|
readonly readAsInstruction?: boolean;
|
|
17
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
17
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
18
18
|
readonly default?: string;
|
|
19
19
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
20
20
|
readonly multiline?: boolean | {
|
|
@@ -37,7 +37,7 @@ export declare const tagEntity: EntityDefinition<{
|
|
|
37
37
|
readonly sensitive?: boolean;
|
|
38
38
|
readonly writeOnly?: boolean;
|
|
39
39
|
readonly readAsInstruction?: boolean;
|
|
40
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
40
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
41
41
|
readonly default?: string;
|
|
42
42
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
43
43
|
readonly multiline?: boolean | {
|
|
@@ -60,7 +60,7 @@ export declare const tagEntity: EntityDefinition<{
|
|
|
60
60
|
readonly sensitive?: boolean;
|
|
61
61
|
readonly writeOnly?: boolean;
|
|
62
62
|
readonly readAsInstruction?: boolean;
|
|
63
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
63
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
64
64
|
readonly default?: string;
|
|
65
65
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
66
66
|
readonly multiline?: boolean | {
|
|
@@ -85,7 +85,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
|
|
|
85
85
|
readonly sensitive?: boolean;
|
|
86
86
|
readonly writeOnly?: boolean;
|
|
87
87
|
readonly readAsInstruction?: boolean;
|
|
88
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
88
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
89
89
|
readonly default?: string;
|
|
90
90
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
91
91
|
readonly multiline?: boolean | {
|
|
@@ -108,7 +108,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
|
|
|
108
108
|
readonly sensitive?: boolean;
|
|
109
109
|
readonly writeOnly?: boolean;
|
|
110
110
|
readonly readAsInstruction?: boolean;
|
|
111
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
111
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
112
112
|
readonly default?: string;
|
|
113
113
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
114
114
|
readonly multiline?: boolean | {
|
|
@@ -131,7 +131,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
|
|
|
131
131
|
readonly sensitive?: boolean;
|
|
132
132
|
readonly writeOnly?: boolean;
|
|
133
133
|
readonly readAsInstruction?: boolean;
|
|
134
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
134
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
135
135
|
readonly default?: string;
|
|
136
136
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
137
137
|
readonly multiline?: boolean | {
|
|
@@ -156,7 +156,7 @@ export declare const tagAssignmentEntity: EntityDefinition<{
|
|
|
156
156
|
readonly sensitive?: boolean;
|
|
157
157
|
readonly writeOnly?: boolean;
|
|
158
158
|
readonly readAsInstruction?: boolean;
|
|
159
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
159
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
160
160
|
readonly default?: string;
|
|
161
161
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
162
162
|
readonly multiline?: boolean | {
|
|
@@ -179,7 +179,7 @@ export declare const tagAssignmentEntity: EntityDefinition<{
|
|
|
179
179
|
readonly sensitive?: boolean;
|
|
180
180
|
readonly writeOnly?: boolean;
|
|
181
181
|
readonly readAsInstruction?: boolean;
|
|
182
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
182
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
183
183
|
readonly default?: string;
|
|
184
184
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
185
185
|
readonly multiline?: boolean | {
|
|
@@ -202,7 +202,7 @@ export declare const tagAssignmentEntity: EntityDefinition<{
|
|
|
202
202
|
readonly sensitive?: boolean;
|
|
203
203
|
readonly writeOnly?: boolean;
|
|
204
204
|
readonly readAsInstruction?: boolean;
|
|
205
|
-
readonly format?: "email" | "url" | "phone" | "password";
|
|
205
|
+
readonly format?: "email" | "url" | "phone" | "password" | "month";
|
|
206
206
|
readonly default?: string;
|
|
207
207
|
readonly access?: import("@cosmicdrift/kumiko-types/fields").FieldAccess;
|
|
208
208
|
readonly multiline?: boolean | {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer
|
|
1
|
+
import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer/web";
|
|
2
2
|
export declare function tagsClient(): ClientFeatureDefinition;
|
|
@@ -9,7 +9,10 @@ export type CompileDocumentBodyOptions = {
|
|
|
9
9
|
readonly buttonInlineStyle?: string;
|
|
10
10
|
readonly headingAsInlineStyle?: boolean;
|
|
11
11
|
readonly stepsColor?: string;
|
|
12
|
+
readonly recordImagesSrc?: (variable: string) => readonly string[];
|
|
13
|
+
readonly recordImagesPlaceholder?: boolean;
|
|
12
14
|
};
|
|
13
15
|
export declare function collectDocumentResourceKeys(html: string): readonly string[];
|
|
16
|
+
export declare function collectDocumentImageVariables(html: string): readonly string[];
|
|
14
17
|
export declare function toResourceDataUri(contentType: string, bytes: Uint8Array): string;
|
|
15
18
|
export declare function compileDocumentBodyToHandlebars(html: string, variables: TemplateVariables, options?: CompileDocumentBodyOptions): string;
|
|
@@ -48,9 +48,13 @@ function valueExpression(path, def, isWholeBlock) {
|
|
|
48
48
|
}
|
|
49
49
|
return `{{${path}}}`;
|
|
50
50
|
}
|
|
51
|
+
// Never interpolated as text: its value is a list of file ids.
|
|
52
|
+
function isImagesVariable(def) {
|
|
53
|
+
return def?.type === "images";
|
|
54
|
+
}
|
|
51
55
|
function rewriteVariable(element, state) {
|
|
52
56
|
const name = element.getAttribute("data-variable") ?? "";
|
|
53
|
-
const replacement = element.ownerDocument.createTextNode(isDocumentReferenceName(name)
|
|
57
|
+
const replacement = element.ownerDocument.createTextNode(isDocumentReferenceName(name) && !isImagesVariable(pathAndDef(name, element, state)[1])
|
|
54
58
|
? marker(state, valueExpression(...pathAndDef(name, element, state), state.blockRichTextSpans.has(element)))
|
|
55
59
|
: "");
|
|
56
60
|
element.replaceWith(replacement);
|
|
@@ -82,6 +86,9 @@ function wrapVariableOnlyBlock(block, state) {
|
|
|
82
86
|
if (name === null)
|
|
83
87
|
return;
|
|
84
88
|
const [path, def] = pathAndDef(name, block, state);
|
|
89
|
+
// skip: images blocks are rewritten by rewriteImagesBlock
|
|
90
|
+
if (isImagesVariable(def))
|
|
91
|
+
return;
|
|
85
92
|
const host = def?.type === "richText" && block.tagName === "P" ? richTextBlockHost(block, state) : block;
|
|
86
93
|
const document = host.ownerDocument;
|
|
87
94
|
host.before(document.createTextNode(marker(state, `{{#if ${path} includeZero=true}}`)));
|
|
@@ -106,8 +113,8 @@ function rewriteLink(element, state) {
|
|
|
106
113
|
if (isDocumentPlaceholderHref(href)) {
|
|
107
114
|
const name = href.slice(2, -2);
|
|
108
115
|
if (isDocumentReferenceName(name)) {
|
|
109
|
-
const [path] = pathAndDef(name, element, state);
|
|
110
|
-
element.setAttribute("href", marker(state, `{{link ${path}}}`));
|
|
116
|
+
const [path, def] = pathAndDef(name, element, state);
|
|
117
|
+
element.setAttribute("href", isImagesVariable(def) ? "#" : marker(state, `{{link ${path}}}`));
|
|
111
118
|
}
|
|
112
119
|
else {
|
|
113
120
|
element.setAttribute("href", "#");
|
|
@@ -152,6 +159,7 @@ function stripActiveContent(element) {
|
|
|
152
159
|
function neutralizeBraces(html) {
|
|
153
160
|
return html.replace(/[{}]/g, (brace) => (brace === "{" ? "{" : "}"));
|
|
154
161
|
}
|
|
162
|
+
const IMAGES_PLACEHOLDER_STYLE = "height:96px;margin:8px 0;border-radius:4px;background:#e5e7eb";
|
|
155
163
|
const MAIL_BUTTON_STYLE = "display:inline-block;padding:10px 20px;border-radius:6px;background-color:#1f5468;color:#ffffff;font-weight:600;text-decoration:none";
|
|
156
164
|
const MAIL_TABLE_STYLE = "width:100%;border-collapse:collapse";
|
|
157
165
|
const MAIL_TABLE_CELL_STYLE = "padding:6px 12px;vertical-align:top;border-bottom:1px solid #e5e5e5";
|
|
@@ -179,6 +187,42 @@ export function collectDocumentResourceKeys(html) {
|
|
|
179
187
|
return [...keys];
|
|
180
188
|
});
|
|
181
189
|
}
|
|
190
|
+
// Unique valid names of the images blocks in document order.
|
|
191
|
+
export function collectDocumentImageVariables(html) {
|
|
192
|
+
return withParsedDocument(html, (root) => {
|
|
193
|
+
const names = new Set();
|
|
194
|
+
for (const element of Array.from(root.querySelectorAll("div[data-images]"))) {
|
|
195
|
+
const name = element.getAttribute("data-images") ?? "";
|
|
196
|
+
if (isDocumentReferenceName(name))
|
|
197
|
+
names.add(name);
|
|
198
|
+
}
|
|
199
|
+
return [...names];
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function rewriteImagesBlock(element, state, options) {
|
|
203
|
+
const name = element.getAttribute("data-images") ?? "";
|
|
204
|
+
const declared = isDocumentReferenceName(name) && isImagesVariable(ownVariable(state.variables, name));
|
|
205
|
+
if (!declared) {
|
|
206
|
+
element.remove();
|
|
207
|
+
// skip: an images block bound to anything but a declared images variable renders nothing
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const document = element.ownerDocument;
|
|
211
|
+
element.removeAttribute("data-images");
|
|
212
|
+
if (options.recordImagesPlaceholder === true) {
|
|
213
|
+
element.setAttribute("class", "kumiko-doc-image-placeholder");
|
|
214
|
+
element.setAttribute("style", IMAGES_PLACEHOLDER_STYLE);
|
|
215
|
+
// skip: preview and drafts never resolve record images
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
element.setAttribute("class", "kumiko-doc-images");
|
|
219
|
+
for (const src of options.recordImagesSrc?.(name) ?? []) {
|
|
220
|
+
const image = document.createElement("img");
|
|
221
|
+
image.setAttribute("src", src);
|
|
222
|
+
image.setAttribute("alt", "");
|
|
223
|
+
element.append(image);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
182
226
|
export function toResourceDataUri(contentType, bytes) {
|
|
183
227
|
if (!isResourceImageContentType(contentType)) {
|
|
184
228
|
throw new Error(`resource content type "${contentType}" cannot be embedded as an image`);
|
|
@@ -273,6 +317,9 @@ export function compileDocumentBodyToHandlebars(html, variables, options = {}) {
|
|
|
273
317
|
color: options.stepsColor,
|
|
274
318
|
});
|
|
275
319
|
}
|
|
320
|
+
for (const element of Array.from(root.querySelectorAll("div[data-images]"))) {
|
|
321
|
+
rewriteImagesBlock(element, state, options);
|
|
322
|
+
}
|
|
276
323
|
for (const block of Array.from(root.querySelectorAll(`[${DOCUMENT_CONDITION_ATTRIBUTE}]`))) {
|
|
277
324
|
rewriteConditionalBlock(block, state);
|
|
278
325
|
}
|
|
@@ -26,3 +26,6 @@ export declare const MAIL_PREVIEW_MAX_BYTES: number;
|
|
|
26
26
|
export declare const TEST_MAIL_SUBJECT_PREFIX = "[TEST] ";
|
|
27
27
|
export { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
28
28
|
export declare const FALLBACK_LOCALE = "de";
|
|
29
|
+
export declare const DEFAULT_COLLECTION_ACCESS: {
|
|
30
|
+
readonly roles: readonly ["SystemAdmin"];
|
|
31
|
+
};
|
|
@@ -66,3 +66,5 @@ export { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
|
66
66
|
// Default-Locale wenn Tenant keinen eigenen Default konfiguriert. Resolver
|
|
67
67
|
// fällt darauf zurück wenn requested locale + tenant-default fehlen.
|
|
68
68
|
export const FALLBACK_LOCALE = "de";
|
|
69
|
+
// A collection mounted without `access` is maintained by the platform only; TenantAdmin access is an explicit mount decision.
|
|
70
|
+
export const DEFAULT_COLLECTION_ACCESS = { roles: ["SystemAdmin"] };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type DocumentToolbarBlock = "image" | "table" | "button" | "divider" | "repeat" | "pageBreak" | "align" | "steps" | "images";
|
|
2
|
+
export declare const MAIL_DOCUMENT_BLOCKS: readonly ["image", "table", "button", "divider", "repeat", "steps"];
|
|
3
|
+
export declare const PDF_DOCUMENT_BLOCKS: readonly ["align", "image", "table", "divider", "repeat", "steps", "images", "pageBreak"];
|
|
4
|
+
/** Block set for a template-resource kind; every kind except "document-pdf" gets the mail set. */
|
|
5
|
+
export declare function documentBlocksForKind(kind: string | undefined): readonly DocumentToolbarBlock[];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const MAIL_DOCUMENT_BLOCKS = [
|
|
2
|
+
"image",
|
|
3
|
+
"table",
|
|
4
|
+
"button",
|
|
5
|
+
"divider",
|
|
6
|
+
"repeat",
|
|
7
|
+
"steps",
|
|
8
|
+
];
|
|
9
|
+
export const PDF_DOCUMENT_BLOCKS = [
|
|
10
|
+
"align",
|
|
11
|
+
"image",
|
|
12
|
+
"table",
|
|
13
|
+
"divider",
|
|
14
|
+
"repeat",
|
|
15
|
+
"steps",
|
|
16
|
+
"images",
|
|
17
|
+
"pageBreak",
|
|
18
|
+
];
|
|
19
|
+
/** Block set for a template-resource kind; every kind except "document-pdf" gets the mail set. */
|
|
20
|
+
export function documentBlocksForKind(kind) {
|
|
21
|
+
return kind === "document-pdf" ? PDF_DOCUMENT_BLOCKS : MAIL_DOCUMENT_BLOCKS;
|
|
22
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// @runtime client
|
|
2
|
+
// The record's photos: a block bound to an `images` variable whose FileRef ids the
|
|
3
|
+
// app passes at render time. Universal like document-schema.ts.
|
|
4
|
+
import { Node } from "@tiptap/core";
|
|
5
|
+
export function createDocumentImagesNode(isReferenceName) {
|
|
6
|
+
return Node.create({
|
|
7
|
+
name: "images",
|
|
8
|
+
group: "block",
|
|
9
|
+
atom: true,
|
|
10
|
+
addAttributes() {
|
|
11
|
+
return { variable: { default: null } };
|
|
12
|
+
},
|
|
13
|
+
parseHTML() {
|
|
14
|
+
return [
|
|
15
|
+
{
|
|
16
|
+
tag: "div[data-images]",
|
|
17
|
+
getAttrs: (element) => {
|
|
18
|
+
const variable = element.getAttribute("data-images");
|
|
19
|
+
return variable !== null && isReferenceName(variable) ? { variable } : false;
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
},
|
|
24
|
+
renderHTML({ node }) {
|
|
25
|
+
return ["div", { "data-images": node.attrs["variable"] }];
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|