@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.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
2
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
4
|
var __esm = (fn, res) => function __init() {
|
|
4
5
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
@@ -7,6 +8,15 @@ var __export = (target, all) => {
|
|
|
7
8
|
for (var name in all)
|
|
8
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
10
|
};
|
|
11
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
12
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
13
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
14
|
+
if (decorator = decorators[i])
|
|
15
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
16
|
+
if (kind && result)
|
|
17
|
+
__defProp(target, key, result);
|
|
18
|
+
return result;
|
|
19
|
+
};
|
|
10
20
|
|
|
11
21
|
// libs/storage/src/lib/storage.types.ts
|
|
12
22
|
var init_storage_types = __esm({
|
|
@@ -294,7 +304,7 @@ function detectMimeType(buffer) {
|
|
|
294
304
|
if (match) {
|
|
295
305
|
if (sig.bytes[0] === 82 && sig.bytes[1] === 73) {
|
|
296
306
|
if (buffer.length >= 12) {
|
|
297
|
-
const formatId = buffer.
|
|
307
|
+
const formatId = String.fromCharCode(...buffer.subarray(8, 12));
|
|
298
308
|
if (formatId === "WEBP") {
|
|
299
309
|
return "image/webp";
|
|
300
310
|
}
|
|
@@ -307,7 +317,7 @@ function detectMimeType(buffer) {
|
|
|
307
317
|
}
|
|
308
318
|
}
|
|
309
319
|
if (sig.mimeType === "video/mp4" && buffer.length >= 8) {
|
|
310
|
-
const boxType = buffer.
|
|
320
|
+
const boxType = String.fromCharCode(...buffer.subarray(4, 8));
|
|
311
321
|
if (boxType === "ftyp") {
|
|
312
322
|
return "video/mp4";
|
|
313
323
|
}
|
|
@@ -316,7 +326,7 @@ function detectMimeType(buffer) {
|
|
|
316
326
|
}
|
|
317
327
|
}
|
|
318
328
|
if (isTextContent(buffer)) {
|
|
319
|
-
const text2 = buffer.
|
|
329
|
+
const text2 = new TextDecoder().decode(buffer.subarray(0, Math.min(buffer.length, 1e3)));
|
|
320
330
|
if (text2.trim().startsWith("{") || text2.trim().startsWith("[")) {
|
|
321
331
|
return "application/json";
|
|
322
332
|
}
|
|
@@ -508,6 +518,1111 @@ var init_src = __esm({
|
|
|
508
518
|
}
|
|
509
519
|
});
|
|
510
520
|
|
|
521
|
+
// libs/email/src/lib/utils/css-inliner.ts
|
|
522
|
+
import juice from "juice";
|
|
523
|
+
function inlineCss(html) {
|
|
524
|
+
return juice(html, {
|
|
525
|
+
removeStyleTags: true,
|
|
526
|
+
preserveMediaQueries: true,
|
|
527
|
+
preserveFontFaces: true,
|
|
528
|
+
insertPreservedExtraCss: true
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
var init_css_inliner = __esm({
|
|
532
|
+
"libs/email/src/lib/utils/css-inliner.ts"() {
|
|
533
|
+
"use strict";
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
// libs/email/src/lib/utils/strip-artifacts.ts
|
|
538
|
+
function stripAngularArtifacts(html) {
|
|
539
|
+
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");
|
|
540
|
+
}
|
|
541
|
+
var init_strip_artifacts = __esm({
|
|
542
|
+
"libs/email/src/lib/utils/strip-artifacts.ts"() {
|
|
543
|
+
"use strict";
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
// libs/email/src/lib/render/render-types.ts
|
|
548
|
+
import { InjectionToken, inject } from "@angular/core";
|
|
549
|
+
function injectEmailData() {
|
|
550
|
+
return inject(EMAIL_DATA);
|
|
551
|
+
}
|
|
552
|
+
var EMAIL_DATA;
|
|
553
|
+
var init_render_types = __esm({
|
|
554
|
+
"libs/email/src/lib/render/render-types.ts"() {
|
|
555
|
+
"use strict";
|
|
556
|
+
EMAIL_DATA = new InjectionToken("EMAIL_DATA");
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
// libs/email/src/lib/render/render-email.ts
|
|
561
|
+
import "@angular/compiler";
|
|
562
|
+
import { reflectComponentType } from "@angular/core";
|
|
563
|
+
import { renderApplication } from "@angular/platform-server";
|
|
564
|
+
import { bootstrapApplication } from "@angular/platform-browser";
|
|
565
|
+
function buildDocument(selector) {
|
|
566
|
+
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>`;
|
|
567
|
+
}
|
|
568
|
+
async function renderEmail(component, data, options) {
|
|
569
|
+
const shouldInlineCss = options?.inlineCss ?? true;
|
|
570
|
+
const shouldStripArtifacts = options?.stripArtifacts ?? true;
|
|
571
|
+
const extraProviders = options?.providers ?? [];
|
|
572
|
+
const mirror = reflectComponentType(component);
|
|
573
|
+
if (!mirror) {
|
|
574
|
+
throw new Error(
|
|
575
|
+
`Cannot reflect component type: ${component.name}. Ensure it has a @Component decorator.`
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
const providers = [...extraProviders, { provide: EMAIL_DATA, useValue: data ?? {} }];
|
|
579
|
+
let html = await renderApplication(
|
|
580
|
+
(context) => bootstrapApplication(component, { providers }, context),
|
|
581
|
+
{
|
|
582
|
+
document: buildDocument(mirror.selector),
|
|
583
|
+
url: "/"
|
|
584
|
+
}
|
|
585
|
+
);
|
|
586
|
+
if (shouldStripArtifacts) {
|
|
587
|
+
html = stripAngularArtifacts(html);
|
|
588
|
+
}
|
|
589
|
+
if (shouldInlineCss) {
|
|
590
|
+
html = inlineCss(html);
|
|
591
|
+
}
|
|
592
|
+
return html;
|
|
593
|
+
}
|
|
594
|
+
var init_render_email = __esm({
|
|
595
|
+
"libs/email/src/lib/render/render-email.ts"() {
|
|
596
|
+
"use strict";
|
|
597
|
+
init_css_inliner();
|
|
598
|
+
init_strip_artifacts();
|
|
599
|
+
init_render_types();
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// libs/email/src/lib/utils/escape-html.ts
|
|
604
|
+
function escapeHtml2(unsafe) {
|
|
605
|
+
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
606
|
+
}
|
|
607
|
+
var init_escape_html = __esm({
|
|
608
|
+
"libs/email/src/lib/utils/escape-html.ts"() {
|
|
609
|
+
"use strict";
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
// libs/email/src/lib/utils/replace-variables.ts
|
|
614
|
+
function replaceVariables(text2, variables) {
|
|
615
|
+
return text2.replace(/\{\{(\w+)\}\}/g, (_, key) => variables[key] ?? "");
|
|
616
|
+
}
|
|
617
|
+
function replaceBlockVariables(blocks2, variables) {
|
|
618
|
+
return blocks2.map((block) => ({
|
|
619
|
+
...block,
|
|
620
|
+
data: replaceDataVariables(block.data, variables)
|
|
621
|
+
}));
|
|
622
|
+
}
|
|
623
|
+
function replaceDataVariables(data, variables) {
|
|
624
|
+
const result = {};
|
|
625
|
+
for (const [key, value] of Object.entries(data)) {
|
|
626
|
+
if (typeof value === "string") {
|
|
627
|
+
result[key] = replaceVariables(value, variables);
|
|
628
|
+
} else if (Array.isArray(value)) {
|
|
629
|
+
result[key] = value.map((item) => {
|
|
630
|
+
if (typeof item === "object" && item !== null && "blocks" in item) {
|
|
631
|
+
const col = item;
|
|
632
|
+
const nestedBlocks = Array.isArray(col["blocks"]) ? col["blocks"] : [];
|
|
633
|
+
return { ...col, blocks: replaceBlockVariables(nestedBlocks, variables) };
|
|
634
|
+
}
|
|
635
|
+
return item;
|
|
636
|
+
});
|
|
637
|
+
} else {
|
|
638
|
+
result[key] = value;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return result;
|
|
642
|
+
}
|
|
643
|
+
var init_replace_variables = __esm({
|
|
644
|
+
"libs/email/src/lib/utils/replace-variables.ts"() {
|
|
645
|
+
"use strict";
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
// libs/email/src/lib/utils/sanitize.ts
|
|
650
|
+
function sanitizeAlignment(value) {
|
|
651
|
+
return VALID_ALIGNMENTS.has(value) ? value : "left";
|
|
652
|
+
}
|
|
653
|
+
function sanitizeCssValue(value) {
|
|
654
|
+
return value.replace(/[;{}()"'<>\\]/g, "");
|
|
655
|
+
}
|
|
656
|
+
function sanitizeFontFamily(value) {
|
|
657
|
+
return value.replace(/[;{}()"<>\\]/g, "");
|
|
658
|
+
}
|
|
659
|
+
function sanitizeCssNumber(value, fallback) {
|
|
660
|
+
if (value === null || value === void 0)
|
|
661
|
+
return String(fallback);
|
|
662
|
+
const num = Number(value);
|
|
663
|
+
return Number.isFinite(num) && num >= 0 ? String(num) : String(fallback);
|
|
664
|
+
}
|
|
665
|
+
function sanitizeUrl(url) {
|
|
666
|
+
const trimmed = url.trim();
|
|
667
|
+
if (!trimmed || trimmed === "#")
|
|
668
|
+
return trimmed || "#";
|
|
669
|
+
try {
|
|
670
|
+
const parsed = new URL(trimmed);
|
|
671
|
+
return SAFE_URL_PROTOCOLS.has(parsed.protocol) ? trimmed : "#";
|
|
672
|
+
} catch {
|
|
673
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("#"))
|
|
674
|
+
return trimmed;
|
|
675
|
+
return "#";
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
var VALID_ALIGNMENTS, SAFE_URL_PROTOCOLS;
|
|
679
|
+
var init_sanitize = __esm({
|
|
680
|
+
"libs/email/src/lib/utils/sanitize.ts"() {
|
|
681
|
+
"use strict";
|
|
682
|
+
VALID_ALIGNMENTS = /* @__PURE__ */ new Set(["left", "center", "right"]);
|
|
683
|
+
SAFE_URL_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:"]);
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
// libs/email/src/types.ts
|
|
688
|
+
var DEFAULT_EMAIL_THEME;
|
|
689
|
+
var init_types = __esm({
|
|
690
|
+
"libs/email/src/types.ts"() {
|
|
691
|
+
"use strict";
|
|
692
|
+
DEFAULT_EMAIL_THEME = {
|
|
693
|
+
primaryColor: "#18181b",
|
|
694
|
+
backgroundColor: "#f4f4f5",
|
|
695
|
+
textColor: "#3f3f46",
|
|
696
|
+
mutedColor: "#71717a",
|
|
697
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
|
|
698
|
+
borderRadius: "8px"
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
// libs/email/src/lib/render/render-blocks.ts
|
|
704
|
+
function renderEmailFromBlocks(template, options) {
|
|
705
|
+
const theme = { ...DEFAULT_EMAIL_THEME, ...template.theme };
|
|
706
|
+
const shouldInline = options?.inlineCss ?? true;
|
|
707
|
+
const blocks2 = options?.variables ? replaceBlockVariables(template.blocks, options.variables) : template.blocks;
|
|
708
|
+
const validBlocks = blocks2.filter((block) => {
|
|
709
|
+
if (!isValidBlock(block)) {
|
|
710
|
+
console.warn("[momentum:email] Skipping invalid email block:", block);
|
|
711
|
+
return false;
|
|
712
|
+
}
|
|
713
|
+
return true;
|
|
714
|
+
});
|
|
715
|
+
const blocksHtml = validBlocks.map((block) => renderBlock(block, theme, 0)).join("\n");
|
|
716
|
+
let html = wrapEmailDocument(blocksHtml, theme);
|
|
717
|
+
if (shouldInline) {
|
|
718
|
+
html = inlineCss(html);
|
|
719
|
+
}
|
|
720
|
+
return html;
|
|
721
|
+
}
|
|
722
|
+
function wrapEmailDocument(content, theme) {
|
|
723
|
+
const fontFamily = sanitizeFontFamily(theme.fontFamily);
|
|
724
|
+
const bgColor = sanitizeCssValue(theme.backgroundColor);
|
|
725
|
+
const borderRadius = sanitizeCssValue(theme.borderRadius);
|
|
726
|
+
return `<!DOCTYPE html>
|
|
727
|
+
<html lang="en">
|
|
728
|
+
<head>
|
|
729
|
+
<meta charset="UTF-8">
|
|
730
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
731
|
+
</head>
|
|
732
|
+
<body style="margin: 0; padding: 0; font-family: ${fontFamily}; background-color: ${bgColor}; line-height: 1.6;">
|
|
733
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color: ${bgColor};">
|
|
734
|
+
<tr>
|
|
735
|
+
<td style="padding: 40px 20px;">
|
|
736
|
+
<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);">
|
|
737
|
+
<tr>
|
|
738
|
+
<td style="padding: 40px;">
|
|
739
|
+
${content}
|
|
740
|
+
</td>
|
|
741
|
+
</tr>
|
|
742
|
+
</table>
|
|
743
|
+
</td>
|
|
744
|
+
</tr>
|
|
745
|
+
</table>
|
|
746
|
+
</body>
|
|
747
|
+
</html>`;
|
|
748
|
+
}
|
|
749
|
+
function isValidBlock(block) {
|
|
750
|
+
const rec = block;
|
|
751
|
+
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;
|
|
752
|
+
}
|
|
753
|
+
function renderBlock(block, theme, depth) {
|
|
754
|
+
switch (block.type) {
|
|
755
|
+
case "header":
|
|
756
|
+
return renderHeaderBlock(block.data, theme);
|
|
757
|
+
case "text":
|
|
758
|
+
return renderTextBlock(block.data, theme);
|
|
759
|
+
case "button":
|
|
760
|
+
return renderButtonBlock(block.data, theme);
|
|
761
|
+
case "image":
|
|
762
|
+
return renderImageBlock(block.data);
|
|
763
|
+
case "divider":
|
|
764
|
+
return renderDividerBlock(block.data);
|
|
765
|
+
case "spacer":
|
|
766
|
+
return renderSpacerBlock(block.data);
|
|
767
|
+
case "columns":
|
|
768
|
+
if (depth >= MAX_BLOCK_DEPTH) {
|
|
769
|
+
console.warn("[momentum:email] Max nesting depth reached, skipping columns block");
|
|
770
|
+
return "";
|
|
771
|
+
}
|
|
772
|
+
return renderColumnsBlock(block.data, theme, depth);
|
|
773
|
+
case "footer":
|
|
774
|
+
return renderFooterBlock(block.data, theme);
|
|
775
|
+
default:
|
|
776
|
+
return `<!-- unknown block type: ${escapeHtml2(block.type)} -->`;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
function renderHeaderBlock(data, theme) {
|
|
780
|
+
const title = escapeHtml2(String(data["title"] ?? ""));
|
|
781
|
+
const subtitle = data["subtitle"] ? escapeHtml2(String(data["subtitle"])) : "";
|
|
782
|
+
const alignment = sanitizeAlignment(String(data["alignment"] ?? "left"));
|
|
783
|
+
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>` : ""}`;
|
|
784
|
+
}
|
|
785
|
+
function renderTextBlock(data, theme) {
|
|
786
|
+
const content = escapeHtml2(String(data["content"] ?? ""));
|
|
787
|
+
const fontSize = sanitizeCssNumber(data["fontSize"], 16);
|
|
788
|
+
const color = sanitizeCssValue(String(data["color"] ?? theme.textColor));
|
|
789
|
+
const alignment = sanitizeAlignment(String(data["alignment"] ?? "left"));
|
|
790
|
+
return `<p style="margin: 0 0 16px; font-size: ${fontSize}px; color: ${color}; text-align: ${alignment}; line-height: 1.6;">${content}</p>`;
|
|
791
|
+
}
|
|
792
|
+
function renderButtonBlock(data, theme) {
|
|
793
|
+
const label = escapeHtml2(String(data["label"] ?? "Click here"));
|
|
794
|
+
const href = escapeHtml2(sanitizeUrl(String(data["href"] ?? "#")));
|
|
795
|
+
const bgColor = sanitizeCssValue(String(data["backgroundColor"] ?? theme.primaryColor));
|
|
796
|
+
const color = sanitizeCssValue(String(data["color"] ?? "#ffffff"));
|
|
797
|
+
const alignment = sanitizeAlignment(String(data["alignment"] ?? "left"));
|
|
798
|
+
return `<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
799
|
+
<tr>
|
|
800
|
+
<td style="padding: 0 0 16px;" align="${alignment}">
|
|
801
|
+
<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>
|
|
802
|
+
</td>
|
|
803
|
+
</tr>
|
|
804
|
+
</table>`;
|
|
805
|
+
}
|
|
806
|
+
function renderImageBlock(data) {
|
|
807
|
+
const rawSrc = String(data["src"] ?? "").trim();
|
|
808
|
+
if (!rawSrc)
|
|
809
|
+
return "<!-- image block: no src configured -->";
|
|
810
|
+
const src = escapeHtml2(sanitizeUrl(rawSrc));
|
|
811
|
+
const alt = escapeHtml2(String(data["alt"] ?? ""));
|
|
812
|
+
const width = sanitizeCssValue(String(data["width"] ?? "100%"));
|
|
813
|
+
const img = `<img src="${src}" alt="${alt}" width="${width}" style="display: block; max-width: 100%; height: auto; border: 0;">`;
|
|
814
|
+
if (data["href"]) {
|
|
815
|
+
const href = escapeHtml2(sanitizeUrl(String(data["href"])));
|
|
816
|
+
return `<a href="${href}" style="display: block;">${img}</a>`;
|
|
817
|
+
}
|
|
818
|
+
return img;
|
|
819
|
+
}
|
|
820
|
+
function renderDividerBlock(data) {
|
|
821
|
+
const color = sanitizeCssValue(String(data["color"] ?? "#e4e4e7"));
|
|
822
|
+
const margin = sanitizeCssValue(String(data["margin"] ?? "24px 0"));
|
|
823
|
+
return `<hr style="border: none; border-top: 1px solid ${color}; margin: ${margin};">`;
|
|
824
|
+
}
|
|
825
|
+
function renderSpacerBlock(data) {
|
|
826
|
+
const height = sanitizeCssNumber(data["height"], 24);
|
|
827
|
+
return `<div style="height: ${height}px; line-height: ${height}px; font-size: 1px;"> </div>`;
|
|
828
|
+
}
|
|
829
|
+
function renderColumnsBlock(data, theme, depth) {
|
|
830
|
+
const rawColumns = data["columns"];
|
|
831
|
+
const columns = Array.isArray(rawColumns) ? rawColumns : [];
|
|
832
|
+
const width = Math.floor(100 / (columns.length || 1));
|
|
833
|
+
const tds = columns.map((col) => {
|
|
834
|
+
const colObj = (
|
|
835
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- narrowing unknown column objects
|
|
836
|
+
typeof col === "object" && col !== null ? col : {}
|
|
837
|
+
);
|
|
838
|
+
const rawBlocks = colObj["blocks"];
|
|
839
|
+
const colContent = (Array.isArray(rawBlocks) ? rawBlocks : []).filter(isValidBlock).map((b) => renderBlock(b, theme, depth + 1)).join("\n");
|
|
840
|
+
return `<td style="width: ${width}%; vertical-align: top; padding: 0 8px;">${colContent}</td>`;
|
|
841
|
+
}).join("\n");
|
|
842
|
+
return `<table role="presentation" width="100%" cellspacing="0" cellpadding="0"><tr>${tds}</tr></table>`;
|
|
843
|
+
}
|
|
844
|
+
function renderFooterBlock(data, theme) {
|
|
845
|
+
const text2 = escapeHtml2(String(data["text"] ?? ""));
|
|
846
|
+
const color = sanitizeCssValue(String(data["color"] ?? theme.mutedColor));
|
|
847
|
+
return `<p style="margin: 16px 0 0; font-size: 12px; color: ${color}; text-align: center;">${text2}</p>`;
|
|
848
|
+
}
|
|
849
|
+
var MAX_BLOCK_DEPTH;
|
|
850
|
+
var init_render_blocks = __esm({
|
|
851
|
+
"libs/email/src/lib/render/render-blocks.ts"() {
|
|
852
|
+
"use strict";
|
|
853
|
+
init_escape_html();
|
|
854
|
+
init_css_inliner();
|
|
855
|
+
init_replace_variables();
|
|
856
|
+
init_sanitize();
|
|
857
|
+
init_types();
|
|
858
|
+
MAX_BLOCK_DEPTH = 5;
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
// libs/email/src/lib/templates/default-templates.ts
|
|
863
|
+
var DEFAULT_PASSWORD_RESET_BLOCKS, DEFAULT_VERIFICATION_BLOCKS;
|
|
864
|
+
var init_default_templates = __esm({
|
|
865
|
+
"libs/email/src/lib/templates/default-templates.ts"() {
|
|
866
|
+
"use strict";
|
|
867
|
+
DEFAULT_PASSWORD_RESET_BLOCKS = [
|
|
868
|
+
{
|
|
869
|
+
id: "pr-header",
|
|
870
|
+
type: "header",
|
|
871
|
+
data: {
|
|
872
|
+
title: "Reset Your Password",
|
|
873
|
+
subtitle: "",
|
|
874
|
+
alignment: "left"
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
{
|
|
878
|
+
id: "pr-greeting",
|
|
879
|
+
type: "text",
|
|
880
|
+
data: {
|
|
881
|
+
content: "{{greeting}}",
|
|
882
|
+
fontSize: 16,
|
|
883
|
+
color: "#3f3f46",
|
|
884
|
+
alignment: "left"
|
|
885
|
+
}
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
id: "pr-body",
|
|
889
|
+
type: "text",
|
|
890
|
+
data: {
|
|
891
|
+
content: "We received a request to reset your password. Click the button below to choose a new password:",
|
|
892
|
+
fontSize: 16,
|
|
893
|
+
color: "#3f3f46",
|
|
894
|
+
alignment: "left"
|
|
895
|
+
}
|
|
896
|
+
},
|
|
897
|
+
{
|
|
898
|
+
id: "pr-button",
|
|
899
|
+
type: "button",
|
|
900
|
+
data: {
|
|
901
|
+
label: "Reset Password",
|
|
902
|
+
href: "{{url}}",
|
|
903
|
+
backgroundColor: "#18181b",
|
|
904
|
+
color: "#ffffff",
|
|
905
|
+
alignment: "center"
|
|
906
|
+
}
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
id: "pr-expiry",
|
|
910
|
+
type: "text",
|
|
911
|
+
data: {
|
|
912
|
+
content: "This link will expire in {{expiresIn}}.",
|
|
913
|
+
fontSize: 14,
|
|
914
|
+
color: "#71717a",
|
|
915
|
+
alignment: "left"
|
|
916
|
+
}
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
id: "pr-ignore",
|
|
920
|
+
type: "text",
|
|
921
|
+
data: {
|
|
922
|
+
content: "If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.",
|
|
923
|
+
fontSize: 14,
|
|
924
|
+
color: "#71717a",
|
|
925
|
+
alignment: "left"
|
|
926
|
+
}
|
|
927
|
+
},
|
|
928
|
+
{
|
|
929
|
+
id: "pr-divider",
|
|
930
|
+
type: "divider",
|
|
931
|
+
data: {
|
|
932
|
+
color: "#e4e4e7",
|
|
933
|
+
margin: "24px 0"
|
|
934
|
+
}
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
id: "pr-footer",
|
|
938
|
+
type: "footer",
|
|
939
|
+
data: {
|
|
940
|
+
text: "The {{appName}} Team",
|
|
941
|
+
color: "#71717a"
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
];
|
|
945
|
+
DEFAULT_VERIFICATION_BLOCKS = [
|
|
946
|
+
{
|
|
947
|
+
id: "ev-header",
|
|
948
|
+
type: "header",
|
|
949
|
+
data: {
|
|
950
|
+
title: "Verify Your Email",
|
|
951
|
+
subtitle: "",
|
|
952
|
+
alignment: "left"
|
|
953
|
+
}
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
id: "ev-greeting",
|
|
957
|
+
type: "text",
|
|
958
|
+
data: {
|
|
959
|
+
content: "{{greeting}}",
|
|
960
|
+
fontSize: 16,
|
|
961
|
+
color: "#3f3f46",
|
|
962
|
+
alignment: "left"
|
|
963
|
+
}
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
id: "ev-body",
|
|
967
|
+
type: "text",
|
|
968
|
+
data: {
|
|
969
|
+
content: "Welcome to {{appName}}! Please verify your email address by clicking the button below:",
|
|
970
|
+
fontSize: 16,
|
|
971
|
+
color: "#3f3f46",
|
|
972
|
+
alignment: "left"
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
{
|
|
976
|
+
id: "ev-button",
|
|
977
|
+
type: "button",
|
|
978
|
+
data: {
|
|
979
|
+
label: "Verify Email",
|
|
980
|
+
href: "{{url}}",
|
|
981
|
+
backgroundColor: "#18181b",
|
|
982
|
+
color: "#ffffff",
|
|
983
|
+
alignment: "center"
|
|
984
|
+
}
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
id: "ev-expiry",
|
|
988
|
+
type: "text",
|
|
989
|
+
data: {
|
|
990
|
+
content: "This link will expire in {{expiresIn}}.",
|
|
991
|
+
fontSize: 14,
|
|
992
|
+
color: "#71717a",
|
|
993
|
+
alignment: "left"
|
|
994
|
+
}
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
id: "ev-ignore",
|
|
998
|
+
type: "text",
|
|
999
|
+
data: {
|
|
1000
|
+
content: "If you didn't create an account, you can safely ignore this email.",
|
|
1001
|
+
fontSize: 14,
|
|
1002
|
+
color: "#71717a",
|
|
1003
|
+
alignment: "left"
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
id: "ev-divider",
|
|
1008
|
+
type: "divider",
|
|
1009
|
+
data: {
|
|
1010
|
+
color: "#e4e4e7",
|
|
1011
|
+
margin: "24px 0"
|
|
1012
|
+
}
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
id: "ev-footer",
|
|
1016
|
+
type: "footer",
|
|
1017
|
+
data: {
|
|
1018
|
+
text: "The {{appName}} Team",
|
|
1019
|
+
color: "#71717a"
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
];
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
// libs/email/src/lib/components/eml-body.component.ts
|
|
1027
|
+
import { Component, ChangeDetectionStrategy, input, computed } from "@angular/core";
|
|
1028
|
+
var EmlBody;
|
|
1029
|
+
var init_eml_body_component = __esm({
|
|
1030
|
+
"libs/email/src/lib/components/eml-body.component.ts"() {
|
|
1031
|
+
"use strict";
|
|
1032
|
+
EmlBody = class {
|
|
1033
|
+
constructor() {
|
|
1034
|
+
this.backgroundColor = input("#f4f4f5");
|
|
1035
|
+
this.fontFamily = input(
|
|
1036
|
+
"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
|
|
1037
|
+
);
|
|
1038
|
+
this.padding = input("40px 20px");
|
|
1039
|
+
this.tableStyle = computed(
|
|
1040
|
+
() => `background-color: ${this.backgroundColor()}; font-family: ${this.fontFamily()}; line-height: 1.6; margin: 0; padding: 0;`
|
|
1041
|
+
);
|
|
1042
|
+
this.cellStyle = computed(() => `padding: ${this.padding()};`);
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
EmlBody = __decorateClass([
|
|
1046
|
+
Component({
|
|
1047
|
+
selector: "eml-body",
|
|
1048
|
+
template: `
|
|
1049
|
+
<table
|
|
1050
|
+
role="presentation"
|
|
1051
|
+
width="100%"
|
|
1052
|
+
cellspacing="0"
|
|
1053
|
+
cellpadding="0"
|
|
1054
|
+
[attr.style]="tableStyle()"
|
|
1055
|
+
>
|
|
1056
|
+
<tr>
|
|
1057
|
+
<td [attr.style]="cellStyle()">
|
|
1058
|
+
<ng-content />
|
|
1059
|
+
</td>
|
|
1060
|
+
</tr>
|
|
1061
|
+
</table>
|
|
1062
|
+
`,
|
|
1063
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
1064
|
+
})
|
|
1065
|
+
], EmlBody);
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
// libs/email/src/lib/components/eml-container.component.ts
|
|
1070
|
+
import { Component as Component2, ChangeDetectionStrategy as ChangeDetectionStrategy2, input as input2, computed as computed2 } from "@angular/core";
|
|
1071
|
+
var EmlContainer;
|
|
1072
|
+
var init_eml_container_component = __esm({
|
|
1073
|
+
"libs/email/src/lib/components/eml-container.component.ts"() {
|
|
1074
|
+
"use strict";
|
|
1075
|
+
EmlContainer = class {
|
|
1076
|
+
constructor() {
|
|
1077
|
+
this.maxWidth = input2("480px");
|
|
1078
|
+
this.backgroundColor = input2("#ffffff");
|
|
1079
|
+
this.borderRadius = input2("8px");
|
|
1080
|
+
this.padding = input2("40px");
|
|
1081
|
+
this.shadow = input2("0 1px 3px rgba(0,0,0,0.1)");
|
|
1082
|
+
this.tableStyle = computed2(
|
|
1083
|
+
() => `max-width: ${this.maxWidth()}; margin: 0 auto; background-color: ${this.backgroundColor()}; border-radius: ${this.borderRadius()}; box-shadow: ${this.shadow()};`
|
|
1084
|
+
);
|
|
1085
|
+
this.cellStyle = computed2(() => `padding: ${this.padding()};`);
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
EmlContainer = __decorateClass([
|
|
1089
|
+
Component2({
|
|
1090
|
+
selector: "eml-container",
|
|
1091
|
+
template: `
|
|
1092
|
+
<table
|
|
1093
|
+
role="presentation"
|
|
1094
|
+
width="100%"
|
|
1095
|
+
cellspacing="0"
|
|
1096
|
+
cellpadding="0"
|
|
1097
|
+
[attr.style]="tableStyle()"
|
|
1098
|
+
>
|
|
1099
|
+
<tr>
|
|
1100
|
+
<td [attr.style]="cellStyle()">
|
|
1101
|
+
<ng-content />
|
|
1102
|
+
</td>
|
|
1103
|
+
</tr>
|
|
1104
|
+
</table>
|
|
1105
|
+
`,
|
|
1106
|
+
changeDetection: ChangeDetectionStrategy2.OnPush
|
|
1107
|
+
})
|
|
1108
|
+
], EmlContainer);
|
|
1109
|
+
}
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1112
|
+
// libs/email/src/lib/components/eml-section.component.ts
|
|
1113
|
+
import { Component as Component3, ChangeDetectionStrategy as ChangeDetectionStrategy3, input as input3, computed as computed3 } from "@angular/core";
|
|
1114
|
+
var EmlSection;
|
|
1115
|
+
var init_eml_section_component = __esm({
|
|
1116
|
+
"libs/email/src/lib/components/eml-section.component.ts"() {
|
|
1117
|
+
"use strict";
|
|
1118
|
+
EmlSection = class {
|
|
1119
|
+
constructor() {
|
|
1120
|
+
this.padding = input3("0");
|
|
1121
|
+
this.cellStyle = computed3(() => `padding: ${this.padding()};`);
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
EmlSection = __decorateClass([
|
|
1125
|
+
Component3({
|
|
1126
|
+
selector: "eml-section",
|
|
1127
|
+
template: `
|
|
1128
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
1129
|
+
<tr>
|
|
1130
|
+
<td [attr.style]="cellStyle()">
|
|
1131
|
+
<ng-content />
|
|
1132
|
+
</td>
|
|
1133
|
+
</tr>
|
|
1134
|
+
</table>
|
|
1135
|
+
`,
|
|
1136
|
+
changeDetection: ChangeDetectionStrategy3.OnPush
|
|
1137
|
+
})
|
|
1138
|
+
], EmlSection);
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
|
|
1142
|
+
// libs/email/src/lib/components/eml-row.component.ts
|
|
1143
|
+
import { Component as Component4, ChangeDetectionStrategy as ChangeDetectionStrategy4 } from "@angular/core";
|
|
1144
|
+
var EmlRow;
|
|
1145
|
+
var init_eml_row_component = __esm({
|
|
1146
|
+
"libs/email/src/lib/components/eml-row.component.ts"() {
|
|
1147
|
+
"use strict";
|
|
1148
|
+
EmlRow = class {
|
|
1149
|
+
};
|
|
1150
|
+
EmlRow = __decorateClass([
|
|
1151
|
+
Component4({
|
|
1152
|
+
selector: "eml-row",
|
|
1153
|
+
template: `
|
|
1154
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
1155
|
+
<tr>
|
|
1156
|
+
<ng-content />
|
|
1157
|
+
</tr>
|
|
1158
|
+
</table>
|
|
1159
|
+
`,
|
|
1160
|
+
changeDetection: ChangeDetectionStrategy4.OnPush
|
|
1161
|
+
})
|
|
1162
|
+
], EmlRow);
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
// libs/email/src/lib/components/eml-column.component.ts
|
|
1167
|
+
import { Component as Component5, ChangeDetectionStrategy as ChangeDetectionStrategy5, input as input4, computed as computed4 } from "@angular/core";
|
|
1168
|
+
var EmlColumn;
|
|
1169
|
+
var init_eml_column_component = __esm({
|
|
1170
|
+
"libs/email/src/lib/components/eml-column.component.ts"() {
|
|
1171
|
+
"use strict";
|
|
1172
|
+
EmlColumn = class {
|
|
1173
|
+
constructor() {
|
|
1174
|
+
this.width = input4(void 0);
|
|
1175
|
+
this.padding = input4("0");
|
|
1176
|
+
this.verticalAlign = input4("top");
|
|
1177
|
+
this.cellStyle = computed4(
|
|
1178
|
+
() => `padding: ${this.padding()}; vertical-align: ${this.verticalAlign()};`
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
EmlColumn = __decorateClass([
|
|
1183
|
+
Component5({
|
|
1184
|
+
selector: "eml-column",
|
|
1185
|
+
template: `
|
|
1186
|
+
<td [attr.style]="cellStyle()" [attr.width]="width()" valign="top">
|
|
1187
|
+
<ng-content />
|
|
1188
|
+
</td>
|
|
1189
|
+
`,
|
|
1190
|
+
changeDetection: ChangeDetectionStrategy5.OnPush
|
|
1191
|
+
})
|
|
1192
|
+
], EmlColumn);
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
// libs/email/src/lib/components/eml-text.component.ts
|
|
1197
|
+
import { Component as Component6, ChangeDetectionStrategy as ChangeDetectionStrategy6, input as input5, computed as computed5 } from "@angular/core";
|
|
1198
|
+
var EmlText;
|
|
1199
|
+
var init_eml_text_component = __esm({
|
|
1200
|
+
"libs/email/src/lib/components/eml-text.component.ts"() {
|
|
1201
|
+
"use strict";
|
|
1202
|
+
EmlText = class {
|
|
1203
|
+
constructor() {
|
|
1204
|
+
this.color = input5("#3f3f46");
|
|
1205
|
+
this.fontSize = input5("16px");
|
|
1206
|
+
this.lineHeight = input5("1.6");
|
|
1207
|
+
this.margin = input5("0 0 16px");
|
|
1208
|
+
this.textAlign = input5("left");
|
|
1209
|
+
this.pStyle = computed5(
|
|
1210
|
+
() => `margin: ${this.margin()}; color: ${this.color()}; font-size: ${this.fontSize()}; line-height: ${this.lineHeight()}; text-align: ${this.textAlign()};`
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
EmlText = __decorateClass([
|
|
1215
|
+
Component6({
|
|
1216
|
+
selector: "eml-text",
|
|
1217
|
+
template: `
|
|
1218
|
+
<p [attr.style]="pStyle()">
|
|
1219
|
+
<ng-content />
|
|
1220
|
+
</p>
|
|
1221
|
+
`,
|
|
1222
|
+
changeDetection: ChangeDetectionStrategy6.OnPush
|
|
1223
|
+
})
|
|
1224
|
+
], EmlText);
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
|
|
1228
|
+
// libs/email/src/lib/components/eml-heading.component.ts
|
|
1229
|
+
import { Component as Component7, ChangeDetectionStrategy as ChangeDetectionStrategy7, input as input6, computed as computed6 } from "@angular/core";
|
|
1230
|
+
var EmlHeading;
|
|
1231
|
+
var init_eml_heading_component = __esm({
|
|
1232
|
+
"libs/email/src/lib/components/eml-heading.component.ts"() {
|
|
1233
|
+
"use strict";
|
|
1234
|
+
EmlHeading = class {
|
|
1235
|
+
constructor() {
|
|
1236
|
+
this.level = input6(1);
|
|
1237
|
+
this.color = input6("#18181b");
|
|
1238
|
+
this.margin = input6("0 0 24px");
|
|
1239
|
+
this.textAlign = input6("left");
|
|
1240
|
+
this.fontSizeMap = {
|
|
1241
|
+
1: "24px",
|
|
1242
|
+
2: "20px",
|
|
1243
|
+
3: "16px"
|
|
1244
|
+
};
|
|
1245
|
+
this.headingStyle = computed6(
|
|
1246
|
+
() => `margin: ${this.margin()}; font-size: ${this.fontSizeMap[this.level()] ?? "24px"}; font-weight: 600; color: ${this.color()}; text-align: ${this.textAlign()};`
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
EmlHeading = __decorateClass([
|
|
1251
|
+
Component7({
|
|
1252
|
+
selector: "eml-heading",
|
|
1253
|
+
template: `<div [attr.style]="headingStyle()" [attr.role]="'heading'" [attr.aria-level]="level()">
|
|
1254
|
+
<ng-content />
|
|
1255
|
+
</div>`,
|
|
1256
|
+
changeDetection: ChangeDetectionStrategy7.OnPush
|
|
1257
|
+
})
|
|
1258
|
+
], EmlHeading);
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
|
|
1262
|
+
// libs/email/src/lib/components/eml-button.component.ts
|
|
1263
|
+
import { Component as Component8, ChangeDetectionStrategy as ChangeDetectionStrategy8, input as input7, computed as computed7 } from "@angular/core";
|
|
1264
|
+
var EmlButton;
|
|
1265
|
+
var init_eml_button_component = __esm({
|
|
1266
|
+
"libs/email/src/lib/components/eml-button.component.ts"() {
|
|
1267
|
+
"use strict";
|
|
1268
|
+
EmlButton = class {
|
|
1269
|
+
constructor() {
|
|
1270
|
+
this.href = input7("");
|
|
1271
|
+
this.backgroundColor = input7("#18181b");
|
|
1272
|
+
this.color = input7("#ffffff");
|
|
1273
|
+
this.borderRadius = input7("6px");
|
|
1274
|
+
this.padding = input7("12px 24px");
|
|
1275
|
+
this.textAlign = input7("left");
|
|
1276
|
+
this.alignStyle = computed7(() => `padding: 0; text-align: ${this.textAlign()};`);
|
|
1277
|
+
this.linkStyle = computed7(
|
|
1278
|
+
() => `display: inline-block; padding: ${this.padding()}; background-color: ${this.backgroundColor()}; color: ${this.color()}; text-decoration: none; border-radius: ${this.borderRadius()}; font-weight: 500;`
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
EmlButton = __decorateClass([
|
|
1283
|
+
Component8({
|
|
1284
|
+
selector: "eml-button",
|
|
1285
|
+
template: `
|
|
1286
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
1287
|
+
<tr>
|
|
1288
|
+
<td [attr.style]="alignStyle()">
|
|
1289
|
+
<a [attr.href]="href()" [attr.style]="linkStyle()" target="_blank">
|
|
1290
|
+
<ng-content />
|
|
1291
|
+
</a>
|
|
1292
|
+
</td>
|
|
1293
|
+
</tr>
|
|
1294
|
+
</table>
|
|
1295
|
+
`,
|
|
1296
|
+
changeDetection: ChangeDetectionStrategy8.OnPush
|
|
1297
|
+
})
|
|
1298
|
+
], EmlButton);
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
// libs/email/src/lib/components/eml-link.component.ts
|
|
1303
|
+
import { Component as Component9, ChangeDetectionStrategy as ChangeDetectionStrategy9, input as input8, computed as computed8 } from "@angular/core";
|
|
1304
|
+
var EmlLink;
|
|
1305
|
+
var init_eml_link_component = __esm({
|
|
1306
|
+
"libs/email/src/lib/components/eml-link.component.ts"() {
|
|
1307
|
+
"use strict";
|
|
1308
|
+
EmlLink = class {
|
|
1309
|
+
constructor() {
|
|
1310
|
+
this.href = input8("");
|
|
1311
|
+
this.color = input8("#18181b");
|
|
1312
|
+
this.textDecoration = input8("underline");
|
|
1313
|
+
this.linkStyle = computed8(
|
|
1314
|
+
() => `color: ${this.color()}; text-decoration: ${this.textDecoration()};`
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
EmlLink = __decorateClass([
|
|
1319
|
+
Component9({
|
|
1320
|
+
selector: "eml-link",
|
|
1321
|
+
template: `
|
|
1322
|
+
<a [attr.href]="href()" [attr.style]="linkStyle()" target="_blank">
|
|
1323
|
+
<ng-content />
|
|
1324
|
+
</a>
|
|
1325
|
+
`,
|
|
1326
|
+
changeDetection: ChangeDetectionStrategy9.OnPush
|
|
1327
|
+
})
|
|
1328
|
+
], EmlLink);
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
|
|
1332
|
+
// libs/email/src/lib/components/eml-image.component.ts
|
|
1333
|
+
import { Component as Component10, ChangeDetectionStrategy as ChangeDetectionStrategy10, input as input9, computed as computed9 } from "@angular/core";
|
|
1334
|
+
var EmlImage;
|
|
1335
|
+
var init_eml_image_component = __esm({
|
|
1336
|
+
"libs/email/src/lib/components/eml-image.component.ts"() {
|
|
1337
|
+
"use strict";
|
|
1338
|
+
EmlImage = class {
|
|
1339
|
+
constructor() {
|
|
1340
|
+
this.src = input9("");
|
|
1341
|
+
this.alt = input9("");
|
|
1342
|
+
this.width = input9(void 0);
|
|
1343
|
+
this.height = input9(void 0);
|
|
1344
|
+
this.borderRadius = input9("0");
|
|
1345
|
+
this.imgStyle = computed9(
|
|
1346
|
+
() => `display: block; max-width: 100%; border: 0; outline: none; border-radius: ${this.borderRadius()};`
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
EmlImage = __decorateClass([
|
|
1351
|
+
Component10({
|
|
1352
|
+
selector: "eml-image",
|
|
1353
|
+
template: `
|
|
1354
|
+
<img
|
|
1355
|
+
[attr.src]="src()"
|
|
1356
|
+
[attr.alt]="alt()"
|
|
1357
|
+
[attr.width]="width()"
|
|
1358
|
+
[attr.height]="height()"
|
|
1359
|
+
[attr.style]="imgStyle()"
|
|
1360
|
+
/>
|
|
1361
|
+
`,
|
|
1362
|
+
changeDetection: ChangeDetectionStrategy10.OnPush
|
|
1363
|
+
})
|
|
1364
|
+
], EmlImage);
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
// libs/email/src/lib/components/eml-divider.component.ts
|
|
1369
|
+
import { Component as Component11, ChangeDetectionStrategy as ChangeDetectionStrategy11, input as input10, computed as computed10 } from "@angular/core";
|
|
1370
|
+
var EmlDivider;
|
|
1371
|
+
var init_eml_divider_component = __esm({
|
|
1372
|
+
"libs/email/src/lib/components/eml-divider.component.ts"() {
|
|
1373
|
+
"use strict";
|
|
1374
|
+
EmlDivider = class {
|
|
1375
|
+
constructor() {
|
|
1376
|
+
this.color = input10("#e4e4e7");
|
|
1377
|
+
this.margin = input10("24px 0");
|
|
1378
|
+
this.hrStyle = computed10(
|
|
1379
|
+
() => `border: none; border-top: 1px solid ${this.color()}; margin: ${this.margin()};`
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
EmlDivider = __decorateClass([
|
|
1384
|
+
Component11({
|
|
1385
|
+
selector: "eml-divider",
|
|
1386
|
+
template: `<hr [attr.style]="hrStyle()" />`,
|
|
1387
|
+
changeDetection: ChangeDetectionStrategy11.OnPush
|
|
1388
|
+
})
|
|
1389
|
+
], EmlDivider);
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
// libs/email/src/lib/components/eml-preview.component.ts
|
|
1394
|
+
import { Component as Component12, ChangeDetectionStrategy as ChangeDetectionStrategy12 } from "@angular/core";
|
|
1395
|
+
var EmlPreview;
|
|
1396
|
+
var init_eml_preview_component = __esm({
|
|
1397
|
+
"libs/email/src/lib/components/eml-preview.component.ts"() {
|
|
1398
|
+
"use strict";
|
|
1399
|
+
EmlPreview = class {
|
|
1400
|
+
};
|
|
1401
|
+
EmlPreview = __decorateClass([
|
|
1402
|
+
Component12({
|
|
1403
|
+
selector: "eml-preview",
|
|
1404
|
+
template: `
|
|
1405
|
+
<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">
|
|
1406
|
+
<ng-content />
|
|
1407
|
+
</div>
|
|
1408
|
+
`,
|
|
1409
|
+
changeDetection: ChangeDetectionStrategy12.OnPush
|
|
1410
|
+
})
|
|
1411
|
+
], EmlPreview);
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1415
|
+
// libs/email/src/lib/components/eml-spacer.component.ts
|
|
1416
|
+
import { Component as Component13, ChangeDetectionStrategy as ChangeDetectionStrategy13, input as input11, computed as computed11 } from "@angular/core";
|
|
1417
|
+
var EmlSpacer;
|
|
1418
|
+
var init_eml_spacer_component = __esm({
|
|
1419
|
+
"libs/email/src/lib/components/eml-spacer.component.ts"() {
|
|
1420
|
+
"use strict";
|
|
1421
|
+
EmlSpacer = class {
|
|
1422
|
+
constructor() {
|
|
1423
|
+
this.height = input11("24px");
|
|
1424
|
+
this.spacerStyle = computed11(
|
|
1425
|
+
() => `height: ${this.height()}; line-height: ${this.height()}; font-size: 1px;`
|
|
1426
|
+
);
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
EmlSpacer = __decorateClass([
|
|
1430
|
+
Component13({
|
|
1431
|
+
selector: "eml-spacer",
|
|
1432
|
+
template: `<div [attr.style]="spacerStyle()"></div>`,
|
|
1433
|
+
changeDetection: ChangeDetectionStrategy13.OnPush
|
|
1434
|
+
})
|
|
1435
|
+
], EmlSpacer);
|
|
1436
|
+
}
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
// libs/email/src/lib/components/eml-footer.component.ts
|
|
1440
|
+
import { Component as Component14, ChangeDetectionStrategy as ChangeDetectionStrategy14, input as input12, computed as computed12 } from "@angular/core";
|
|
1441
|
+
var EmlFooter;
|
|
1442
|
+
var init_eml_footer_component = __esm({
|
|
1443
|
+
"libs/email/src/lib/components/eml-footer.component.ts"() {
|
|
1444
|
+
"use strict";
|
|
1445
|
+
EmlFooter = class {
|
|
1446
|
+
constructor() {
|
|
1447
|
+
this.maxWidth = input12("480px");
|
|
1448
|
+
this.color = input12("#71717a");
|
|
1449
|
+
this.fontSize = input12("12px");
|
|
1450
|
+
this.textAlign = input12("center");
|
|
1451
|
+
this.padding = input12("20px 0 0");
|
|
1452
|
+
this.tableStyle = computed12(() => `max-width: ${this.maxWidth()}; margin: 0 auto;`);
|
|
1453
|
+
this.cellStyle = computed12(
|
|
1454
|
+
() => `text-align: ${this.textAlign()}; color: ${this.color()}; font-size: ${this.fontSize()}; padding: ${this.padding()};`
|
|
1455
|
+
);
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
EmlFooter = __decorateClass([
|
|
1459
|
+
Component14({
|
|
1460
|
+
selector: "eml-footer",
|
|
1461
|
+
template: `
|
|
1462
|
+
<table
|
|
1463
|
+
role="presentation"
|
|
1464
|
+
width="100%"
|
|
1465
|
+
cellspacing="0"
|
|
1466
|
+
cellpadding="0"
|
|
1467
|
+
[attr.style]="tableStyle()"
|
|
1468
|
+
>
|
|
1469
|
+
<tr>
|
|
1470
|
+
<td [attr.style]="cellStyle()">
|
|
1471
|
+
<ng-content />
|
|
1472
|
+
</td>
|
|
1473
|
+
</tr>
|
|
1474
|
+
</table>
|
|
1475
|
+
`,
|
|
1476
|
+
changeDetection: ChangeDetectionStrategy14.OnPush
|
|
1477
|
+
})
|
|
1478
|
+
], EmlFooter);
|
|
1479
|
+
}
|
|
1480
|
+
});
|
|
1481
|
+
|
|
1482
|
+
// libs/email/src/lib/utils/blocks-to-plain-text.ts
|
|
1483
|
+
function blocksToPlainText(blocks2, depth = 0) {
|
|
1484
|
+
const lines = [];
|
|
1485
|
+
for (const block of blocks2) {
|
|
1486
|
+
const text2 = blockToText(block, depth);
|
|
1487
|
+
if (text2) {
|
|
1488
|
+
lines.push(text2);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
return lines.join("\n\n");
|
|
1492
|
+
}
|
|
1493
|
+
function blockToText(block, depth) {
|
|
1494
|
+
switch (block.type) {
|
|
1495
|
+
case "header":
|
|
1496
|
+
return headerToText(block.data);
|
|
1497
|
+
case "text":
|
|
1498
|
+
return String(block.data["content"] ?? "");
|
|
1499
|
+
case "button":
|
|
1500
|
+
return buttonToText(block.data);
|
|
1501
|
+
case "footer":
|
|
1502
|
+
return String(block.data["text"] ?? "");
|
|
1503
|
+
case "columns":
|
|
1504
|
+
if (depth >= MAX_BLOCK_DEPTH2) {
|
|
1505
|
+
console.warn("[momentum:email] Max nesting depth reached, skipping columns block");
|
|
1506
|
+
return "";
|
|
1507
|
+
}
|
|
1508
|
+
return columnsToText(block.data, depth);
|
|
1509
|
+
case "divider":
|
|
1510
|
+
case "spacer":
|
|
1511
|
+
case "image":
|
|
1512
|
+
return "";
|
|
1513
|
+
default:
|
|
1514
|
+
return "";
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
function headerToText(data) {
|
|
1518
|
+
const title = String(data["title"] ?? "");
|
|
1519
|
+
const subtitle = data["subtitle"] ? String(data["subtitle"]) : "";
|
|
1520
|
+
if (!title && !subtitle)
|
|
1521
|
+
return "";
|
|
1522
|
+
if (!subtitle)
|
|
1523
|
+
return title;
|
|
1524
|
+
return `${title}
|
|
1525
|
+
${subtitle}`;
|
|
1526
|
+
}
|
|
1527
|
+
function buttonToText(data) {
|
|
1528
|
+
const label = String(data["label"] ?? "");
|
|
1529
|
+
const href = data["href"] ? String(data["href"]) : "";
|
|
1530
|
+
if (!label)
|
|
1531
|
+
return "";
|
|
1532
|
+
if (!href)
|
|
1533
|
+
return label;
|
|
1534
|
+
return `${label}: ${href}`;
|
|
1535
|
+
}
|
|
1536
|
+
function columnsToText(data, depth) {
|
|
1537
|
+
const columns = data["columns"];
|
|
1538
|
+
if (!Array.isArray(columns))
|
|
1539
|
+
return "";
|
|
1540
|
+
const parts = [];
|
|
1541
|
+
for (const col of columns) {
|
|
1542
|
+
if (col && typeof col === "object" && Array.isArray(col.blocks)) {
|
|
1543
|
+
const colText = blocksToPlainText(col.blocks, depth + 1);
|
|
1544
|
+
if (colText) {
|
|
1545
|
+
parts.push(colText);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
return parts.join("\n\n");
|
|
1550
|
+
}
|
|
1551
|
+
var MAX_BLOCK_DEPTH2;
|
|
1552
|
+
var init_blocks_to_plain_text = __esm({
|
|
1553
|
+
"libs/email/src/lib/utils/blocks-to-plain-text.ts"() {
|
|
1554
|
+
"use strict";
|
|
1555
|
+
MAX_BLOCK_DEPTH2 = 5;
|
|
1556
|
+
}
|
|
1557
|
+
});
|
|
1558
|
+
|
|
1559
|
+
// libs/email/src/index.ts
|
|
1560
|
+
var src_exports2 = {};
|
|
1561
|
+
__export(src_exports2, {
|
|
1562
|
+
DEFAULT_EMAIL_THEME: () => DEFAULT_EMAIL_THEME,
|
|
1563
|
+
DEFAULT_PASSWORD_RESET_BLOCKS: () => DEFAULT_PASSWORD_RESET_BLOCKS,
|
|
1564
|
+
DEFAULT_VERIFICATION_BLOCKS: () => DEFAULT_VERIFICATION_BLOCKS,
|
|
1565
|
+
EMAIL_DATA: () => EMAIL_DATA,
|
|
1566
|
+
EmlBody: () => EmlBody,
|
|
1567
|
+
EmlButton: () => EmlButton,
|
|
1568
|
+
EmlColumn: () => EmlColumn,
|
|
1569
|
+
EmlContainer: () => EmlContainer,
|
|
1570
|
+
EmlDivider: () => EmlDivider,
|
|
1571
|
+
EmlFooter: () => EmlFooter,
|
|
1572
|
+
EmlHeading: () => EmlHeading,
|
|
1573
|
+
EmlImage: () => EmlImage,
|
|
1574
|
+
EmlLink: () => EmlLink,
|
|
1575
|
+
EmlPreview: () => EmlPreview,
|
|
1576
|
+
EmlRow: () => EmlRow,
|
|
1577
|
+
EmlSection: () => EmlSection,
|
|
1578
|
+
EmlSpacer: () => EmlSpacer,
|
|
1579
|
+
EmlText: () => EmlText,
|
|
1580
|
+
blocksToPlainText: () => blocksToPlainText,
|
|
1581
|
+
escapeHtml: () => escapeHtml2,
|
|
1582
|
+
injectEmailData: () => injectEmailData,
|
|
1583
|
+
inlineCss: () => inlineCss,
|
|
1584
|
+
isValidBlock: () => isValidBlock,
|
|
1585
|
+
renderEmail: () => renderEmail,
|
|
1586
|
+
renderEmailFromBlocks: () => renderEmailFromBlocks,
|
|
1587
|
+
replaceBlockVariables: () => replaceBlockVariables,
|
|
1588
|
+
replaceVariables: () => replaceVariables,
|
|
1589
|
+
sanitizeAlignment: () => sanitizeAlignment,
|
|
1590
|
+
sanitizeCssNumber: () => sanitizeCssNumber,
|
|
1591
|
+
sanitizeCssValue: () => sanitizeCssValue,
|
|
1592
|
+
sanitizeFontFamily: () => sanitizeFontFamily,
|
|
1593
|
+
sanitizeUrl: () => sanitizeUrl
|
|
1594
|
+
});
|
|
1595
|
+
var init_src2 = __esm({
|
|
1596
|
+
"libs/email/src/index.ts"() {
|
|
1597
|
+
"use strict";
|
|
1598
|
+
init_render_email();
|
|
1599
|
+
init_render_blocks();
|
|
1600
|
+
init_render_types();
|
|
1601
|
+
init_replace_variables();
|
|
1602
|
+
init_default_templates();
|
|
1603
|
+
init_eml_body_component();
|
|
1604
|
+
init_eml_container_component();
|
|
1605
|
+
init_eml_section_component();
|
|
1606
|
+
init_eml_row_component();
|
|
1607
|
+
init_eml_column_component();
|
|
1608
|
+
init_eml_text_component();
|
|
1609
|
+
init_eml_heading_component();
|
|
1610
|
+
init_eml_button_component();
|
|
1611
|
+
init_eml_link_component();
|
|
1612
|
+
init_eml_image_component();
|
|
1613
|
+
init_eml_divider_component();
|
|
1614
|
+
init_eml_preview_component();
|
|
1615
|
+
init_eml_spacer_component();
|
|
1616
|
+
init_eml_footer_component();
|
|
1617
|
+
init_escape_html();
|
|
1618
|
+
init_css_inliner();
|
|
1619
|
+
init_sanitize();
|
|
1620
|
+
init_blocks_to_plain_text();
|
|
1621
|
+
init_render_blocks();
|
|
1622
|
+
init_types();
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
|
|
511
1626
|
// libs/server-express/src/lib/server-express.ts
|
|
512
1627
|
import { Router, json as jsonParser } from "express";
|
|
513
1628
|
import multer from "multer";
|
|
@@ -985,6 +2100,31 @@ function validateRowCount(name, label, count, minRows, maxRows, errors) {
|
|
|
985
2100
|
}
|
|
986
2101
|
|
|
987
2102
|
// libs/core/src/lib/collections/media.collection.ts
|
|
2103
|
+
var validateFocalPoint = (value) => {
|
|
2104
|
+
if (value === null || value === void 0)
|
|
2105
|
+
return true;
|
|
2106
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
2107
|
+
return "Focal point must be an object with x and y coordinates";
|
|
2108
|
+
}
|
|
2109
|
+
const fp = Object.fromEntries(Object.entries(value));
|
|
2110
|
+
if (!("x" in fp) || !("y" in fp)) {
|
|
2111
|
+
return "Focal point must have both x and y properties";
|
|
2112
|
+
}
|
|
2113
|
+
const { x, y } = fp;
|
|
2114
|
+
if (typeof x !== "number" || !Number.isFinite(x)) {
|
|
2115
|
+
return "Focal point x must be a finite number";
|
|
2116
|
+
}
|
|
2117
|
+
if (typeof y !== "number" || !Number.isFinite(y)) {
|
|
2118
|
+
return "Focal point y must be a finite number";
|
|
2119
|
+
}
|
|
2120
|
+
if (x < 0 || x > 1) {
|
|
2121
|
+
return `Focal point x must be between 0 and 1 (received ${x})`;
|
|
2122
|
+
}
|
|
2123
|
+
if (y < 0 || y > 1) {
|
|
2124
|
+
return `Focal point y must be between 0 and 1 (received ${y})`;
|
|
2125
|
+
}
|
|
2126
|
+
return true;
|
|
2127
|
+
};
|
|
988
2128
|
var MediaCollection = defineCollection({
|
|
989
2129
|
slug: "media",
|
|
990
2130
|
labels: {
|
|
@@ -1039,6 +2179,14 @@ var MediaCollection = defineCollection({
|
|
|
1039
2179
|
json("focalPoint", {
|
|
1040
2180
|
label: "Focal Point",
|
|
1041
2181
|
description: "Focal point coordinates for image cropping",
|
|
2182
|
+
validate: validateFocalPoint,
|
|
2183
|
+
admin: {
|
|
2184
|
+
hidden: true
|
|
2185
|
+
}
|
|
2186
|
+
}),
|
|
2187
|
+
json("sizes", {
|
|
2188
|
+
label: "Image Sizes",
|
|
2189
|
+
description: "Generated image size variants",
|
|
1042
2190
|
admin: {
|
|
1043
2191
|
hidden: true
|
|
1044
2192
|
}
|
|
@@ -1897,6 +3045,15 @@ function deepEqual(a, b) {
|
|
|
1897
3045
|
(key) => Object.prototype.hasOwnProperty.call(bRec, key) && deepEqual(aRec[key], bRec[key])
|
|
1898
3046
|
);
|
|
1899
3047
|
}
|
|
3048
|
+
function stripTransientKeys(data) {
|
|
3049
|
+
const result = {};
|
|
3050
|
+
for (const [key, value] of Object.entries(data)) {
|
|
3051
|
+
if (!key.startsWith("_")) {
|
|
3052
|
+
result[key] = value;
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
return result;
|
|
3056
|
+
}
|
|
1900
3057
|
function flattenWhereClause(where) {
|
|
1901
3058
|
if (!where)
|
|
1902
3059
|
return {};
|
|
@@ -2060,6 +3217,7 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2060
3217
|
);
|
|
2061
3218
|
}
|
|
2062
3219
|
processedData = await this.runHooks("beforeChange", processedData, "create");
|
|
3220
|
+
processedData = stripTransientKeys(processedData);
|
|
2063
3221
|
const doc = await this.adapter.create(this.slug, processedData);
|
|
2064
3222
|
await this.runHooks("afterChange", doc, "create");
|
|
2065
3223
|
if (hasFieldHooks(this.collectionConfig.fields)) {
|
|
@@ -2120,6 +3278,7 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2120
3278
|
);
|
|
2121
3279
|
}
|
|
2122
3280
|
processedData = await this.runHooks("beforeChange", processedData, "update", originalDoc);
|
|
3281
|
+
processedData = stripTransientKeys(processedData);
|
|
2123
3282
|
const doc = await this.adapter.update(this.slug, id, processedData);
|
|
2124
3283
|
await this.runHooks("afterChange", doc, "update", originalDoc);
|
|
2125
3284
|
if (hasFieldHooks(this.collectionConfig.fields)) {
|
|
@@ -4247,7 +5406,8 @@ async function handleUpload(config, request) {
|
|
|
4247
5406
|
filesize: file.size,
|
|
4248
5407
|
path: storedFile.path,
|
|
4249
5408
|
url: storedFile.url,
|
|
4250
|
-
alt: alt ?? ""
|
|
5409
|
+
alt: alt ?? "",
|
|
5410
|
+
_file: file
|
|
4251
5411
|
};
|
|
4252
5412
|
const api = getMomentumAPI().setContext({ user });
|
|
4253
5413
|
const doc = await api.collection(collection).create(mediaData);
|
|
@@ -4314,7 +5474,8 @@ async function handleCollectionUpload(globalConfig, request) {
|
|
|
4314
5474
|
mimeType: file.mimeType,
|
|
4315
5475
|
filesize: file.size,
|
|
4316
5476
|
path: storedFile.path,
|
|
4317
|
-
url: storedFile.url
|
|
5477
|
+
url: storedFile.url,
|
|
5478
|
+
_file: file
|
|
4318
5479
|
};
|
|
4319
5480
|
const api = getMomentumAPI().setContext({ user });
|
|
4320
5481
|
const doc = await api.collection(collectionSlug).create(docData);
|
|
@@ -4351,6 +5512,7 @@ function getMimeTypeFromPath(path) {
|
|
|
4351
5512
|
png: "image/png",
|
|
4352
5513
|
gif: "image/gif",
|
|
4353
5514
|
webp: "image/webp",
|
|
5515
|
+
avif: "image/avif",
|
|
4354
5516
|
svg: "image/svg+xml",
|
|
4355
5517
|
pdf: "application/pdf",
|
|
4356
5518
|
json: "application/json",
|
|
@@ -5016,11 +6178,11 @@ SwaggerUIBundle({
|
|
|
5016
6178
|
|
|
5017
6179
|
// libs/server-core/src/lib/preview-renderer.ts
|
|
5018
6180
|
function renderPreviewHTML(options) {
|
|
5019
|
-
const { doc, collection } = options;
|
|
6181
|
+
const { doc, collection, customFieldRenderers } = options;
|
|
5020
6182
|
const titleField = collection.admin?.useAsTitle ?? "id";
|
|
5021
6183
|
const title = escapeHtml(String(doc[titleField] ?? doc["id"] ?? "Untitled"));
|
|
5022
6184
|
const fields = collection.fields ?? [];
|
|
5023
|
-
const fieldHtml = fields.filter((f) => !isHiddenField(f) && !isLayoutField(f) && f.name !== titleField).map((f) => renderField(f, doc)).filter(Boolean).join("\n");
|
|
6185
|
+
const fieldHtml = fields.filter((f) => !isHiddenField(f) && !isLayoutField(f) && f.name !== titleField).map((f) => renderField(f, doc, customFieldRenderers)).filter(Boolean).join("\n");
|
|
5024
6186
|
const richTextFields = fields.filter((f) => f.type === "richText").map((f) => f.name);
|
|
5025
6187
|
return `<!DOCTYPE html>
|
|
5026
6188
|
<html lang="en">
|
|
@@ -5054,6 +6216,7 @@ ${fieldHtml}
|
|
|
5054
6216
|
(function(){
|
|
5055
6217
|
var richTextFields=${JSON.stringify(richTextFields)};
|
|
5056
6218
|
window.addEventListener('message',function(e){
|
|
6219
|
+
if(e.origin!==window.location.origin)return;
|
|
5057
6220
|
if(!e.data||e.data.type!=='momentum-preview-update')return;
|
|
5058
6221
|
var d=e.data.data;if(!d)return;
|
|
5059
6222
|
document.querySelectorAll('[data-field]').forEach(function(el){
|
|
@@ -5074,16 +6237,20 @@ if(titleEl){var tf=titleEl.getAttribute('data-field');if(d[tf]!==undefined)title
|
|
|
5074
6237
|
</body>
|
|
5075
6238
|
</html>`;
|
|
5076
6239
|
}
|
|
5077
|
-
function renderField(field, doc) {
|
|
6240
|
+
function renderField(field, doc, customRenderers) {
|
|
5078
6241
|
const value = doc[field.name];
|
|
5079
6242
|
if (value === void 0 || value === null) {
|
|
5080
6243
|
return renderFieldWrapper(field, "");
|
|
5081
6244
|
}
|
|
6245
|
+
const editorKey = field.admin?.editor;
|
|
6246
|
+
if (editorKey && customRenderers?.[editorKey]) {
|
|
6247
|
+
return renderFieldWrapper(field, customRenderers[editorKey](value, field));
|
|
6248
|
+
}
|
|
5082
6249
|
switch (field.type) {
|
|
5083
6250
|
case "richText":
|
|
5084
6251
|
return renderFieldWrapper(
|
|
5085
6252
|
field,
|
|
5086
|
-
`<div class="field-value rich-text" data-field="${escapeHtml(field.name)}">${String(value)}</div>`
|
|
6253
|
+
`<div class="field-value rich-text" data-field="${escapeHtml(field.name)}">${escapeHtml(String(value))}</div>`
|
|
5087
6254
|
);
|
|
5088
6255
|
case "checkbox":
|
|
5089
6256
|
return renderFieldWrapper(
|
|
@@ -5430,6 +6597,20 @@ function getPluginProviders() {
|
|
|
5430
6597
|
}
|
|
5431
6598
|
|
|
5432
6599
|
// libs/server-express/src/lib/server-express.ts
|
|
6600
|
+
function getEmailBuilderFieldName(collection) {
|
|
6601
|
+
const field = collection.fields.find(
|
|
6602
|
+
(f) => f.type === "json" && f.admin?.editor === "email-builder"
|
|
6603
|
+
);
|
|
6604
|
+
return field?.name;
|
|
6605
|
+
}
|
|
6606
|
+
async function renderEmailPreviewHTML(doc, blocksFieldName) {
|
|
6607
|
+
const { renderEmailFromBlocks: renderEmailFromBlocks2 } = await Promise.resolve().then(() => (init_src2(), src_exports2));
|
|
6608
|
+
const blocks2 = doc[blocksFieldName];
|
|
6609
|
+
if (!Array.isArray(blocks2) || blocks2.length === 0) {
|
|
6610
|
+
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>';
|
|
6611
|
+
}
|
|
6612
|
+
return renderEmailFromBlocks2({ blocks: blocks2 });
|
|
6613
|
+
}
|
|
5433
6614
|
function extractUserFromRequest(req) {
|
|
5434
6615
|
const authReq = req;
|
|
5435
6616
|
if (authReq.user?.id) {
|
|
@@ -5844,16 +7025,13 @@ function momentumApiMiddleware(config) {
|
|
|
5844
7025
|
res.status(500).json({ error: "Failed to get status", message });
|
|
5845
7026
|
}
|
|
5846
7027
|
});
|
|
5847
|
-
|
|
7028
|
+
async function handlePreviewRequest(req, res) {
|
|
5848
7029
|
try {
|
|
5849
7030
|
const slug2 = req.params["collection"];
|
|
5850
7031
|
const id = req.params["id"];
|
|
5851
7032
|
const user = extractUserFromRequest(req);
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
const doc = await contextApi.collection(slug2).findById(id);
|
|
5855
|
-
if (!doc) {
|
|
5856
|
-
res.status(404).json({ error: "Document not found" });
|
|
7033
|
+
if (!user) {
|
|
7034
|
+
res.status(401).json({ error: "Authentication required to access preview" });
|
|
5857
7035
|
return;
|
|
5858
7036
|
}
|
|
5859
7037
|
const collectionConfig = config.collections.find((c) => c.slug === slug2);
|
|
@@ -5861,7 +7039,29 @@ function momentumApiMiddleware(config) {
|
|
|
5861
7039
|
res.status(404).json({ error: "Collection not found" });
|
|
5862
7040
|
return;
|
|
5863
7041
|
}
|
|
5864
|
-
const
|
|
7042
|
+
const accessFn = collectionConfig.access?.read;
|
|
7043
|
+
if (accessFn) {
|
|
7044
|
+
const allowed = await Promise.resolve(accessFn({ req: { user } }));
|
|
7045
|
+
if (!allowed) {
|
|
7046
|
+
res.status(403).json({ error: "Access denied" });
|
|
7047
|
+
return;
|
|
7048
|
+
}
|
|
7049
|
+
}
|
|
7050
|
+
let doc;
|
|
7051
|
+
if (req.method === "POST" && req.body?.data) {
|
|
7052
|
+
doc = req.body.data;
|
|
7053
|
+
} else {
|
|
7054
|
+
const api = getMomentumAPI();
|
|
7055
|
+
const contextApi = user ? api.setContext({ user }) : api;
|
|
7056
|
+
const dbDoc = await contextApi.collection(slug2).findById(id);
|
|
7057
|
+
if (!dbDoc) {
|
|
7058
|
+
res.status(404).json({ error: "Document not found" });
|
|
7059
|
+
return;
|
|
7060
|
+
}
|
|
7061
|
+
doc = dbDoc;
|
|
7062
|
+
}
|
|
7063
|
+
const emailField = getEmailBuilderFieldName(collectionConfig);
|
|
7064
|
+
const html = emailField ? await renderEmailPreviewHTML(doc, emailField) : renderPreviewHTML({ doc, collection: collectionConfig });
|
|
5865
7065
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
5866
7066
|
res.send(html);
|
|
5867
7067
|
} catch (error) {
|
|
@@ -5876,7 +7076,9 @@ function momentumApiMiddleware(config) {
|
|
|
5876
7076
|
}
|
|
5877
7077
|
res.status(500).json({ error: "Preview failed", message });
|
|
5878
7078
|
}
|
|
5879
|
-
}
|
|
7079
|
+
}
|
|
7080
|
+
router.get("/:collection/:id/preview", handlePreviewRequest);
|
|
7081
|
+
router.post("/:collection/:id/preview", handlePreviewRequest);
|
|
5880
7082
|
const upload2 = multer({
|
|
5881
7083
|
storage: multer.memoryStorage(),
|
|
5882
7084
|
limits: {
|
|
@@ -7599,6 +8801,7 @@ export {
|
|
|
7599
8801
|
createProtectMiddleware,
|
|
7600
8802
|
createSessionResolverMiddleware,
|
|
7601
8803
|
createSetupMiddleware,
|
|
8804
|
+
getPluginMiddleware,
|
|
7602
8805
|
getPluginProviders,
|
|
7603
8806
|
initializeMomentum,
|
|
7604
8807
|
momentumApiMiddleware
|