payload-plugin-newsletter 0.16.7 → 0.16.9
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 +30 -0
- package/dist/collections.cjs +128 -39
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +128 -39
- package/dist/collections.js.map +1 -1
- package/dist/components.cjs +3 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.js +3 -0
- package/dist/components.js.map +1 -1
- package/dist/index.cjs +128 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +128 -39
- package/dist/index.js.map +1 -1
- package/dist/utils.cjs +3 -0
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -0
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
## [0.16.9] - 2025-01-29
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
- **Deferred Provider Sync for Empty Broadcasts** - Fixed issue where broadcasts created empty were never synced to provider
|
|
5
|
+
- Update operation now handles "deferred create" scenario when providerId is missing
|
|
6
|
+
- When a broadcast has subject and content but no providerId, it creates the broadcast in the provider
|
|
7
|
+
- Normal update sync works for broadcasts that already exist in the provider
|
|
8
|
+
- Resolves workflow: create empty → add content → save → now syncs to provider
|
|
9
|
+
|
|
10
|
+
### Technical
|
|
11
|
+
- Modified update operation handler to check for missing providerId
|
|
12
|
+
- Added deferred create logic in update afterChange hook
|
|
13
|
+
- Separated normal update logic to only run when providerId exists
|
|
14
|
+
- Added comprehensive error handling for both deferred create and normal update scenarios
|
|
15
|
+
|
|
16
|
+
## [0.16.8] - 2025-01-29
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- **Handle Empty Content When Creating Broadcasts** - Fixed error when creating broadcasts without content
|
|
20
|
+
- Added null/undefined check in convertToEmailSafeHtml function
|
|
21
|
+
- Skip provider sync when subject or content is missing on create
|
|
22
|
+
- Skip provider sync when content is empty after conversion
|
|
23
|
+
- Prevents "Cannot destructure property 'root' of 'editorState'" error
|
|
24
|
+
- Broadcasts can now be created empty and synced later when content is added
|
|
25
|
+
|
|
26
|
+
### Technical
|
|
27
|
+
- Updated convertToEmailSafeHtml to accept undefined/null editorState
|
|
28
|
+
- Added pre-sync validation in afterChange hook for create operations
|
|
29
|
+
- Empty broadcasts are saved in Payload but not synced to provider until content is added
|
|
30
|
+
|
|
1
31
|
## [0.16.7] - 2025-07-29
|
|
2
32
|
|
|
3
33
|
### Added
|
package/dist/collections.cjs
CHANGED
|
@@ -960,6 +960,9 @@ var EMAIL_SAFE_CONFIG = {
|
|
|
960
960
|
FORBID_ATTR: ["class", "id", "onclick", "onload", "onerror"]
|
|
961
961
|
};
|
|
962
962
|
async function convertToEmailSafeHtml(editorState, options) {
|
|
963
|
+
if (!editorState) {
|
|
964
|
+
return "";
|
|
965
|
+
}
|
|
963
966
|
const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl);
|
|
964
967
|
const sanitizedHtml = import_isomorphic_dompurify.default.sanitize(rawHtml, EMAIL_SAFE_CONFIG);
|
|
965
968
|
if (options?.wrapInTemplate) {
|
|
@@ -1457,6 +1460,10 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1457
1460
|
async ({ doc, operation, req, previousDoc }) => {
|
|
1458
1461
|
if (!hasProviders) return doc;
|
|
1459
1462
|
if (operation === "create") {
|
|
1463
|
+
if (!doc.subject || !doc.contentSection?.content) {
|
|
1464
|
+
req.payload.logger.info("Skipping provider sync - broadcast has no subject or content yet");
|
|
1465
|
+
return doc;
|
|
1466
|
+
}
|
|
1460
1467
|
try {
|
|
1461
1468
|
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
1462
1469
|
if (!providerConfig || !providerConfig.token) {
|
|
@@ -1467,6 +1474,10 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1467
1474
|
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1468
1475
|
req.payload.logger.info("Converting content to HTML...");
|
|
1469
1476
|
const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content);
|
|
1477
|
+
if (!htmlContent || htmlContent.trim() === "") {
|
|
1478
|
+
req.payload.logger.info("Skipping provider sync - content is empty after conversion");
|
|
1479
|
+
return doc;
|
|
1480
|
+
}
|
|
1470
1481
|
const createData = {
|
|
1471
1482
|
name: doc.subject,
|
|
1472
1483
|
// Use subject as name since we removed the name field
|
|
@@ -1538,7 +1549,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1538
1549
|
return doc;
|
|
1539
1550
|
}
|
|
1540
1551
|
}
|
|
1541
|
-
if (operation === "update"
|
|
1552
|
+
if (operation === "update") {
|
|
1542
1553
|
req.payload.logger.info("Broadcast afterChange update hook triggered", {
|
|
1543
1554
|
operation,
|
|
1544
1555
|
hasProviderId: !!doc.providerId,
|
|
@@ -1553,57 +1564,135 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1553
1564
|
}
|
|
1554
1565
|
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
1555
1566
|
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
return doc;
|
|
1561
|
-
}
|
|
1562
|
-
const contentChanged = doc.subject !== previousDoc?.subject || doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader || JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content) || doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens || doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks || doc.settings?.replyTo !== previousDoc?.settings?.replyTo || JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds);
|
|
1563
|
-
if (contentChanged) {
|
|
1564
|
-
const updates = {};
|
|
1565
|
-
if (doc.subject !== previousDoc?.subject) {
|
|
1566
|
-
updates.name = doc.subject;
|
|
1567
|
-
updates.subject = doc.subject;
|
|
1568
|
-
}
|
|
1569
|
-
if (doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader) {
|
|
1570
|
-
updates.preheader = doc.contentSection?.preheader;
|
|
1567
|
+
if (!doc.providerId) {
|
|
1568
|
+
if (!doc.subject || !doc.contentSection?.content) {
|
|
1569
|
+
req.payload.logger.info("Still missing required fields for provider sync");
|
|
1570
|
+
return doc;
|
|
1571
1571
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1572
|
+
try {
|
|
1573
|
+
req.payload.logger.info("Creating broadcast in provider (deferred from initial create)...");
|
|
1574
|
+
const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content);
|
|
1575
|
+
if (!htmlContent || htmlContent.trim() === "") {
|
|
1576
|
+
req.payload.logger.info("Skipping provider sync - content is empty after conversion");
|
|
1577
|
+
return doc;
|
|
1578
|
+
}
|
|
1579
|
+
const createData = {
|
|
1580
|
+
name: doc.subject,
|
|
1581
|
+
subject: doc.subject,
|
|
1582
|
+
preheader: doc.contentSection?.preheader,
|
|
1583
|
+
content: htmlContent,
|
|
1584
|
+
trackOpens: doc.settings?.trackOpens,
|
|
1585
|
+
trackClicks: doc.settings?.trackClicks,
|
|
1586
|
+
replyTo: doc.settings?.replyTo || providerConfig.replyTo,
|
|
1587
|
+
audienceIds: doc.audienceIds?.map((a) => a.audienceId)
|
|
1588
|
+
};
|
|
1589
|
+
req.payload.logger.info("Creating broadcast with data:", {
|
|
1590
|
+
name: createData.name,
|
|
1591
|
+
subject: createData.subject,
|
|
1592
|
+
preheader: createData.preheader || "NONE",
|
|
1593
|
+
contentLength: htmlContent ? htmlContent.length : 0,
|
|
1594
|
+
contentPreview: htmlContent ? htmlContent.substring(0, 100) + "..." : "EMPTY",
|
|
1595
|
+
apiUrl: providerConfig.apiUrl,
|
|
1596
|
+
hasToken: !!providerConfig.token
|
|
1597
|
+
});
|
|
1598
|
+
const providerBroadcast = await provider.create(createData);
|
|
1599
|
+
await req.payload.update({
|
|
1600
|
+
collection: "broadcasts",
|
|
1601
|
+
id: doc.id,
|
|
1602
|
+
data: {
|
|
1603
|
+
providerId: providerBroadcast.id,
|
|
1604
|
+
providerData: providerBroadcast.providerData
|
|
1605
|
+
},
|
|
1606
|
+
req
|
|
1607
|
+
});
|
|
1608
|
+
req.payload.logger.info(`Broadcast ${doc.id} created in provider successfully (deferred)`);
|
|
1609
|
+
return {
|
|
1610
|
+
...doc,
|
|
1611
|
+
providerId: providerBroadcast.id,
|
|
1612
|
+
providerData: providerBroadcast.providerData
|
|
1613
|
+
};
|
|
1614
|
+
} catch (error) {
|
|
1615
|
+
req.payload.logger.error("Raw error from broadcast provider (deferred create):");
|
|
1616
|
+
req.payload.logger.error(error);
|
|
1617
|
+
if (error instanceof Error) {
|
|
1618
|
+
req.payload.logger.error("Error is instance of Error:", {
|
|
1619
|
+
message: error.message,
|
|
1620
|
+
stack: error.stack,
|
|
1621
|
+
name: error.name,
|
|
1622
|
+
...error.details,
|
|
1623
|
+
...error.response,
|
|
1624
|
+
...error.data,
|
|
1625
|
+
...error.status,
|
|
1626
|
+
...error.statusText
|
|
1627
|
+
});
|
|
1628
|
+
} else if (typeof error === "string") {
|
|
1629
|
+
req.payload.logger.error("Error is a string:", error);
|
|
1630
|
+
} else if (error && typeof error === "object") {
|
|
1631
|
+
req.payload.logger.error("Error is an object:", JSON.stringify(error, null, 2));
|
|
1632
|
+
} else {
|
|
1633
|
+
req.payload.logger.error("Unknown error type:", typeof error);
|
|
1634
|
+
}
|
|
1635
|
+
req.payload.logger.error("Failed broadcast document (deferred create):", {
|
|
1636
|
+
id: doc.id,
|
|
1637
|
+
subject: doc.subject,
|
|
1638
|
+
hasContent: !!doc.contentSection?.content,
|
|
1639
|
+
contentType: doc.contentSection?.content ? typeof doc.contentSection.content : "none"
|
|
1640
|
+
});
|
|
1641
|
+
return doc;
|
|
1580
1642
|
}
|
|
1581
|
-
|
|
1582
|
-
|
|
1643
|
+
}
|
|
1644
|
+
if (doc.providerId) {
|
|
1645
|
+
const capabilities = provider.getCapabilities();
|
|
1646
|
+
const sendStatus = doc.sendStatus || "draft" /* DRAFT */;
|
|
1647
|
+
if (!capabilities.editableStatuses.includes(sendStatus)) {
|
|
1648
|
+
req.payload.logger.info(`Skipping sync for broadcast in status: ${sendStatus}`);
|
|
1649
|
+
return doc;
|
|
1583
1650
|
}
|
|
1584
|
-
|
|
1585
|
-
|
|
1651
|
+
const contentChanged = doc.subject !== previousDoc?.subject || doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader || JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content) || doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens || doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks || doc.settings?.replyTo !== previousDoc?.settings?.replyTo || JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds);
|
|
1652
|
+
if (contentChanged) {
|
|
1653
|
+
const updates = {};
|
|
1654
|
+
if (doc.subject !== previousDoc?.subject) {
|
|
1655
|
+
updates.name = doc.subject;
|
|
1656
|
+
updates.subject = doc.subject;
|
|
1657
|
+
}
|
|
1658
|
+
if (doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader) {
|
|
1659
|
+
updates.preheader = doc.contentSection?.preheader;
|
|
1660
|
+
}
|
|
1661
|
+
if (JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content)) {
|
|
1662
|
+
updates.content = await convertToEmailSafeHtml(doc.contentSection?.content);
|
|
1663
|
+
}
|
|
1664
|
+
if (doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens) {
|
|
1665
|
+
updates.trackOpens = doc.settings.trackOpens;
|
|
1666
|
+
}
|
|
1667
|
+
if (doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks) {
|
|
1668
|
+
updates.trackClicks = doc.settings.trackClicks;
|
|
1669
|
+
}
|
|
1670
|
+
if (doc.settings?.replyTo !== previousDoc?.settings?.replyTo) {
|
|
1671
|
+
updates.replyTo = doc.settings.replyTo || providerConfig.replyTo;
|
|
1672
|
+
}
|
|
1673
|
+
if (JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds)) {
|
|
1674
|
+
updates.audienceIds = doc.audienceIds?.map((a) => a.audienceId);
|
|
1675
|
+
}
|
|
1676
|
+
req.payload.logger.info("Syncing broadcast updates to provider", {
|
|
1677
|
+
providerId: doc.providerId,
|
|
1678
|
+
updates
|
|
1679
|
+
});
|
|
1680
|
+
await provider.update(doc.providerId, updates);
|
|
1681
|
+
req.payload.logger.info(`Broadcast ${doc.id} synced to provider successfully`);
|
|
1682
|
+
} else {
|
|
1683
|
+
req.payload.logger.info("No content changes to sync to provider");
|
|
1586
1684
|
}
|
|
1587
|
-
req.payload.logger.info("Syncing broadcast updates to provider", {
|
|
1588
|
-
providerId: doc.providerId,
|
|
1589
|
-
updates
|
|
1590
|
-
});
|
|
1591
|
-
await provider.update(doc.providerId, updates);
|
|
1592
|
-
req.payload.logger.info(`Broadcast ${doc.id} synced to provider successfully`);
|
|
1593
|
-
} else {
|
|
1594
|
-
req.payload.logger.info("No content changes to sync to provider");
|
|
1595
1685
|
}
|
|
1596
1686
|
} catch (error) {
|
|
1597
1687
|
if (error instanceof Error) {
|
|
1598
|
-
req.payload.logger.error("Failed to
|
|
1688
|
+
req.payload.logger.error("Failed to handle broadcast update operation:", {
|
|
1599
1689
|
message: error.message,
|
|
1600
1690
|
stack: error.stack,
|
|
1601
1691
|
name: error.name,
|
|
1602
|
-
// If it's a BroadcastProviderError, it might have additional details
|
|
1603
1692
|
...error.details
|
|
1604
1693
|
});
|
|
1605
1694
|
} else {
|
|
1606
|
-
req.payload.logger.error("Failed to
|
|
1695
|
+
req.payload.logger.error("Failed to handle broadcast update operation:", error);
|
|
1607
1696
|
}
|
|
1608
1697
|
}
|
|
1609
1698
|
}
|