@momentumcms/server-express 0.5.0 → 0.5.2
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/index.cjs +1224 -19
- package/index.js +1220 -17
- package/package.json +6 -1
- package/src/index.d.ts +1 -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({
|
|
@@ -317,7 +326,7 @@ function detectMimeType(buffer) {
|
|
|
317
326
|
if (match) {
|
|
318
327
|
if (sig.bytes[0] === 82 && sig.bytes[1] === 73) {
|
|
319
328
|
if (buffer.length >= 12) {
|
|
320
|
-
const formatId = buffer.
|
|
329
|
+
const formatId = String.fromCharCode(...buffer.subarray(8, 12));
|
|
321
330
|
if (formatId === "WEBP") {
|
|
322
331
|
return "image/webp";
|
|
323
332
|
}
|
|
@@ -330,7 +339,7 @@ function detectMimeType(buffer) {
|
|
|
330
339
|
}
|
|
331
340
|
}
|
|
332
341
|
if (sig.mimeType === "video/mp4" && buffer.length >= 8) {
|
|
333
|
-
const boxType = buffer.
|
|
342
|
+
const boxType = String.fromCharCode(...buffer.subarray(4, 8));
|
|
334
343
|
if (boxType === "ftyp") {
|
|
335
344
|
return "video/mp4";
|
|
336
345
|
}
|
|
@@ -339,7 +348,7 @@ function detectMimeType(buffer) {
|
|
|
339
348
|
}
|
|
340
349
|
}
|
|
341
350
|
if (isTextContent(buffer)) {
|
|
342
|
-
const text2 = buffer.
|
|
351
|
+
const text2 = new TextDecoder().decode(buffer.subarray(0, Math.min(buffer.length, 1e3)));
|
|
343
352
|
if (text2.trim().startsWith("{") || text2.trim().startsWith("[")) {
|
|
344
353
|
return "application/json";
|
|
345
354
|
}
|
|
@@ -531,9 +540,1116 @@ var init_src = __esm({
|
|
|
531
540
|
}
|
|
532
541
|
});
|
|
533
542
|
|
|
534
|
-
// 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
|
|
535
1584
|
var src_exports2 = {};
|
|
536
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, {
|
|
537
1653
|
createApiKeyResolverMiddleware: () => createApiKeyResolverMiddleware,
|
|
538
1654
|
createApiKeyRoutes: () => createApiKeyRoutes,
|
|
539
1655
|
createAuthMiddleware: () => createAuthMiddleware,
|
|
@@ -544,11 +1660,12 @@ __export(src_exports2, {
|
|
|
544
1660
|
createProtectMiddleware: () => createProtectMiddleware,
|
|
545
1661
|
createSessionResolverMiddleware: () => createSessionResolverMiddleware,
|
|
546
1662
|
createSetupMiddleware: () => createSetupMiddleware,
|
|
1663
|
+
getPluginMiddleware: () => getPluginMiddleware,
|
|
547
1664
|
getPluginProviders: () => getPluginProviders,
|
|
548
1665
|
initializeMomentum: () => initializeMomentum,
|
|
549
1666
|
momentumApiMiddleware: () => momentumApiMiddleware
|
|
550
1667
|
});
|
|
551
|
-
module.exports = __toCommonJS(
|
|
1668
|
+
module.exports = __toCommonJS(src_exports3);
|
|
552
1669
|
|
|
553
1670
|
// libs/server-express/src/lib/server-express.ts
|
|
554
1671
|
var import_express = require("express");
|
|
@@ -1027,6 +2144,31 @@ function validateRowCount(name, label, count, minRows, maxRows, errors) {
|
|
|
1027
2144
|
}
|
|
1028
2145
|
|
|
1029
2146
|
// libs/core/src/lib/collections/media.collection.ts
|
|
2147
|
+
var validateFocalPoint = (value) => {
|
|
2148
|
+
if (value === null || value === void 0)
|
|
2149
|
+
return true;
|
|
2150
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
2151
|
+
return "Focal point must be an object with x and y coordinates";
|
|
2152
|
+
}
|
|
2153
|
+
const fp = Object.fromEntries(Object.entries(value));
|
|
2154
|
+
if (!("x" in fp) || !("y" in fp)) {
|
|
2155
|
+
return "Focal point must have both x and y properties";
|
|
2156
|
+
}
|
|
2157
|
+
const { x, y } = fp;
|
|
2158
|
+
if (typeof x !== "number" || !Number.isFinite(x)) {
|
|
2159
|
+
return "Focal point x must be a finite number";
|
|
2160
|
+
}
|
|
2161
|
+
if (typeof y !== "number" || !Number.isFinite(y)) {
|
|
2162
|
+
return "Focal point y must be a finite number";
|
|
2163
|
+
}
|
|
2164
|
+
if (x < 0 || x > 1) {
|
|
2165
|
+
return `Focal point x must be between 0 and 1 (received ${x})`;
|
|
2166
|
+
}
|
|
2167
|
+
if (y < 0 || y > 1) {
|
|
2168
|
+
return `Focal point y must be between 0 and 1 (received ${y})`;
|
|
2169
|
+
}
|
|
2170
|
+
return true;
|
|
2171
|
+
};
|
|
1030
2172
|
var MediaCollection = defineCollection({
|
|
1031
2173
|
slug: "media",
|
|
1032
2174
|
labels: {
|
|
@@ -1081,6 +2223,14 @@ var MediaCollection = defineCollection({
|
|
|
1081
2223
|
json("focalPoint", {
|
|
1082
2224
|
label: "Focal Point",
|
|
1083
2225
|
description: "Focal point coordinates for image cropping",
|
|
2226
|
+
validate: validateFocalPoint,
|
|
2227
|
+
admin: {
|
|
2228
|
+
hidden: true
|
|
2229
|
+
}
|
|
2230
|
+
}),
|
|
2231
|
+
json("sizes", {
|
|
2232
|
+
label: "Image Sizes",
|
|
2233
|
+
description: "Generated image size variants",
|
|
1084
2234
|
admin: {
|
|
1085
2235
|
hidden: true
|
|
1086
2236
|
}
|
|
@@ -1939,6 +3089,15 @@ function deepEqual(a, b) {
|
|
|
1939
3089
|
(key) => Object.prototype.hasOwnProperty.call(bRec, key) && deepEqual(aRec[key], bRec[key])
|
|
1940
3090
|
);
|
|
1941
3091
|
}
|
|
3092
|
+
function stripTransientKeys(data) {
|
|
3093
|
+
const result = {};
|
|
3094
|
+
for (const [key, value] of Object.entries(data)) {
|
|
3095
|
+
if (!key.startsWith("_")) {
|
|
3096
|
+
result[key] = value;
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
return result;
|
|
3100
|
+
}
|
|
1942
3101
|
function flattenWhereClause(where) {
|
|
1943
3102
|
if (!where)
|
|
1944
3103
|
return {};
|
|
@@ -2102,6 +3261,7 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2102
3261
|
);
|
|
2103
3262
|
}
|
|
2104
3263
|
processedData = await this.runHooks("beforeChange", processedData, "create");
|
|
3264
|
+
processedData = stripTransientKeys(processedData);
|
|
2105
3265
|
const doc = await this.adapter.create(this.slug, processedData);
|
|
2106
3266
|
await this.runHooks("afterChange", doc, "create");
|
|
2107
3267
|
if (hasFieldHooks(this.collectionConfig.fields)) {
|
|
@@ -2162,6 +3322,7 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2162
3322
|
);
|
|
2163
3323
|
}
|
|
2164
3324
|
processedData = await this.runHooks("beforeChange", processedData, "update", originalDoc);
|
|
3325
|
+
processedData = stripTransientKeys(processedData);
|
|
2165
3326
|
const doc = await this.adapter.update(this.slug, id, processedData);
|
|
2166
3327
|
await this.runHooks("afterChange", doc, "update", originalDoc);
|
|
2167
3328
|
if (hasFieldHooks(this.collectionConfig.fields)) {
|
|
@@ -4276,7 +5437,8 @@ async function handleUpload(config, request) {
|
|
|
4276
5437
|
filesize: file.size,
|
|
4277
5438
|
path: storedFile.path,
|
|
4278
5439
|
url: storedFile.url,
|
|
4279
|
-
alt: alt ?? ""
|
|
5440
|
+
alt: alt ?? "",
|
|
5441
|
+
_file: file
|
|
4280
5442
|
};
|
|
4281
5443
|
const api = getMomentumAPI().setContext({ user });
|
|
4282
5444
|
const doc = await api.collection(collection).create(mediaData);
|
|
@@ -4343,7 +5505,8 @@ async function handleCollectionUpload(globalConfig, request) {
|
|
|
4343
5505
|
mimeType: file.mimeType,
|
|
4344
5506
|
filesize: file.size,
|
|
4345
5507
|
path: storedFile.path,
|
|
4346
|
-
url: storedFile.url
|
|
5508
|
+
url: storedFile.url,
|
|
5509
|
+
_file: file
|
|
4347
5510
|
};
|
|
4348
5511
|
const api = getMomentumAPI().setContext({ user });
|
|
4349
5512
|
const doc = await api.collection(collectionSlug).create(docData);
|
|
@@ -4380,6 +5543,7 @@ function getMimeTypeFromPath(path) {
|
|
|
4380
5543
|
png: "image/png",
|
|
4381
5544
|
gif: "image/gif",
|
|
4382
5545
|
webp: "image/webp",
|
|
5546
|
+
avif: "image/avif",
|
|
4383
5547
|
svg: "image/svg+xml",
|
|
4384
5548
|
pdf: "application/pdf",
|
|
4385
5549
|
json: "application/json",
|
|
@@ -5045,11 +6209,11 @@ SwaggerUIBundle({
|
|
|
5045
6209
|
|
|
5046
6210
|
// libs/server-core/src/lib/preview-renderer.ts
|
|
5047
6211
|
function renderPreviewHTML(options) {
|
|
5048
|
-
const { doc, collection } = options;
|
|
6212
|
+
const { doc, collection, customFieldRenderers } = options;
|
|
5049
6213
|
const titleField = collection.admin?.useAsTitle ?? "id";
|
|
5050
6214
|
const title = escapeHtml(String(doc[titleField] ?? doc["id"] ?? "Untitled"));
|
|
5051
6215
|
const fields = collection.fields ?? [];
|
|
5052
|
-
const fieldHtml = fields.filter((f) => !isHiddenField(f) && !isLayoutField(f) && f.name !== titleField).map((f) => renderField(f, doc)).filter(Boolean).join("\n");
|
|
6216
|
+
const fieldHtml = fields.filter((f) => !isHiddenField(f) && !isLayoutField(f) && f.name !== titleField).map((f) => renderField(f, doc, customFieldRenderers)).filter(Boolean).join("\n");
|
|
5053
6217
|
const richTextFields = fields.filter((f) => f.type === "richText").map((f) => f.name);
|
|
5054
6218
|
return `<!DOCTYPE html>
|
|
5055
6219
|
<html lang="en">
|
|
@@ -5083,6 +6247,7 @@ ${fieldHtml}
|
|
|
5083
6247
|
(function(){
|
|
5084
6248
|
var richTextFields=${JSON.stringify(richTextFields)};
|
|
5085
6249
|
window.addEventListener('message',function(e){
|
|
6250
|
+
if(e.origin!==window.location.origin)return;
|
|
5086
6251
|
if(!e.data||e.data.type!=='momentum-preview-update')return;
|
|
5087
6252
|
var d=e.data.data;if(!d)return;
|
|
5088
6253
|
document.querySelectorAll('[data-field]').forEach(function(el){
|
|
@@ -5103,16 +6268,20 @@ if(titleEl){var tf=titleEl.getAttribute('data-field');if(d[tf]!==undefined)title
|
|
|
5103
6268
|
</body>
|
|
5104
6269
|
</html>`;
|
|
5105
6270
|
}
|
|
5106
|
-
function renderField(field, doc) {
|
|
6271
|
+
function renderField(field, doc, customRenderers) {
|
|
5107
6272
|
const value = doc[field.name];
|
|
5108
6273
|
if (value === void 0 || value === null) {
|
|
5109
6274
|
return renderFieldWrapper(field, "");
|
|
5110
6275
|
}
|
|
6276
|
+
const editorKey = field.admin?.editor;
|
|
6277
|
+
if (editorKey && customRenderers?.[editorKey]) {
|
|
6278
|
+
return renderFieldWrapper(field, customRenderers[editorKey](value, field));
|
|
6279
|
+
}
|
|
5111
6280
|
switch (field.type) {
|
|
5112
6281
|
case "richText":
|
|
5113
6282
|
return renderFieldWrapper(
|
|
5114
6283
|
field,
|
|
5115
|
-
`<div class="field-value rich-text" data-field="${escapeHtml(field.name)}">${String(value)}</div>`
|
|
6284
|
+
`<div class="field-value rich-text" data-field="${escapeHtml(field.name)}">${escapeHtml(String(value))}</div>`
|
|
5116
6285
|
);
|
|
5117
6286
|
case "checkbox":
|
|
5118
6287
|
return renderFieldWrapper(
|
|
@@ -5459,6 +6628,20 @@ function getPluginProviders() {
|
|
|
5459
6628
|
}
|
|
5460
6629
|
|
|
5461
6630
|
// libs/server-express/src/lib/server-express.ts
|
|
6631
|
+
function getEmailBuilderFieldName(collection) {
|
|
6632
|
+
const field = collection.fields.find(
|
|
6633
|
+
(f) => f.type === "json" && f.admin?.editor === "email-builder"
|
|
6634
|
+
);
|
|
6635
|
+
return field?.name;
|
|
6636
|
+
}
|
|
6637
|
+
async function renderEmailPreviewHTML(doc, blocksFieldName) {
|
|
6638
|
+
const { renderEmailFromBlocks: renderEmailFromBlocks2 } = await Promise.resolve().then(() => (init_src2(), src_exports2));
|
|
6639
|
+
const blocks2 = doc[blocksFieldName];
|
|
6640
|
+
if (!Array.isArray(blocks2) || blocks2.length === 0) {
|
|
6641
|
+
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>';
|
|
6642
|
+
}
|
|
6643
|
+
return renderEmailFromBlocks2({ blocks: blocks2 });
|
|
6644
|
+
}
|
|
5462
6645
|
function extractUserFromRequest(req) {
|
|
5463
6646
|
const authReq = req;
|
|
5464
6647
|
if (authReq.user?.id) {
|
|
@@ -5873,16 +7056,13 @@ function momentumApiMiddleware(config) {
|
|
|
5873
7056
|
res.status(500).json({ error: "Failed to get status", message });
|
|
5874
7057
|
}
|
|
5875
7058
|
});
|
|
5876
|
-
|
|
7059
|
+
async function handlePreviewRequest(req, res) {
|
|
5877
7060
|
try {
|
|
5878
7061
|
const slug2 = req.params["collection"];
|
|
5879
7062
|
const id = req.params["id"];
|
|
5880
7063
|
const user = extractUserFromRequest(req);
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
const doc = await contextApi.collection(slug2).findById(id);
|
|
5884
|
-
if (!doc) {
|
|
5885
|
-
res.status(404).json({ error: "Document not found" });
|
|
7064
|
+
if (!user) {
|
|
7065
|
+
res.status(401).json({ error: "Authentication required to access preview" });
|
|
5886
7066
|
return;
|
|
5887
7067
|
}
|
|
5888
7068
|
const collectionConfig = config.collections.find((c) => c.slug === slug2);
|
|
@@ -5890,7 +7070,29 @@ function momentumApiMiddleware(config) {
|
|
|
5890
7070
|
res.status(404).json({ error: "Collection not found" });
|
|
5891
7071
|
return;
|
|
5892
7072
|
}
|
|
5893
|
-
const
|
|
7073
|
+
const accessFn = collectionConfig.access?.read;
|
|
7074
|
+
if (accessFn) {
|
|
7075
|
+
const allowed = await Promise.resolve(accessFn({ req: { user } }));
|
|
7076
|
+
if (!allowed) {
|
|
7077
|
+
res.status(403).json({ error: "Access denied" });
|
|
7078
|
+
return;
|
|
7079
|
+
}
|
|
7080
|
+
}
|
|
7081
|
+
let doc;
|
|
7082
|
+
if (req.method === "POST" && req.body?.data) {
|
|
7083
|
+
doc = req.body.data;
|
|
7084
|
+
} else {
|
|
7085
|
+
const api = getMomentumAPI();
|
|
7086
|
+
const contextApi = user ? api.setContext({ user }) : api;
|
|
7087
|
+
const dbDoc = await contextApi.collection(slug2).findById(id);
|
|
7088
|
+
if (!dbDoc) {
|
|
7089
|
+
res.status(404).json({ error: "Document not found" });
|
|
7090
|
+
return;
|
|
7091
|
+
}
|
|
7092
|
+
doc = dbDoc;
|
|
7093
|
+
}
|
|
7094
|
+
const emailField = getEmailBuilderFieldName(collectionConfig);
|
|
7095
|
+
const html = emailField ? await renderEmailPreviewHTML(doc, emailField) : renderPreviewHTML({ doc, collection: collectionConfig });
|
|
5894
7096
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
5895
7097
|
res.send(html);
|
|
5896
7098
|
} catch (error) {
|
|
@@ -5905,7 +7107,9 @@ function momentumApiMiddleware(config) {
|
|
|
5905
7107
|
}
|
|
5906
7108
|
res.status(500).json({ error: "Preview failed", message });
|
|
5907
7109
|
}
|
|
5908
|
-
}
|
|
7110
|
+
}
|
|
7111
|
+
router.get("/:collection/:id/preview", handlePreviewRequest);
|
|
7112
|
+
router.post("/:collection/:id/preview", handlePreviewRequest);
|
|
5909
7113
|
const upload2 = (0, import_multer.default)({
|
|
5910
7114
|
storage: import_multer.default.memoryStorage(),
|
|
5911
7115
|
limits: {
|
|
@@ -7629,6 +8833,7 @@ async function createMomentumServer(options) {
|
|
|
7629
8833
|
createProtectMiddleware,
|
|
7630
8834
|
createSessionResolverMiddleware,
|
|
7631
8835
|
createSetupMiddleware,
|
|
8836
|
+
getPluginMiddleware,
|
|
7632
8837
|
getPluginProviders,
|
|
7633
8838
|
initializeMomentum,
|
|
7634
8839
|
momentumApiMiddleware
|