payload-plugin-newsletter 0.25.11 → 0.25.12
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 +15 -0
- package/dist/collections.cjs +69 -43
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +69 -43
- package/dist/collections.js.map +1 -1
- package/dist/server.js +69 -43
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [0.25.12] - 2026-01-02
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
- New exported utilities for external preview generation:
|
|
5
|
+
- `generateBroadcastPreviewHtml()` - Generate preview HTML from broadcast content
|
|
6
|
+
- `populateMediaFields()` - Resolve Media IDs to full media objects with URLs
|
|
7
|
+
- `convertToEmailSafeHtml()` - Convert Lexical content to email-safe HTML
|
|
8
|
+
- `PreviewOptions` and `PreviewResult` types for TypeScript support
|
|
9
|
+
- Extracted media population logic to `src/utils/mediaPopulation.ts` for cleaner code organization
|
|
10
|
+
- Created `src/utils/preview.ts` with high-level preview generation utility
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Refactored `src/endpoints/broadcasts/preview.ts` to use new utility files
|
|
14
|
+
- Improved TypeScript types for media population functions
|
|
15
|
+
|
|
1
16
|
## [0.25.11] - 2025-12-11
|
|
2
17
|
|
|
3
18
|
### Fixed
|
package/dist/collections.cjs
CHANGED
|
@@ -1758,25 +1758,28 @@ var createTestBroadcastEndpoint = (config, collectionSlug) => {
|
|
|
1758
1758
|
};
|
|
1759
1759
|
};
|
|
1760
1760
|
|
|
1761
|
-
// src/
|
|
1761
|
+
// src/utils/mediaPopulation.ts
|
|
1762
1762
|
async function populateMediaFields(content, payload, config) {
|
|
1763
1763
|
if (!content || typeof content !== "object") return content;
|
|
1764
|
-
|
|
1765
|
-
|
|
1764
|
+
const typedContent = content;
|
|
1765
|
+
if (typedContent.root?.children) {
|
|
1766
|
+
for (const child of typedContent.root.children) {
|
|
1766
1767
|
await populateBlockMediaFields(child, payload, config);
|
|
1767
1768
|
}
|
|
1768
1769
|
}
|
|
1769
1770
|
return content;
|
|
1770
1771
|
}
|
|
1771
1772
|
async function populateBlockMediaFields(node, payload, config) {
|
|
1772
|
-
if (node
|
|
1773
|
-
|
|
1773
|
+
if (!node || typeof node !== "object") return;
|
|
1774
|
+
const typedNode = node;
|
|
1775
|
+
if (typedNode.type === "block" && typedNode.fields) {
|
|
1776
|
+
const blockType = typedNode.fields.blockType || typedNode.fields.blockName;
|
|
1774
1777
|
const customBlocks = config.customizations?.broadcasts?.customBlocks || [];
|
|
1775
1778
|
const blockConfig = customBlocks.find((b) => b.slug === blockType);
|
|
1776
1779
|
if (blockConfig && blockConfig.fields) {
|
|
1777
1780
|
for (const field of blockConfig.fields) {
|
|
1778
|
-
if (field.type === "upload" && field.relationTo &&
|
|
1779
|
-
const fieldValue =
|
|
1781
|
+
if (field.type === "upload" && field.relationTo && typedNode.fields[field.name]) {
|
|
1782
|
+
const fieldValue = typedNode.fields[field.name];
|
|
1780
1783
|
const collectionName = Array.isArray(field.relationTo) ? field.relationTo[0] : field.relationTo;
|
|
1781
1784
|
if (typeof fieldValue === "string" && fieldValue.match(/^[a-f0-9]{24}$/i)) {
|
|
1782
1785
|
try {
|
|
@@ -1786,26 +1789,33 @@ async function populateBlockMediaFields(node, payload, config) {
|
|
|
1786
1789
|
depth: 0
|
|
1787
1790
|
});
|
|
1788
1791
|
if (media) {
|
|
1789
|
-
|
|
1790
|
-
payload.logger?.info(
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1792
|
+
typedNode.fields[field.name] = media;
|
|
1793
|
+
payload.logger?.info(
|
|
1794
|
+
{
|
|
1795
|
+
mediaId: fieldValue,
|
|
1796
|
+
mediaUrl: media.url,
|
|
1797
|
+
filename: media.filename
|
|
1798
|
+
},
|
|
1799
|
+
`Populated ${field.name} for block ${blockType}`
|
|
1800
|
+
);
|
|
1795
1801
|
}
|
|
1796
1802
|
} catch (error) {
|
|
1797
|
-
payload.logger?.error(
|
|
1803
|
+
payload.logger?.error(
|
|
1804
|
+
{ error: String(error) },
|
|
1805
|
+
`Failed to populate ${field.name} for block ${blockType}`
|
|
1806
|
+
);
|
|
1798
1807
|
}
|
|
1799
1808
|
}
|
|
1800
1809
|
}
|
|
1801
1810
|
if (field.type === "array" && field.fields) {
|
|
1802
|
-
const arrayValue =
|
|
1811
|
+
const arrayValue = typedNode.fields[field.name];
|
|
1803
1812
|
if (Array.isArray(arrayValue)) {
|
|
1804
1813
|
for (const arrayItem of arrayValue) {
|
|
1805
1814
|
if (arrayItem && typeof arrayItem === "object") {
|
|
1815
|
+
const typedArrayItem = arrayItem;
|
|
1806
1816
|
for (const arrayField of field.fields) {
|
|
1807
|
-
if (arrayField.type === "upload" && arrayField.relationTo &&
|
|
1808
|
-
const arrayFieldValue =
|
|
1817
|
+
if (arrayField.type === "upload" && arrayField.relationTo && typedArrayItem[arrayField.name]) {
|
|
1818
|
+
const arrayFieldValue = typedArrayItem[arrayField.name];
|
|
1809
1819
|
const arrayCollectionName = Array.isArray(arrayField.relationTo) ? arrayField.relationTo[0] : arrayField.relationTo;
|
|
1810
1820
|
if (typeof arrayFieldValue === "string" && arrayFieldValue.match(/^[a-f0-9]{24}$/i)) {
|
|
1811
1821
|
try {
|
|
@@ -1815,15 +1825,21 @@ async function populateBlockMediaFields(node, payload, config) {
|
|
|
1815
1825
|
depth: 0
|
|
1816
1826
|
});
|
|
1817
1827
|
if (media) {
|
|
1818
|
-
|
|
1819
|
-
payload.logger?.info(
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1828
|
+
typedArrayItem[arrayField.name] = media;
|
|
1829
|
+
payload.logger?.info(
|
|
1830
|
+
{
|
|
1831
|
+
mediaId: arrayFieldValue,
|
|
1832
|
+
mediaUrl: media.url,
|
|
1833
|
+
filename: media.filename
|
|
1834
|
+
},
|
|
1835
|
+
`Populated array ${arrayField.name} for block ${blockType}`
|
|
1836
|
+
);
|
|
1824
1837
|
}
|
|
1825
1838
|
} catch (error) {
|
|
1826
|
-
payload.logger?.error(
|
|
1839
|
+
payload.logger?.error(
|
|
1840
|
+
{ error: String(error) },
|
|
1841
|
+
`Failed to populate array ${arrayField.name} for block ${blockType}`
|
|
1842
|
+
);
|
|
1827
1843
|
}
|
|
1828
1844
|
}
|
|
1829
1845
|
}
|
|
@@ -1832,23 +1848,24 @@ async function populateBlockMediaFields(node, payload, config) {
|
|
|
1832
1848
|
}
|
|
1833
1849
|
}
|
|
1834
1850
|
}
|
|
1835
|
-
if (field.type === "richText" &&
|
|
1836
|
-
await populateRichTextUploads(
|
|
1851
|
+
if (field.type === "richText" && typedNode.fields[field.name]) {
|
|
1852
|
+
await populateRichTextUploads(typedNode.fields[field.name], payload);
|
|
1837
1853
|
payload.logger?.info(`Processed rich text field ${field.name} for upload nodes`);
|
|
1838
1854
|
}
|
|
1839
1855
|
}
|
|
1840
1856
|
}
|
|
1841
1857
|
}
|
|
1842
|
-
if (
|
|
1843
|
-
for (const child of
|
|
1858
|
+
if (typedNode.children) {
|
|
1859
|
+
for (const child of typedNode.children) {
|
|
1844
1860
|
await populateBlockMediaFields(child, payload, config);
|
|
1845
1861
|
}
|
|
1846
1862
|
}
|
|
1847
1863
|
}
|
|
1848
1864
|
async function populateRichTextUploads(content, payload) {
|
|
1849
1865
|
if (!content || typeof content !== "object") return;
|
|
1850
|
-
|
|
1851
|
-
|
|
1866
|
+
const typedContent = content;
|
|
1867
|
+
if (typedContent.root?.children) {
|
|
1868
|
+
await processNodeArray(typedContent.root.children);
|
|
1852
1869
|
}
|
|
1853
1870
|
if (Array.isArray(content)) {
|
|
1854
1871
|
await processNodeArray(content);
|
|
@@ -1858,33 +1875,42 @@ async function populateRichTextUploads(content, payload) {
|
|
|
1858
1875
|
}
|
|
1859
1876
|
async function processNode(node) {
|
|
1860
1877
|
if (!node || typeof node !== "object") return;
|
|
1861
|
-
|
|
1878
|
+
const typedNode = node;
|
|
1879
|
+
if (typedNode.type === "upload" && typedNode.relationTo === "media" && typeof typedNode.value === "string" && typedNode.value.match(/^[a-f0-9]{24}$/i)) {
|
|
1862
1880
|
try {
|
|
1863
1881
|
const media = await payload.findByID({
|
|
1864
1882
|
collection: "media",
|
|
1865
|
-
id:
|
|
1883
|
+
id: typedNode.value,
|
|
1866
1884
|
depth: 0
|
|
1867
1885
|
});
|
|
1868
1886
|
if (media) {
|
|
1869
|
-
|
|
1870
|
-
payload.logger?.info(
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1887
|
+
typedNode.value = media;
|
|
1888
|
+
payload.logger?.info(
|
|
1889
|
+
{
|
|
1890
|
+
mediaId: typedNode.value,
|
|
1891
|
+
mediaUrl: media.url,
|
|
1892
|
+
filename: media.filename
|
|
1893
|
+
},
|
|
1894
|
+
"Populated rich text upload node"
|
|
1895
|
+
);
|
|
1875
1896
|
}
|
|
1876
1897
|
} catch (error) {
|
|
1877
|
-
payload.logger?.error(
|
|
1898
|
+
payload.logger?.error(
|
|
1899
|
+
{ error: String(error) },
|
|
1900
|
+
`Failed to populate rich text upload ${typedNode.value}`
|
|
1901
|
+
);
|
|
1878
1902
|
}
|
|
1879
1903
|
}
|
|
1880
|
-
if (
|
|
1881
|
-
await processNodeArray(
|
|
1904
|
+
if (typedNode.children && Array.isArray(typedNode.children)) {
|
|
1905
|
+
await processNodeArray(typedNode.children);
|
|
1882
1906
|
}
|
|
1883
|
-
if (
|
|
1884
|
-
await processNodeArray(
|
|
1907
|
+
if (typedNode.root?.children && Array.isArray(typedNode.root.children)) {
|
|
1908
|
+
await processNodeArray(typedNode.root.children);
|
|
1885
1909
|
}
|
|
1886
1910
|
}
|
|
1887
1911
|
}
|
|
1912
|
+
|
|
1913
|
+
// src/endpoints/broadcasts/preview.ts
|
|
1888
1914
|
var createBroadcastPreviewEndpoint = (config, _collectionSlug) => {
|
|
1889
1915
|
return {
|
|
1890
1916
|
path: "/preview",
|