payload-plugin-newsletter 0.16.5 → 0.16.6
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 +21 -0
- package/dist/collections.cjs +120 -119
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +120 -119
- package/dist/collections.js.map +1 -1
- package/dist/index.cjs +120 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +120 -119
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## [0.16.6] - 2025-07-29
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
- **Critical: Update Sync Now Works** - Fixed the afterChange hook that was blocking update operations
|
|
5
|
+
- Removed the `operation !== 'create'` check that prevented the afterChange hook from running on updates
|
|
6
|
+
- Moved update sync logic from beforeChange to afterChange for proper architectural pattern
|
|
7
|
+
- Updates are now synced AFTER they're saved to Payload, ensuring consistency
|
|
8
|
+
- Provider sync failures no longer block Payload updates
|
|
9
|
+
|
|
10
|
+
### Improved
|
|
11
|
+
- **Better Hook Architecture** - Sync operations now happen in the correct lifecycle stage
|
|
12
|
+
- beforeChange was architecturally wrong - if provider sync failed, data would be inconsistent
|
|
13
|
+
- afterChange ensures Payload data is saved first, then syncs to provider
|
|
14
|
+
- More resilient to network failures and API errors
|
|
15
|
+
|
|
16
|
+
### Technical
|
|
17
|
+
- Consolidated create and update logic in a single afterChange hook
|
|
18
|
+
- Added comprehensive content change detection before syncing
|
|
19
|
+
- Enhanced logging for update sync operations
|
|
20
|
+
- Removed redundant beforeChange hook logic
|
|
21
|
+
|
|
1
22
|
## [0.16.5] - 2025-07-29
|
|
2
23
|
|
|
3
24
|
### Breaking Changes
|
package/dist/collections.cjs
CHANGED
|
@@ -1435,58 +1435,130 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1435
1435
|
}
|
|
1436
1436
|
],
|
|
1437
1437
|
hooks: {
|
|
1438
|
-
// Sync with provider on create
|
|
1438
|
+
// Sync with provider on create and update
|
|
1439
1439
|
afterChange: [
|
|
1440
|
-
async ({ doc, operation, req }) => {
|
|
1441
|
-
if (!hasProviders
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1440
|
+
async ({ doc, operation, req, previousDoc }) => {
|
|
1441
|
+
if (!hasProviders) return doc;
|
|
1442
|
+
if (operation === "create") {
|
|
1443
|
+
try {
|
|
1444
|
+
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
1445
|
+
if (!providerConfig || !providerConfig.token) {
|
|
1446
|
+
req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
|
|
1447
|
+
return doc;
|
|
1448
|
+
}
|
|
1449
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
1450
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1451
|
+
const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content);
|
|
1452
|
+
const providerBroadcast = await provider.create({
|
|
1453
|
+
name: doc.subject,
|
|
1454
|
+
// Use subject as name since we removed the name field
|
|
1455
|
+
subject: doc.subject,
|
|
1456
|
+
preheader: doc.contentSection?.preheader,
|
|
1457
|
+
content: htmlContent,
|
|
1458
|
+
trackOpens: doc.settings?.trackOpens,
|
|
1459
|
+
trackClicks: doc.settings?.trackClicks,
|
|
1460
|
+
replyTo: doc.settings?.replyTo || providerConfig.replyTo,
|
|
1461
|
+
audienceIds: doc.audienceIds?.map((a) => a.audienceId)
|
|
1462
|
+
});
|
|
1463
|
+
await req.payload.update({
|
|
1464
|
+
collection: "broadcasts",
|
|
1465
|
+
id: doc.id,
|
|
1466
|
+
data: {
|
|
1467
|
+
providerId: providerBroadcast.id,
|
|
1468
|
+
providerData: providerBroadcast.providerData
|
|
1469
|
+
},
|
|
1470
|
+
req
|
|
1471
|
+
});
|
|
1472
|
+
return {
|
|
1473
|
+
...doc,
|
|
1466
1474
|
providerId: providerBroadcast.id,
|
|
1467
1475
|
providerData: providerBroadcast.providerData
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1476
|
+
};
|
|
1477
|
+
} catch (error) {
|
|
1478
|
+
if (error instanceof Error) {
|
|
1479
|
+
req.payload.logger.error("Failed to create broadcast in provider:", {
|
|
1480
|
+
message: error.message,
|
|
1481
|
+
stack: error.stack,
|
|
1482
|
+
name: error.name,
|
|
1483
|
+
// If it's a BroadcastProviderError, it might have additional details
|
|
1484
|
+
...error.details
|
|
1485
|
+
});
|
|
1486
|
+
} else {
|
|
1487
|
+
req.payload.logger.error("Failed to create broadcast in provider:", error);
|
|
1488
|
+
}
|
|
1489
|
+
return doc;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
if (operation === "update" && doc.providerId) {
|
|
1493
|
+
req.payload.logger.info("Broadcast afterChange update hook triggered", {
|
|
1494
|
+
operation,
|
|
1495
|
+
hasProviderId: !!doc.providerId,
|
|
1496
|
+
sendStatus: doc.sendStatus,
|
|
1497
|
+
publishStatus: doc._status
|
|
1470
1498
|
});
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1499
|
+
try {
|
|
1500
|
+
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
1501
|
+
if (!providerConfig || !providerConfig.token) {
|
|
1502
|
+
req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
|
|
1503
|
+
return doc;
|
|
1504
|
+
}
|
|
1505
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
1506
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1507
|
+
const capabilities = provider.getCapabilities();
|
|
1508
|
+
const sendStatus = doc.sendStatus || "draft" /* DRAFT */;
|
|
1509
|
+
if (!capabilities.editableStatuses.includes(sendStatus)) {
|
|
1510
|
+
req.payload.logger.info(`Skipping sync for broadcast in status: ${sendStatus}`);
|
|
1511
|
+
return doc;
|
|
1512
|
+
}
|
|
1513
|
+
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);
|
|
1514
|
+
if (contentChanged) {
|
|
1515
|
+
const updates = {};
|
|
1516
|
+
if (doc.subject !== previousDoc?.subject) {
|
|
1517
|
+
updates.name = doc.subject;
|
|
1518
|
+
updates.subject = doc.subject;
|
|
1519
|
+
}
|
|
1520
|
+
if (doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader) {
|
|
1521
|
+
updates.preheader = doc.contentSection?.preheader;
|
|
1522
|
+
}
|
|
1523
|
+
if (JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content)) {
|
|
1524
|
+
updates.content = await convertToEmailSafeHtml(doc.contentSection?.content);
|
|
1525
|
+
}
|
|
1526
|
+
if (doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens) {
|
|
1527
|
+
updates.trackOpens = doc.settings.trackOpens;
|
|
1528
|
+
}
|
|
1529
|
+
if (doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks) {
|
|
1530
|
+
updates.trackClicks = doc.settings.trackClicks;
|
|
1531
|
+
}
|
|
1532
|
+
if (doc.settings?.replyTo !== previousDoc?.settings?.replyTo) {
|
|
1533
|
+
updates.replyTo = doc.settings.replyTo || providerConfig.replyTo;
|
|
1534
|
+
}
|
|
1535
|
+
if (JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds)) {
|
|
1536
|
+
updates.audienceIds = doc.audienceIds?.map((a) => a.audienceId);
|
|
1537
|
+
}
|
|
1538
|
+
req.payload.logger.info("Syncing broadcast updates to provider", {
|
|
1539
|
+
providerId: doc.providerId,
|
|
1540
|
+
updates
|
|
1541
|
+
});
|
|
1542
|
+
await provider.update(doc.providerId, updates);
|
|
1543
|
+
req.payload.logger.info(`Broadcast ${doc.id} synced to provider successfully`);
|
|
1544
|
+
} else {
|
|
1545
|
+
req.payload.logger.info("No content changes to sync to provider");
|
|
1546
|
+
}
|
|
1547
|
+
} catch (error) {
|
|
1548
|
+
if (error instanceof Error) {
|
|
1549
|
+
req.payload.logger.error("Failed to sync broadcast update to provider:", {
|
|
1550
|
+
message: error.message,
|
|
1551
|
+
stack: error.stack,
|
|
1552
|
+
name: error.name,
|
|
1553
|
+
// If it's a BroadcastProviderError, it might have additional details
|
|
1554
|
+
...error.details
|
|
1555
|
+
});
|
|
1556
|
+
} else {
|
|
1557
|
+
req.payload.logger.error("Failed to sync broadcast update to provider:", error);
|
|
1558
|
+
}
|
|
1487
1559
|
}
|
|
1488
|
-
return doc;
|
|
1489
1560
|
}
|
|
1561
|
+
return doc;
|
|
1490
1562
|
},
|
|
1491
1563
|
// Hook to send when published
|
|
1492
1564
|
async ({ doc, operation, previousDoc, req }) => {
|
|
@@ -1544,79 +1616,8 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1544
1616
|
return doc;
|
|
1545
1617
|
}
|
|
1546
1618
|
],
|
|
1547
|
-
//
|
|
1548
|
-
beforeChange: [
|
|
1549
|
-
async ({ data, originalDoc, operation, req }) => {
|
|
1550
|
-
if (!hasProviders || !originalDoc?.providerId || operation !== "update") return data;
|
|
1551
|
-
req.payload.logger.info("Broadcast beforeChange update hook triggered", {
|
|
1552
|
-
operation,
|
|
1553
|
-
hasProviderId: !!originalDoc?.providerId,
|
|
1554
|
-
originalSendStatus: originalDoc?.sendStatus,
|
|
1555
|
-
originalPublishStatus: originalDoc?._status,
|
|
1556
|
-
newSendStatus: data?.sendStatus,
|
|
1557
|
-
newPublishStatus: data?._status
|
|
1558
|
-
});
|
|
1559
|
-
try {
|
|
1560
|
-
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
1561
|
-
if (!providerConfig || !providerConfig.token) {
|
|
1562
|
-
req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
|
|
1563
|
-
return data;
|
|
1564
|
-
}
|
|
1565
|
-
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
1566
|
-
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1567
|
-
const capabilities = provider.getCapabilities();
|
|
1568
|
-
const sendStatus = originalDoc.sendStatus || "draft" /* DRAFT */;
|
|
1569
|
-
if (!capabilities.editableStatuses.includes(sendStatus)) {
|
|
1570
|
-
req.payload.logger.info(`Skipping sync for broadcast in status: ${sendStatus}`);
|
|
1571
|
-
return data;
|
|
1572
|
-
}
|
|
1573
|
-
const updates = {};
|
|
1574
|
-
if (data.subject !== originalDoc.subject) {
|
|
1575
|
-
updates.name = data.subject;
|
|
1576
|
-
updates.subject = data.subject;
|
|
1577
|
-
}
|
|
1578
|
-
if (data.contentSection?.preheader !== originalDoc.contentSection?.preheader) updates.preheader = data.contentSection?.preheader;
|
|
1579
|
-
if (data.contentSection?.content !== originalDoc.contentSection?.content) {
|
|
1580
|
-
updates.content = await convertToEmailSafeHtml(data.contentSection?.content);
|
|
1581
|
-
}
|
|
1582
|
-
if (data.settings?.trackOpens !== originalDoc.settings?.trackOpens) {
|
|
1583
|
-
updates.trackOpens = data.settings.trackOpens;
|
|
1584
|
-
}
|
|
1585
|
-
if (data.settings?.trackClicks !== originalDoc.settings?.trackClicks) {
|
|
1586
|
-
updates.trackClicks = data.settings.trackClicks;
|
|
1587
|
-
}
|
|
1588
|
-
if (data.settings?.replyTo !== originalDoc.settings?.replyTo) {
|
|
1589
|
-
updates.replyTo = data.settings.replyTo || providerConfig.replyTo;
|
|
1590
|
-
}
|
|
1591
|
-
if (JSON.stringify(data.audienceIds) !== JSON.stringify(originalDoc.audienceIds)) {
|
|
1592
|
-
updates.audienceIds = data.audienceIds?.map((a) => a.audienceId);
|
|
1593
|
-
}
|
|
1594
|
-
if (Object.keys(updates).length > 0) {
|
|
1595
|
-
req.payload.logger.info("Syncing broadcast updates to provider", {
|
|
1596
|
-
providerId: originalDoc.providerId,
|
|
1597
|
-
updates
|
|
1598
|
-
});
|
|
1599
|
-
await provider.update(originalDoc.providerId, updates);
|
|
1600
|
-
req.payload.logger.info("Successfully synced broadcast updates to provider");
|
|
1601
|
-
} else {
|
|
1602
|
-
req.payload.logger.info("No broadcast updates to sync to provider");
|
|
1603
|
-
}
|
|
1604
|
-
} catch (error) {
|
|
1605
|
-
if (error instanceof Error) {
|
|
1606
|
-
req.payload.logger.error("Failed to update broadcast in provider:", {
|
|
1607
|
-
message: error.message,
|
|
1608
|
-
stack: error.stack,
|
|
1609
|
-
name: error.name,
|
|
1610
|
-
// If it's a BroadcastProviderError, it might have additional details
|
|
1611
|
-
...error.details
|
|
1612
|
-
});
|
|
1613
|
-
} else {
|
|
1614
|
-
req.payload.logger.error("Failed to update broadcast in provider:", error);
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
return data;
|
|
1618
|
-
}
|
|
1619
|
-
],
|
|
1619
|
+
// beforeChange hooks can be added here if needed
|
|
1620
|
+
beforeChange: [],
|
|
1620
1621
|
// Handle deletion
|
|
1621
1622
|
afterDelete: [
|
|
1622
1623
|
async ({ doc, req }) => {
|