payload-plugin-newsletter 0.25.11 → 0.25.13

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 CHANGED
@@ -1,3 +1,24 @@
1
+ ## [0.25.13] - 2026-01-02
2
+
3
+ ### Fixed
4
+ - Fixed server exports to include preview utilities from `src/server/index.ts`
5
+ - Preview utilities are now properly accessible when importing from `payload-plugin-newsletter`
6
+
7
+ ## [0.25.12] - 2026-01-02
8
+
9
+ ### Added
10
+ - New exported utilities for external preview generation:
11
+ - `generateBroadcastPreviewHtml()` - Generate preview HTML from broadcast content
12
+ - `populateMediaFields()` - Resolve Media IDs to full media objects with URLs
13
+ - `convertToEmailSafeHtml()` - Convert Lexical content to email-safe HTML
14
+ - `PreviewOptions` and `PreviewResult` types for TypeScript support
15
+ - Extracted media population logic to `src/utils/mediaPopulation.ts` for cleaner code organization
16
+ - Created `src/utils/preview.ts` with high-level preview generation utility
17
+
18
+ ### Changed
19
+ - Refactored `src/endpoints/broadcasts/preview.ts` to use new utility files
20
+ - Improved TypeScript types for media population functions
21
+
1
22
  ## [0.25.11] - 2025-12-11
2
23
 
3
24
  ### Fixed
@@ -1758,25 +1758,28 @@ var createTestBroadcastEndpoint = (config, collectionSlug) => {
1758
1758
  };
1759
1759
  };
1760
1760
 
1761
- // src/endpoints/broadcasts/preview.ts
1761
+ // src/utils/mediaPopulation.ts
1762
1762
  async function populateMediaFields(content, payload, config) {
1763
1763
  if (!content || typeof content !== "object") return content;
1764
- if (content.root?.children) {
1765
- for (const child of content.root.children) {
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.type === "block" && node.fields) {
1773
- const blockType = node.fields.blockType || node.fields.blockName;
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 && node.fields[field.name]) {
1779
- const fieldValue = node.fields[field.name];
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
- node.fields[field.name] = media;
1790
- payload.logger?.info({
1791
- mediaId: fieldValue,
1792
- mediaUrl: media.url,
1793
- filename: media.filename
1794
- }, `Populated ${field.name} for block ${blockType}`);
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({ error: String(error) }, `Failed to populate ${field.name} for block ${blockType}`);
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 = node.fields[field.name];
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 && arrayItem[arrayField.name]) {
1808
- const arrayFieldValue = arrayItem[arrayField.name];
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
- arrayItem[arrayField.name] = media;
1819
- payload.logger?.info({
1820
- mediaId: arrayFieldValue,
1821
- mediaUrl: media.url,
1822
- filename: media.filename
1823
- }, `Populated array ${arrayField.name} for block ${blockType}`);
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({ error: String(error) }, `Failed to populate array ${arrayField.name} for block ${blockType}`);
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" && node.fields[field.name]) {
1836
- await populateRichTextUploads(node.fields[field.name], payload);
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 (node.children) {
1843
- for (const child of node.children) {
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
- if (content.root?.children) {
1851
- await processNodeArray(content.root.children);
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
- if (node.type === "upload" && node.relationTo === "media" && typeof node.value === "string" && node.value.match(/^[a-f0-9]{24}$/i)) {
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: node.value,
1883
+ id: typedNode.value,
1866
1884
  depth: 0
1867
1885
  });
1868
1886
  if (media) {
1869
- node.value = media;
1870
- payload.logger?.info({
1871
- mediaId: node.value,
1872
- mediaUrl: media.url,
1873
- filename: media.filename
1874
- }, "Populated rich text upload node");
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({ error: String(error) }, `Failed to populate rich text upload ${node.value}`);
1898
+ payload.logger?.error(
1899
+ { error: String(error) },
1900
+ `Failed to populate rich text upload ${typedNode.value}`
1901
+ );
1878
1902
  }
1879
1903
  }
1880
- if (node.children && Array.isArray(node.children)) {
1881
- await processNodeArray(node.children);
1904
+ if (typedNode.children && Array.isArray(typedNode.children)) {
1905
+ await processNodeArray(typedNode.children);
1882
1906
  }
1883
- if (node.root?.children && Array.isArray(node.root.children)) {
1884
- await processNodeArray(node.root.children);
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",