payload-plugin-newsletter 0.25.8 → 0.25.11
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 +22 -0
- package/dist/collections.cjs +68 -142
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +68 -142
- package/dist/collections.js.map +1 -1
- package/dist/fields.cjs +4 -38
- package/dist/fields.cjs.map +1 -1
- package/dist/fields.js +4 -38
- package/dist/fields.js.map +1 -1
- package/dist/server.js +68 -142
- package/dist/utils.cjs +2 -2
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.js +2 -2
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
## [0.25.11] - 2025-12-11
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
- Fixed LinkFeature configuration causing URLs to disappear in the Lexical editor link drawer
|
|
5
|
+
- The custom `fields` array was replacing Payload's default fields, removing the crucial `linkType` field
|
|
6
|
+
- Now uses default LinkFeature fields which include all required fields for the drawer UI to work correctly
|
|
7
|
+
- Fixed TypeScript errors with Payload logger API (pino signature requires object first, then message)
|
|
8
|
+
- Fixed `relationTo` type errors where `string | string[]` was passed to `findByID`
|
|
9
|
+
|
|
10
|
+
## [0.25.9] - 2025-08-19
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Fixed providerId/externalId not being saved during broadcast creation
|
|
14
|
+
- Moved provider creation from create to update operation
|
|
15
|
+
- Provider broadcast is now created on first update when subject and content are present
|
|
16
|
+
- Ensures IDs are properly persisted to the database
|
|
17
|
+
- Prevents issues with Payload's create operation not persisting afterChange modifications
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Broadcasts are now created in Payload first, then synced to provider on first meaningful update
|
|
21
|
+
- More robust approach that works with Payload's hook lifecycle
|
|
22
|
+
|
|
1
23
|
## [0.25.8] - 2025-08-19
|
|
2
24
|
|
|
3
25
|
### Fixed
|
package/dist/collections.cjs
CHANGED
|
@@ -754,25 +754,8 @@ var createEmailSafeFeatures = (additionalBlocks) => {
|
|
|
754
754
|
(0, import_richtext_lexical.ItalicFeature)(),
|
|
755
755
|
(0, import_richtext_lexical.UnderlineFeature)(),
|
|
756
756
|
(0, import_richtext_lexical.StrikethroughFeature)(),
|
|
757
|
-
// Links
|
|
758
|
-
(0, import_richtext_lexical.LinkFeature)(
|
|
759
|
-
fields: [
|
|
760
|
-
{
|
|
761
|
-
name: "url",
|
|
762
|
-
type: "text",
|
|
763
|
-
required: true,
|
|
764
|
-
admin: {
|
|
765
|
-
description: "Enter the full URL (including https://)"
|
|
766
|
-
}
|
|
767
|
-
},
|
|
768
|
-
{
|
|
769
|
-
name: "newTab",
|
|
770
|
-
type: "checkbox",
|
|
771
|
-
label: "Open in new tab",
|
|
772
|
-
defaultValue: false
|
|
773
|
-
}
|
|
774
|
-
]
|
|
775
|
-
}),
|
|
757
|
+
// Links - use default fields to ensure drawer UI works correctly
|
|
758
|
+
(0, import_richtext_lexical.LinkFeature)(),
|
|
776
759
|
// Lists
|
|
777
760
|
(0, import_richtext_lexical.OrderedListFeature)(),
|
|
778
761
|
(0, import_richtext_lexical.UnorderedListFeature)(),
|
|
@@ -828,25 +811,8 @@ var createEmailLexicalEditor = (customBlocks = []) => {
|
|
|
828
811
|
(0, import_richtext_lexical.ItalicFeature)(),
|
|
829
812
|
(0, import_richtext_lexical.UnderlineFeature)(),
|
|
830
813
|
(0, import_richtext_lexical.StrikethroughFeature)(),
|
|
831
|
-
// Links
|
|
832
|
-
(0, import_richtext_lexical.LinkFeature)(
|
|
833
|
-
fields: [
|
|
834
|
-
{
|
|
835
|
-
name: "url",
|
|
836
|
-
type: "text",
|
|
837
|
-
required: true,
|
|
838
|
-
admin: {
|
|
839
|
-
description: "Enter the full URL (including https://)"
|
|
840
|
-
}
|
|
841
|
-
},
|
|
842
|
-
{
|
|
843
|
-
name: "newTab",
|
|
844
|
-
type: "checkbox",
|
|
845
|
-
label: "Open in new tab",
|
|
846
|
-
defaultValue: false
|
|
847
|
-
}
|
|
848
|
-
]
|
|
849
|
-
}),
|
|
814
|
+
// Links - use default fields to ensure drawer UI works correctly
|
|
815
|
+
(0, import_richtext_lexical.LinkFeature)(),
|
|
850
816
|
// Lists
|
|
851
817
|
(0, import_richtext_lexical.OrderedListFeature)(),
|
|
852
818
|
(0, import_richtext_lexical.UnorderedListFeature)(),
|
|
@@ -1414,7 +1380,7 @@ async function getBroadcastConfig(req, pluginConfig) {
|
|
|
1414
1380
|
}
|
|
1415
1381
|
return pluginConfig.providers?.broadcast || null;
|
|
1416
1382
|
} catch (error) {
|
|
1417
|
-
req.payload.logger.error("Failed to get broadcast config from settings
|
|
1383
|
+
req.payload.logger.error({ error: String(error) }, "Failed to get broadcast config from settings");
|
|
1418
1384
|
return pluginConfig.providers?.broadcast || null;
|
|
1419
1385
|
}
|
|
1420
1386
|
}
|
|
@@ -1811,23 +1777,24 @@ async function populateBlockMediaFields(node, payload, config) {
|
|
|
1811
1777
|
for (const field of blockConfig.fields) {
|
|
1812
1778
|
if (field.type === "upload" && field.relationTo && node.fields[field.name]) {
|
|
1813
1779
|
const fieldValue = node.fields[field.name];
|
|
1780
|
+
const collectionName = Array.isArray(field.relationTo) ? field.relationTo[0] : field.relationTo;
|
|
1814
1781
|
if (typeof fieldValue === "string" && fieldValue.match(/^[a-f0-9]{24}$/i)) {
|
|
1815
1782
|
try {
|
|
1816
1783
|
const media = await payload.findByID({
|
|
1817
|
-
collection:
|
|
1784
|
+
collection: collectionName,
|
|
1818
1785
|
id: fieldValue,
|
|
1819
1786
|
depth: 0
|
|
1820
1787
|
});
|
|
1821
1788
|
if (media) {
|
|
1822
1789
|
node.fields[field.name] = media;
|
|
1823
|
-
payload.logger?.info(
|
|
1790
|
+
payload.logger?.info({
|
|
1824
1791
|
mediaId: fieldValue,
|
|
1825
1792
|
mediaUrl: media.url,
|
|
1826
1793
|
filename: media.filename
|
|
1827
|
-
});
|
|
1794
|
+
}, `Populated ${field.name} for block ${blockType}`);
|
|
1828
1795
|
}
|
|
1829
1796
|
} catch (error) {
|
|
1830
|
-
payload.logger?.error(`Failed to populate ${field.name} for block ${blockType}
|
|
1797
|
+
payload.logger?.error({ error: String(error) }, `Failed to populate ${field.name} for block ${blockType}`);
|
|
1831
1798
|
}
|
|
1832
1799
|
}
|
|
1833
1800
|
}
|
|
@@ -1839,23 +1806,24 @@ async function populateBlockMediaFields(node, payload, config) {
|
|
|
1839
1806
|
for (const arrayField of field.fields) {
|
|
1840
1807
|
if (arrayField.type === "upload" && arrayField.relationTo && arrayItem[arrayField.name]) {
|
|
1841
1808
|
const arrayFieldValue = arrayItem[arrayField.name];
|
|
1809
|
+
const arrayCollectionName = Array.isArray(arrayField.relationTo) ? arrayField.relationTo[0] : arrayField.relationTo;
|
|
1842
1810
|
if (typeof arrayFieldValue === "string" && arrayFieldValue.match(/^[a-f0-9]{24}$/i)) {
|
|
1843
1811
|
try {
|
|
1844
1812
|
const media = await payload.findByID({
|
|
1845
|
-
collection:
|
|
1813
|
+
collection: arrayCollectionName,
|
|
1846
1814
|
id: arrayFieldValue,
|
|
1847
1815
|
depth: 0
|
|
1848
1816
|
});
|
|
1849
1817
|
if (media) {
|
|
1850
1818
|
arrayItem[arrayField.name] = media;
|
|
1851
|
-
payload.logger?.info(
|
|
1819
|
+
payload.logger?.info({
|
|
1852
1820
|
mediaId: arrayFieldValue,
|
|
1853
1821
|
mediaUrl: media.url,
|
|
1854
1822
|
filename: media.filename
|
|
1855
|
-
});
|
|
1823
|
+
}, `Populated array ${arrayField.name} for block ${blockType}`);
|
|
1856
1824
|
}
|
|
1857
1825
|
} catch (error) {
|
|
1858
|
-
payload.logger?.error(`Failed to populate array ${arrayField.name} for block ${blockType}
|
|
1826
|
+
payload.logger?.error({ error: String(error) }, `Failed to populate array ${arrayField.name} for block ${blockType}`);
|
|
1859
1827
|
}
|
|
1860
1828
|
}
|
|
1861
1829
|
}
|
|
@@ -1899,14 +1867,14 @@ async function populateRichTextUploads(content, payload) {
|
|
|
1899
1867
|
});
|
|
1900
1868
|
if (media) {
|
|
1901
1869
|
node.value = media;
|
|
1902
|
-
payload.logger?.info(
|
|
1870
|
+
payload.logger?.info({
|
|
1903
1871
|
mediaId: node.value,
|
|
1904
1872
|
mediaUrl: media.url,
|
|
1905
1873
|
filename: media.filename
|
|
1906
|
-
});
|
|
1874
|
+
}, "Populated rich text upload node");
|
|
1907
1875
|
}
|
|
1908
1876
|
} catch (error) {
|
|
1909
|
-
payload.logger?.error(`Failed to populate rich text upload ${node.value}
|
|
1877
|
+
payload.logger?.error({ error: String(error) }, `Failed to populate rich text upload ${node.value}`);
|
|
1910
1878
|
}
|
|
1911
1879
|
}
|
|
1912
1880
|
if (node.children && Array.isArray(node.children)) {
|
|
@@ -2306,93 +2274,19 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
2306
2274
|
async ({ doc, operation, req, previousDoc }) => {
|
|
2307
2275
|
if (!hasProviders) return doc;
|
|
2308
2276
|
if (operation === "create") {
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
docId: doc.id,
|
|
2312
|
-
docIdType: typeof doc.id,
|
|
2313
|
-
hasDoc: !!doc,
|
|
2314
|
-
operation,
|
|
2315
|
-
status: doc._status,
|
|
2316
|
-
hasExternalId: !!doc.externalId,
|
|
2317
|
-
hasProviderId: !!doc.providerId
|
|
2318
|
-
});
|
|
2319
|
-
if (doc.externalId || doc.providerId) {
|
|
2320
|
-
req.payload.logger.info("Broadcast already has provider IDs, skipping creation", {
|
|
2321
|
-
externalId: doc.externalId,
|
|
2322
|
-
providerId: doc.providerId
|
|
2323
|
-
});
|
|
2324
|
-
return doc;
|
|
2325
|
-
}
|
|
2326
|
-
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
2327
|
-
if (!providerConfig || !providerConfig.token) {
|
|
2328
|
-
req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
|
|
2329
|
-
return doc;
|
|
2330
|
-
}
|
|
2331
|
-
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
2332
|
-
const provider = new BroadcastApiProvider2(providerConfig);
|
|
2333
|
-
const subject = doc.subject || `Draft Broadcast ${(/* @__PURE__ */ new Date()).toISOString()}`;
|
|
2334
|
-
const htmlContent = doc.contentSection?.content ? await convertToEmailSafeHtml(
|
|
2335
|
-
await populateMediaFields(doc.contentSection.content, req.payload, pluginConfig),
|
|
2336
|
-
{
|
|
2337
|
-
wrapInTemplate: pluginConfig.customizations?.broadcasts?.emailPreview?.wrapInTemplate ?? true,
|
|
2338
|
-
customWrapper: pluginConfig.customizations?.broadcasts?.emailPreview?.customWrapper,
|
|
2339
|
-
preheader: doc.contentSection?.preheader,
|
|
2340
|
-
subject,
|
|
2341
|
-
documentData: doc,
|
|
2342
|
-
customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter
|
|
2343
|
-
}
|
|
2344
|
-
) : "<p>Draft content - to be updated</p>";
|
|
2345
|
-
const createData = {
|
|
2346
|
-
name: subject,
|
|
2347
|
-
// Use subject as name
|
|
2348
|
-
subject,
|
|
2349
|
-
preheader: doc.contentSection?.preheader || "",
|
|
2350
|
-
content: htmlContent,
|
|
2351
|
-
trackOpens: doc.settings?.trackOpens ?? true,
|
|
2352
|
-
trackClicks: doc.settings?.trackClicks ?? true,
|
|
2353
|
-
replyTo: doc.settings?.replyTo || providerConfig.replyTo,
|
|
2354
|
-
audienceIds: doc.audienceIds?.map((a) => a.audienceId) || []
|
|
2355
|
-
};
|
|
2356
|
-
req.payload.logger.info("Creating broadcast in provider with minimal data to establish association", {
|
|
2357
|
-
subject: createData.subject,
|
|
2358
|
-
hasActualContent: !!doc.contentSection?.content
|
|
2359
|
-
});
|
|
2360
|
-
const providerBroadcast = await provider.create(createData);
|
|
2361
|
-
req.payload.logger.info("Provider broadcast created:", {
|
|
2362
|
-
providerBroadcastId: providerBroadcast.id,
|
|
2363
|
-
providerBroadcastIdType: typeof providerBroadcast.id,
|
|
2364
|
-
docId: doc.id,
|
|
2365
|
-
docIdType: typeof doc.id
|
|
2366
|
-
});
|
|
2367
|
-
req.payload.logger.info(`Broadcast ${doc.id} created in provider with ID ${providerBroadcast.id}`);
|
|
2368
|
-
return {
|
|
2369
|
-
...doc,
|
|
2370
|
-
providerId: providerBroadcast.id,
|
|
2371
|
-
externalId: providerBroadcast.id,
|
|
2372
|
-
// Include externalId in return value
|
|
2373
|
-
providerData: providerBroadcast.providerData
|
|
2374
|
-
};
|
|
2375
|
-
} catch (error) {
|
|
2376
|
-
req.payload.logger.error("Failed to create broadcast in provider during initial creation:");
|
|
2377
|
-
if (error instanceof Error) {
|
|
2378
|
-
req.payload.logger.error("Error details:", {
|
|
2379
|
-
message: error.message,
|
|
2380
|
-
stack: error.stack,
|
|
2381
|
-
name: error.name
|
|
2382
|
-
});
|
|
2383
|
-
} else {
|
|
2384
|
-
req.payload.logger.error("Raw error:", error);
|
|
2385
|
-
}
|
|
2386
|
-
return doc;
|
|
2387
|
-
}
|
|
2277
|
+
req.payload.logger.info("Broadcast created in Payload, provider sync will happen on first update with content");
|
|
2278
|
+
return doc;
|
|
2388
2279
|
}
|
|
2389
2280
|
if (operation === "update") {
|
|
2390
|
-
req.payload.logger.info(
|
|
2281
|
+
req.payload.logger.info({
|
|
2391
2282
|
operation,
|
|
2392
2283
|
hasProviderId: !!doc.providerId,
|
|
2284
|
+
hasExternalId: !!doc.externalId,
|
|
2393
2285
|
sendStatus: doc.sendStatus,
|
|
2394
|
-
publishStatus: doc._status
|
|
2395
|
-
|
|
2286
|
+
publishStatus: doc._status,
|
|
2287
|
+
hasSubject: !!doc.subject,
|
|
2288
|
+
hasContent: !!doc.contentSection?.content
|
|
2289
|
+
}, "Broadcast afterChange update hook triggered");
|
|
2396
2290
|
try {
|
|
2397
2291
|
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
2398
2292
|
if (!providerConfig || !providerConfig.token) {
|
|
@@ -2401,8 +2295,40 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
2401
2295
|
}
|
|
2402
2296
|
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
2403
2297
|
const provider = new BroadcastApiProvider2(providerConfig);
|
|
2298
|
+
if (!doc.providerId && !doc.externalId && doc.subject && doc.contentSection?.content) {
|
|
2299
|
+
req.payload.logger.info("Creating broadcast in provider on first update with content");
|
|
2300
|
+
const htmlContent = await convertToEmailSafeHtml(
|
|
2301
|
+
await populateMediaFields(doc.contentSection.content, req.payload, pluginConfig),
|
|
2302
|
+
{
|
|
2303
|
+
wrapInTemplate: pluginConfig.customizations?.broadcasts?.emailPreview?.wrapInTemplate ?? true,
|
|
2304
|
+
customWrapper: pluginConfig.customizations?.broadcasts?.emailPreview?.customWrapper,
|
|
2305
|
+
preheader: doc.contentSection?.preheader,
|
|
2306
|
+
subject: doc.subject,
|
|
2307
|
+
documentData: doc,
|
|
2308
|
+
customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter
|
|
2309
|
+
}
|
|
2310
|
+
);
|
|
2311
|
+
const createData = {
|
|
2312
|
+
name: doc.subject,
|
|
2313
|
+
subject: doc.subject,
|
|
2314
|
+
preheader: doc.contentSection?.preheader || "",
|
|
2315
|
+
content: htmlContent,
|
|
2316
|
+
trackOpens: doc.settings?.trackOpens ?? true,
|
|
2317
|
+
trackClicks: doc.settings?.trackClicks ?? true,
|
|
2318
|
+
replyTo: doc.settings?.replyTo || providerConfig.replyTo,
|
|
2319
|
+
audienceIds: doc.audienceIds?.map((a) => a.audienceId) || []
|
|
2320
|
+
};
|
|
2321
|
+
const providerBroadcast = await provider.create(createData);
|
|
2322
|
+
req.payload.logger.info(`Broadcast ${doc.id} created in provider with ID ${providerBroadcast.id}`);
|
|
2323
|
+
return {
|
|
2324
|
+
...doc,
|
|
2325
|
+
providerId: providerBroadcast.id,
|
|
2326
|
+
externalId: providerBroadcast.id,
|
|
2327
|
+
providerData: providerBroadcast.providerData
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2404
2330
|
if (!doc.providerId) {
|
|
2405
|
-
req.payload.logger.
|
|
2331
|
+
req.payload.logger.info(`Broadcast ${doc.id} has no providerId and insufficient content for creation - skipping sync`);
|
|
2406
2332
|
return doc;
|
|
2407
2333
|
}
|
|
2408
2334
|
if (doc.providerId) {
|
|
@@ -2447,10 +2373,10 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
2447
2373
|
if (JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds)) {
|
|
2448
2374
|
updates.audienceIds = doc.audienceIds?.map((a) => a.audienceId);
|
|
2449
2375
|
}
|
|
2450
|
-
req.payload.logger.info(
|
|
2376
|
+
req.payload.logger.info({
|
|
2451
2377
|
providerId: doc.providerId,
|
|
2452
2378
|
updates
|
|
2453
|
-
});
|
|
2379
|
+
}, "Syncing broadcast updates to provider");
|
|
2454
2380
|
await provider.update(doc.providerId, updates);
|
|
2455
2381
|
req.payload.logger.info(`Broadcast ${doc.id} synced to provider successfully`);
|
|
2456
2382
|
} else {
|
|
@@ -2472,18 +2398,18 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
2472
2398
|
...error.statusText
|
|
2473
2399
|
});
|
|
2474
2400
|
} else if (typeof error === "string") {
|
|
2475
|
-
req.payload.logger.error("Error is a string
|
|
2401
|
+
req.payload.logger.error({ errorValue: error }, "Error is a string");
|
|
2476
2402
|
} else if (error && typeof error === "object") {
|
|
2477
|
-
req.payload.logger.error(
|
|
2403
|
+
req.payload.logger.error({ errorValue: JSON.stringify(error, null, 2) }, "Error is an object");
|
|
2478
2404
|
} else {
|
|
2479
|
-
req.payload.logger.error("Unknown error type
|
|
2405
|
+
req.payload.logger.error({ errorType: typeof error }, "Unknown error type");
|
|
2480
2406
|
}
|
|
2481
|
-
req.payload.logger.error(
|
|
2407
|
+
req.payload.logger.error({
|
|
2482
2408
|
id: doc.id,
|
|
2483
2409
|
subject: doc.subject,
|
|
2484
2410
|
hasContent: !!doc.contentSection?.content,
|
|
2485
2411
|
contentType: doc.contentSection?.content ? typeof doc.contentSection.content : "none"
|
|
2486
|
-
});
|
|
2412
|
+
}, "Failed broadcast document (update operation)");
|
|
2487
2413
|
}
|
|
2488
2414
|
}
|
|
2489
2415
|
return doc;
|
|
@@ -2529,7 +2455,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
2529
2455
|
...error.details
|
|
2530
2456
|
});
|
|
2531
2457
|
} else {
|
|
2532
|
-
req.payload.logger.error(`Failed to send broadcast ${doc.id}
|
|
2458
|
+
req.payload.logger.error({ error: String(error) }, `Failed to send broadcast ${doc.id}`);
|
|
2533
2459
|
}
|
|
2534
2460
|
await req.payload.update({
|
|
2535
2461
|
collection: "broadcasts",
|
|
@@ -2572,7 +2498,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
2572
2498
|
...error.details
|
|
2573
2499
|
});
|
|
2574
2500
|
} else {
|
|
2575
|
-
req.payload.logger.error("Failed to delete broadcast from provider
|
|
2501
|
+
req.payload.logger.error({ error: String(error) }, "Failed to delete broadcast from provider");
|
|
2576
2502
|
}
|
|
2577
2503
|
}
|
|
2578
2504
|
return doc;
|