@cosmicdrift/kumiko-bundled-features 0.364.0 → 0.365.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/feature.js +1 -1
- package/dist/audit/run-escape-hatch-retention.d.ts +1 -0
- package/dist/audit/run-escape-hatch-retention.js +39 -12
- package/dist/auth-email-password/bootstrap.d.ts +1 -3
- package/dist/auth-email-password/feature.js +2 -0
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +2 -3
- package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -3
- package/dist/auth-email-password/handlers/invite-create.write.js +4 -1
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -0
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +1 -1
- package/dist/auth-email-password/magic-link-mail.js +6 -3
- package/dist/auth-email-password/mail-i18n.d.ts +3 -0
- package/dist/auth-email-password/mail-i18n.js +10 -0
- package/dist/billing-foundation/consumer-protection/termination-record.js +8 -1
- package/dist/billing-foundation/i18n.js +6 -0
- package/dist/billing-foundation/subscription-tier-sync.js +8 -2
- package/dist/cap-counter/book-cap-usage.js +8 -0
- package/dist/cap-counter/cap-reservation.js +7 -3
- package/dist/cap-counter/handlers/increment-rolling.write.d.ts +1 -0
- package/dist/cap-counter/handlers/increment-rolling.write.js +2 -0
- package/dist/cap-overview/handlers/tenant-caps-list.query.js +3 -1
- package/dist/cap-overview/handlers/tenant-options.query.js +3 -1
- package/dist/channel-discord/discord-channel.js +3 -2
- package/dist/channel-email/email-channel.js +24 -19
- package/dist/channel-email/smtp-transport.js +6 -1
- package/dist/channel-slack/slack-channel.js +3 -2
- package/dist/channel-teams/teams-channel.js +3 -2
- package/dist/crypto-shredding/handlers/forget-subject.write.js +2 -2
- package/dist/custom-fields/db/queries/retention.d.ts +1 -1
- package/dist/custom-fields/db/queries/retention.js +2 -1
- package/dist/data-retention/run-retention-cleanup.js +4 -5
- package/dist/delivery/attachment-ref.js +16 -1
- package/dist/delivery/delivery-service.js +71 -82
- package/dist/feature-toggles/toggle-runtime.d.ts +5 -3
- package/dist/feature-toggles/toggle-runtime.js +25 -12
- package/dist/files-tenant-data/index.d.ts +1 -0
- package/dist/files-tenant-data/index.js +2 -1
- package/dist/form-draft/db/queries/owned-file-refs.d.ts +1 -1
- package/dist/form-draft/db/queries/owned-file-refs.js +9 -2
- package/dist/form-draft/handlers/discard.write.js +1 -1
- package/dist/jobs/job-run-logger.js +5 -2
- package/dist/metrics/filters.d.ts +16 -1
- package/dist/metrics/filters.js +27 -11
- package/dist/personal-access-tokens/revoke-for-user.js +3 -7
- package/dist/secrets/handlers/delete.write.js +3 -1
- package/dist/secrets/write-gate.d.ts +3 -1
- package/dist/secrets/write-gate.js +6 -3
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/resource-image-formats.d.ts +23 -0
- package/dist/shared/resource-image-formats.js +21 -0
- package/dist/shared/sharp-input-limits.js +7 -8
- package/dist/step-dispatcher/webhook-runner.js +2 -2
- package/dist/template-resolver/compile-document-body.d.ts +3 -0
- package/dist/template-resolver/compile-document-body.js +63 -12
- package/dist/template-resolver/compile-steps-block.d.ts +10 -0
- package/dist/template-resolver/compile-steps-block.js +45 -0
- package/dist/template-resolver/constants.d.ts +4 -1
- package/dist/template-resolver/constants.js +10 -1
- package/dist/template-resolver/document-sanitizer.js +3 -0
- package/dist/template-resolver/document-schema.d.ts +2 -1
- package/dist/template-resolver/document-schema.js +6 -1
- package/dist/template-resolver/document-steps.d.ts +17 -0
- package/dist/template-resolver/document-steps.js +93 -0
- package/dist/template-resolver/feature.js +9 -0
- package/dist/template-resolver/handlers/bundle-import.d.ts +7 -1
- package/dist/template-resolver/handlers/bundle-import.js +63 -38
- package/dist/template-resolver/handlers/collection-set.write.js +4 -3
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +1 -1
- package/dist/template-resolver/handlers/copy-locale.write.js +83 -55
- package/dist/template-resolver/handlers/diff-to-system.query.js +1 -0
- package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +1 -1
- package/dist/template-resolver/handlers/import-bundle-system.write.js +16 -5
- package/dist/template-resolver/handlers/import-bundle.write.d.ts +1 -1
- package/dist/template-resolver/handlers/import-bundle.write.js +11 -1
- package/dist/template-resolver/handlers/overview.query.d.ts +3 -0
- package/dist/template-resolver/handlers/overview.query.js +9 -1
- package/dist/template-resolver/handlers/resource-upload.write.d.ts +2 -2
- package/dist/template-resolver/handlers/resource-upload.write.js +35 -5
- package/dist/template-resolver/handlers/validate.query.js +4 -1
- package/dist/template-resolver/index.d.ts +3 -3
- package/dist/template-resolver/index.js +3 -3
- package/dist/template-resolver/layout-appearance.d.ts +1 -1
- package/dist/template-resolver/layout-appearance.js +19 -9
- package/dist/template-resolver/mail-constants.d.ts +2 -0
- package/dist/template-resolver/mail-constants.js +3 -0
- package/dist/template-resolver/resource-image.d.ts +2 -9
- package/dist/template-resolver/resource-image.js +2 -11
- package/dist/template-resolver/seeding.js +2 -0
- package/dist/template-resolver/store-template-resource.d.ts +53 -9
- package/dist/template-resolver/store-template-resource.js +250 -97
- package/dist/template-resolver/template-bundle.js +4 -0
- package/dist/template-resolver/template-draft.d.ts +3 -0
- package/dist/template-resolver/template-draft.js +1 -1
- package/dist/template-resolver/template-meta.d.ts +5 -1
- package/dist/template-resolver/template-meta.js +7 -0
- package/dist/template-resolver/template-preview.js +1 -2
- package/dist/template-resolver/template-resources.d.ts +0 -5
- package/dist/template-resolver/template-resources.js +2 -34
- package/dist/template-resolver/template-validation.js +12 -9
- package/dist/template-resolver/web/document-editor-extensions.d.ts +1 -0
- package/dist/template-resolver/web/document-editor-extensions.js +26 -1
- package/dist/template-resolver/web/document-editor.js +5 -1
- package/dist/template-resolver/web/document-tiptap-editor.js +30 -7
- package/dist/template-resolver/web/document-toolbar-blocks.d.ts +5 -5
- package/dist/template-resolver/web/document-toolbar-blocks.js +2 -0
- package/dist/template-resolver/web/i18n.js +5 -0
- package/dist/template-resolver/web/index.d.ts +1 -0
- package/dist/template-resolver/web/index.js +1 -0
- package/dist/template-resolver/web/placeholder-line-editor.d.ts +4 -2
- package/dist/template-resolver/web/placeholder-line-editor.js +8 -6
- package/dist/tenant/handlers/update-member-roles.write.js +3 -6
- package/dist/tenant/seeding.js +23 -24
- package/dist/tenant-handover/move-entity-graph.js +14 -4
- package/dist/tenant-lifecycle/run-tenant-destroy.d.ts +1 -0
- package/dist/tenant-lifecycle/run-tenant-destroy.js +8 -1
- package/dist/tenant-lifecycle/stages.js +3 -2
- package/dist/tier-engine/constants.d.ts +2 -0
- package/dist/tier-engine/constants.js +4 -0
- package/dist/tier-engine/entity.d.ts +15 -14
- package/dist/tier-engine/entity.js +4 -3
- package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +5 -0
- package/dist/tier-engine/handlers/set-tenant-tier.write.js +7 -4
- package/dist/tier-engine/index.d.ts +1 -1
- package/dist/tier-engine/index.js +1 -1
- package/dist/tier-engine/tier-resolver.d.ts +13 -13
- package/dist/user/constants.d.ts +1 -0
- package/dist/user/invited-global-roles.d.ts +2 -4
- package/dist/user-data-rights/email-templates.js +0 -1
- package/dist/user-data-rights/i18n.js +5 -0
- package/dist/user-data-rights/lib/update-user-lifecycle.d.ts +2 -0
- package/dist/user-data-rights/lib/update-user-lifecycle.js +6 -2
- package/dist/user-data-rights/run-forget-cleanup.js +2 -2
- package/dist/waitlist/feature.js +1 -1
- package/dist/waitlist/handlers/invite.write.js +21 -3
- package/dist/waitlist/handlers/reject.write.js +3 -16
- package/dist/waitlist/handlers/submit.write.js +29 -7
- package/dist/waitlist/i18n.js +9 -0
- package/dist/waitlist/lib.d.ts +2 -1
- package/dist/waitlist/lib.js +15 -0
- package/dist/workflow-runner/feature.js +1 -1
- package/package.json +10 -10
- package/src/audit/changes.json +12 -0
- package/src/auth-email-password/changes.json +18 -0
- package/src/billing-foundation/changes.json +12 -0
- package/src/cap-counter/changes.json +13 -0
- package/src/cap-overview/changes.json +6 -0
- package/src/channel-email/changes.json +18 -0
- package/src/delivery/changes.json +12 -0
- package/src/document-ingest-foundation/changes.json +6 -0
- package/src/feature-toggles/changes.json +7 -0
- package/src/jobs/changes.json +12 -0
- package/src/metrics/changes.json +6 -0
- package/src/personal-access-tokens/changes.json +6 -0
- package/src/secrets/changes.json +6 -0
- package/src/template-resolver/changes.json +81 -0
- package/src/tenant/changes.json +6 -0
- package/src/tenant-lifecycle/changes.json +6 -0
- package/src/tier-engine/changes.json +12 -0
- package/src/user-data-rights/changes.json +6 -0
- package/src/waitlist/changes.json +18 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { ValidationError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
3
|
import { requireForTenant } from "../../shared/index.js";
|
|
4
|
-
import { attachmentLimitFailure, attachmentNeedsMailTemplate,
|
|
4
|
+
import { attachmentLimitFailure, attachmentNeedsMailTemplate, createPreparedBundleResourceEntry, prepareTemplateAttachmentBeforeTransaction, prepareTemplateResourceBeforeTransaction, readTemplateResourceMaxBytes, releaseAll, } from "../store-template-resource.js";
|
|
5
5
|
import { templateResourcesTable } from "../table.js";
|
|
6
6
|
import { attachTemplateResources } from "../template-resources.js";
|
|
7
7
|
import { resolveTemplateVariables } from "../template-validation.js";
|
|
@@ -49,56 +49,81 @@ async function collectBundleIssues(args, status) {
|
|
|
49
49
|
}
|
|
50
50
|
return issues;
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.length + pdfLinks.length;
|
|
57
|
-
if (attachmentCount > 0 && args.bundle.kind !== "mail")
|
|
52
|
+
function checkBundleAttachments(bundle) {
|
|
53
|
+
const attachmentCount = Object.values(bundle.resources).filter((resource) => resource.purpose === "attachment").length +
|
|
54
|
+
Object.keys(bundle.pdfAttachments).length;
|
|
55
|
+
if (attachmentCount > 0 && bundle.kind !== "mail")
|
|
58
56
|
return attachmentNeedsMailTemplate();
|
|
59
57
|
if (attachmentCount > MAX_FILE_ATTACHMENTS_PER_TEMPLATE)
|
|
60
58
|
return attachmentLimitFailure();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
return { isSuccess: true, data: null };
|
|
60
|
+
}
|
|
61
|
+
// beforeTransaction half: validates the bundle and uploads every resource file to the target
|
|
62
|
+
// tenant's storage. A failed or throwing upload releases the ones before it.
|
|
63
|
+
export async function prepareTemplateBundleBeforeTransaction(args) {
|
|
64
|
+
const refused = refuseTenantScopeUnderSystemTenant(args.scope, args.tenantId);
|
|
65
|
+
if (refused !== null)
|
|
66
|
+
return refused;
|
|
67
|
+
const issues = await collectBundleIssues(args, args.status);
|
|
68
|
+
if (issues.length > 0)
|
|
69
|
+
return writeFailure(new ValidationError({ fields: [...issues] }));
|
|
70
|
+
const attachmentCheck = checkBundleAttachments(args.bundle);
|
|
71
|
+
if (!attachmentCheck.isSuccess)
|
|
72
|
+
return attachmentCheck;
|
|
64
73
|
const maxBytes = await readTemplateResourceMaxBytes(args.ctx.config, args.ctx.registry);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
const prepared = new Map();
|
|
75
|
+
const releases = [];
|
|
76
|
+
try {
|
|
77
|
+
// A bundle is untrusted input like an upload: the same re-encoding decides the stored
|
|
78
|
+
// content type, so a declared image/png can't smuggle SVG or HTML into a mail or PDF.
|
|
79
|
+
for (const [key, resource] of Object.entries(args.bundle.resources)) {
|
|
80
|
+
const bytes = Uint8Array.from(Buffer.from(resource.base64, "base64"));
|
|
81
|
+
const common = {
|
|
71
82
|
ctx: args.ctx,
|
|
72
83
|
user: args.user,
|
|
73
84
|
tenantId: args.tenantId,
|
|
74
85
|
bytes,
|
|
75
86
|
maxBytes,
|
|
76
87
|
key,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
};
|
|
89
|
+
const result = resource.purpose === "attachment"
|
|
90
|
+
? await prepareTemplateAttachmentBeforeTransaction({
|
|
91
|
+
...common,
|
|
92
|
+
fileName: resource.fileName,
|
|
93
|
+
})
|
|
94
|
+
: await prepareTemplateResourceBeforeTransaction(common);
|
|
95
|
+
if (!result.isSuccess) {
|
|
96
|
+
await releaseAll(releases);
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
if (result.release)
|
|
100
|
+
releases.push(result.release);
|
|
101
|
+
prepared.set(key, result.data);
|
|
83
102
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
}
|
|
104
|
+
catch (cause) {
|
|
105
|
+
await releaseAll(releases);
|
|
106
|
+
throw cause;
|
|
107
|
+
}
|
|
108
|
+
return { isSuccess: true, data: prepared, release: () => releaseAll(releases) };
|
|
109
|
+
}
|
|
110
|
+
async function linkPreparedBundleResources(args, prepared) {
|
|
111
|
+
const linked = {};
|
|
112
|
+
// The document template is not looked up: a bundle may carry it later than the mail.
|
|
113
|
+
for (const [key, link] of Object.entries(args.bundle.pdfAttachments)) {
|
|
114
|
+
linked[key] = { kind: "rendered-pdf", ...link };
|
|
115
|
+
}
|
|
116
|
+
for (const [key, file] of prepared) {
|
|
117
|
+
const entry = await createPreparedBundleResourceEntry({ ctx: args.ctx, user: args.user, tenantId: args.tenantId }, file);
|
|
118
|
+
if (!entry.isSuccess)
|
|
119
|
+
return entry;
|
|
120
|
+
linked[key] = entry.data;
|
|
95
121
|
}
|
|
96
122
|
return { isSuccess: true, data: linked };
|
|
97
123
|
}
|
|
98
|
-
// Validates every locale first,
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
// for the resources before it stay as unlinked FileRefs, like a replaced upload.
|
|
124
|
+
// Validates every locale first, so a bad locale is refused before anything is written.
|
|
125
|
+
// The resource files are uploaded in the beforeTransaction phase; a rolled-back transaction
|
|
126
|
+
// releases them there. A crash between the upload and the commit leaves the binaries unreferenced.
|
|
102
127
|
export async function importTemplateBundle(args) {
|
|
103
128
|
const refused = refuseTenantScopeUnderSystemTenant(args.scope, args.tenantId);
|
|
104
129
|
if (refused !== null)
|
|
@@ -106,7 +131,7 @@ export async function importTemplateBundle(args) {
|
|
|
106
131
|
const issues = await collectBundleIssues(args, args.status);
|
|
107
132
|
if (issues.length > 0)
|
|
108
133
|
return writeFailure(new ValidationError({ fields: [...issues] }));
|
|
109
|
-
const linked = await
|
|
134
|
+
const linked = await linkPreparedBundleResources(args, args.prepared);
|
|
110
135
|
if (!linked.isSuccess)
|
|
111
136
|
return linked;
|
|
112
137
|
const variableSchema = args.bundle.variableSchema ?? {};
|
|
@@ -3,7 +3,7 @@ import { defineWriteHandler, } from "@cosmicdrift/kumiko-framework/engine";
|
|
|
3
3
|
import { ValidationError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { requireForTenant } from "../../shared/index.js";
|
|
6
|
-
import {
|
|
6
|
+
import { isCollectionRefusedKind } from "../constants.js";
|
|
7
7
|
import { documentViolationsToFieldIssues, sanitizeTemplateBody } from "../document-sanitizer.js";
|
|
8
8
|
import { collectionStore, DEFAULT_COLLECTION_ACCESS, } from "./collection-shared.js";
|
|
9
9
|
import { contentFormatSchema, folderSchema, localeSchema, slugSchema } from "./shared.js";
|
|
@@ -33,11 +33,12 @@ export function makeCollectionSetWrite(collection) {
|
|
|
33
33
|
? `Creates or overwrites the caller's own "${collection.id}" entry at a slug and locale, live on save with no draft stage; it always writes the acting user's entry, never another user's.`
|
|
34
34
|
: `Creates or overwrites the tenant-wide "${collection.id}" entry at a slug and locale, live on save with no draft stage; the change is visible to everyone who may reach this shared collection.`,
|
|
35
35
|
handler: async (event, ctx) => {
|
|
36
|
-
|
|
36
|
+
// Defense in depth: createTemplateResolverFeature already refuses these kinds at mount.
|
|
37
|
+
if (isCollectionRefusedKind(collection.kind)) {
|
|
37
38
|
return writeFailure(new ValidationError({
|
|
38
39
|
fields: [
|
|
39
40
|
{
|
|
40
|
-
path: "
|
|
41
|
+
path: "",
|
|
41
42
|
code: "kind_not_allowed",
|
|
42
43
|
i18nKey: "templateResolver.errors.collection.kindNotAllowed",
|
|
43
44
|
params: { detail: collection.kind },
|
|
@@ -16,4 +16,4 @@ export declare const copyLocaleWrite: import("@cosmicdrift/kumiko-types/define-h
|
|
|
16
16
|
system: "system";
|
|
17
17
|
tenant: "tenant";
|
|
18
18
|
}>>;
|
|
19
|
-
}, z.core.$strip>, import("./template-write.js").WrittenTemplateRow, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap,
|
|
19
|
+
}, z.core.$strip>, import("./template-write.js").WrittenTemplateRow, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, readonly import("../store-template-resource.js").PreparedLinkedResourceCopy[]>;
|
|
@@ -7,9 +7,10 @@ import { requireForTenant } from "../../shared/index.js";
|
|
|
7
7
|
import { CONTENT_FORMATS } from "../constants.js";
|
|
8
8
|
import { parseLinkedResourceEntries } from "../linked-resources.js";
|
|
9
9
|
import { TemplateResolverErrors } from "../qualified-names.js";
|
|
10
|
+
import { createLinkedResourceCopies, prepareLinkedResourceCopiesBeforeTransaction, } from "../store-template-resource.js";
|
|
10
11
|
import { templateResourcesTable } from "../table.js";
|
|
11
12
|
import { toWorkingRow } from "../template-draft.js";
|
|
12
|
-
import { attachTemplateResources
|
|
13
|
+
import { attachTemplateResources } from "../template-resources.js";
|
|
13
14
|
import { resolveTemplateVariables } from "../template-validation.js";
|
|
14
15
|
import { targetExistsIssue } from "./bundle-import.js";
|
|
15
16
|
import { kindSchema, localeSchema, templateSlugSchema } from "./shared.js";
|
|
@@ -39,54 +40,88 @@ async function findCopySource(ctx, where) {
|
|
|
39
40
|
status: "active",
|
|
40
41
|
});
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
43
|
+
const copyLocaleSchema = z.object({
|
|
44
|
+
slug: templateSlugSchema,
|
|
45
|
+
kind: kindSchema,
|
|
46
|
+
fromLocale: localeSchema,
|
|
47
|
+
toLocale: localeSchema,
|
|
48
|
+
overwrite: z.boolean().default(false),
|
|
49
|
+
/** "system" copies between system-default rows; SystemAdmin only. */
|
|
50
|
+
scope: z.enum(["tenant", "system"]).default("tenant"),
|
|
51
|
+
});
|
|
52
|
+
// Runs before the upload (reject early, store nothing) and again in the transaction on the fresh rows.
|
|
53
|
+
async function checkCopyRequest(ctx, event) {
|
|
54
|
+
const { slug, kind, fromLocale, toLocale, overwrite, scope } = event.payload;
|
|
55
|
+
if (scope === "system" && !event.user.roles.includes("SystemAdmin")) {
|
|
56
|
+
return writeFailure(new AccessDeniedError({
|
|
57
|
+
i18nKey: "errors.access.denied",
|
|
58
|
+
details: { reason: TemplateResolverErrors.systemAdminRequired },
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
const tenantId = scope === "system" ? SYSTEM_TENANT_ID : event.user.tenantId;
|
|
62
|
+
const refused = refuseTenantScopeUnderSystemTenant(scope, tenantId);
|
|
63
|
+
if (refused !== null)
|
|
64
|
+
return refused;
|
|
65
|
+
const readDb = readDbFor(ctx, scope);
|
|
66
|
+
const storedSource = await findCopySource(ctx, {
|
|
67
|
+
scope,
|
|
68
|
+
tenantId,
|
|
69
|
+
slug,
|
|
70
|
+
kind,
|
|
71
|
+
locale: fromLocale,
|
|
72
|
+
});
|
|
73
|
+
const source = storedSource && toWorkingRow(storedSource, tenantId);
|
|
74
|
+
const contentFormat = source?.contentFormat;
|
|
75
|
+
if (!source || !isOneOf(CONTENT_FORMATS, contentFormat)) {
|
|
76
|
+
return writeFailure(new NotFoundError("template-resource", `${slug}/${fromLocale}`));
|
|
77
|
+
}
|
|
78
|
+
if (!overwrite) {
|
|
79
|
+
const target = await fetchOne(readDb, templateResourcesTable, {
|
|
70
80
|
tenantId,
|
|
71
81
|
slug,
|
|
72
82
|
kind,
|
|
73
|
-
locale:
|
|
83
|
+
locale: toLocale,
|
|
74
84
|
});
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return writeFailure(new NotFoundError("template-resource", `${slug}/${fromLocale}`));
|
|
78
|
-
}
|
|
79
|
-
if (!overwrite) {
|
|
80
|
-
const target = await fetchOne(readDb, templateResourcesTable, {
|
|
81
|
-
tenantId,
|
|
82
|
-
slug,
|
|
83
|
-
kind,
|
|
84
|
-
locale: toLocale,
|
|
85
|
-
});
|
|
86
|
-
if (target) {
|
|
87
|
-
return writeFailure(new ValidationError({ fields: [targetExistsIssue(toLocale)] }));
|
|
88
|
-
}
|
|
85
|
+
if (target) {
|
|
86
|
+
return writeFailure(new ValidationError({ fields: [targetExistsIssue(toLocale)] }));
|
|
89
87
|
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
isSuccess: true,
|
|
91
|
+
data: {
|
|
92
|
+
scope,
|
|
93
|
+
tenantId,
|
|
94
|
+
source,
|
|
95
|
+
contentFormat,
|
|
96
|
+
sourceResources: parseLinkedResourceEntries(source.linkedResources),
|
|
97
|
+
sourceIsForeign: source.tenantId !== tenantId,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
export const copyLocaleWrite = defineWriteHandler({
|
|
102
|
+
name: "copy-locale",
|
|
103
|
+
schema: copyLocaleSchema,
|
|
104
|
+
access: { roles: ["TenantAdmin", "SystemAdmin"] },
|
|
105
|
+
description: "Copies the text of one locale of a template into another locale as a draft override of the caller's tenant, taking the tenant's own row or else the system default as the source; use it as the starting point for a translation. It refuses an existing target unless overwrite is set. With scope system (SystemAdmin only) both source and target are system-default rows and the target lands as a system draft; any other caller gets a 403.",
|
|
106
|
+
beforeTransaction: async (event, ctx) => {
|
|
107
|
+
const request = await checkCopyRequest(ctx, event);
|
|
108
|
+
if (!request.isSuccess)
|
|
109
|
+
return request;
|
|
110
|
+
if (!request.data.sourceIsForeign)
|
|
111
|
+
return { isSuccess: true, data: [] };
|
|
112
|
+
return prepareLinkedResourceCopiesBeforeTransaction({
|
|
113
|
+
ctx,
|
|
114
|
+
user: event.user,
|
|
115
|
+
tenantId: request.data.tenantId,
|
|
116
|
+
resources: request.data.sourceResources,
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
handler: async (event, ctx) => {
|
|
120
|
+
const { slug, kind, toLocale } = event.payload;
|
|
121
|
+
const request = await checkCopyRequest(ctx, event);
|
|
122
|
+
if (!request.isSuccess)
|
|
123
|
+
return request;
|
|
124
|
+
const { scope, tenantId, source, contentFormat, sourceResources, sourceIsForeign } = request.data;
|
|
90
125
|
const variableSchema = storedRecord(source.variableSchema);
|
|
91
126
|
const written = await writeTemplateRow({
|
|
92
127
|
ctx,
|
|
@@ -100,7 +135,7 @@ export const copyLocaleWrite = defineWriteHandler({
|
|
|
100
135
|
kind,
|
|
101
136
|
locale: toLocale,
|
|
102
137
|
content: source.content,
|
|
103
|
-
contentFormat
|
|
138
|
+
contentFormat,
|
|
104
139
|
variableSchema,
|
|
105
140
|
meta: parseStoredJson(source.meta),
|
|
106
141
|
title: source.title,
|
|
@@ -109,16 +144,9 @@ export const copyLocaleWrite = defineWriteHandler({
|
|
|
109
144
|
});
|
|
110
145
|
if (!written.isSuccess)
|
|
111
146
|
return written;
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const resources = source.tenantId === tenantId
|
|
116
|
-
? { isSuccess: true, data: sourceResources }
|
|
117
|
-
: await duplicateLinkedResourceFiles(ctx, {
|
|
118
|
-
user: event.user,
|
|
119
|
-
tenantId,
|
|
120
|
-
resources: sourceResources,
|
|
121
|
-
});
|
|
147
|
+
const resources = sourceIsForeign
|
|
148
|
+
? await createLinkedResourceCopies({ ctx, user: event.user, tenantId }, event.prepared)
|
|
149
|
+
: { isSuccess: true, data: sourceResources };
|
|
122
150
|
if (!resources.isSuccess)
|
|
123
151
|
return resources;
|
|
124
152
|
const attached = await attachTemplateResources(ctx, {
|
|
@@ -22,6 +22,7 @@ export const diffToSystemQuery = defineQueryHandler({
|
|
|
22
22
|
slug: tenantRow.slug,
|
|
23
23
|
kind: tenantRow.kind,
|
|
24
24
|
locale: tenantRow.locale,
|
|
25
|
+
status: "active",
|
|
25
26
|
});
|
|
26
27
|
if (!systemRow || isHiddenSystemDraft(query.user, systemRow))
|
|
27
28
|
throw new NotFoundError("template-resource", tenantRow.slug);
|
|
@@ -36,4 +36,4 @@ export declare const importBundleSystemWrite: import("@cosmicdrift/kumiko-types/
|
|
|
36
36
|
}, z.core.$strict>;
|
|
37
37
|
}, z.core.$strip>, {
|
|
38
38
|
ids: Record<string, string>;
|
|
39
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap,
|
|
39
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, import("./bundle-import.js").PreparedBundleResources>;
|
|
@@ -2,7 +2,7 @@ import { defineWriteHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framew
|
|
|
2
2
|
import { ValidationError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
import { templateBundleSchema } from "../template-bundle.js";
|
|
5
|
-
import { importTemplateBundle } from "./bundle-import.js";
|
|
5
|
+
import { importTemplateBundle, prepareTemplateBundleBeforeTransaction } from "./bundle-import.js";
|
|
6
6
|
export const importBundleSystemWrite = defineWriteHandler({
|
|
7
7
|
name: "import-bundle-system",
|
|
8
8
|
schema: z.object({ bundle: templateBundleSchema }),
|
|
@@ -10,10 +10,11 @@ export const importBundleSystemWrite = defineWriteHandler({
|
|
|
10
10
|
// A system template change reaches every tenant without an override.
|
|
11
11
|
agent: { risk: "high" },
|
|
12
12
|
description: "Imports a template bundle as system-default templates, active straight away and overwriting existing system rows; SystemAdmin only. Bundles with resource files need a caller in the system tenant, because the files are stored in the caller's file space.",
|
|
13
|
-
|
|
13
|
+
beforeTransaction: async (event, ctx) => {
|
|
14
14
|
const { bundle } = event.payload;
|
|
15
|
+
// Must run before any upload.
|
|
15
16
|
if (Object.keys(bundle.resources).length > 0 && event.user.tenantId !== SYSTEM_TENANT_ID) {
|
|
16
|
-
return
|
|
17
|
+
return writeFailure(new ValidationError({
|
|
17
18
|
fields: [
|
|
18
19
|
{
|
|
19
20
|
path: "bundle.resources",
|
|
@@ -21,9 +22,9 @@ export const importBundleSystemWrite = defineWriteHandler({
|
|
|
21
22
|
i18nKey: "templateResolver.errors.template.systemResourcesNeedSystemTenant",
|
|
22
23
|
},
|
|
23
24
|
],
|
|
24
|
-
}))
|
|
25
|
+
}));
|
|
25
26
|
}
|
|
26
|
-
return
|
|
27
|
+
return prepareTemplateBundleBeforeTransaction({
|
|
27
28
|
ctx,
|
|
28
29
|
user: event.user,
|
|
29
30
|
tenantId: SYSTEM_TENANT_ID,
|
|
@@ -33,4 +34,14 @@ export const importBundleSystemWrite = defineWriteHandler({
|
|
|
33
34
|
overwrite: true,
|
|
34
35
|
});
|
|
35
36
|
},
|
|
37
|
+
handler: (event, ctx) => importTemplateBundle({
|
|
38
|
+
ctx,
|
|
39
|
+
user: event.user,
|
|
40
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
41
|
+
scope: "system",
|
|
42
|
+
status: "active",
|
|
43
|
+
bundle: event.payload.bundle,
|
|
44
|
+
overwrite: true,
|
|
45
|
+
prepared: event.prepared,
|
|
46
|
+
}),
|
|
36
47
|
});
|
|
@@ -37,4 +37,4 @@ export declare const importBundleWrite: import("@cosmicdrift/kumiko-types/define
|
|
|
37
37
|
overwrite: z.ZodDefault<z.ZodBoolean>;
|
|
38
38
|
}, z.core.$strip>, {
|
|
39
39
|
ids: Record<string, string>;
|
|
40
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap,
|
|
40
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, import("./bundle-import.js").PreparedBundleResources>;
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
import { templateBundleSchema } from "../template-bundle.js";
|
|
4
|
-
import { importTemplateBundle } from "./bundle-import.js";
|
|
4
|
+
import { importTemplateBundle, prepareTemplateBundleBeforeTransaction } from "./bundle-import.js";
|
|
5
5
|
export const importBundleWrite = defineWriteHandler({
|
|
6
6
|
name: "import-bundle",
|
|
7
7
|
schema: z.object({ bundle: templateBundleSchema, overwrite: z.boolean().default(false) }),
|
|
8
8
|
access: { roles: ["TenantAdmin", "SystemAdmin"] },
|
|
9
9
|
description: "Imports a template bundle (all locales plus resource files, as produced by export-bundle) into the caller's tenant as drafts; it refuses a locale that already has an override unless overwrite is set, and publish makes the drafts live.",
|
|
10
|
+
beforeTransaction: (event, ctx) => prepareTemplateBundleBeforeTransaction({
|
|
11
|
+
ctx,
|
|
12
|
+
user: event.user,
|
|
13
|
+
tenantId: event.user.tenantId,
|
|
14
|
+
scope: "tenant",
|
|
15
|
+
status: "draft",
|
|
16
|
+
bundle: event.payload.bundle,
|
|
17
|
+
overwrite: event.payload.overwrite,
|
|
18
|
+
}),
|
|
10
19
|
handler: (event, ctx) => importTemplateBundle({
|
|
11
20
|
ctx,
|
|
12
21
|
user: event.user,
|
|
@@ -15,5 +24,6 @@ export const importBundleWrite = defineWriteHandler({
|
|
|
15
24
|
status: "draft",
|
|
16
25
|
bundle: event.payload.bundle,
|
|
17
26
|
overwrite: event.payload.overwrite,
|
|
27
|
+
prepared: event.prepared,
|
|
18
28
|
}),
|
|
19
29
|
});
|
|
@@ -34,6 +34,8 @@ export type OverviewResponse = {
|
|
|
34
34
|
readonly rows: readonly OverviewRow[];
|
|
35
35
|
readonly nextCursor: null;
|
|
36
36
|
readonly total: number;
|
|
37
|
+
/** True when the tenant has more template rows than the overview reads; rows and total are then incomplete. */
|
|
38
|
+
readonly truncated: boolean;
|
|
37
39
|
};
|
|
38
40
|
export declare function effectiveMailLayoutSlugs(rows: readonly TemplateResourceRow[]): EffectiveMailLayoutSlugs;
|
|
39
41
|
export declare const overviewQuery: import("@cosmicdrift/kumiko-types/define-handler").QueryHandlerDefinition<"overview", z.ZodObject<{
|
|
@@ -61,5 +63,6 @@ export declare const overviewQuery: import("@cosmicdrift/kumiko-types/define-han
|
|
|
61
63
|
rows: OverviewRow[];
|
|
62
64
|
nextCursor: null;
|
|
63
65
|
total: number;
|
|
66
|
+
truncated: boolean;
|
|
64
67
|
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
65
68
|
export {};
|
|
@@ -186,7 +186,14 @@ export const overviewQuery = defineQueryHandler({
|
|
|
186
186
|
description: "Lists mail, PDF and layout templates as one row per slug and kind for the caller's tenant (optionally only the mails and PDFs using one layout slug): where the effective version comes from (own override or system), its locales, status and version (the served one; `draftVersion` is set when an unpublished draft waits beside it, `activeSince` says since when the served version applies), the system version it overrides, the layout it uses and which notifications or templates use it; use it to browse and filter all templates at a glance.",
|
|
187
187
|
handler: async (query, ctx) => {
|
|
188
188
|
const payload = query.payload;
|
|
189
|
-
const
|
|
189
|
+
const fetchedRows = await selectMany(ctx.db, templateResourcesTable, { kind: { in: [...OVERVIEW_KINDS] } }, { orderBy: { col: "id", direction: "asc" }, limit: OVERVIEW_ROW_LIMIT + 1 });
|
|
190
|
+
const truncated = fetchedRows.length > OVERVIEW_ROW_LIMIT;
|
|
191
|
+
const rows = truncated ? fetchedRows.slice(0, OVERVIEW_ROW_LIMIT) : fetchedRows;
|
|
192
|
+
if (truncated) {
|
|
193
|
+
ctx.log?.warn("template overview: row limit reached, result is incomplete", {
|
|
194
|
+
limit: OVERVIEW_ROW_LIMIT,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
190
197
|
const visibleRows = rows.filter((row) => !isHiddenSystemDraft(query.user, row));
|
|
191
198
|
const { drafts, layoutSlugByDocument } = buildDrafts(groupBySlugAndKind(visibleRows), query.user);
|
|
192
199
|
const effectiveLayouts = effectiveMailLayoutSlugs(visibleRows);
|
|
@@ -200,6 +207,7 @@ export const overviewQuery = defineQueryHandler({
|
|
|
200
207
|
rows: filtered.slice(offset, offset + payload.limit),
|
|
201
208
|
nextCursor: null,
|
|
202
209
|
total: filtered.length,
|
|
210
|
+
truncated,
|
|
203
211
|
};
|
|
204
212
|
},
|
|
205
213
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
import { type StoredTemplateAttachment, type StoredTemplateResource } from "../store-template-resource.js";
|
|
2
|
+
import { type PreparedTemplateFile, type StoredTemplateAttachment, type StoredTemplateResource } from "../store-template-resource.js";
|
|
3
3
|
export declare const resourceUploadWrite: import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"resource-upload", z.ZodObject<{
|
|
4
4
|
templateId: z.ZodOptional<z.ZodString>;
|
|
5
5
|
key: z.ZodOptional<z.ZodString>;
|
|
@@ -10,4 +10,4 @@ export declare const resourceUploadWrite: import("@cosmicdrift/kumiko-types/defi
|
|
|
10
10
|
image: "image";
|
|
11
11
|
}>>;
|
|
12
12
|
fileName: z.ZodOptional<z.ZodString>;
|
|
13
|
-
}, z.core.$strip>, StoredTemplateAttachment | StoredTemplateResource, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap,
|
|
13
|
+
}, z.core.$strip>, StoredTemplateAttachment | StoredTemplateResource, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, PreparedTemplateFile>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
1
|
+
import { defineWriteHandler, } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { NotFoundError, UnprocessableError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
import { FileRefTooLargeError, fileContextForTenant, readFileRefContent, } from "../../file-foundation/index.js";
|
|
5
5
|
import { attachmentFileNameSchema } from "../../shared/index.js";
|
|
6
6
|
import { TemplateResolverErrors } from "../qualified-names.js";
|
|
7
|
-
import { readTemplateResourceMaxBytes,
|
|
7
|
+
import { prepareTemplateAttachmentBeforeTransaction, prepareTemplateResourceBeforeTransaction, readTemplateResourceMaxBytes, storePreparedTemplateAttachment, storePreparedTemplateResource, } from "../store-template-resource.js";
|
|
8
8
|
import { RESOURCE_PURPOSES, resourceKeySchema, resourceTargetTenantId } from "./resource-shared.js";
|
|
9
9
|
function attachmentTargetOf(input) {
|
|
10
10
|
const { templateId, key, fileName } = input;
|
|
@@ -37,7 +37,7 @@ export const resourceUploadWrite = defineWriteHandler({
|
|
|
37
37
|
}),
|
|
38
38
|
access: { roles: ["TenantAdmin", "SystemAdmin"] },
|
|
39
39
|
description: "Turns a file uploaded via POST /api/files (multipart, no entityType) into a template resource. With purpose image (default) the upload is checked against the resource-max-bytes cap, re-encoded to a supported image format and stored as a new file; with templateId and key it is linked into that template under the key, without them only the file is created. With purpose attachment, templateId, key and fileName are required: a PDF, PNG or JPEG is stored unchanged and linked into a mail template as a file attached to every mail sent from it, at most 10 per template. A cap above the files route's maxUploadSize has no effect.",
|
|
40
|
-
|
|
40
|
+
beforeTransaction: async (event, ctx) => {
|
|
41
41
|
const { templateId, key, sourceFileRefId, purpose, fileName } = event.payload;
|
|
42
42
|
const attachmentTarget = purpose === "attachment" ? attachmentTargetOf({ templateId, key, fileName }) : null;
|
|
43
43
|
if (purpose === "attachment" && attachmentTarget === null) {
|
|
@@ -76,7 +76,7 @@ export const resourceUploadWrite = defineWriteHandler({
|
|
|
76
76
|
// The source upload stays as an unclaimed file: bundled-features cannot reach the fileRef executor to delete it.
|
|
77
77
|
const tenantId = resourceTargetTenantId(event.payload.tenantIdOverride, event.user.tenantId);
|
|
78
78
|
if (attachmentTarget !== null) {
|
|
79
|
-
return
|
|
79
|
+
return prepareTemplateAttachmentBeforeTransaction({
|
|
80
80
|
ctx,
|
|
81
81
|
user: event.user,
|
|
82
82
|
tenantId,
|
|
@@ -85,7 +85,7 @@ export const resourceUploadWrite = defineWriteHandler({
|
|
|
85
85
|
...attachmentTarget,
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
|
-
return
|
|
88
|
+
return prepareTemplateResourceBeforeTransaction({
|
|
89
89
|
ctx,
|
|
90
90
|
user: event.user,
|
|
91
91
|
tenantId,
|
|
@@ -95,4 +95,34 @@ export const resourceUploadWrite = defineWriteHandler({
|
|
|
95
95
|
...(key !== undefined && { key }),
|
|
96
96
|
});
|
|
97
97
|
},
|
|
98
|
+
handler: async (event, ctx) => {
|
|
99
|
+
const { templateId, key } = event.payload;
|
|
100
|
+
const tenantId = resourceTargetTenantId(event.payload.tenantIdOverride, event.user.tenantId);
|
|
101
|
+
const { prepared } = event;
|
|
102
|
+
if (prepared.kind === "attachment") {
|
|
103
|
+
// The phase accepts attachments only with a complete target, so these are set.
|
|
104
|
+
if (templateId === undefined || key === undefined) {
|
|
105
|
+
return writeFailure(new UnprocessableError(TemplateResolverErrors.attachmentInvalid, {
|
|
106
|
+
i18nKey: "templateResolver.errors.attachment.invalid",
|
|
107
|
+
details: { code: "target_required" },
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
return storePreparedTemplateAttachment({
|
|
111
|
+
ctx,
|
|
112
|
+
user: event.user,
|
|
113
|
+
tenantId,
|
|
114
|
+
prepared,
|
|
115
|
+
templateId,
|
|
116
|
+
key,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return storePreparedTemplateResource({
|
|
120
|
+
ctx,
|
|
121
|
+
user: event.user,
|
|
122
|
+
tenantId,
|
|
123
|
+
prepared,
|
|
124
|
+
...(templateId !== undefined && { templateId }),
|
|
125
|
+
...(key !== undefined && { key }),
|
|
126
|
+
});
|
|
127
|
+
},
|
|
98
128
|
});
|
|
@@ -3,6 +3,7 @@ import { defineQueryHandler, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-frame
|
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
import { imageResourceKeysOf } from "../linked-resources.js";
|
|
5
5
|
import { templateResourcesTable } from "../table.js";
|
|
6
|
+
import { isSystemAdmin } from "../template-draft.js";
|
|
6
7
|
import { resolveTemplateVariables, systemPlaceholdersFor } from "../template-validation.js";
|
|
7
8
|
import { contentFormatSchema, kindSchema, localeSchema, templateSlugSchema } from "./shared.js";
|
|
8
9
|
import { evaluateTemplateWrite } from "./template-gates.js";
|
|
@@ -20,7 +21,9 @@ async function knownResourceKeys(db, where) {
|
|
|
20
21
|
// The dry run of the write pipeline; preview feeds on the evaluation, validate returns the response.
|
|
21
22
|
export async function validateTemplateDraft(ctx, user, payload) {
|
|
22
23
|
const variables = resolveTemplateVariables(ctx.registry, payload.slug, payload.variableSchema);
|
|
23
|
-
const { slug, kind,
|
|
24
|
+
const { slug, kind, locale } = payload;
|
|
25
|
+
// The dry run must not promise what the caller's write handlers would refuse.
|
|
26
|
+
const scope = isSystemAdmin(user) ? payload.scope : "tenant";
|
|
24
27
|
const known = [
|
|
25
28
|
...(await knownResourceKeys(ctx.db, {
|
|
26
29
|
tenantId: SYSTEM_TENANT_ID,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { createTemplateResolverApi, type ResolveRequest, requireTemplateResolver, TemplateNotFoundError, type TemplateResolverApi, type TemplateResource, } from "./api.js";
|
|
2
2
|
export { type CompileDocumentBodyOptions, collectDocumentResourceKeys, compileDocumentBodyToHandlebars, toResourceDataUri, } from "./compile-document-body.js";
|
|
3
|
-
export { CONTENT_FORMATS, type ContentFormat, DEFAULT_MAIL_LAYOUT_SLUG, DOCUMENT_HTML_KINDS, type DocumentHtmlKind, FALLBACK_LOCALE, NON_RENDER_KINDS, type NonRenderKind, PDF_DEFAULT_MARGIN_MM, PDF_FOOTER_MARGIN_MM, PDF_HEADER_MARGIN_MM, PDF_ORIENTATIONS, PDF_PAGE_FORMATS, type PdfOrientation, type PdfPageFormat, RENDER_KINDS, type RenderKind, SYSTEM_TENANT_ID, TEMPLATE_KINDS, TEMPLATE_SCOPES, TEMPLATE_STATUSES, TEXT_BLOCK_KIND, type TemplateKind, type TemplateScope, type TemplateStatus, UPSERT_KINDS, type UpsertKind, } from "./constants.js";
|
|
3
|
+
export { CONTENT_FORMATS, type ContentFormat, DEFAULT_MAIL_LAYOUT_SLUG, DEFAULT_PDF_LAYOUT_SLUG, DOCUMENT_HTML_KINDS, type DocumentHtmlKind, FALLBACK_LOCALE, NON_RENDER_KINDS, type NonRenderKind, PDF_DEFAULT_MARGIN_MM, PDF_FOOTER_MARGIN_MM, PDF_HEADER_MARGIN_MM, PDF_ORIENTATIONS, PDF_PAGE_FORMATS, type PdfOrientation, type PdfPageFormat, RENDER_KINDS, type RenderKind, SYSTEM_TENANT_ID, TEMPLATE_KINDS, TEMPLATE_SCOPES, TEMPLATE_STATUSES, TEXT_BLOCK_KIND, type TemplateKind, type TemplateScope, type TemplateStatus, UPSERT_KINDS, type UpsertKind, } from "./constants.js";
|
|
4
4
|
export { type DocumentViolation, type DocumentViolationCode, documentViolationsToFieldIssues, type SanitizeBodyResult, type SanitizeHtmlResult, sanitizeDocumentHtml, sanitizeTemplateBody, } from "./document-sanitizer.js";
|
|
5
5
|
export { DOCUMENT_NODE_NAMES, DOCUMENT_TEXT_ALIGNMENTS, DocumentButton, DocumentDivider, DocumentLink, DocumentPageBreak, DocumentResource, DocumentTable, DocumentTableCell, DocumentTableHeader, DocumentTableRow, DocumentTextAlign, type DocumentTextAlignment, DocumentVariable, documentExtensions, isAllowedDocumentLinkHref, isDocumentPlaceholderHref, isDocumentReferenceName, isDocumentResourceKey, } from "./document-schema.js";
|
|
6
6
|
export { createTemplateResolverFeature } from "./feature.js";
|
|
@@ -13,10 +13,10 @@ export { fileAttachmentsOf, imageLinksOf, type LinkedResourceEntry, parseLinkedR
|
|
|
13
13
|
export { prepareDocumentForPrint } from "./print-document.js";
|
|
14
14
|
export { TEMPLATE_RESOLVER_FEATURE, TemplateResolverErrors, TemplateResolverHandlers, TemplateResolverQueries, } from "./qualified-names.js";
|
|
15
15
|
export { mapRecordToVariables } from "./record-mapping.js";
|
|
16
|
-
export { readTemplateResourceMaxBytes, type StoredTemplateResource,
|
|
16
|
+
export { type PreparedTemplateImage, prepareTemplateResourceBeforeTransaction, readTemplateResourceMaxBytes, type StoredTemplateResource, storePreparedTemplateResource, } from "./store-template-resource.js";
|
|
17
17
|
export { type TemplateResourceRow, templateResourceEntity, templateResourcesTable, } from "./table.js";
|
|
18
18
|
export { type TemplateBundle, templateBundleSchema } from "./template-bundle.js";
|
|
19
|
-
export { isMailTemplateMeta, type LayoutTemplateMeta, MAIL_CATEGORIES, type MailCategory, type MailLayoutMeta, type MailTemplateMeta, type PdfTemplateMeta, parseTemplateMeta, pdfAttachmentFileNamePatternSchema, readMailLayoutMeta, readPdfTemplateMeta, type TemplateMeta, type TemplateMetaIssue, type TemplateMetaResult, templateSlugSchema, } from "./template-meta.js";
|
|
19
|
+
export { isMailTemplateMeta, type LayoutTemplateMeta, MAIL_CATEGORIES, MAIL_TONES, type MailCategory, type MailLayoutMeta, type MailTemplateMeta, type MailTone, type PdfTemplateMeta, parseTemplateMeta, pdfAttachmentFileNamePatternSchema, readMailLayoutMeta, readPdfTemplateMeta, type TemplateMeta, type TemplateMetaIssue, type TemplateMetaResult, templateSlugSchema, } from "./template-meta.js";
|
|
20
20
|
export { type DocumentDraftOutcome, evaluateDocumentDraft } from "./template-preview.js";
|
|
21
21
|
export { readTemplateResource, type TemplateResourceFile } from "./template-resources.js";
|
|
22
22
|
export { collectTemplateUsage, type TemplateUsageEntry } from "./template-usage.js";
|