payload-plugin-newsletter 0.16.9 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/dist/collections.cjs +130 -102
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +130 -102
- package/dist/collections.js.map +1 -1
- package/dist/components.cjs +295 -275
- package/dist/components.cjs.map +1 -1
- package/dist/components.js +305 -285
- package/dist/components.js.map +1 -1
- package/dist/index.cjs +175 -104
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +175 -104
- package/dist/index.js.map +1 -1
- package/dist/types.d.cts +7 -0
- package/dist/types.d.ts +7 -0
- package/dist/utils.cjs +64 -28
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +64 -28
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/collections.js
CHANGED
|
@@ -950,62 +950,73 @@ async function convertToEmailSafeHtml(editorState, options) {
|
|
|
950
950
|
if (!editorState) {
|
|
951
951
|
return "";
|
|
952
952
|
}
|
|
953
|
-
const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl);
|
|
953
|
+
const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl, options?.customBlockConverter);
|
|
954
954
|
const sanitizedHtml = DOMPurify.sanitize(rawHtml, EMAIL_SAFE_CONFIG);
|
|
955
955
|
if (options?.wrapInTemplate) {
|
|
956
956
|
return wrapInEmailTemplate(sanitizedHtml, options.preheader);
|
|
957
957
|
}
|
|
958
958
|
return sanitizedHtml;
|
|
959
959
|
}
|
|
960
|
-
async function lexicalToEmailHtml(editorState, mediaUrl) {
|
|
960
|
+
async function lexicalToEmailHtml(editorState, mediaUrl, customBlockConverter) {
|
|
961
961
|
const { root } = editorState;
|
|
962
962
|
if (!root || !root.children) {
|
|
963
963
|
return "";
|
|
964
964
|
}
|
|
965
|
-
const
|
|
966
|
-
|
|
965
|
+
const htmlParts = await Promise.all(
|
|
966
|
+
root.children.map((node) => convertNode(node, mediaUrl, customBlockConverter))
|
|
967
|
+
);
|
|
968
|
+
return htmlParts.join("");
|
|
967
969
|
}
|
|
968
|
-
function convertNode(node, mediaUrl) {
|
|
970
|
+
async function convertNode(node, mediaUrl, customBlockConverter) {
|
|
969
971
|
switch (node.type) {
|
|
970
972
|
case "paragraph":
|
|
971
|
-
return convertParagraph(node, mediaUrl);
|
|
973
|
+
return convertParagraph(node, mediaUrl, customBlockConverter);
|
|
972
974
|
case "heading":
|
|
973
|
-
return convertHeading(node, mediaUrl);
|
|
975
|
+
return convertHeading(node, mediaUrl, customBlockConverter);
|
|
974
976
|
case "list":
|
|
975
|
-
return convertList(node, mediaUrl);
|
|
977
|
+
return convertList(node, mediaUrl, customBlockConverter);
|
|
976
978
|
case "listitem":
|
|
977
|
-
return convertListItem(node, mediaUrl);
|
|
979
|
+
return convertListItem(node, mediaUrl, customBlockConverter);
|
|
978
980
|
case "blockquote":
|
|
979
|
-
return convertBlockquote(node, mediaUrl);
|
|
981
|
+
return convertBlockquote(node, mediaUrl, customBlockConverter);
|
|
980
982
|
case "text":
|
|
981
983
|
return convertText(node);
|
|
982
984
|
case "link":
|
|
983
|
-
return convertLink(node, mediaUrl);
|
|
985
|
+
return convertLink(node, mediaUrl, customBlockConverter);
|
|
984
986
|
case "linebreak":
|
|
985
987
|
return "<br>";
|
|
986
988
|
case "upload":
|
|
987
989
|
return convertUpload(node, mediaUrl);
|
|
988
990
|
case "block":
|
|
989
|
-
return convertBlock(node, mediaUrl);
|
|
991
|
+
return await convertBlock(node, mediaUrl, customBlockConverter);
|
|
990
992
|
default:
|
|
991
993
|
if (node.children) {
|
|
992
|
-
|
|
994
|
+
const childParts = await Promise.all(
|
|
995
|
+
node.children.map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
996
|
+
);
|
|
997
|
+
return childParts.join("");
|
|
993
998
|
}
|
|
994
999
|
return "";
|
|
995
1000
|
}
|
|
996
1001
|
}
|
|
997
|
-
function convertParagraph(node, mediaUrl) {
|
|
1002
|
+
async function convertParagraph(node, mediaUrl, customBlockConverter) {
|
|
998
1003
|
const align = getAlignment(node.format);
|
|
999
|
-
const
|
|
1004
|
+
const childParts = await Promise.all(
|
|
1005
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1006
|
+
);
|
|
1007
|
+
const children = childParts.join("");
|
|
1000
1008
|
if (!children.trim()) {
|
|
1001
1009
|
return '<p style="margin: 0 0 16px 0; min-height: 1em;"> </p>';
|
|
1002
1010
|
}
|
|
1003
1011
|
return `<p style="margin: 0 0 16px 0; text-align: ${align};">${children}</p>`;
|
|
1004
1012
|
}
|
|
1005
|
-
function convertHeading(node, mediaUrl) {
|
|
1013
|
+
async function convertHeading(node, mediaUrl, customBlockConverter) {
|
|
1006
1014
|
const tag = node.tag || "h1";
|
|
1007
1015
|
const align = getAlignment(node.format);
|
|
1008
|
-
const
|
|
1016
|
+
const childParts = await Promise.all(
|
|
1017
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1018
|
+
);
|
|
1019
|
+
const children = childParts.join("");
|
|
1009
1020
|
const styles2 = {
|
|
1010
1021
|
h1: "font-size: 32px; font-weight: 700; margin: 0 0 24px 0; line-height: 1.2;",
|
|
1011
1022
|
h2: "font-size: 24px; font-weight: 600; margin: 0 0 16px 0; line-height: 1.3;",
|
|
@@ -1014,18 +1025,27 @@ function convertHeading(node, mediaUrl) {
|
|
|
1014
1025
|
const style = `${styles2[tag] || styles2.h3} text-align: ${align};`;
|
|
1015
1026
|
return `<${tag} style="${style}">${children}</${tag}>`;
|
|
1016
1027
|
}
|
|
1017
|
-
function convertList(node, mediaUrl) {
|
|
1028
|
+
async function convertList(node, mediaUrl, customBlockConverter) {
|
|
1018
1029
|
const tag = node.listType === "number" ? "ol" : "ul";
|
|
1019
|
-
const
|
|
1030
|
+
const childParts = await Promise.all(
|
|
1031
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1032
|
+
);
|
|
1033
|
+
const children = childParts.join("");
|
|
1020
1034
|
const style = tag === "ul" ? "margin: 0 0 16px 0; padding-left: 24px; list-style-type: disc;" : "margin: 0 0 16px 0; padding-left: 24px; list-style-type: decimal;";
|
|
1021
1035
|
return `<${tag} style="${style}">${children}</${tag}>`;
|
|
1022
1036
|
}
|
|
1023
|
-
function convertListItem(node, mediaUrl) {
|
|
1024
|
-
const
|
|
1037
|
+
async function convertListItem(node, mediaUrl, customBlockConverter) {
|
|
1038
|
+
const childParts = await Promise.all(
|
|
1039
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1040
|
+
);
|
|
1041
|
+
const children = childParts.join("");
|
|
1025
1042
|
return `<li style="margin: 0 0 8px 0;">${children}</li>`;
|
|
1026
1043
|
}
|
|
1027
|
-
function convertBlockquote(node, mediaUrl) {
|
|
1028
|
-
const
|
|
1044
|
+
async function convertBlockquote(node, mediaUrl, customBlockConverter) {
|
|
1045
|
+
const childParts = await Promise.all(
|
|
1046
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1047
|
+
);
|
|
1048
|
+
const children = childParts.join("");
|
|
1029
1049
|
const style = "margin: 0 0 16px 0; padding-left: 16px; border-left: 4px solid #e5e7eb; color: #6b7280;";
|
|
1030
1050
|
return `<blockquote style="${style}">${children}</blockquote>`;
|
|
1031
1051
|
}
|
|
@@ -1045,8 +1065,11 @@ function convertText(node) {
|
|
|
1045
1065
|
}
|
|
1046
1066
|
return text;
|
|
1047
1067
|
}
|
|
1048
|
-
function convertLink(node, mediaUrl) {
|
|
1049
|
-
const
|
|
1068
|
+
async function convertLink(node, mediaUrl, customBlockConverter) {
|
|
1069
|
+
const childParts = await Promise.all(
|
|
1070
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1071
|
+
);
|
|
1072
|
+
const children = childParts.join("");
|
|
1050
1073
|
const url = node.fields?.url || "#";
|
|
1051
1074
|
const newTab = node.fields?.newTab ?? false;
|
|
1052
1075
|
const targetAttr = newTab ? ' target="_blank"' : "";
|
|
@@ -1077,8 +1100,18 @@ function convertUpload(node, mediaUrl) {
|
|
|
1077
1100
|
}
|
|
1078
1101
|
return `<div style="margin: 0 0 16px 0; text-align: center;">${imgHtml}</div>`;
|
|
1079
1102
|
}
|
|
1080
|
-
function convertBlock(node, mediaUrl) {
|
|
1081
|
-
const blockType = node.fields?.blockName;
|
|
1103
|
+
async function convertBlock(node, mediaUrl, customBlockConverter) {
|
|
1104
|
+
const blockType = node.fields?.blockName || node.blockName;
|
|
1105
|
+
if (customBlockConverter) {
|
|
1106
|
+
try {
|
|
1107
|
+
const customHtml = await customBlockConverter(node, mediaUrl);
|
|
1108
|
+
if (customHtml) {
|
|
1109
|
+
return customHtml;
|
|
1110
|
+
}
|
|
1111
|
+
} catch (error) {
|
|
1112
|
+
console.error(`Custom block converter error for ${blockType}:`, error);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1082
1115
|
switch (blockType) {
|
|
1083
1116
|
case "button":
|
|
1084
1117
|
return convertButtonBlock(node.fields);
|
|
@@ -1086,7 +1119,10 @@ function convertBlock(node, mediaUrl) {
|
|
|
1086
1119
|
return convertDividerBlock(node.fields);
|
|
1087
1120
|
default:
|
|
1088
1121
|
if (node.children) {
|
|
1089
|
-
|
|
1122
|
+
const childParts = await Promise.all(
|
|
1123
|
+
node.children.map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1124
|
+
);
|
|
1125
|
+
return childParts.join("");
|
|
1090
1126
|
}
|
|
1091
1127
|
return "";
|
|
1092
1128
|
}
|
|
@@ -1460,7 +1496,9 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1460
1496
|
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
1461
1497
|
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1462
1498
|
req.payload.logger.info("Converting content to HTML...");
|
|
1463
|
-
const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content
|
|
1499
|
+
const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content, {
|
|
1500
|
+
customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter
|
|
1501
|
+
});
|
|
1464
1502
|
if (!htmlContent || htmlContent.trim() === "") {
|
|
1465
1503
|
req.payload.logger.info("Skipping provider sync - content is empty after conversion");
|
|
1466
1504
|
return doc;
|
|
@@ -1556,77 +1594,49 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1556
1594
|
req.payload.logger.info("Still missing required fields for provider sync");
|
|
1557
1595
|
return doc;
|
|
1558
1596
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
}
|
|
1566
|
-
const createData = {
|
|
1567
|
-
name: doc.subject,
|
|
1568
|
-
subject: doc.subject,
|
|
1569
|
-
preheader: doc.contentSection?.preheader,
|
|
1570
|
-
content: htmlContent,
|
|
1571
|
-
trackOpens: doc.settings?.trackOpens,
|
|
1572
|
-
trackClicks: doc.settings?.trackClicks,
|
|
1573
|
-
replyTo: doc.settings?.replyTo || providerConfig.replyTo,
|
|
1574
|
-
audienceIds: doc.audienceIds?.map((a) => a.audienceId)
|
|
1575
|
-
};
|
|
1576
|
-
req.payload.logger.info("Creating broadcast with data:", {
|
|
1577
|
-
name: createData.name,
|
|
1578
|
-
subject: createData.subject,
|
|
1579
|
-
preheader: createData.preheader || "NONE",
|
|
1580
|
-
contentLength: htmlContent ? htmlContent.length : 0,
|
|
1581
|
-
contentPreview: htmlContent ? htmlContent.substring(0, 100) + "..." : "EMPTY",
|
|
1582
|
-
apiUrl: providerConfig.apiUrl,
|
|
1583
|
-
hasToken: !!providerConfig.token
|
|
1584
|
-
});
|
|
1585
|
-
const providerBroadcast = await provider.create(createData);
|
|
1586
|
-
await req.payload.update({
|
|
1587
|
-
collection: "broadcasts",
|
|
1588
|
-
id: doc.id,
|
|
1589
|
-
data: {
|
|
1590
|
-
providerId: providerBroadcast.id,
|
|
1591
|
-
providerData: providerBroadcast.providerData
|
|
1592
|
-
},
|
|
1593
|
-
req
|
|
1594
|
-
});
|
|
1595
|
-
req.payload.logger.info(`Broadcast ${doc.id} created in provider successfully (deferred)`);
|
|
1596
|
-
return {
|
|
1597
|
-
...doc,
|
|
1598
|
-
providerId: providerBroadcast.id,
|
|
1599
|
-
providerData: providerBroadcast.providerData
|
|
1600
|
-
};
|
|
1601
|
-
} catch (error) {
|
|
1602
|
-
req.payload.logger.error("Raw error from broadcast provider (deferred create):");
|
|
1603
|
-
req.payload.logger.error(error);
|
|
1604
|
-
if (error instanceof Error) {
|
|
1605
|
-
req.payload.logger.error("Error is instance of Error:", {
|
|
1606
|
-
message: error.message,
|
|
1607
|
-
stack: error.stack,
|
|
1608
|
-
name: error.name,
|
|
1609
|
-
...error.details,
|
|
1610
|
-
...error.response,
|
|
1611
|
-
...error.data,
|
|
1612
|
-
...error.status,
|
|
1613
|
-
...error.statusText
|
|
1614
|
-
});
|
|
1615
|
-
} else if (typeof error === "string") {
|
|
1616
|
-
req.payload.logger.error("Error is a string:", error);
|
|
1617
|
-
} else if (error && typeof error === "object") {
|
|
1618
|
-
req.payload.logger.error("Error is an object:", JSON.stringify(error, null, 2));
|
|
1619
|
-
} else {
|
|
1620
|
-
req.payload.logger.error("Unknown error type:", typeof error);
|
|
1621
|
-
}
|
|
1622
|
-
req.payload.logger.error("Failed broadcast document (deferred create):", {
|
|
1623
|
-
id: doc.id,
|
|
1624
|
-
subject: doc.subject,
|
|
1625
|
-
hasContent: !!doc.contentSection?.content,
|
|
1626
|
-
contentType: doc.contentSection?.content ? typeof doc.contentSection.content : "none"
|
|
1627
|
-
});
|
|
1597
|
+
req.payload.logger.info("Creating broadcast in provider (deferred from initial create)...");
|
|
1598
|
+
const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content, {
|
|
1599
|
+
customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter
|
|
1600
|
+
});
|
|
1601
|
+
if (!htmlContent || htmlContent.trim() === "") {
|
|
1602
|
+
req.payload.logger.info("Skipping provider sync - content is empty after conversion");
|
|
1628
1603
|
return doc;
|
|
1629
1604
|
}
|
|
1605
|
+
const createData = {
|
|
1606
|
+
name: doc.subject,
|
|
1607
|
+
subject: doc.subject,
|
|
1608
|
+
preheader: doc.contentSection?.preheader,
|
|
1609
|
+
content: htmlContent,
|
|
1610
|
+
trackOpens: doc.settings?.trackOpens,
|
|
1611
|
+
trackClicks: doc.settings?.trackClicks,
|
|
1612
|
+
replyTo: doc.settings?.replyTo || providerConfig.replyTo,
|
|
1613
|
+
audienceIds: doc.audienceIds?.map((a) => a.audienceId)
|
|
1614
|
+
};
|
|
1615
|
+
req.payload.logger.info("Creating broadcast with data:", {
|
|
1616
|
+
name: createData.name,
|
|
1617
|
+
subject: createData.subject,
|
|
1618
|
+
preheader: createData.preheader || "NONE",
|
|
1619
|
+
contentLength: htmlContent ? htmlContent.length : 0,
|
|
1620
|
+
contentPreview: htmlContent ? htmlContent.substring(0, 100) + "..." : "EMPTY",
|
|
1621
|
+
apiUrl: providerConfig.apiUrl,
|
|
1622
|
+
hasToken: !!providerConfig.token
|
|
1623
|
+
});
|
|
1624
|
+
const providerBroadcast = await provider.create(createData);
|
|
1625
|
+
await req.payload.update({
|
|
1626
|
+
collection: "broadcasts",
|
|
1627
|
+
id: doc.id,
|
|
1628
|
+
data: {
|
|
1629
|
+
providerId: providerBroadcast.id,
|
|
1630
|
+
providerData: providerBroadcast.providerData
|
|
1631
|
+
},
|
|
1632
|
+
req
|
|
1633
|
+
});
|
|
1634
|
+
req.payload.logger.info(`Broadcast ${doc.id} created in provider successfully (deferred)`);
|
|
1635
|
+
return {
|
|
1636
|
+
...doc,
|
|
1637
|
+
providerId: providerBroadcast.id,
|
|
1638
|
+
providerData: providerBroadcast.providerData
|
|
1639
|
+
};
|
|
1630
1640
|
}
|
|
1631
1641
|
if (doc.providerId) {
|
|
1632
1642
|
const capabilities = provider.getCapabilities();
|
|
@@ -1646,7 +1656,9 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1646
1656
|
updates.preheader = doc.contentSection?.preheader;
|
|
1647
1657
|
}
|
|
1648
1658
|
if (JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content)) {
|
|
1649
|
-
updates.content = await convertToEmailSafeHtml(doc.contentSection?.content
|
|
1659
|
+
updates.content = await convertToEmailSafeHtml(doc.contentSection?.content, {
|
|
1660
|
+
customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter
|
|
1661
|
+
});
|
|
1650
1662
|
}
|
|
1651
1663
|
if (doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens) {
|
|
1652
1664
|
updates.trackOpens = doc.settings.trackOpens;
|
|
@@ -1671,16 +1683,32 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1671
1683
|
}
|
|
1672
1684
|
}
|
|
1673
1685
|
} catch (error) {
|
|
1686
|
+
req.payload.logger.error("Raw error from broadcast update operation:");
|
|
1687
|
+
req.payload.logger.error(error);
|
|
1674
1688
|
if (error instanceof Error) {
|
|
1675
|
-
req.payload.logger.error("
|
|
1689
|
+
req.payload.logger.error("Error is instance of Error:", {
|
|
1676
1690
|
message: error.message,
|
|
1677
1691
|
stack: error.stack,
|
|
1678
1692
|
name: error.name,
|
|
1679
|
-
...error.details
|
|
1693
|
+
...error.details,
|
|
1694
|
+
...error.response,
|
|
1695
|
+
...error.data,
|
|
1696
|
+
...error.status,
|
|
1697
|
+
...error.statusText
|
|
1680
1698
|
});
|
|
1699
|
+
} else if (typeof error === "string") {
|
|
1700
|
+
req.payload.logger.error("Error is a string:", error);
|
|
1701
|
+
} else if (error && typeof error === "object") {
|
|
1702
|
+
req.payload.logger.error("Error is an object:", JSON.stringify(error, null, 2));
|
|
1681
1703
|
} else {
|
|
1682
|
-
req.payload.logger.error("
|
|
1704
|
+
req.payload.logger.error("Unknown error type:", typeof error);
|
|
1683
1705
|
}
|
|
1706
|
+
req.payload.logger.error("Failed broadcast document (update operation):", {
|
|
1707
|
+
id: doc.id,
|
|
1708
|
+
subject: doc.subject,
|
|
1709
|
+
hasContent: !!doc.contentSection?.content,
|
|
1710
|
+
contentType: doc.contentSection?.content ? typeof doc.contentSection.content : "none"
|
|
1711
|
+
});
|
|
1684
1712
|
}
|
|
1685
1713
|
}
|
|
1686
1714
|
return doc;
|