@momentumcms/server-express 0.4.1 → 0.5.1
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/CHANGELOG.md +8 -0
- package/index.cjs +1212 -62
- package/index.js +1209 -60
- package/package.json +6 -1
package/index.cjs
CHANGED
|
@@ -29,6 +29,15 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
29
29
|
mod
|
|
30
30
|
));
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
33
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
34
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
35
|
+
if (decorator = decorators[i])
|
|
36
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
37
|
+
if (kind && result)
|
|
38
|
+
__defProp(target, key, result);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
32
41
|
|
|
33
42
|
// libs/storage/src/lib/storage.types.ts
|
|
34
43
|
var init_storage_types = __esm({
|
|
@@ -37,6 +46,35 @@ var init_storage_types = __esm({
|
|
|
37
46
|
}
|
|
38
47
|
});
|
|
39
48
|
|
|
49
|
+
// libs/storage/src/lib/storage-utils.ts
|
|
50
|
+
function getExtensionFromMimeType(mimeType) {
|
|
51
|
+
return MIME_TO_EXT[mimeType] ?? "";
|
|
52
|
+
}
|
|
53
|
+
var MIME_TO_EXT;
|
|
54
|
+
var init_storage_utils = __esm({
|
|
55
|
+
"libs/storage/src/lib/storage-utils.ts"() {
|
|
56
|
+
"use strict";
|
|
57
|
+
MIME_TO_EXT = {
|
|
58
|
+
"image/jpeg": ".jpg",
|
|
59
|
+
"image/png": ".png",
|
|
60
|
+
"image/gif": ".gif",
|
|
61
|
+
"image/webp": ".webp",
|
|
62
|
+
"image/svg+xml": ".svg",
|
|
63
|
+
"application/pdf": ".pdf",
|
|
64
|
+
"application/json": ".json",
|
|
65
|
+
"text/plain": ".txt",
|
|
66
|
+
"text/html": ".html",
|
|
67
|
+
"text/css": ".css",
|
|
68
|
+
"application/javascript": ".js",
|
|
69
|
+
"video/mp4": ".mp4",
|
|
70
|
+
"video/webm": ".webm",
|
|
71
|
+
"audio/mpeg": ".mp3",
|
|
72
|
+
"audio/wav": ".wav",
|
|
73
|
+
"application/zip": ".zip"
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
40
78
|
// libs/storage/src/lib/storage-local.ts
|
|
41
79
|
function localStorageAdapter(options) {
|
|
42
80
|
const { directory, baseUrl } = options;
|
|
@@ -103,27 +141,6 @@ function localStorageAdapter(options) {
|
|
|
103
141
|
}
|
|
104
142
|
};
|
|
105
143
|
}
|
|
106
|
-
function getExtensionFromMimeType(mimeType) {
|
|
107
|
-
const mimeToExt = {
|
|
108
|
-
"image/jpeg": ".jpg",
|
|
109
|
-
"image/png": ".png",
|
|
110
|
-
"image/gif": ".gif",
|
|
111
|
-
"image/webp": ".webp",
|
|
112
|
-
"image/svg+xml": ".svg",
|
|
113
|
-
"application/pdf": ".pdf",
|
|
114
|
-
"application/json": ".json",
|
|
115
|
-
"text/plain": ".txt",
|
|
116
|
-
"text/html": ".html",
|
|
117
|
-
"text/css": ".css",
|
|
118
|
-
"application/javascript": ".js",
|
|
119
|
-
"video/mp4": ".mp4",
|
|
120
|
-
"video/webm": ".webm",
|
|
121
|
-
"audio/mpeg": ".mp3",
|
|
122
|
-
"audio/wav": ".wav",
|
|
123
|
-
"application/zip": ".zip"
|
|
124
|
-
};
|
|
125
|
-
return mimeToExt[mimeType] ?? "";
|
|
126
|
-
}
|
|
127
144
|
var import_node_fs, import_node_path, import_node_crypto5;
|
|
128
145
|
var init_storage_local = __esm({
|
|
129
146
|
"libs/storage/src/lib/storage-local.ts"() {
|
|
@@ -131,6 +148,7 @@ var init_storage_local = __esm({
|
|
|
131
148
|
import_node_fs = require("node:fs");
|
|
132
149
|
import_node_path = require("node:path");
|
|
133
150
|
import_node_crypto5 = require("node:crypto");
|
|
151
|
+
init_storage_utils();
|
|
134
152
|
}
|
|
135
153
|
});
|
|
136
154
|
|
|
@@ -192,7 +210,7 @@ function s3StorageAdapter(options) {
|
|
|
192
210
|
return {
|
|
193
211
|
async upload(file, uploadOptions) {
|
|
194
212
|
const s3 = await getClient();
|
|
195
|
-
const ext = (0, import_node_path2.extname)(file.originalName) ||
|
|
213
|
+
const ext = (0, import_node_path2.extname)(file.originalName) || getExtensionFromMimeType(file.mimeType);
|
|
196
214
|
const filename = uploadOptions?.filename ? `${uploadOptions.filename}${ext}` : `${(0, import_node_crypto6.randomUUID)()}${ext}`;
|
|
197
215
|
const key = uploadOptions?.directory ? `${uploadOptions.directory}/${filename}` : filename;
|
|
198
216
|
await s3.send(
|
|
@@ -281,33 +299,13 @@ function s3StorageAdapter(options) {
|
|
|
281
299
|
}
|
|
282
300
|
};
|
|
283
301
|
}
|
|
284
|
-
function getExtensionFromMimeType2(mimeType) {
|
|
285
|
-
const mimeToExt = {
|
|
286
|
-
"image/jpeg": ".jpg",
|
|
287
|
-
"image/png": ".png",
|
|
288
|
-
"image/gif": ".gif",
|
|
289
|
-
"image/webp": ".webp",
|
|
290
|
-
"image/svg+xml": ".svg",
|
|
291
|
-
"application/pdf": ".pdf",
|
|
292
|
-
"application/json": ".json",
|
|
293
|
-
"text/plain": ".txt",
|
|
294
|
-
"text/html": ".html",
|
|
295
|
-
"text/css": ".css",
|
|
296
|
-
"application/javascript": ".js",
|
|
297
|
-
"video/mp4": ".mp4",
|
|
298
|
-
"video/webm": ".webm",
|
|
299
|
-
"audio/mpeg": ".mp3",
|
|
300
|
-
"audio/wav": ".wav",
|
|
301
|
-
"application/zip": ".zip"
|
|
302
|
-
};
|
|
303
|
-
return mimeToExt[mimeType] ?? "";
|
|
304
|
-
}
|
|
305
302
|
var import_node_crypto6, import_node_path2, S3Client, PutObjectCommand, DeleteObjectCommand, HeadObjectCommand, GetObjectCommand, getSignedUrl;
|
|
306
303
|
var init_storage_s3 = __esm({
|
|
307
304
|
"libs/storage/src/lib/storage-s3.ts"() {
|
|
308
305
|
"use strict";
|
|
309
306
|
import_node_crypto6 = require("node:crypto");
|
|
310
307
|
import_node_path2 = require("node:path");
|
|
308
|
+
init_storage_utils();
|
|
311
309
|
}
|
|
312
310
|
});
|
|
313
311
|
|
|
@@ -328,7 +326,7 @@ function detectMimeType(buffer) {
|
|
|
328
326
|
if (match) {
|
|
329
327
|
if (sig.bytes[0] === 82 && sig.bytes[1] === 73) {
|
|
330
328
|
if (buffer.length >= 12) {
|
|
331
|
-
const formatId = buffer.
|
|
329
|
+
const formatId = String.fromCharCode(...buffer.subarray(8, 12));
|
|
332
330
|
if (formatId === "WEBP") {
|
|
333
331
|
return "image/webp";
|
|
334
332
|
}
|
|
@@ -341,7 +339,7 @@ function detectMimeType(buffer) {
|
|
|
341
339
|
}
|
|
342
340
|
}
|
|
343
341
|
if (sig.mimeType === "video/mp4" && buffer.length >= 8) {
|
|
344
|
-
const boxType = buffer.
|
|
342
|
+
const boxType = String.fromCharCode(...buffer.subarray(4, 8));
|
|
345
343
|
if (boxType === "ftyp") {
|
|
346
344
|
return "video/mp4";
|
|
347
345
|
}
|
|
@@ -350,7 +348,7 @@ function detectMimeType(buffer) {
|
|
|
350
348
|
}
|
|
351
349
|
}
|
|
352
350
|
if (isTextContent(buffer)) {
|
|
353
|
-
const text2 = buffer.
|
|
351
|
+
const text2 = new TextDecoder().decode(buffer.subarray(0, Math.min(buffer.length, 1e3)));
|
|
354
352
|
if (text2.trim().startsWith("{") || text2.trim().startsWith("[")) {
|
|
355
353
|
return "application/json";
|
|
356
354
|
}
|
|
@@ -524,6 +522,7 @@ var init_mime_validator = __esm({
|
|
|
524
522
|
var src_exports = {};
|
|
525
523
|
__export(src_exports, {
|
|
526
524
|
detectMimeType: () => detectMimeType,
|
|
525
|
+
getExtensionFromMimeType: () => getExtensionFromMimeType,
|
|
527
526
|
isMimeTypeAllowed: () => isMimeTypeAllowed,
|
|
528
527
|
localStorageAdapter: () => localStorageAdapter,
|
|
529
528
|
mimeTypeMatches: () => mimeTypeMatches,
|
|
@@ -536,13 +535,1121 @@ var init_src = __esm({
|
|
|
536
535
|
init_storage_types();
|
|
537
536
|
init_storage_local();
|
|
538
537
|
init_storage_s3();
|
|
538
|
+
init_storage_utils();
|
|
539
539
|
init_mime_validator();
|
|
540
540
|
}
|
|
541
541
|
});
|
|
542
542
|
|
|
543
|
-
// libs/
|
|
543
|
+
// libs/email/src/lib/utils/css-inliner.ts
|
|
544
|
+
function inlineCss(html) {
|
|
545
|
+
return (0, import_juice.default)(html, {
|
|
546
|
+
removeStyleTags: true,
|
|
547
|
+
preserveMediaQueries: true,
|
|
548
|
+
preserveFontFaces: true,
|
|
549
|
+
insertPreservedExtraCss: true
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
var import_juice;
|
|
553
|
+
var init_css_inliner = __esm({
|
|
554
|
+
"libs/email/src/lib/utils/css-inliner.ts"() {
|
|
555
|
+
"use strict";
|
|
556
|
+
import_juice = __toESM(require("juice"));
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
// libs/email/src/lib/utils/strip-artifacts.ts
|
|
561
|
+
function stripAngularArtifacts(html) {
|
|
562
|
+
return html.replace(/<!--[\s\S]*?-->/g, "").replace(/\s*ng-reflect-[\w-]+="[^"]*"/g, "").replace(/\s*_ng(?:host|content)-[\w-]+(?:="")?/g, "").replace(/\s*ng-version="[^"]*"/g, "").replace(/\s*ng-server-context="[^"]*"/g, "").replace(/\s*ngh="[^"]*"/g, "").replace(/\n\s*\n/g, "\n");
|
|
563
|
+
}
|
|
564
|
+
var init_strip_artifacts = __esm({
|
|
565
|
+
"libs/email/src/lib/utils/strip-artifacts.ts"() {
|
|
566
|
+
"use strict";
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
// libs/email/src/lib/render/render-types.ts
|
|
571
|
+
function injectEmailData() {
|
|
572
|
+
return (0, import_core11.inject)(EMAIL_DATA);
|
|
573
|
+
}
|
|
574
|
+
var import_core11, EMAIL_DATA;
|
|
575
|
+
var init_render_types = __esm({
|
|
576
|
+
"libs/email/src/lib/render/render-types.ts"() {
|
|
577
|
+
"use strict";
|
|
578
|
+
import_core11 = require("@angular/core");
|
|
579
|
+
EMAIL_DATA = new import_core11.InjectionToken("EMAIL_DATA");
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
// libs/email/src/lib/render/render-email.ts
|
|
584
|
+
function buildDocument(selector) {
|
|
585
|
+
return `<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"></head><body><${selector}></${selector}></body></html>`;
|
|
586
|
+
}
|
|
587
|
+
async function renderEmail(component, data, options) {
|
|
588
|
+
const shouldInlineCss = options?.inlineCss ?? true;
|
|
589
|
+
const shouldStripArtifacts = options?.stripArtifacts ?? true;
|
|
590
|
+
const extraProviders = options?.providers ?? [];
|
|
591
|
+
const mirror = (0, import_core12.reflectComponentType)(component);
|
|
592
|
+
if (!mirror) {
|
|
593
|
+
throw new Error(
|
|
594
|
+
`Cannot reflect component type: ${component.name}. Ensure it has a @Component decorator.`
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
const providers = [...extraProviders, { provide: EMAIL_DATA, useValue: data ?? {} }];
|
|
598
|
+
let html = await (0, import_platform_server.renderApplication)(
|
|
599
|
+
(context) => (0, import_platform_browser.bootstrapApplication)(component, { providers }, context),
|
|
600
|
+
{
|
|
601
|
+
document: buildDocument(mirror.selector),
|
|
602
|
+
url: "/"
|
|
603
|
+
}
|
|
604
|
+
);
|
|
605
|
+
if (shouldStripArtifacts) {
|
|
606
|
+
html = stripAngularArtifacts(html);
|
|
607
|
+
}
|
|
608
|
+
if (shouldInlineCss) {
|
|
609
|
+
html = inlineCss(html);
|
|
610
|
+
}
|
|
611
|
+
return html;
|
|
612
|
+
}
|
|
613
|
+
var import_compiler, import_core12, import_platform_server, import_platform_browser;
|
|
614
|
+
var init_render_email = __esm({
|
|
615
|
+
"libs/email/src/lib/render/render-email.ts"() {
|
|
616
|
+
"use strict";
|
|
617
|
+
import_compiler = require("@angular/compiler");
|
|
618
|
+
import_core12 = require("@angular/core");
|
|
619
|
+
import_platform_server = require("@angular/platform-server");
|
|
620
|
+
import_platform_browser = require("@angular/platform-browser");
|
|
621
|
+
init_css_inliner();
|
|
622
|
+
init_strip_artifacts();
|
|
623
|
+
init_render_types();
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// libs/email/src/lib/utils/escape-html.ts
|
|
628
|
+
function escapeHtml2(unsafe) {
|
|
629
|
+
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
630
|
+
}
|
|
631
|
+
var init_escape_html = __esm({
|
|
632
|
+
"libs/email/src/lib/utils/escape-html.ts"() {
|
|
633
|
+
"use strict";
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
// libs/email/src/lib/utils/replace-variables.ts
|
|
638
|
+
function replaceVariables(text2, variables) {
|
|
639
|
+
return text2.replace(/\{\{(\w+)\}\}/g, (_, key) => variables[key] ?? "");
|
|
640
|
+
}
|
|
641
|
+
function replaceBlockVariables(blocks2, variables) {
|
|
642
|
+
return blocks2.map((block) => ({
|
|
643
|
+
...block,
|
|
644
|
+
data: replaceDataVariables(block.data, variables)
|
|
645
|
+
}));
|
|
646
|
+
}
|
|
647
|
+
function replaceDataVariables(data, variables) {
|
|
648
|
+
const result = {};
|
|
649
|
+
for (const [key, value] of Object.entries(data)) {
|
|
650
|
+
if (typeof value === "string") {
|
|
651
|
+
result[key] = replaceVariables(value, variables);
|
|
652
|
+
} else if (Array.isArray(value)) {
|
|
653
|
+
result[key] = value.map((item) => {
|
|
654
|
+
if (typeof item === "object" && item !== null && "blocks" in item) {
|
|
655
|
+
const col = item;
|
|
656
|
+
const nestedBlocks = Array.isArray(col["blocks"]) ? col["blocks"] : [];
|
|
657
|
+
return { ...col, blocks: replaceBlockVariables(nestedBlocks, variables) };
|
|
658
|
+
}
|
|
659
|
+
return item;
|
|
660
|
+
});
|
|
661
|
+
} else {
|
|
662
|
+
result[key] = value;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return result;
|
|
666
|
+
}
|
|
667
|
+
var init_replace_variables = __esm({
|
|
668
|
+
"libs/email/src/lib/utils/replace-variables.ts"() {
|
|
669
|
+
"use strict";
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
// libs/email/src/lib/utils/sanitize.ts
|
|
674
|
+
function sanitizeAlignment(value) {
|
|
675
|
+
return VALID_ALIGNMENTS.has(value) ? value : "left";
|
|
676
|
+
}
|
|
677
|
+
function sanitizeCssValue(value) {
|
|
678
|
+
return value.replace(/[;{}()"'<>\\]/g, "");
|
|
679
|
+
}
|
|
680
|
+
function sanitizeFontFamily(value) {
|
|
681
|
+
return value.replace(/[;{}()"<>\\]/g, "");
|
|
682
|
+
}
|
|
683
|
+
function sanitizeCssNumber(value, fallback) {
|
|
684
|
+
if (value === null || value === void 0)
|
|
685
|
+
return String(fallback);
|
|
686
|
+
const num = Number(value);
|
|
687
|
+
return Number.isFinite(num) && num >= 0 ? String(num) : String(fallback);
|
|
688
|
+
}
|
|
689
|
+
function sanitizeUrl(url) {
|
|
690
|
+
const trimmed = url.trim();
|
|
691
|
+
if (!trimmed || trimmed === "#")
|
|
692
|
+
return trimmed || "#";
|
|
693
|
+
try {
|
|
694
|
+
const parsed = new URL(trimmed);
|
|
695
|
+
return SAFE_URL_PROTOCOLS.has(parsed.protocol) ? trimmed : "#";
|
|
696
|
+
} catch {
|
|
697
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("#"))
|
|
698
|
+
return trimmed;
|
|
699
|
+
return "#";
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
var VALID_ALIGNMENTS, SAFE_URL_PROTOCOLS;
|
|
703
|
+
var init_sanitize = __esm({
|
|
704
|
+
"libs/email/src/lib/utils/sanitize.ts"() {
|
|
705
|
+
"use strict";
|
|
706
|
+
VALID_ALIGNMENTS = /* @__PURE__ */ new Set(["left", "center", "right"]);
|
|
707
|
+
SAFE_URL_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:"]);
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// libs/email/src/types.ts
|
|
712
|
+
var DEFAULT_EMAIL_THEME;
|
|
713
|
+
var init_types = __esm({
|
|
714
|
+
"libs/email/src/types.ts"() {
|
|
715
|
+
"use strict";
|
|
716
|
+
DEFAULT_EMAIL_THEME = {
|
|
717
|
+
primaryColor: "#18181b",
|
|
718
|
+
backgroundColor: "#f4f4f5",
|
|
719
|
+
textColor: "#3f3f46",
|
|
720
|
+
mutedColor: "#71717a",
|
|
721
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
|
|
722
|
+
borderRadius: "8px"
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
// libs/email/src/lib/render/render-blocks.ts
|
|
728
|
+
function renderEmailFromBlocks(template, options) {
|
|
729
|
+
const theme = { ...DEFAULT_EMAIL_THEME, ...template.theme };
|
|
730
|
+
const shouldInline = options?.inlineCss ?? true;
|
|
731
|
+
const blocks2 = options?.variables ? replaceBlockVariables(template.blocks, options.variables) : template.blocks;
|
|
732
|
+
const validBlocks = blocks2.filter((block) => {
|
|
733
|
+
if (!isValidBlock(block)) {
|
|
734
|
+
console.warn("[momentum:email] Skipping invalid email block:", block);
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
return true;
|
|
738
|
+
});
|
|
739
|
+
const blocksHtml = validBlocks.map((block) => renderBlock(block, theme, 0)).join("\n");
|
|
740
|
+
let html = wrapEmailDocument(blocksHtml, theme);
|
|
741
|
+
if (shouldInline) {
|
|
742
|
+
html = inlineCss(html);
|
|
743
|
+
}
|
|
744
|
+
return html;
|
|
745
|
+
}
|
|
746
|
+
function wrapEmailDocument(content, theme) {
|
|
747
|
+
const fontFamily = sanitizeFontFamily(theme.fontFamily);
|
|
748
|
+
const bgColor = sanitizeCssValue(theme.backgroundColor);
|
|
749
|
+
const borderRadius = sanitizeCssValue(theme.borderRadius);
|
|
750
|
+
return `<!DOCTYPE html>
|
|
751
|
+
<html lang="en">
|
|
752
|
+
<head>
|
|
753
|
+
<meta charset="UTF-8">
|
|
754
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
755
|
+
</head>
|
|
756
|
+
<body style="margin: 0; padding: 0; font-family: ${fontFamily}; background-color: ${bgColor}; line-height: 1.6;">
|
|
757
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color: ${bgColor};">
|
|
758
|
+
<tr>
|
|
759
|
+
<td style="padding: 40px 20px;">
|
|
760
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width: 480px; margin: 0 auto; background-color: #ffffff; border-radius: ${borderRadius}; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
|
|
761
|
+
<tr>
|
|
762
|
+
<td style="padding: 40px;">
|
|
763
|
+
${content}
|
|
764
|
+
</td>
|
|
765
|
+
</tr>
|
|
766
|
+
</table>
|
|
767
|
+
</td>
|
|
768
|
+
</tr>
|
|
769
|
+
</table>
|
|
770
|
+
</body>
|
|
771
|
+
</html>`;
|
|
772
|
+
}
|
|
773
|
+
function isValidBlock(block) {
|
|
774
|
+
const rec = block;
|
|
775
|
+
return typeof block === "object" && block !== null && typeof rec["id"] === "string" && rec["id"].length > 0 && typeof rec["type"] === "string" && typeof rec["data"] === "object" && rec["data"] !== null;
|
|
776
|
+
}
|
|
777
|
+
function renderBlock(block, theme, depth) {
|
|
778
|
+
switch (block.type) {
|
|
779
|
+
case "header":
|
|
780
|
+
return renderHeaderBlock(block.data, theme);
|
|
781
|
+
case "text":
|
|
782
|
+
return renderTextBlock(block.data, theme);
|
|
783
|
+
case "button":
|
|
784
|
+
return renderButtonBlock(block.data, theme);
|
|
785
|
+
case "image":
|
|
786
|
+
return renderImageBlock(block.data);
|
|
787
|
+
case "divider":
|
|
788
|
+
return renderDividerBlock(block.data);
|
|
789
|
+
case "spacer":
|
|
790
|
+
return renderSpacerBlock(block.data);
|
|
791
|
+
case "columns":
|
|
792
|
+
if (depth >= MAX_BLOCK_DEPTH) {
|
|
793
|
+
console.warn("[momentum:email] Max nesting depth reached, skipping columns block");
|
|
794
|
+
return "";
|
|
795
|
+
}
|
|
796
|
+
return renderColumnsBlock(block.data, theme, depth);
|
|
797
|
+
case "footer":
|
|
798
|
+
return renderFooterBlock(block.data, theme);
|
|
799
|
+
default:
|
|
800
|
+
return `<!-- unknown block type: ${escapeHtml2(block.type)} -->`;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
function renderHeaderBlock(data, theme) {
|
|
804
|
+
const title = escapeHtml2(String(data["title"] ?? ""));
|
|
805
|
+
const subtitle = data["subtitle"] ? escapeHtml2(String(data["subtitle"])) : "";
|
|
806
|
+
const alignment = sanitizeAlignment(String(data["alignment"] ?? "left"));
|
|
807
|
+
return `<h1 style="margin: 0 0 8px; font-size: 24px; font-weight: 600; color: ${sanitizeCssValue(theme.textColor)}; text-align: ${alignment};">${title}</h1>${subtitle ? `<p style="margin: 0 0 16px; font-size: 16px; color: ${sanitizeCssValue(theme.mutedColor)}; text-align: ${alignment};">${subtitle}</p>` : ""}`;
|
|
808
|
+
}
|
|
809
|
+
function renderTextBlock(data, theme) {
|
|
810
|
+
const content = escapeHtml2(String(data["content"] ?? ""));
|
|
811
|
+
const fontSize = sanitizeCssNumber(data["fontSize"], 16);
|
|
812
|
+
const color = sanitizeCssValue(String(data["color"] ?? theme.textColor));
|
|
813
|
+
const alignment = sanitizeAlignment(String(data["alignment"] ?? "left"));
|
|
814
|
+
return `<p style="margin: 0 0 16px; font-size: ${fontSize}px; color: ${color}; text-align: ${alignment}; line-height: 1.6;">${content}</p>`;
|
|
815
|
+
}
|
|
816
|
+
function renderButtonBlock(data, theme) {
|
|
817
|
+
const label = escapeHtml2(String(data["label"] ?? "Click here"));
|
|
818
|
+
const href = escapeHtml2(sanitizeUrl(String(data["href"] ?? "#")));
|
|
819
|
+
const bgColor = sanitizeCssValue(String(data["backgroundColor"] ?? theme.primaryColor));
|
|
820
|
+
const color = sanitizeCssValue(String(data["color"] ?? "#ffffff"));
|
|
821
|
+
const alignment = sanitizeAlignment(String(data["alignment"] ?? "left"));
|
|
822
|
+
return `<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
823
|
+
<tr>
|
|
824
|
+
<td style="padding: 0 0 16px;" align="${alignment}">
|
|
825
|
+
<a href="${href}" style="display: inline-block; padding: 12px 24px; background-color: ${bgColor}; color: ${color}; text-decoration: none; border-radius: 6px; font-weight: 500; font-size: 16px;">${label}</a>
|
|
826
|
+
</td>
|
|
827
|
+
</tr>
|
|
828
|
+
</table>`;
|
|
829
|
+
}
|
|
830
|
+
function renderImageBlock(data) {
|
|
831
|
+
const rawSrc = String(data["src"] ?? "").trim();
|
|
832
|
+
if (!rawSrc)
|
|
833
|
+
return "<!-- image block: no src configured -->";
|
|
834
|
+
const src = escapeHtml2(sanitizeUrl(rawSrc));
|
|
835
|
+
const alt = escapeHtml2(String(data["alt"] ?? ""));
|
|
836
|
+
const width = sanitizeCssValue(String(data["width"] ?? "100%"));
|
|
837
|
+
const img = `<img src="${src}" alt="${alt}" width="${width}" style="display: block; max-width: 100%; height: auto; border: 0;">`;
|
|
838
|
+
if (data["href"]) {
|
|
839
|
+
const href = escapeHtml2(sanitizeUrl(String(data["href"])));
|
|
840
|
+
return `<a href="${href}" style="display: block;">${img}</a>`;
|
|
841
|
+
}
|
|
842
|
+
return img;
|
|
843
|
+
}
|
|
844
|
+
function renderDividerBlock(data) {
|
|
845
|
+
const color = sanitizeCssValue(String(data["color"] ?? "#e4e4e7"));
|
|
846
|
+
const margin = sanitizeCssValue(String(data["margin"] ?? "24px 0"));
|
|
847
|
+
return `<hr style="border: none; border-top: 1px solid ${color}; margin: ${margin};">`;
|
|
848
|
+
}
|
|
849
|
+
function renderSpacerBlock(data) {
|
|
850
|
+
const height = sanitizeCssNumber(data["height"], 24);
|
|
851
|
+
return `<div style="height: ${height}px; line-height: ${height}px; font-size: 1px;"> </div>`;
|
|
852
|
+
}
|
|
853
|
+
function renderColumnsBlock(data, theme, depth) {
|
|
854
|
+
const rawColumns = data["columns"];
|
|
855
|
+
const columns = Array.isArray(rawColumns) ? rawColumns : [];
|
|
856
|
+
const width = Math.floor(100 / (columns.length || 1));
|
|
857
|
+
const tds = columns.map((col) => {
|
|
858
|
+
const colObj = (
|
|
859
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- narrowing unknown column objects
|
|
860
|
+
typeof col === "object" && col !== null ? col : {}
|
|
861
|
+
);
|
|
862
|
+
const rawBlocks = colObj["blocks"];
|
|
863
|
+
const colContent = (Array.isArray(rawBlocks) ? rawBlocks : []).filter(isValidBlock).map((b) => renderBlock(b, theme, depth + 1)).join("\n");
|
|
864
|
+
return `<td style="width: ${width}%; vertical-align: top; padding: 0 8px;">${colContent}</td>`;
|
|
865
|
+
}).join("\n");
|
|
866
|
+
return `<table role="presentation" width="100%" cellspacing="0" cellpadding="0"><tr>${tds}</tr></table>`;
|
|
867
|
+
}
|
|
868
|
+
function renderFooterBlock(data, theme) {
|
|
869
|
+
const text2 = escapeHtml2(String(data["text"] ?? ""));
|
|
870
|
+
const color = sanitizeCssValue(String(data["color"] ?? theme.mutedColor));
|
|
871
|
+
return `<p style="margin: 16px 0 0; font-size: 12px; color: ${color}; text-align: center;">${text2}</p>`;
|
|
872
|
+
}
|
|
873
|
+
var MAX_BLOCK_DEPTH;
|
|
874
|
+
var init_render_blocks = __esm({
|
|
875
|
+
"libs/email/src/lib/render/render-blocks.ts"() {
|
|
876
|
+
"use strict";
|
|
877
|
+
init_escape_html();
|
|
878
|
+
init_css_inliner();
|
|
879
|
+
init_replace_variables();
|
|
880
|
+
init_sanitize();
|
|
881
|
+
init_types();
|
|
882
|
+
MAX_BLOCK_DEPTH = 5;
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
// libs/email/src/lib/templates/default-templates.ts
|
|
887
|
+
var DEFAULT_PASSWORD_RESET_BLOCKS, DEFAULT_VERIFICATION_BLOCKS;
|
|
888
|
+
var init_default_templates = __esm({
|
|
889
|
+
"libs/email/src/lib/templates/default-templates.ts"() {
|
|
890
|
+
"use strict";
|
|
891
|
+
DEFAULT_PASSWORD_RESET_BLOCKS = [
|
|
892
|
+
{
|
|
893
|
+
id: "pr-header",
|
|
894
|
+
type: "header",
|
|
895
|
+
data: {
|
|
896
|
+
title: "Reset Your Password",
|
|
897
|
+
subtitle: "",
|
|
898
|
+
alignment: "left"
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
id: "pr-greeting",
|
|
903
|
+
type: "text",
|
|
904
|
+
data: {
|
|
905
|
+
content: "{{greeting}}",
|
|
906
|
+
fontSize: 16,
|
|
907
|
+
color: "#3f3f46",
|
|
908
|
+
alignment: "left"
|
|
909
|
+
}
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
id: "pr-body",
|
|
913
|
+
type: "text",
|
|
914
|
+
data: {
|
|
915
|
+
content: "We received a request to reset your password. Click the button below to choose a new password:",
|
|
916
|
+
fontSize: 16,
|
|
917
|
+
color: "#3f3f46",
|
|
918
|
+
alignment: "left"
|
|
919
|
+
}
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
id: "pr-button",
|
|
923
|
+
type: "button",
|
|
924
|
+
data: {
|
|
925
|
+
label: "Reset Password",
|
|
926
|
+
href: "{{url}}",
|
|
927
|
+
backgroundColor: "#18181b",
|
|
928
|
+
color: "#ffffff",
|
|
929
|
+
alignment: "center"
|
|
930
|
+
}
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
id: "pr-expiry",
|
|
934
|
+
type: "text",
|
|
935
|
+
data: {
|
|
936
|
+
content: "This link will expire in {{expiresIn}}.",
|
|
937
|
+
fontSize: 14,
|
|
938
|
+
color: "#71717a",
|
|
939
|
+
alignment: "left"
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
id: "pr-ignore",
|
|
944
|
+
type: "text",
|
|
945
|
+
data: {
|
|
946
|
+
content: "If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.",
|
|
947
|
+
fontSize: 14,
|
|
948
|
+
color: "#71717a",
|
|
949
|
+
alignment: "left"
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
id: "pr-divider",
|
|
954
|
+
type: "divider",
|
|
955
|
+
data: {
|
|
956
|
+
color: "#e4e4e7",
|
|
957
|
+
margin: "24px 0"
|
|
958
|
+
}
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
id: "pr-footer",
|
|
962
|
+
type: "footer",
|
|
963
|
+
data: {
|
|
964
|
+
text: "The {{appName}} Team",
|
|
965
|
+
color: "#71717a"
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
];
|
|
969
|
+
DEFAULT_VERIFICATION_BLOCKS = [
|
|
970
|
+
{
|
|
971
|
+
id: "ev-header",
|
|
972
|
+
type: "header",
|
|
973
|
+
data: {
|
|
974
|
+
title: "Verify Your Email",
|
|
975
|
+
subtitle: "",
|
|
976
|
+
alignment: "left"
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
id: "ev-greeting",
|
|
981
|
+
type: "text",
|
|
982
|
+
data: {
|
|
983
|
+
content: "{{greeting}}",
|
|
984
|
+
fontSize: 16,
|
|
985
|
+
color: "#3f3f46",
|
|
986
|
+
alignment: "left"
|
|
987
|
+
}
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
id: "ev-body",
|
|
991
|
+
type: "text",
|
|
992
|
+
data: {
|
|
993
|
+
content: "Welcome to {{appName}}! Please verify your email address by clicking the button below:",
|
|
994
|
+
fontSize: 16,
|
|
995
|
+
color: "#3f3f46",
|
|
996
|
+
alignment: "left"
|
|
997
|
+
}
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
id: "ev-button",
|
|
1001
|
+
type: "button",
|
|
1002
|
+
data: {
|
|
1003
|
+
label: "Verify Email",
|
|
1004
|
+
href: "{{url}}",
|
|
1005
|
+
backgroundColor: "#18181b",
|
|
1006
|
+
color: "#ffffff",
|
|
1007
|
+
alignment: "center"
|
|
1008
|
+
}
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
id: "ev-expiry",
|
|
1012
|
+
type: "text",
|
|
1013
|
+
data: {
|
|
1014
|
+
content: "This link will expire in {{expiresIn}}.",
|
|
1015
|
+
fontSize: 14,
|
|
1016
|
+
color: "#71717a",
|
|
1017
|
+
alignment: "left"
|
|
1018
|
+
}
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
id: "ev-ignore",
|
|
1022
|
+
type: "text",
|
|
1023
|
+
data: {
|
|
1024
|
+
content: "If you didn't create an account, you can safely ignore this email.",
|
|
1025
|
+
fontSize: 14,
|
|
1026
|
+
color: "#71717a",
|
|
1027
|
+
alignment: "left"
|
|
1028
|
+
}
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
id: "ev-divider",
|
|
1032
|
+
type: "divider",
|
|
1033
|
+
data: {
|
|
1034
|
+
color: "#e4e4e7",
|
|
1035
|
+
margin: "24px 0"
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
id: "ev-footer",
|
|
1040
|
+
type: "footer",
|
|
1041
|
+
data: {
|
|
1042
|
+
text: "The {{appName}} Team",
|
|
1043
|
+
color: "#71717a"
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
];
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
// libs/email/src/lib/components/eml-body.component.ts
|
|
1051
|
+
var import_core13, EmlBody;
|
|
1052
|
+
var init_eml_body_component = __esm({
|
|
1053
|
+
"libs/email/src/lib/components/eml-body.component.ts"() {
|
|
1054
|
+
"use strict";
|
|
1055
|
+
import_core13 = require("@angular/core");
|
|
1056
|
+
EmlBody = class {
|
|
1057
|
+
constructor() {
|
|
1058
|
+
this.backgroundColor = (0, import_core13.input)("#f4f4f5");
|
|
1059
|
+
this.fontFamily = (0, import_core13.input)(
|
|
1060
|
+
"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
|
|
1061
|
+
);
|
|
1062
|
+
this.padding = (0, import_core13.input)("40px 20px");
|
|
1063
|
+
this.tableStyle = (0, import_core13.computed)(
|
|
1064
|
+
() => `background-color: ${this.backgroundColor()}; font-family: ${this.fontFamily()}; line-height: 1.6; margin: 0; padding: 0;`
|
|
1065
|
+
);
|
|
1066
|
+
this.cellStyle = (0, import_core13.computed)(() => `padding: ${this.padding()};`);
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
EmlBody = __decorateClass([
|
|
1070
|
+
(0, import_core13.Component)({
|
|
1071
|
+
selector: "eml-body",
|
|
1072
|
+
template: `
|
|
1073
|
+
<table
|
|
1074
|
+
role="presentation"
|
|
1075
|
+
width="100%"
|
|
1076
|
+
cellspacing="0"
|
|
1077
|
+
cellpadding="0"
|
|
1078
|
+
[attr.style]="tableStyle()"
|
|
1079
|
+
>
|
|
1080
|
+
<tr>
|
|
1081
|
+
<td [attr.style]="cellStyle()">
|
|
1082
|
+
<ng-content />
|
|
1083
|
+
</td>
|
|
1084
|
+
</tr>
|
|
1085
|
+
</table>
|
|
1086
|
+
`,
|
|
1087
|
+
changeDetection: import_core13.ChangeDetectionStrategy.OnPush
|
|
1088
|
+
})
|
|
1089
|
+
], EmlBody);
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
// libs/email/src/lib/components/eml-container.component.ts
|
|
1094
|
+
var import_core14, EmlContainer;
|
|
1095
|
+
var init_eml_container_component = __esm({
|
|
1096
|
+
"libs/email/src/lib/components/eml-container.component.ts"() {
|
|
1097
|
+
"use strict";
|
|
1098
|
+
import_core14 = require("@angular/core");
|
|
1099
|
+
EmlContainer = class {
|
|
1100
|
+
constructor() {
|
|
1101
|
+
this.maxWidth = (0, import_core14.input)("480px");
|
|
1102
|
+
this.backgroundColor = (0, import_core14.input)("#ffffff");
|
|
1103
|
+
this.borderRadius = (0, import_core14.input)("8px");
|
|
1104
|
+
this.padding = (0, import_core14.input)("40px");
|
|
1105
|
+
this.shadow = (0, import_core14.input)("0 1px 3px rgba(0,0,0,0.1)");
|
|
1106
|
+
this.tableStyle = (0, import_core14.computed)(
|
|
1107
|
+
() => `max-width: ${this.maxWidth()}; margin: 0 auto; background-color: ${this.backgroundColor()}; border-radius: ${this.borderRadius()}; box-shadow: ${this.shadow()};`
|
|
1108
|
+
);
|
|
1109
|
+
this.cellStyle = (0, import_core14.computed)(() => `padding: ${this.padding()};`);
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
EmlContainer = __decorateClass([
|
|
1113
|
+
(0, import_core14.Component)({
|
|
1114
|
+
selector: "eml-container",
|
|
1115
|
+
template: `
|
|
1116
|
+
<table
|
|
1117
|
+
role="presentation"
|
|
1118
|
+
width="100%"
|
|
1119
|
+
cellspacing="0"
|
|
1120
|
+
cellpadding="0"
|
|
1121
|
+
[attr.style]="tableStyle()"
|
|
1122
|
+
>
|
|
1123
|
+
<tr>
|
|
1124
|
+
<td [attr.style]="cellStyle()">
|
|
1125
|
+
<ng-content />
|
|
1126
|
+
</td>
|
|
1127
|
+
</tr>
|
|
1128
|
+
</table>
|
|
1129
|
+
`,
|
|
1130
|
+
changeDetection: import_core14.ChangeDetectionStrategy.OnPush
|
|
1131
|
+
})
|
|
1132
|
+
], EmlContainer);
|
|
1133
|
+
}
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
// libs/email/src/lib/components/eml-section.component.ts
|
|
1137
|
+
var import_core15, EmlSection;
|
|
1138
|
+
var init_eml_section_component = __esm({
|
|
1139
|
+
"libs/email/src/lib/components/eml-section.component.ts"() {
|
|
1140
|
+
"use strict";
|
|
1141
|
+
import_core15 = require("@angular/core");
|
|
1142
|
+
EmlSection = class {
|
|
1143
|
+
constructor() {
|
|
1144
|
+
this.padding = (0, import_core15.input)("0");
|
|
1145
|
+
this.cellStyle = (0, import_core15.computed)(() => `padding: ${this.padding()};`);
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
1148
|
+
EmlSection = __decorateClass([
|
|
1149
|
+
(0, import_core15.Component)({
|
|
1150
|
+
selector: "eml-section",
|
|
1151
|
+
template: `
|
|
1152
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
1153
|
+
<tr>
|
|
1154
|
+
<td [attr.style]="cellStyle()">
|
|
1155
|
+
<ng-content />
|
|
1156
|
+
</td>
|
|
1157
|
+
</tr>
|
|
1158
|
+
</table>
|
|
1159
|
+
`,
|
|
1160
|
+
changeDetection: import_core15.ChangeDetectionStrategy.OnPush
|
|
1161
|
+
})
|
|
1162
|
+
], EmlSection);
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
// libs/email/src/lib/components/eml-row.component.ts
|
|
1167
|
+
var import_core16, EmlRow;
|
|
1168
|
+
var init_eml_row_component = __esm({
|
|
1169
|
+
"libs/email/src/lib/components/eml-row.component.ts"() {
|
|
1170
|
+
"use strict";
|
|
1171
|
+
import_core16 = require("@angular/core");
|
|
1172
|
+
EmlRow = class {
|
|
1173
|
+
};
|
|
1174
|
+
EmlRow = __decorateClass([
|
|
1175
|
+
(0, import_core16.Component)({
|
|
1176
|
+
selector: "eml-row",
|
|
1177
|
+
template: `
|
|
1178
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
1179
|
+
<tr>
|
|
1180
|
+
<ng-content />
|
|
1181
|
+
</tr>
|
|
1182
|
+
</table>
|
|
1183
|
+
`,
|
|
1184
|
+
changeDetection: import_core16.ChangeDetectionStrategy.OnPush
|
|
1185
|
+
})
|
|
1186
|
+
], EmlRow);
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
// libs/email/src/lib/components/eml-column.component.ts
|
|
1191
|
+
var import_core17, EmlColumn;
|
|
1192
|
+
var init_eml_column_component = __esm({
|
|
1193
|
+
"libs/email/src/lib/components/eml-column.component.ts"() {
|
|
1194
|
+
"use strict";
|
|
1195
|
+
import_core17 = require("@angular/core");
|
|
1196
|
+
EmlColumn = class {
|
|
1197
|
+
constructor() {
|
|
1198
|
+
this.width = (0, import_core17.input)(void 0);
|
|
1199
|
+
this.padding = (0, import_core17.input)("0");
|
|
1200
|
+
this.verticalAlign = (0, import_core17.input)("top");
|
|
1201
|
+
this.cellStyle = (0, import_core17.computed)(
|
|
1202
|
+
() => `padding: ${this.padding()}; vertical-align: ${this.verticalAlign()};`
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
EmlColumn = __decorateClass([
|
|
1207
|
+
(0, import_core17.Component)({
|
|
1208
|
+
selector: "eml-column",
|
|
1209
|
+
template: `
|
|
1210
|
+
<td [attr.style]="cellStyle()" [attr.width]="width()" valign="top">
|
|
1211
|
+
<ng-content />
|
|
1212
|
+
</td>
|
|
1213
|
+
`,
|
|
1214
|
+
changeDetection: import_core17.ChangeDetectionStrategy.OnPush
|
|
1215
|
+
})
|
|
1216
|
+
], EmlColumn);
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
// libs/email/src/lib/components/eml-text.component.ts
|
|
1221
|
+
var import_core18, EmlText;
|
|
1222
|
+
var init_eml_text_component = __esm({
|
|
1223
|
+
"libs/email/src/lib/components/eml-text.component.ts"() {
|
|
1224
|
+
"use strict";
|
|
1225
|
+
import_core18 = require("@angular/core");
|
|
1226
|
+
EmlText = class {
|
|
1227
|
+
constructor() {
|
|
1228
|
+
this.color = (0, import_core18.input)("#3f3f46");
|
|
1229
|
+
this.fontSize = (0, import_core18.input)("16px");
|
|
1230
|
+
this.lineHeight = (0, import_core18.input)("1.6");
|
|
1231
|
+
this.margin = (0, import_core18.input)("0 0 16px");
|
|
1232
|
+
this.textAlign = (0, import_core18.input)("left");
|
|
1233
|
+
this.pStyle = (0, import_core18.computed)(
|
|
1234
|
+
() => `margin: ${this.margin()}; color: ${this.color()}; font-size: ${this.fontSize()}; line-height: ${this.lineHeight()}; text-align: ${this.textAlign()};`
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
EmlText = __decorateClass([
|
|
1239
|
+
(0, import_core18.Component)({
|
|
1240
|
+
selector: "eml-text",
|
|
1241
|
+
template: `
|
|
1242
|
+
<p [attr.style]="pStyle()">
|
|
1243
|
+
<ng-content />
|
|
1244
|
+
</p>
|
|
1245
|
+
`,
|
|
1246
|
+
changeDetection: import_core18.ChangeDetectionStrategy.OnPush
|
|
1247
|
+
})
|
|
1248
|
+
], EmlText);
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
|
|
1252
|
+
// libs/email/src/lib/components/eml-heading.component.ts
|
|
1253
|
+
var import_core19, EmlHeading;
|
|
1254
|
+
var init_eml_heading_component = __esm({
|
|
1255
|
+
"libs/email/src/lib/components/eml-heading.component.ts"() {
|
|
1256
|
+
"use strict";
|
|
1257
|
+
import_core19 = require("@angular/core");
|
|
1258
|
+
EmlHeading = class {
|
|
1259
|
+
constructor() {
|
|
1260
|
+
this.level = (0, import_core19.input)(1);
|
|
1261
|
+
this.color = (0, import_core19.input)("#18181b");
|
|
1262
|
+
this.margin = (0, import_core19.input)("0 0 24px");
|
|
1263
|
+
this.textAlign = (0, import_core19.input)("left");
|
|
1264
|
+
this.fontSizeMap = {
|
|
1265
|
+
1: "24px",
|
|
1266
|
+
2: "20px",
|
|
1267
|
+
3: "16px"
|
|
1268
|
+
};
|
|
1269
|
+
this.headingStyle = (0, import_core19.computed)(
|
|
1270
|
+
() => `margin: ${this.margin()}; font-size: ${this.fontSizeMap[this.level()] ?? "24px"}; font-weight: 600; color: ${this.color()}; text-align: ${this.textAlign()};`
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
EmlHeading = __decorateClass([
|
|
1275
|
+
(0, import_core19.Component)({
|
|
1276
|
+
selector: "eml-heading",
|
|
1277
|
+
template: `<div [attr.style]="headingStyle()" [attr.role]="'heading'" [attr.aria-level]="level()">
|
|
1278
|
+
<ng-content />
|
|
1279
|
+
</div>`,
|
|
1280
|
+
changeDetection: import_core19.ChangeDetectionStrategy.OnPush
|
|
1281
|
+
})
|
|
1282
|
+
], EmlHeading);
|
|
1283
|
+
}
|
|
1284
|
+
});
|
|
1285
|
+
|
|
1286
|
+
// libs/email/src/lib/components/eml-button.component.ts
|
|
1287
|
+
var import_core20, EmlButton;
|
|
1288
|
+
var init_eml_button_component = __esm({
|
|
1289
|
+
"libs/email/src/lib/components/eml-button.component.ts"() {
|
|
1290
|
+
"use strict";
|
|
1291
|
+
import_core20 = require("@angular/core");
|
|
1292
|
+
EmlButton = class {
|
|
1293
|
+
constructor() {
|
|
1294
|
+
this.href = (0, import_core20.input)("");
|
|
1295
|
+
this.backgroundColor = (0, import_core20.input)("#18181b");
|
|
1296
|
+
this.color = (0, import_core20.input)("#ffffff");
|
|
1297
|
+
this.borderRadius = (0, import_core20.input)("6px");
|
|
1298
|
+
this.padding = (0, import_core20.input)("12px 24px");
|
|
1299
|
+
this.textAlign = (0, import_core20.input)("left");
|
|
1300
|
+
this.alignStyle = (0, import_core20.computed)(() => `padding: 0; text-align: ${this.textAlign()};`);
|
|
1301
|
+
this.linkStyle = (0, import_core20.computed)(
|
|
1302
|
+
() => `display: inline-block; padding: ${this.padding()}; background-color: ${this.backgroundColor()}; color: ${this.color()}; text-decoration: none; border-radius: ${this.borderRadius()}; font-weight: 500;`
|
|
1303
|
+
);
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
EmlButton = __decorateClass([
|
|
1307
|
+
(0, import_core20.Component)({
|
|
1308
|
+
selector: "eml-button",
|
|
1309
|
+
template: `
|
|
1310
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
1311
|
+
<tr>
|
|
1312
|
+
<td [attr.style]="alignStyle()">
|
|
1313
|
+
<a [attr.href]="href()" [attr.style]="linkStyle()" target="_blank">
|
|
1314
|
+
<ng-content />
|
|
1315
|
+
</a>
|
|
1316
|
+
</td>
|
|
1317
|
+
</tr>
|
|
1318
|
+
</table>
|
|
1319
|
+
`,
|
|
1320
|
+
changeDetection: import_core20.ChangeDetectionStrategy.OnPush
|
|
1321
|
+
})
|
|
1322
|
+
], EmlButton);
|
|
1323
|
+
}
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1326
|
+
// libs/email/src/lib/components/eml-link.component.ts
|
|
1327
|
+
var import_core21, EmlLink;
|
|
1328
|
+
var init_eml_link_component = __esm({
|
|
1329
|
+
"libs/email/src/lib/components/eml-link.component.ts"() {
|
|
1330
|
+
"use strict";
|
|
1331
|
+
import_core21 = require("@angular/core");
|
|
1332
|
+
EmlLink = class {
|
|
1333
|
+
constructor() {
|
|
1334
|
+
this.href = (0, import_core21.input)("");
|
|
1335
|
+
this.color = (0, import_core21.input)("#18181b");
|
|
1336
|
+
this.textDecoration = (0, import_core21.input)("underline");
|
|
1337
|
+
this.linkStyle = (0, import_core21.computed)(
|
|
1338
|
+
() => `color: ${this.color()}; text-decoration: ${this.textDecoration()};`
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
EmlLink = __decorateClass([
|
|
1343
|
+
(0, import_core21.Component)({
|
|
1344
|
+
selector: "eml-link",
|
|
1345
|
+
template: `
|
|
1346
|
+
<a [attr.href]="href()" [attr.style]="linkStyle()" target="_blank">
|
|
1347
|
+
<ng-content />
|
|
1348
|
+
</a>
|
|
1349
|
+
`,
|
|
1350
|
+
changeDetection: import_core21.ChangeDetectionStrategy.OnPush
|
|
1351
|
+
})
|
|
1352
|
+
], EmlLink);
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
// libs/email/src/lib/components/eml-image.component.ts
|
|
1357
|
+
var import_core22, EmlImage;
|
|
1358
|
+
var init_eml_image_component = __esm({
|
|
1359
|
+
"libs/email/src/lib/components/eml-image.component.ts"() {
|
|
1360
|
+
"use strict";
|
|
1361
|
+
import_core22 = require("@angular/core");
|
|
1362
|
+
EmlImage = class {
|
|
1363
|
+
constructor() {
|
|
1364
|
+
this.src = (0, import_core22.input)("");
|
|
1365
|
+
this.alt = (0, import_core22.input)("");
|
|
1366
|
+
this.width = (0, import_core22.input)(void 0);
|
|
1367
|
+
this.height = (0, import_core22.input)(void 0);
|
|
1368
|
+
this.borderRadius = (0, import_core22.input)("0");
|
|
1369
|
+
this.imgStyle = (0, import_core22.computed)(
|
|
1370
|
+
() => `display: block; max-width: 100%; border: 0; outline: none; border-radius: ${this.borderRadius()};`
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
EmlImage = __decorateClass([
|
|
1375
|
+
(0, import_core22.Component)({
|
|
1376
|
+
selector: "eml-image",
|
|
1377
|
+
template: `
|
|
1378
|
+
<img
|
|
1379
|
+
[attr.src]="src()"
|
|
1380
|
+
[attr.alt]="alt()"
|
|
1381
|
+
[attr.width]="width()"
|
|
1382
|
+
[attr.height]="height()"
|
|
1383
|
+
[attr.style]="imgStyle()"
|
|
1384
|
+
/>
|
|
1385
|
+
`,
|
|
1386
|
+
changeDetection: import_core22.ChangeDetectionStrategy.OnPush
|
|
1387
|
+
})
|
|
1388
|
+
], EmlImage);
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
// libs/email/src/lib/components/eml-divider.component.ts
|
|
1393
|
+
var import_core23, EmlDivider;
|
|
1394
|
+
var init_eml_divider_component = __esm({
|
|
1395
|
+
"libs/email/src/lib/components/eml-divider.component.ts"() {
|
|
1396
|
+
"use strict";
|
|
1397
|
+
import_core23 = require("@angular/core");
|
|
1398
|
+
EmlDivider = class {
|
|
1399
|
+
constructor() {
|
|
1400
|
+
this.color = (0, import_core23.input)("#e4e4e7");
|
|
1401
|
+
this.margin = (0, import_core23.input)("24px 0");
|
|
1402
|
+
this.hrStyle = (0, import_core23.computed)(
|
|
1403
|
+
() => `border: none; border-top: 1px solid ${this.color()}; margin: ${this.margin()};`
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
EmlDivider = __decorateClass([
|
|
1408
|
+
(0, import_core23.Component)({
|
|
1409
|
+
selector: "eml-divider",
|
|
1410
|
+
template: `<hr [attr.style]="hrStyle()" />`,
|
|
1411
|
+
changeDetection: import_core23.ChangeDetectionStrategy.OnPush
|
|
1412
|
+
})
|
|
1413
|
+
], EmlDivider);
|
|
1414
|
+
}
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1417
|
+
// libs/email/src/lib/components/eml-preview.component.ts
|
|
1418
|
+
var import_core24, EmlPreview;
|
|
1419
|
+
var init_eml_preview_component = __esm({
|
|
1420
|
+
"libs/email/src/lib/components/eml-preview.component.ts"() {
|
|
1421
|
+
"use strict";
|
|
1422
|
+
import_core24 = require("@angular/core");
|
|
1423
|
+
EmlPreview = class {
|
|
1424
|
+
};
|
|
1425
|
+
EmlPreview = __decorateClass([
|
|
1426
|
+
(0, import_core24.Component)({
|
|
1427
|
+
selector: "eml-preview",
|
|
1428
|
+
template: `
|
|
1429
|
+
<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">
|
|
1430
|
+
<ng-content />
|
|
1431
|
+
</div>
|
|
1432
|
+
`,
|
|
1433
|
+
changeDetection: import_core24.ChangeDetectionStrategy.OnPush
|
|
1434
|
+
})
|
|
1435
|
+
], EmlPreview);
|
|
1436
|
+
}
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
// libs/email/src/lib/components/eml-spacer.component.ts
|
|
1440
|
+
var import_core25, EmlSpacer;
|
|
1441
|
+
var init_eml_spacer_component = __esm({
|
|
1442
|
+
"libs/email/src/lib/components/eml-spacer.component.ts"() {
|
|
1443
|
+
"use strict";
|
|
1444
|
+
import_core25 = require("@angular/core");
|
|
1445
|
+
EmlSpacer = class {
|
|
1446
|
+
constructor() {
|
|
1447
|
+
this.height = (0, import_core25.input)("24px");
|
|
1448
|
+
this.spacerStyle = (0, import_core25.computed)(
|
|
1449
|
+
() => `height: ${this.height()}; line-height: ${this.height()}; font-size: 1px;`
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
};
|
|
1453
|
+
EmlSpacer = __decorateClass([
|
|
1454
|
+
(0, import_core25.Component)({
|
|
1455
|
+
selector: "eml-spacer",
|
|
1456
|
+
template: `<div [attr.style]="spacerStyle()"></div>`,
|
|
1457
|
+
changeDetection: import_core25.ChangeDetectionStrategy.OnPush
|
|
1458
|
+
})
|
|
1459
|
+
], EmlSpacer);
|
|
1460
|
+
}
|
|
1461
|
+
});
|
|
1462
|
+
|
|
1463
|
+
// libs/email/src/lib/components/eml-footer.component.ts
|
|
1464
|
+
var import_core26, EmlFooter;
|
|
1465
|
+
var init_eml_footer_component = __esm({
|
|
1466
|
+
"libs/email/src/lib/components/eml-footer.component.ts"() {
|
|
1467
|
+
"use strict";
|
|
1468
|
+
import_core26 = require("@angular/core");
|
|
1469
|
+
EmlFooter = class {
|
|
1470
|
+
constructor() {
|
|
1471
|
+
this.maxWidth = (0, import_core26.input)("480px");
|
|
1472
|
+
this.color = (0, import_core26.input)("#71717a");
|
|
1473
|
+
this.fontSize = (0, import_core26.input)("12px");
|
|
1474
|
+
this.textAlign = (0, import_core26.input)("center");
|
|
1475
|
+
this.padding = (0, import_core26.input)("20px 0 0");
|
|
1476
|
+
this.tableStyle = (0, import_core26.computed)(() => `max-width: ${this.maxWidth()}; margin: 0 auto;`);
|
|
1477
|
+
this.cellStyle = (0, import_core26.computed)(
|
|
1478
|
+
() => `text-align: ${this.textAlign()}; color: ${this.color()}; font-size: ${this.fontSize()}; padding: ${this.padding()};`
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
EmlFooter = __decorateClass([
|
|
1483
|
+
(0, import_core26.Component)({
|
|
1484
|
+
selector: "eml-footer",
|
|
1485
|
+
template: `
|
|
1486
|
+
<table
|
|
1487
|
+
role="presentation"
|
|
1488
|
+
width="100%"
|
|
1489
|
+
cellspacing="0"
|
|
1490
|
+
cellpadding="0"
|
|
1491
|
+
[attr.style]="tableStyle()"
|
|
1492
|
+
>
|
|
1493
|
+
<tr>
|
|
1494
|
+
<td [attr.style]="cellStyle()">
|
|
1495
|
+
<ng-content />
|
|
1496
|
+
</td>
|
|
1497
|
+
</tr>
|
|
1498
|
+
</table>
|
|
1499
|
+
`,
|
|
1500
|
+
changeDetection: import_core26.ChangeDetectionStrategy.OnPush
|
|
1501
|
+
})
|
|
1502
|
+
], EmlFooter);
|
|
1503
|
+
}
|
|
1504
|
+
});
|
|
1505
|
+
|
|
1506
|
+
// libs/email/src/lib/utils/blocks-to-plain-text.ts
|
|
1507
|
+
function blocksToPlainText(blocks2, depth = 0) {
|
|
1508
|
+
const lines = [];
|
|
1509
|
+
for (const block of blocks2) {
|
|
1510
|
+
const text2 = blockToText(block, depth);
|
|
1511
|
+
if (text2) {
|
|
1512
|
+
lines.push(text2);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
return lines.join("\n\n");
|
|
1516
|
+
}
|
|
1517
|
+
function blockToText(block, depth) {
|
|
1518
|
+
switch (block.type) {
|
|
1519
|
+
case "header":
|
|
1520
|
+
return headerToText(block.data);
|
|
1521
|
+
case "text":
|
|
1522
|
+
return String(block.data["content"] ?? "");
|
|
1523
|
+
case "button":
|
|
1524
|
+
return buttonToText(block.data);
|
|
1525
|
+
case "footer":
|
|
1526
|
+
return String(block.data["text"] ?? "");
|
|
1527
|
+
case "columns":
|
|
1528
|
+
if (depth >= MAX_BLOCK_DEPTH2) {
|
|
1529
|
+
console.warn("[momentum:email] Max nesting depth reached, skipping columns block");
|
|
1530
|
+
return "";
|
|
1531
|
+
}
|
|
1532
|
+
return columnsToText(block.data, depth);
|
|
1533
|
+
case "divider":
|
|
1534
|
+
case "spacer":
|
|
1535
|
+
case "image":
|
|
1536
|
+
return "";
|
|
1537
|
+
default:
|
|
1538
|
+
return "";
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
function headerToText(data) {
|
|
1542
|
+
const title = String(data["title"] ?? "");
|
|
1543
|
+
const subtitle = data["subtitle"] ? String(data["subtitle"]) : "";
|
|
1544
|
+
if (!title && !subtitle)
|
|
1545
|
+
return "";
|
|
1546
|
+
if (!subtitle)
|
|
1547
|
+
return title;
|
|
1548
|
+
return `${title}
|
|
1549
|
+
${subtitle}`;
|
|
1550
|
+
}
|
|
1551
|
+
function buttonToText(data) {
|
|
1552
|
+
const label = String(data["label"] ?? "");
|
|
1553
|
+
const href = data["href"] ? String(data["href"]) : "";
|
|
1554
|
+
if (!label)
|
|
1555
|
+
return "";
|
|
1556
|
+
if (!href)
|
|
1557
|
+
return label;
|
|
1558
|
+
return `${label}: ${href}`;
|
|
1559
|
+
}
|
|
1560
|
+
function columnsToText(data, depth) {
|
|
1561
|
+
const columns = data["columns"];
|
|
1562
|
+
if (!Array.isArray(columns))
|
|
1563
|
+
return "";
|
|
1564
|
+
const parts = [];
|
|
1565
|
+
for (const col of columns) {
|
|
1566
|
+
if (col && typeof col === "object" && Array.isArray(col.blocks)) {
|
|
1567
|
+
const colText = blocksToPlainText(col.blocks, depth + 1);
|
|
1568
|
+
if (colText) {
|
|
1569
|
+
parts.push(colText);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
return parts.join("\n\n");
|
|
1574
|
+
}
|
|
1575
|
+
var MAX_BLOCK_DEPTH2;
|
|
1576
|
+
var init_blocks_to_plain_text = __esm({
|
|
1577
|
+
"libs/email/src/lib/utils/blocks-to-plain-text.ts"() {
|
|
1578
|
+
"use strict";
|
|
1579
|
+
MAX_BLOCK_DEPTH2 = 5;
|
|
1580
|
+
}
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
// libs/email/src/index.ts
|
|
544
1584
|
var src_exports2 = {};
|
|
545
1585
|
__export(src_exports2, {
|
|
1586
|
+
DEFAULT_EMAIL_THEME: () => DEFAULT_EMAIL_THEME,
|
|
1587
|
+
DEFAULT_PASSWORD_RESET_BLOCKS: () => DEFAULT_PASSWORD_RESET_BLOCKS,
|
|
1588
|
+
DEFAULT_VERIFICATION_BLOCKS: () => DEFAULT_VERIFICATION_BLOCKS,
|
|
1589
|
+
EMAIL_DATA: () => EMAIL_DATA,
|
|
1590
|
+
EmlBody: () => EmlBody,
|
|
1591
|
+
EmlButton: () => EmlButton,
|
|
1592
|
+
EmlColumn: () => EmlColumn,
|
|
1593
|
+
EmlContainer: () => EmlContainer,
|
|
1594
|
+
EmlDivider: () => EmlDivider,
|
|
1595
|
+
EmlFooter: () => EmlFooter,
|
|
1596
|
+
EmlHeading: () => EmlHeading,
|
|
1597
|
+
EmlImage: () => EmlImage,
|
|
1598
|
+
EmlLink: () => EmlLink,
|
|
1599
|
+
EmlPreview: () => EmlPreview,
|
|
1600
|
+
EmlRow: () => EmlRow,
|
|
1601
|
+
EmlSection: () => EmlSection,
|
|
1602
|
+
EmlSpacer: () => EmlSpacer,
|
|
1603
|
+
EmlText: () => EmlText,
|
|
1604
|
+
blocksToPlainText: () => blocksToPlainText,
|
|
1605
|
+
escapeHtml: () => escapeHtml2,
|
|
1606
|
+
injectEmailData: () => injectEmailData,
|
|
1607
|
+
inlineCss: () => inlineCss,
|
|
1608
|
+
isValidBlock: () => isValidBlock,
|
|
1609
|
+
renderEmail: () => renderEmail,
|
|
1610
|
+
renderEmailFromBlocks: () => renderEmailFromBlocks,
|
|
1611
|
+
replaceBlockVariables: () => replaceBlockVariables,
|
|
1612
|
+
replaceVariables: () => replaceVariables,
|
|
1613
|
+
sanitizeAlignment: () => sanitizeAlignment,
|
|
1614
|
+
sanitizeCssNumber: () => sanitizeCssNumber,
|
|
1615
|
+
sanitizeCssValue: () => sanitizeCssValue,
|
|
1616
|
+
sanitizeFontFamily: () => sanitizeFontFamily,
|
|
1617
|
+
sanitizeUrl: () => sanitizeUrl
|
|
1618
|
+
});
|
|
1619
|
+
var init_src2 = __esm({
|
|
1620
|
+
"libs/email/src/index.ts"() {
|
|
1621
|
+
"use strict";
|
|
1622
|
+
init_render_email();
|
|
1623
|
+
init_render_blocks();
|
|
1624
|
+
init_render_types();
|
|
1625
|
+
init_replace_variables();
|
|
1626
|
+
init_default_templates();
|
|
1627
|
+
init_eml_body_component();
|
|
1628
|
+
init_eml_container_component();
|
|
1629
|
+
init_eml_section_component();
|
|
1630
|
+
init_eml_row_component();
|
|
1631
|
+
init_eml_column_component();
|
|
1632
|
+
init_eml_text_component();
|
|
1633
|
+
init_eml_heading_component();
|
|
1634
|
+
init_eml_button_component();
|
|
1635
|
+
init_eml_link_component();
|
|
1636
|
+
init_eml_image_component();
|
|
1637
|
+
init_eml_divider_component();
|
|
1638
|
+
init_eml_preview_component();
|
|
1639
|
+
init_eml_spacer_component();
|
|
1640
|
+
init_eml_footer_component();
|
|
1641
|
+
init_escape_html();
|
|
1642
|
+
init_css_inliner();
|
|
1643
|
+
init_sanitize();
|
|
1644
|
+
init_blocks_to_plain_text();
|
|
1645
|
+
init_render_blocks();
|
|
1646
|
+
init_types();
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
|
|
1650
|
+
// libs/server-express/src/index.ts
|
|
1651
|
+
var src_exports3 = {};
|
|
1652
|
+
__export(src_exports3, {
|
|
546
1653
|
createApiKeyResolverMiddleware: () => createApiKeyResolverMiddleware,
|
|
547
1654
|
createApiKeyRoutes: () => createApiKeyRoutes,
|
|
548
1655
|
createAuthMiddleware: () => createAuthMiddleware,
|
|
@@ -557,7 +1664,7 @@ __export(src_exports2, {
|
|
|
557
1664
|
initializeMomentum: () => initializeMomentum,
|
|
558
1665
|
momentumApiMiddleware: () => momentumApiMiddleware
|
|
559
1666
|
});
|
|
560
|
-
module.exports = __toCommonJS(
|
|
1667
|
+
module.exports = __toCommonJS(src_exports3);
|
|
561
1668
|
|
|
562
1669
|
// libs/server-express/src/lib/server-express.ts
|
|
563
1670
|
var import_express = require("express");
|
|
@@ -5054,11 +6161,11 @@ SwaggerUIBundle({
|
|
|
5054
6161
|
|
|
5055
6162
|
// libs/server-core/src/lib/preview-renderer.ts
|
|
5056
6163
|
function renderPreviewHTML(options) {
|
|
5057
|
-
const { doc, collection } = options;
|
|
6164
|
+
const { doc, collection, customFieldRenderers } = options;
|
|
5058
6165
|
const titleField = collection.admin?.useAsTitle ?? "id";
|
|
5059
6166
|
const title = escapeHtml(String(doc[titleField] ?? doc["id"] ?? "Untitled"));
|
|
5060
6167
|
const fields = collection.fields ?? [];
|
|
5061
|
-
const fieldHtml = fields.filter((f) => !isHiddenField(f) && !isLayoutField(f) && f.name !== titleField).map((f) => renderField(f, doc)).filter(Boolean).join("\n");
|
|
6168
|
+
const fieldHtml = fields.filter((f) => !isHiddenField(f) && !isLayoutField(f) && f.name !== titleField).map((f) => renderField(f, doc, customFieldRenderers)).filter(Boolean).join("\n");
|
|
5062
6169
|
const richTextFields = fields.filter((f) => f.type === "richText").map((f) => f.name);
|
|
5063
6170
|
return `<!DOCTYPE html>
|
|
5064
6171
|
<html lang="en">
|
|
@@ -5092,6 +6199,7 @@ ${fieldHtml}
|
|
|
5092
6199
|
(function(){
|
|
5093
6200
|
var richTextFields=${JSON.stringify(richTextFields)};
|
|
5094
6201
|
window.addEventListener('message',function(e){
|
|
6202
|
+
if(e.origin!==window.location.origin)return;
|
|
5095
6203
|
if(!e.data||e.data.type!=='momentum-preview-update')return;
|
|
5096
6204
|
var d=e.data.data;if(!d)return;
|
|
5097
6205
|
document.querySelectorAll('[data-field]').forEach(function(el){
|
|
@@ -5112,16 +6220,20 @@ if(titleEl){var tf=titleEl.getAttribute('data-field');if(d[tf]!==undefined)title
|
|
|
5112
6220
|
</body>
|
|
5113
6221
|
</html>`;
|
|
5114
6222
|
}
|
|
5115
|
-
function renderField(field, doc) {
|
|
6223
|
+
function renderField(field, doc, customRenderers) {
|
|
5116
6224
|
const value = doc[field.name];
|
|
5117
6225
|
if (value === void 0 || value === null) {
|
|
5118
6226
|
return renderFieldWrapper(field, "");
|
|
5119
6227
|
}
|
|
6228
|
+
const editorKey = field.admin?.editor;
|
|
6229
|
+
if (editorKey && customRenderers?.[editorKey]) {
|
|
6230
|
+
return renderFieldWrapper(field, customRenderers[editorKey](value, field));
|
|
6231
|
+
}
|
|
5120
6232
|
switch (field.type) {
|
|
5121
6233
|
case "richText":
|
|
5122
6234
|
return renderFieldWrapper(
|
|
5123
6235
|
field,
|
|
5124
|
-
`<div class="field-value rich-text" data-field="${escapeHtml(field.name)}">${String(value)}</div>`
|
|
6236
|
+
`<div class="field-value rich-text" data-field="${escapeHtml(field.name)}">${escapeHtml(String(value))}</div>`
|
|
5125
6237
|
);
|
|
5126
6238
|
case "checkbox":
|
|
5127
6239
|
return renderFieldWrapper(
|
|
@@ -5468,6 +6580,20 @@ function getPluginProviders() {
|
|
|
5468
6580
|
}
|
|
5469
6581
|
|
|
5470
6582
|
// libs/server-express/src/lib/server-express.ts
|
|
6583
|
+
function getEmailBuilderFieldName(collection) {
|
|
6584
|
+
const field = collection.fields.find(
|
|
6585
|
+
(f) => f.type === "json" && f.admin?.editor === "email-builder"
|
|
6586
|
+
);
|
|
6587
|
+
return field?.name;
|
|
6588
|
+
}
|
|
6589
|
+
async function renderEmailPreviewHTML(doc, blocksFieldName) {
|
|
6590
|
+
const { renderEmailFromBlocks: renderEmailFromBlocks2 } = await Promise.resolve().then(() => (init_src2(), src_exports2));
|
|
6591
|
+
const blocks2 = doc[blocksFieldName];
|
|
6592
|
+
if (!Array.isArray(blocks2) || blocks2.length === 0) {
|
|
6593
|
+
return '<html><body style="display:flex;align-items:center;justify-content:center;min-height:100vh;color:#666;font-family:sans-serif"><p>No email blocks yet.</p></body></html>';
|
|
6594
|
+
}
|
|
6595
|
+
return renderEmailFromBlocks2({ blocks: blocks2 });
|
|
6596
|
+
}
|
|
5471
6597
|
function extractUserFromRequest(req) {
|
|
5472
6598
|
const authReq = req;
|
|
5473
6599
|
if (authReq.user?.id) {
|
|
@@ -5882,16 +7008,13 @@ function momentumApiMiddleware(config) {
|
|
|
5882
7008
|
res.status(500).json({ error: "Failed to get status", message });
|
|
5883
7009
|
}
|
|
5884
7010
|
});
|
|
5885
|
-
|
|
7011
|
+
async function handlePreviewRequest(req, res) {
|
|
5886
7012
|
try {
|
|
5887
7013
|
const slug2 = req.params["collection"];
|
|
5888
7014
|
const id = req.params["id"];
|
|
5889
7015
|
const user = extractUserFromRequest(req);
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
const doc = await contextApi.collection(slug2).findById(id);
|
|
5893
|
-
if (!doc) {
|
|
5894
|
-
res.status(404).json({ error: "Document not found" });
|
|
7016
|
+
if (!user) {
|
|
7017
|
+
res.status(401).json({ error: "Authentication required to access preview" });
|
|
5895
7018
|
return;
|
|
5896
7019
|
}
|
|
5897
7020
|
const collectionConfig = config.collections.find((c) => c.slug === slug2);
|
|
@@ -5899,7 +7022,29 @@ function momentumApiMiddleware(config) {
|
|
|
5899
7022
|
res.status(404).json({ error: "Collection not found" });
|
|
5900
7023
|
return;
|
|
5901
7024
|
}
|
|
5902
|
-
const
|
|
7025
|
+
const accessFn = collectionConfig.access?.read;
|
|
7026
|
+
if (accessFn) {
|
|
7027
|
+
const allowed = await Promise.resolve(accessFn({ req: { user } }));
|
|
7028
|
+
if (!allowed) {
|
|
7029
|
+
res.status(403).json({ error: "Access denied" });
|
|
7030
|
+
return;
|
|
7031
|
+
}
|
|
7032
|
+
}
|
|
7033
|
+
let doc;
|
|
7034
|
+
if (req.method === "POST" && req.body?.data) {
|
|
7035
|
+
doc = req.body.data;
|
|
7036
|
+
} else {
|
|
7037
|
+
const api = getMomentumAPI();
|
|
7038
|
+
const contextApi = user ? api.setContext({ user }) : api;
|
|
7039
|
+
const dbDoc = await contextApi.collection(slug2).findById(id);
|
|
7040
|
+
if (!dbDoc) {
|
|
7041
|
+
res.status(404).json({ error: "Document not found" });
|
|
7042
|
+
return;
|
|
7043
|
+
}
|
|
7044
|
+
doc = dbDoc;
|
|
7045
|
+
}
|
|
7046
|
+
const emailField = getEmailBuilderFieldName(collectionConfig);
|
|
7047
|
+
const html = emailField ? await renderEmailPreviewHTML(doc, emailField) : renderPreviewHTML({ doc, collection: collectionConfig });
|
|
5903
7048
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
5904
7049
|
res.send(html);
|
|
5905
7050
|
} catch (error) {
|
|
@@ -5914,7 +7059,9 @@ function momentumApiMiddleware(config) {
|
|
|
5914
7059
|
}
|
|
5915
7060
|
res.status(500).json({ error: "Preview failed", message });
|
|
5916
7061
|
}
|
|
5917
|
-
}
|
|
7062
|
+
}
|
|
7063
|
+
router.get("/:collection/:id/preview", handlePreviewRequest);
|
|
7064
|
+
router.post("/:collection/:id/preview", handlePreviewRequest);
|
|
5918
7065
|
const upload2 = (0, import_multer.default)({
|
|
5919
7066
|
storage: import_multer.default.memoryStorage(),
|
|
5920
7067
|
limits: {
|
|
@@ -6585,7 +7732,6 @@ var AUTH_ROLES = [
|
|
|
6585
7732
|
|
|
6586
7733
|
// libs/auth/src/lib/auth.ts
|
|
6587
7734
|
var import_better_auth = require("better-auth");
|
|
6588
|
-
var import_plugins = require("better-auth/plugins");
|
|
6589
7735
|
|
|
6590
7736
|
// libs/auth/src/lib/email.ts
|
|
6591
7737
|
var nodemailer = __toESM(require("nodemailer"));
|
|
@@ -6767,7 +7913,7 @@ var AuthApiKeysCollection = defineCollection({
|
|
|
6767
7913
|
});
|
|
6768
7914
|
|
|
6769
7915
|
// libs/auth/src/lib/plugins/two-factor.ts
|
|
6770
|
-
var
|
|
7916
|
+
var import_plugins = require("better-auth/plugins");
|
|
6771
7917
|
var AuthTwoFactorCollection = defineCollection({
|
|
6772
7918
|
slug: "auth-two-factor",
|
|
6773
7919
|
dbName: "twoFactor",
|
|
@@ -6792,7 +7938,11 @@ var AuthTwoFactorCollection = defineCollection({
|
|
|
6792
7938
|
}
|
|
6793
7939
|
});
|
|
6794
7940
|
|
|
7941
|
+
// libs/auth/src/lib/plugins/admin.ts
|
|
7942
|
+
var import_plugins2 = require("better-auth/plugins");
|
|
7943
|
+
|
|
6795
7944
|
// libs/auth/src/lib/plugins/organization.ts
|
|
7945
|
+
var import_plugins3 = require("better-auth/plugins");
|
|
6796
7946
|
var AuthOrganizationCollection = defineCollection({
|
|
6797
7947
|
slug: "auth-organization",
|
|
6798
7948
|
dbName: "organization",
|