payload-plugin-newsletter 0.4.5 → 0.6.1
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 +29 -2
- package/ESM_FIX_SUMMARY.md +74 -0
- package/dist/index.cjs +47 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +47 -119
- package/dist/index.js.map +1 -1
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
## [0.
|
|
1
|
+
## [0.6.1] - 2025-06-20
|
|
2
|
+
|
|
3
|
+
- fix: update Broadcast provider to use correct API endpoints
|
|
4
|
+
|
|
2
5
|
|
|
3
|
-
- fix:
|
|
6
|
+
- fix: resolve ESLint error for unused variable in test mock
|
|
7
|
+
- fix: update tests to support newsletter settings as global
|
|
8
|
+
- feat: convert newsletter settings from collection to global
|
|
4
9
|
|
|
5
10
|
|
|
6
11
|
All notable changes to this project will be documented in this file.
|
|
@@ -8,6 +13,28 @@ All notable changes to this project will be documented in this file.
|
|
|
8
13
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
9
14
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
10
15
|
|
|
16
|
+
## [0.5.0] - 2025-06-20
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- **BREAKING**: Changed newsletter settings from a collection back to a global configuration
|
|
20
|
+
- Settings are now accessed as a single global config instead of multiple documents
|
|
21
|
+
- Removed the "name" and "active" fields as they're no longer needed for globals
|
|
22
|
+
- Settings now appear as a single page in the admin UI instead of a list view
|
|
23
|
+
- Updated all code to use `payload.findGlobal()` instead of `payload.find()`
|
|
24
|
+
|
|
25
|
+
### Migration Required
|
|
26
|
+
- Users with existing newsletter-settings collections will need to manually copy their active configuration to the new global settings
|
|
27
|
+
- After migration, the old newsletter-settings collection can be removed from the database
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- Resolved user confusion around having multiple settings documents when only one could be active
|
|
31
|
+
- Settings now follow the standard Payload pattern for configuration globals
|
|
32
|
+
|
|
33
|
+
## [0.4.5] - 2025-06-19
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
- Added tsup build system for proper ESM/CJS dual package support
|
|
37
|
+
|
|
11
38
|
## [0.4.4] - 2025-06-16
|
|
12
39
|
|
|
13
40
|
### Fixed
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ESM/CJS Module Resolution Fix Summary
|
|
2
|
+
|
|
3
|
+
## Changes Made
|
|
4
|
+
|
|
5
|
+
### 1. **Added tsup Build Tool**
|
|
6
|
+
- Added `tsup` v8.3.5 to devDependencies for proper ESM/CJS dual package support
|
|
7
|
+
- Created `tsup.config.ts` with configuration for building both ESM and CJS outputs
|
|
8
|
+
|
|
9
|
+
### 2. **Updated package.json**
|
|
10
|
+
- Changed entry points from source files (`./src/`) to built files (`./dist/`)
|
|
11
|
+
- Added proper exports configuration with both ESM and CJS support:
|
|
12
|
+
- ESM files: `.js` extension with `.d.ts` types
|
|
13
|
+
- CJS files: `.cjs` extension with `.d.cts` types
|
|
14
|
+
- Updated build scripts to use tsup
|
|
15
|
+
- Added `module` field pointing to ESM entry
|
|
16
|
+
|
|
17
|
+
### 3. **Updated tsconfig.json**
|
|
18
|
+
- Changed `moduleResolution` from "bundler" to "node" for better compatibility
|
|
19
|
+
|
|
20
|
+
### 4. **Created Build Script**
|
|
21
|
+
- Added `build-and-commit.sh` for easy building and verification
|
|
22
|
+
|
|
23
|
+
## File Structure After Build
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
dist/
|
|
27
|
+
├── index.js # ESM main entry
|
|
28
|
+
├── index.cjs # CJS main entry
|
|
29
|
+
├── index.d.ts # TypeScript definitions
|
|
30
|
+
├── index.d.cts # CJS TypeScript definitions
|
|
31
|
+
├── client.js # ESM client export
|
|
32
|
+
├── client.cjs # CJS client export
|
|
33
|
+
├── client.d.ts # Client TypeScript definitions
|
|
34
|
+
├── client.d.cts # CJS Client TypeScript definitions
|
|
35
|
+
├── types.js # ESM types export
|
|
36
|
+
├── types.cjs # CJS types export
|
|
37
|
+
├── types.d.ts # Types TypeScript definitions
|
|
38
|
+
├── types.d.cts # CJS Types TypeScript definitions
|
|
39
|
+
├── components.js # ESM components export
|
|
40
|
+
├── components.cjs # CJS components export
|
|
41
|
+
├── components.d.ts # Components TypeScript definitions
|
|
42
|
+
└── components.d.cts # CJS Components TypeScript definitions
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## How to Build and Publish
|
|
46
|
+
|
|
47
|
+
1. **Install dependencies**: `bun install`
|
|
48
|
+
2. **Build the package**: `bun run build`
|
|
49
|
+
3. **Test locally**: Link the package to test in your project
|
|
50
|
+
4. **Publish**: `npm publish` (dist files will be included automatically)
|
|
51
|
+
|
|
52
|
+
## Benefits
|
|
53
|
+
|
|
54
|
+
1. **Proper ESM Support**: Next.js apps using `"type": "module"` can now import the package without issues
|
|
55
|
+
2. **CJS Compatibility**: Still works with CommonJS projects
|
|
56
|
+
3. **TypeScript Support**: Proper type definitions for both module systems
|
|
57
|
+
4. **Clean Exports**: Clear separation of server/client code with proper exports
|
|
58
|
+
5. **Future Proof**: Ready for the ESM-first ecosystem
|
|
59
|
+
|
|
60
|
+
## Testing the Fix
|
|
61
|
+
|
|
62
|
+
To test in your ContentQuant project:
|
|
63
|
+
1. Build the plugin: `bun run build`
|
|
64
|
+
2. Link locally: `npm link` in plugin directory
|
|
65
|
+
3. Link in project: `npm link payload-plugin-newsletter` in ContentQuant
|
|
66
|
+
4. Import and use normally
|
|
67
|
+
|
|
68
|
+
## Next Steps
|
|
69
|
+
|
|
70
|
+
1. Run `./build-and-commit.sh` to build the package
|
|
71
|
+
2. Test the built package locally
|
|
72
|
+
3. Commit changes with message: "fix: add tsup build system for proper ESM/CJS dual package support"
|
|
73
|
+
4. Push to repository
|
|
74
|
+
5. Publish new version to npm
|
package/dist/index.cjs
CHANGED
|
@@ -357,40 +357,17 @@ var createSubscribersCollection = (pluginConfig) => {
|
|
|
357
357
|
return subscribersCollection;
|
|
358
358
|
};
|
|
359
359
|
|
|
360
|
-
// src/
|
|
361
|
-
var
|
|
360
|
+
// src/globals/NewsletterSettings.ts
|
|
361
|
+
var createNewsletterSettingsGlobal = (pluginConfig) => {
|
|
362
362
|
const slug = pluginConfig.settingsSlug || "newsletter-settings";
|
|
363
363
|
return {
|
|
364
364
|
slug,
|
|
365
|
-
|
|
366
|
-
singular: "Newsletter Setting",
|
|
367
|
-
plural: "Newsletter Settings"
|
|
368
|
-
},
|
|
365
|
+
label: "Newsletter Settings",
|
|
369
366
|
admin: {
|
|
370
|
-
useAsTitle: "name",
|
|
371
|
-
defaultColumns: ["name", "provider", "active", "updatedAt"],
|
|
372
367
|
group: "Newsletter",
|
|
373
368
|
description: "Configure email provider settings and templates"
|
|
374
369
|
},
|
|
375
370
|
fields: [
|
|
376
|
-
{
|
|
377
|
-
name: "name",
|
|
378
|
-
type: "text",
|
|
379
|
-
label: "Configuration Name",
|
|
380
|
-
required: true,
|
|
381
|
-
admin: {
|
|
382
|
-
description: 'A descriptive name for this configuration (e.g., "Production", "Development", "Marketing Emails")'
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
{
|
|
386
|
-
name: "active",
|
|
387
|
-
type: "checkbox",
|
|
388
|
-
label: "Active",
|
|
389
|
-
defaultValue: false,
|
|
390
|
-
admin: {
|
|
391
|
-
description: "Only one configuration can be active at a time"
|
|
392
|
-
}
|
|
393
|
-
},
|
|
394
371
|
{
|
|
395
372
|
type: "tabs",
|
|
396
373
|
tabs: [
|
|
@@ -650,53 +627,16 @@ var createNewsletterSettingsCollection = (pluginConfig) => {
|
|
|
650
627
|
],
|
|
651
628
|
hooks: {
|
|
652
629
|
beforeChange: [
|
|
653
|
-
async ({ data, req
|
|
630
|
+
async ({ data, req }) => {
|
|
654
631
|
if (!req.user || req.user.collection !== "users") {
|
|
655
632
|
throw new Error("Only administrators can modify newsletter settings");
|
|
656
633
|
}
|
|
657
|
-
if (data?.active && operation !== "create") {
|
|
658
|
-
await req.payload.update({
|
|
659
|
-
collection: slug,
|
|
660
|
-
where: {
|
|
661
|
-
id: {
|
|
662
|
-
not_equals: data.id
|
|
663
|
-
}
|
|
664
|
-
},
|
|
665
|
-
data: {
|
|
666
|
-
active: false
|
|
667
|
-
}
|
|
668
|
-
// Keep overrideAccess: true for admin operations after verification
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
if (operation === "create" && data?.active) {
|
|
672
|
-
const existingActive = await req.payload.find({
|
|
673
|
-
collection: slug,
|
|
674
|
-
where: {
|
|
675
|
-
active: {
|
|
676
|
-
equals: true
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
// Keep overrideAccess: true for admin operations
|
|
680
|
-
});
|
|
681
|
-
if (existingActive.docs.length > 0) {
|
|
682
|
-
for (const doc of existingActive.docs) {
|
|
683
|
-
await req.payload.update({
|
|
684
|
-
collection: slug,
|
|
685
|
-
id: doc.id,
|
|
686
|
-
data: {
|
|
687
|
-
active: false
|
|
688
|
-
}
|
|
689
|
-
// Keep overrideAccess: true for admin operations
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
634
|
return data;
|
|
695
635
|
}
|
|
696
636
|
],
|
|
697
637
|
afterChange: [
|
|
698
638
|
async ({ doc, req }) => {
|
|
699
|
-
if (req.payload.newsletterEmailService
|
|
639
|
+
if (req.payload.newsletterEmailService) {
|
|
700
640
|
try {
|
|
701
641
|
console.warn("Newsletter settings updated, reinitializing service...");
|
|
702
642
|
} catch {
|
|
@@ -709,11 +649,8 @@ var createNewsletterSettingsCollection = (pluginConfig) => {
|
|
|
709
649
|
access: {
|
|
710
650
|
read: () => true,
|
|
711
651
|
// Settings can be read publicly for validation
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
delete: adminOnly(pluginConfig)
|
|
715
|
-
},
|
|
716
|
-
timestamps: true
|
|
652
|
+
update: adminOnly(pluginConfig)
|
|
653
|
+
}
|
|
717
654
|
};
|
|
718
655
|
};
|
|
719
656
|
|
|
@@ -900,20 +837,22 @@ var BroadcastProvider = class {
|
|
|
900
837
|
}
|
|
901
838
|
async addContact(contact) {
|
|
902
839
|
try {
|
|
903
|
-
const
|
|
840
|
+
const [firstName, ...lastNameParts] = (contact.name || "").split(" ");
|
|
841
|
+
const lastName = lastNameParts.join(" ");
|
|
842
|
+
const response = await fetch(`${this.apiUrl}/api/v1/subscribers.json`, {
|
|
904
843
|
method: "POST",
|
|
905
844
|
headers: {
|
|
906
845
|
"Authorization": `Bearer ${this.token}`,
|
|
907
846
|
"Content-Type": "application/json"
|
|
908
847
|
},
|
|
909
848
|
body: JSON.stringify({
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
849
|
+
subscriber: {
|
|
850
|
+
email: contact.email,
|
|
851
|
+
first_name: firstName || void 0,
|
|
852
|
+
last_name: lastName || void 0,
|
|
853
|
+
tags: [`lang:${contact.locale || "en"}`],
|
|
854
|
+
is_active: contact.subscriptionStatus === "active",
|
|
855
|
+
source: contact.source
|
|
917
856
|
}
|
|
918
857
|
})
|
|
919
858
|
});
|
|
@@ -932,7 +871,7 @@ var BroadcastProvider = class {
|
|
|
932
871
|
async updateContact(contact) {
|
|
933
872
|
try {
|
|
934
873
|
const searchResponse = await fetch(
|
|
935
|
-
`${this.apiUrl}/api/v1/
|
|
874
|
+
`${this.apiUrl}/api/v1/subscribers/find.json?email=${encodeURIComponent(contact.email)}`,
|
|
936
875
|
{
|
|
937
876
|
headers: {
|
|
938
877
|
"Authorization": `Bearer ${this.token}`
|
|
@@ -943,26 +882,27 @@ var BroadcastProvider = class {
|
|
|
943
882
|
await this.addContact(contact);
|
|
944
883
|
return;
|
|
945
884
|
}
|
|
946
|
-
const
|
|
947
|
-
|
|
948
|
-
if (!existingContact) {
|
|
885
|
+
const existingContact = await searchResponse.json();
|
|
886
|
+
if (!existingContact || !existingContact.id) {
|
|
949
887
|
await this.addContact(contact);
|
|
950
888
|
return;
|
|
951
889
|
}
|
|
952
|
-
const
|
|
953
|
-
|
|
890
|
+
const [firstName, ...lastNameParts] = (contact.name || "").split(" ");
|
|
891
|
+
const lastName = lastNameParts.join(" ");
|
|
892
|
+
const response = await fetch(`${this.apiUrl}/api/v1/subscribers.json`, {
|
|
893
|
+
method: "PATCH",
|
|
954
894
|
headers: {
|
|
955
895
|
"Authorization": `Bearer ${this.token}`,
|
|
956
896
|
"Content-Type": "application/json"
|
|
957
897
|
},
|
|
958
898
|
body: JSON.stringify({
|
|
959
899
|
email: contact.email,
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
900
|
+
subscriber: {
|
|
901
|
+
first_name: firstName || void 0,
|
|
902
|
+
last_name: lastName || void 0,
|
|
903
|
+
tags: [`lang:${contact.locale || "en"}`],
|
|
904
|
+
is_active: contact.subscriptionStatus === "active",
|
|
905
|
+
source: contact.source
|
|
966
906
|
}
|
|
967
907
|
})
|
|
968
908
|
});
|
|
@@ -981,7 +921,7 @@ var BroadcastProvider = class {
|
|
|
981
921
|
async removeContact(email) {
|
|
982
922
|
try {
|
|
983
923
|
const searchResponse = await fetch(
|
|
984
|
-
`${this.apiUrl}/api/v1/
|
|
924
|
+
`${this.apiUrl}/api/v1/subscribers/find.json?email=${encodeURIComponent(email)}`,
|
|
985
925
|
{
|
|
986
926
|
headers: {
|
|
987
927
|
"Authorization": `Bearer ${this.token}`
|
|
@@ -991,16 +931,17 @@ var BroadcastProvider = class {
|
|
|
991
931
|
if (!searchResponse.ok) {
|
|
992
932
|
return;
|
|
993
933
|
}
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
if (!contact) {
|
|
934
|
+
const contact = await searchResponse.json();
|
|
935
|
+
if (!contact || !contact.id) {
|
|
997
936
|
return;
|
|
998
937
|
}
|
|
999
|
-
const response = await fetch(`${this.apiUrl}/api/v1/
|
|
1000
|
-
method: "
|
|
938
|
+
const response = await fetch(`${this.apiUrl}/api/v1/subscribers/deactivate.json`, {
|
|
939
|
+
method: "POST",
|
|
1001
940
|
headers: {
|
|
1002
|
-
"Authorization": `Bearer ${this.token}
|
|
1003
|
-
|
|
941
|
+
"Authorization": `Bearer ${this.token}`,
|
|
942
|
+
"Content-Type": "application/json"
|
|
943
|
+
},
|
|
944
|
+
body: JSON.stringify({ email })
|
|
1004
945
|
});
|
|
1005
946
|
if (!response.ok) {
|
|
1006
947
|
const error = await response.text();
|
|
@@ -1193,18 +1134,11 @@ var createSubscribeEndpoint = (config) => {
|
|
|
1193
1134
|
errors: validation.errors
|
|
1194
1135
|
});
|
|
1195
1136
|
}
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
where: {
|
|
1199
|
-
active: {
|
|
1200
|
-
equals: true
|
|
1201
|
-
}
|
|
1202
|
-
},
|
|
1203
|
-
limit: 1,
|
|
1137
|
+
const settings = await req.payload.findGlobal({
|
|
1138
|
+
slug: config.settingsSlug || "newsletter-settings",
|
|
1204
1139
|
overrideAccess: false
|
|
1205
1140
|
// No user context for public endpoint
|
|
1206
1141
|
});
|
|
1207
|
-
const settings = settingsResult.docs[0];
|
|
1208
1142
|
const allowedDomains = settings?.subscriptionSettings?.allowedDomains?.map((d) => d.domain) || [];
|
|
1209
1143
|
if (!isDomainAllowed(trimmedEmail, allowedDomains)) {
|
|
1210
1144
|
return res.status(400).json({
|
|
@@ -1909,8 +1843,8 @@ var newsletterPlugin = (pluginConfig) => (incomingConfig) => {
|
|
|
1909
1843
|
return incomingConfig;
|
|
1910
1844
|
}
|
|
1911
1845
|
const subscribersCollection = createSubscribersCollection(config);
|
|
1912
|
-
const
|
|
1913
|
-
let collections = [...incomingConfig.collections || [], subscribersCollection
|
|
1846
|
+
const settingsGlobal = createNewsletterSettingsGlobal(config);
|
|
1847
|
+
let collections = [...incomingConfig.collections || [], subscribersCollection];
|
|
1914
1848
|
if (config.features?.newsletterScheduling?.enabled) {
|
|
1915
1849
|
const targetCollections = config.features.newsletterScheduling.collections || "articles";
|
|
1916
1850
|
const collectionsToExtend = Array.isArray(targetCollections) ? targetCollections : [targetCollections];
|
|
@@ -1933,7 +1867,8 @@ var newsletterPlugin = (pluginConfig) => (incomingConfig) => {
|
|
|
1933
1867
|
...incomingConfig,
|
|
1934
1868
|
collections,
|
|
1935
1869
|
globals: [
|
|
1936
|
-
...incomingConfig.globals || []
|
|
1870
|
+
...incomingConfig.globals || [],
|
|
1871
|
+
settingsGlobal
|
|
1937
1872
|
],
|
|
1938
1873
|
endpoints: [
|
|
1939
1874
|
...incomingConfig.endpoints || [],
|
|
@@ -1941,16 +1876,9 @@ var newsletterPlugin = (pluginConfig) => (incomingConfig) => {
|
|
|
1941
1876
|
],
|
|
1942
1877
|
onInit: async (payload) => {
|
|
1943
1878
|
try {
|
|
1944
|
-
const
|
|
1945
|
-
|
|
1946
|
-
where: {
|
|
1947
|
-
active: {
|
|
1948
|
-
equals: true
|
|
1949
|
-
}
|
|
1950
|
-
},
|
|
1951
|
-
limit: 1
|
|
1879
|
+
const settings = await payload.findGlobal({
|
|
1880
|
+
slug: config.settingsSlug || "newsletter-settings"
|
|
1952
1881
|
});
|
|
1953
|
-
const settings = settingsResult.docs[0];
|
|
1954
1882
|
let emailServiceConfig;
|
|
1955
1883
|
if (settings) {
|
|
1956
1884
|
emailServiceConfig = {
|