payload-plugin-newsletter 0.20.0 → 0.20.2

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.
@@ -0,0 +1,735 @@
1
+ import { Field, Block, RichTextField, Config, Endpoint, CollectionConfig, GlobalConfig } from 'payload';
2
+
3
+ /**
4
+ * Core types for broadcast management functionality
5
+ */
6
+ /**
7
+ * Represents a broadcast (individual email campaign) in the system
8
+ */
9
+ interface Broadcast {
10
+ id: string;
11
+ name: string;
12
+ subject: string;
13
+ preheader?: string;
14
+ content: string;
15
+ sendStatus: BroadcastStatus;
16
+ trackOpens: boolean;
17
+ trackClicks: boolean;
18
+ replyTo?: string;
19
+ recipientCount?: number;
20
+ sentAt?: Date;
21
+ scheduledAt?: Date;
22
+ createdAt: Date;
23
+ updatedAt: Date;
24
+ providerData?: Record<string, any>;
25
+ providerId?: string;
26
+ providerType?: 'broadcast' | 'resend';
27
+ }
28
+ /**
29
+ * Possible statuses for a broadcast
30
+ */
31
+ declare enum BroadcastStatus {
32
+ DRAFT = "draft",
33
+ SCHEDULED = "scheduled",
34
+ SENDING = "sending",
35
+ SENT = "sent",
36
+ FAILED = "failed",
37
+ PAUSED = "paused",
38
+ CANCELED = "canceled"
39
+ }
40
+ /**
41
+ * Options for listing broadcasts
42
+ */
43
+ interface ListBroadcastOptions {
44
+ limit?: number;
45
+ offset?: number;
46
+ status?: BroadcastStatus;
47
+ sortBy?: 'createdAt' | 'updatedAt' | 'sentAt' | 'name';
48
+ sortOrder?: 'asc' | 'desc';
49
+ }
50
+ /**
51
+ * Response from listing broadcasts
52
+ */
53
+ interface ListBroadcastResponse<T = Broadcast> {
54
+ items: T[];
55
+ total: number;
56
+ limit: number;
57
+ offset: number;
58
+ hasMore: boolean;
59
+ }
60
+ /**
61
+ * Input for creating a new broadcast
62
+ */
63
+ interface CreateBroadcastInput {
64
+ name: string;
65
+ subject: string;
66
+ preheader?: string;
67
+ content: string;
68
+ trackOpens?: boolean;
69
+ trackClicks?: boolean;
70
+ replyTo?: string;
71
+ audienceIds?: string[];
72
+ }
73
+ /**
74
+ * Input for updating an existing broadcast
75
+ */
76
+ interface UpdateBroadcastInput {
77
+ name?: string;
78
+ subject?: string;
79
+ preheader?: string;
80
+ content?: string;
81
+ trackOpens?: boolean;
82
+ trackClicks?: boolean;
83
+ replyTo?: string;
84
+ audienceIds?: string[];
85
+ }
86
+ /**
87
+ * Options for sending a broadcast
88
+ */
89
+ interface SendBroadcastOptions {
90
+ audienceIds?: string[];
91
+ testMode?: boolean;
92
+ testRecipients?: string[];
93
+ }
94
+ /**
95
+ * Analytics data for a broadcast
96
+ */
97
+ interface BroadcastAnalytics {
98
+ sent: number;
99
+ delivered: number;
100
+ opened: number;
101
+ clicked: number;
102
+ bounced: number;
103
+ complained: number;
104
+ unsubscribed: number;
105
+ deliveryRate?: number;
106
+ openRate?: number;
107
+ clickRate?: number;
108
+ bounceRate?: number;
109
+ }
110
+ /**
111
+ * Capabilities that a broadcast provider supports
112
+ */
113
+ interface BroadcastProviderCapabilities {
114
+ supportsScheduling: boolean;
115
+ supportsSegmentation: boolean;
116
+ supportsAnalytics: boolean;
117
+ supportsABTesting: boolean;
118
+ supportsTemplates: boolean;
119
+ supportsPersonalization: boolean;
120
+ maxRecipientsPerSend?: number;
121
+ editableStatuses: BroadcastStatus[];
122
+ supportedContentTypes: ('html' | 'text' | 'react')[];
123
+ supportsMultipleChannels: boolean;
124
+ supportsChannelSegmentation: boolean;
125
+ }
126
+
127
+ /**
128
+ * Provider interfaces for broadcast management
129
+ */
130
+
131
+ /**
132
+ * Main interface for broadcast providers
133
+ */
134
+ interface BroadcastProvider$1 {
135
+ /**
136
+ * Get the provider name
137
+ */
138
+ readonly name: string;
139
+ /**
140
+ * List broadcasts with pagination
141
+ */
142
+ list(options?: ListBroadcastOptions): Promise<ListBroadcastResponse<Broadcast>>;
143
+ /**
144
+ * Get a specific broadcast by ID
145
+ */
146
+ get(id: string): Promise<Broadcast>;
147
+ /**
148
+ * Create a new broadcast
149
+ */
150
+ create(data: CreateBroadcastInput): Promise<Broadcast>;
151
+ /**
152
+ * Update an existing broadcast
153
+ */
154
+ update(id: string, data: UpdateBroadcastInput): Promise<Broadcast>;
155
+ /**
156
+ * Delete a broadcast
157
+ */
158
+ delete(id: string): Promise<void>;
159
+ /**
160
+ * Send a broadcast immediately or to test recipients
161
+ */
162
+ send(id: string, options?: SendBroadcastOptions): Promise<Broadcast>;
163
+ /**
164
+ * Schedule a broadcast for future sending
165
+ */
166
+ schedule(id: string, scheduledAt: Date): Promise<Broadcast>;
167
+ /**
168
+ * Cancel a scheduled broadcast
169
+ */
170
+ cancelSchedule(id: string): Promise<Broadcast>;
171
+ /**
172
+ * Get analytics for a broadcast
173
+ */
174
+ getAnalytics(id: string): Promise<BroadcastAnalytics>;
175
+ /**
176
+ * Get provider capabilities
177
+ */
178
+ getCapabilities(): BroadcastProviderCapabilities;
179
+ /**
180
+ * Validate that the provider is properly configured
181
+ */
182
+ validateConfiguration(): Promise<boolean>;
183
+ }
184
+
185
+ interface BroadcastCustomizations {
186
+ additionalFields?: Field[];
187
+ customBlocks?: Block[];
188
+ fieldOverrides?: {
189
+ content?: (defaultField: RichTextField) => RichTextField;
190
+ };
191
+ /**
192
+ * Custom block email converter
193
+ * @param node - The block node from Lexical editor state
194
+ * @param mediaUrl - Base URL for media files
195
+ * @returns Promise<string> - The email-safe HTML for the block
196
+ */
197
+ customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>;
198
+ /**
199
+ * Fields to populate in custom blocks before email conversion
200
+ * Can be an array of field names or a function that returns field names based on block type
201
+ * This is useful for upload fields that need to be populated with full media objects
202
+ *
203
+ * @example
204
+ * // Array of field names to always populate
205
+ * populateFields: ['bannerImage', 'sponsorLogo']
206
+ *
207
+ * @example
208
+ * // Function to return fields based on block type
209
+ * populateFields: (blockType) => {
210
+ * if (blockType === 'newsletter-hero') return ['bannerImage', 'sponsorLogo']
211
+ * if (blockType === 'content-section') return ['featuredImage']
212
+ * return []
213
+ * }
214
+ */
215
+ populateFields?: string[] | ((blockType: string) => string[]);
216
+ /**
217
+ * Email preview customization options
218
+ */
219
+ emailPreview?: {
220
+ /**
221
+ * Whether to wrap preview content in default email template
222
+ * @default true
223
+ */
224
+ wrapInTemplate?: boolean;
225
+ /**
226
+ * Custom wrapper function for preview content
227
+ * Receives the converted HTML and should return wrapped HTML
228
+ */
229
+ customWrapper?: (content: string, options?: {
230
+ subject?: string;
231
+ preheader?: string;
232
+ }) => string | Promise<string>;
233
+ /**
234
+ * Custom preview component to replace the default one entirely
235
+ * If provided, this component will be used instead of the default EmailPreview
236
+ */
237
+ customPreviewComponent?: string;
238
+ };
239
+ }
240
+ interface NewsletterPluginConfig {
241
+ /**
242
+ * Enable or disable the plugin
243
+ * @default true
244
+ */
245
+ enabled?: boolean;
246
+ /**
247
+ * Slug for the subscribers collection
248
+ * @default 'subscribers'
249
+ */
250
+ subscribersSlug?: string;
251
+ /**
252
+ * Slug for the newsletter settings global
253
+ * @default 'newsletter-settings'
254
+ */
255
+ settingsSlug?: string;
256
+ /**
257
+ * Authentication configuration for magic links
258
+ */
259
+ auth?: {
260
+ /**
261
+ * Enable magic link authentication
262
+ * @default true
263
+ */
264
+ enabled?: boolean;
265
+ /**
266
+ * Token expiration time
267
+ * @default '7d'
268
+ */
269
+ tokenExpiration?: string;
270
+ /**
271
+ * Path where magic link redirects
272
+ * @default '/newsletter/verify'
273
+ */
274
+ magicLinkPath?: string;
275
+ /**
276
+ * Allow unsubscribed users to sign in
277
+ * @default false
278
+ */
279
+ allowUnsubscribedSignin?: boolean;
280
+ /**
281
+ * Allow unsubscribed users to resubscribe
282
+ * @default false
283
+ */
284
+ allowResubscribe?: boolean;
285
+ };
286
+ /**
287
+ * Access control configuration
288
+ */
289
+ access?: {
290
+ /**
291
+ * Custom function to determine if a user is an admin
292
+ * @param user - The authenticated user object
293
+ * @returns true if the user should have admin access
294
+ */
295
+ isAdmin?: (user: any) => boolean;
296
+ };
297
+ /**
298
+ * Email provider configuration
299
+ */
300
+ providers: {
301
+ /**
302
+ * Default provider to use
303
+ */
304
+ default: 'resend' | 'broadcast' | string;
305
+ /**
306
+ * Resend provider configuration
307
+ */
308
+ resend?: ResendProviderConfig;
309
+ /**
310
+ * Broadcast provider configuration
311
+ */
312
+ broadcast?: BroadcastProviderConfig;
313
+ };
314
+ /**
315
+ * Field customization options
316
+ */
317
+ fields?: {
318
+ /**
319
+ * Override default fields
320
+ */
321
+ overrides?: (args: {
322
+ defaultFields: Field[];
323
+ }) => Field[];
324
+ /**
325
+ * Additional custom fields
326
+ */
327
+ additional?: Field[];
328
+ };
329
+ /**
330
+ * Email template components
331
+ */
332
+ templates?: {
333
+ /**
334
+ * Welcome email template
335
+ */
336
+ welcome?: React.ComponentType<WelcomeEmailProps>;
337
+ /**
338
+ * Magic link email template
339
+ */
340
+ magicLink?: React.ComponentType<MagicLinkEmailProps>;
341
+ };
342
+ /**
343
+ * Plugin hooks
344
+ */
345
+ hooks?: {
346
+ beforeSubscribe?: (args: BeforeSubscribeArgs) => void | Promise<void>;
347
+ afterSubscribe?: (args: AfterSubscribeArgs) => void | Promise<void>;
348
+ beforeUnsubscribe?: (args: BeforeUnsubscribeArgs) => void | Promise<void>;
349
+ afterUnsubscribe?: (args: AfterUnsubscribeArgs) => void | Promise<void>;
350
+ afterUnsubscribeSync?: (args: AfterUnsubscribeSyncArgs) => void | Promise<void>;
351
+ };
352
+ /**
353
+ * UI component overrides
354
+ */
355
+ components?: {
356
+ signupForm?: React.ComponentType<SignupFormProps>;
357
+ preferencesForm?: React.ComponentType<PreferencesFormProps>;
358
+ };
359
+ /**
360
+ * Feature flags
361
+ */
362
+ features?: {
363
+ /**
364
+ * Lead magnet configuration
365
+ */
366
+ leadMagnets?: {
367
+ enabled?: boolean;
368
+ collection?: string;
369
+ };
370
+ /**
371
+ * Post-signup survey configuration
372
+ */
373
+ surveys?: {
374
+ enabled?: boolean;
375
+ questions?: SurveyQuestion[];
376
+ };
377
+ /**
378
+ * UTM tracking configuration
379
+ */
380
+ utmTracking?: {
381
+ enabled?: boolean;
382
+ fields?: string[];
383
+ };
384
+ /**
385
+ * Newsletter scheduling configuration
386
+ */
387
+ newsletterScheduling?: {
388
+ enabled?: boolean;
389
+ /**
390
+ * Collections to add newsletter fields to
391
+ * Can be a string for single collection or array for multiple
392
+ * @example 'articles' or ['articles', 'posts', 'updates']
393
+ */
394
+ collections?: string | string[];
395
+ /**
396
+ * Field configuration
397
+ */
398
+ fields?: {
399
+ /**
400
+ * Group name for newsletter fields
401
+ * @default 'newsletterScheduling'
402
+ */
403
+ groupName?: string;
404
+ /**
405
+ * Rich text field name to use for content
406
+ * @default 'content'
407
+ */
408
+ contentField?: string;
409
+ /**
410
+ * Whether to create a markdown companion field
411
+ * @default true
412
+ */
413
+ createMarkdownField?: boolean;
414
+ };
415
+ };
416
+ /**
417
+ * Unsubscribe sync configuration
418
+ */
419
+ unsubscribeSync?: {
420
+ /**
421
+ * Enable sync of unsubscribes from email service to Payload
422
+ * @default false
423
+ */
424
+ enabled?: boolean;
425
+ /**
426
+ * Cron schedule for sync job (e.g., '0 * * * *' for hourly)
427
+ * If not provided, job must be triggered manually
428
+ */
429
+ schedule?: string;
430
+ /**
431
+ * Queue name for the sync job
432
+ * @default 'newsletter-sync'
433
+ */
434
+ queue?: string;
435
+ };
436
+ /**
437
+ * Newsletter management configuration
438
+ */
439
+ newsletterManagement?: {
440
+ /**
441
+ * Enable newsletter management features
442
+ * @default false
443
+ */
444
+ enabled?: boolean;
445
+ /**
446
+ * Collection names for broadcast management
447
+ */
448
+ collections?: {
449
+ /**
450
+ * Broadcasts collection slug
451
+ * @default 'broadcasts'
452
+ */
453
+ broadcasts?: string;
454
+ };
455
+ /**
456
+ * Optional: Custom broadcast provider implementation
457
+ * If not provided, will use the default email provider
458
+ */
459
+ provider?: BroadcastProvider$1;
460
+ };
461
+ };
462
+ /**
463
+ * Internationalization configuration
464
+ */
465
+ i18n?: {
466
+ defaultLocale?: string;
467
+ locales?: string[];
468
+ };
469
+ /**
470
+ * Custom email templates
471
+ */
472
+ customTemplates?: {
473
+ [key: string]: React.ComponentType<any>;
474
+ };
475
+ /**
476
+ * Customization options for plugin collections
477
+ */
478
+ customizations?: {
479
+ broadcasts?: BroadcastCustomizations;
480
+ };
481
+ }
482
+ interface ResendProviderConfig {
483
+ apiKey: string;
484
+ fromEmail?: string;
485
+ fromAddress?: string;
486
+ fromName?: string;
487
+ replyTo?: string;
488
+ audienceIds?: {
489
+ [locale: string]: {
490
+ production?: string;
491
+ development?: string;
492
+ };
493
+ };
494
+ }
495
+ interface BroadcastProviderConfig {
496
+ apiUrl: string;
497
+ token: string;
498
+ fromEmail?: string;
499
+ fromAddress?: string;
500
+ fromName?: string;
501
+ replyTo?: string;
502
+ }
503
+ interface Subscriber {
504
+ id: string;
505
+ email: string;
506
+ name?: string;
507
+ locale?: string;
508
+ subscriptionStatus: 'active' | 'unsubscribed' | 'pending';
509
+ emailPreferences?: {
510
+ newsletter?: boolean;
511
+ announcements?: boolean;
512
+ [key: string]: boolean | undefined;
513
+ };
514
+ source?: string;
515
+ utmParameters?: {
516
+ source?: string;
517
+ medium?: string;
518
+ campaign?: string;
519
+ content?: string;
520
+ term?: string;
521
+ };
522
+ signupMetadata?: {
523
+ ipAddress?: string;
524
+ userAgent?: string;
525
+ referrer?: string;
526
+ signupPage?: string;
527
+ };
528
+ leadMagnet?: string;
529
+ unsubscribedAt?: string;
530
+ magicLinkToken?: string;
531
+ magicLinkTokenExpiry?: string;
532
+ createdAt: string;
533
+ updatedAt: string;
534
+ }
535
+ interface WelcomeEmailProps {
536
+ subscriber: Subscriber;
537
+ unsubscribeUrl: string;
538
+ preferencesUrl: string;
539
+ }
540
+ interface MagicLinkEmailProps {
541
+ magicLinkUrl: string;
542
+ subscriber: Subscriber;
543
+ }
544
+ interface SignupFormProps {
545
+ onSuccess?: (subscriber: Subscriber) => void;
546
+ onError?: (error: Error) => void;
547
+ showName?: boolean;
548
+ showPreferences?: boolean;
549
+ leadMagnet?: {
550
+ id: string;
551
+ title: string;
552
+ description?: string;
553
+ };
554
+ className?: string;
555
+ styles?: {
556
+ form?: React.CSSProperties;
557
+ inputGroup?: React.CSSProperties;
558
+ label?: React.CSSProperties;
559
+ input?: React.CSSProperties;
560
+ button?: React.CSSProperties;
561
+ buttonDisabled?: React.CSSProperties;
562
+ error?: React.CSSProperties;
563
+ success?: React.CSSProperties;
564
+ checkbox?: React.CSSProperties;
565
+ checkboxInput?: React.CSSProperties;
566
+ checkboxLabel?: React.CSSProperties;
567
+ };
568
+ apiEndpoint?: string;
569
+ buttonText?: string;
570
+ loadingText?: string;
571
+ successMessage?: string;
572
+ placeholders?: {
573
+ email?: string;
574
+ name?: string;
575
+ };
576
+ labels?: {
577
+ email?: string;
578
+ name?: string;
579
+ newsletter?: string;
580
+ announcements?: string;
581
+ };
582
+ }
583
+ interface PreferencesFormProps {
584
+ subscriber?: Subscriber;
585
+ onSuccess?: (subscriber: Subscriber) => void;
586
+ onError?: (error: Error) => void;
587
+ className?: string;
588
+ styles?: {
589
+ container?: React.CSSProperties;
590
+ heading?: React.CSSProperties;
591
+ form?: React.CSSProperties;
592
+ section?: React.CSSProperties;
593
+ sectionTitle?: React.CSSProperties;
594
+ inputGroup?: React.CSSProperties;
595
+ label?: React.CSSProperties;
596
+ input?: React.CSSProperties;
597
+ select?: React.CSSProperties;
598
+ checkbox?: React.CSSProperties;
599
+ checkboxInput?: React.CSSProperties;
600
+ checkboxLabel?: React.CSSProperties;
601
+ buttonGroup?: React.CSSProperties;
602
+ button?: React.CSSProperties;
603
+ primaryButton?: React.CSSProperties;
604
+ secondaryButton?: React.CSSProperties;
605
+ dangerButton?: React.CSSProperties;
606
+ error?: React.CSSProperties;
607
+ success?: React.CSSProperties;
608
+ info?: React.CSSProperties;
609
+ };
610
+ sessionToken?: string;
611
+ apiEndpoint?: string;
612
+ showUnsubscribe?: boolean;
613
+ locales?: string[];
614
+ labels?: {
615
+ title?: string;
616
+ personalInfo?: string;
617
+ emailPreferences?: string;
618
+ name?: string;
619
+ language?: string;
620
+ newsletter?: string;
621
+ announcements?: string;
622
+ saveButton?: string;
623
+ unsubscribeButton?: string;
624
+ saving?: string;
625
+ saved?: string;
626
+ unsubscribeConfirm?: string;
627
+ };
628
+ }
629
+ interface BeforeSubscribeArgs {
630
+ data: Partial<Subscriber>;
631
+ req: any;
632
+ }
633
+ interface AfterSubscribeArgs {
634
+ doc: Subscriber;
635
+ req: any;
636
+ }
637
+ interface BeforeUnsubscribeArgs {
638
+ email: string;
639
+ req: any;
640
+ }
641
+ interface AfterUnsubscribeArgs {
642
+ doc: Subscriber;
643
+ req: any;
644
+ }
645
+ interface AfterUnsubscribeSyncArgs {
646
+ req: any;
647
+ syncedCount: number;
648
+ }
649
+ interface SurveyQuestion {
650
+ id: string;
651
+ question: string;
652
+ type: 'text' | 'select' | 'multiselect' | 'radio';
653
+ options?: string[];
654
+ required?: boolean;
655
+ }
656
+
657
+ declare module 'payload' {
658
+ interface BasePayload {
659
+ newsletterEmailService?: any;
660
+ broadcastProvider?: BroadcastProvider$1;
661
+ newsletterProvider?: BroadcastProvider$1;
662
+ }
663
+ }
664
+ declare const newsletterPlugin: (pluginConfig: NewsletterPluginConfig) => (incomingConfig: Config) => Config;
665
+
666
+ declare const createSubscribeEndpoint: (config: NewsletterPluginConfig) => Endpoint;
667
+
668
+ declare const createUnsubscribeEndpoint: (config: NewsletterPluginConfig) => Endpoint;
669
+
670
+ declare const createPreferencesEndpoint: (config: NewsletterPluginConfig) => Endpoint;
671
+
672
+ declare const createVerifyMagicLinkEndpoint: (config: NewsletterPluginConfig) => Endpoint;
673
+
674
+ declare const createSubscribersCollection: (pluginConfig: NewsletterPluginConfig) => CollectionConfig;
675
+
676
+ declare const createBroadcastsCollection: (pluginConfig: NewsletterPluginConfig) => CollectionConfig;
677
+
678
+ declare const createNewsletterSettingsGlobal: (pluginConfig: NewsletterPluginConfig) => GlobalConfig;
679
+
680
+ interface EmailProvider {
681
+ send(params: SendEmailParams): Promise<void>;
682
+ addContact(contact: Subscriber): Promise<void>;
683
+ updateContact(contact: Subscriber): Promise<void>;
684
+ removeContact(email: string): Promise<void>;
685
+ getProvider(): string;
686
+ }
687
+ interface SendEmailParams {
688
+ to: string | string[];
689
+ subject: string;
690
+ html?: string;
691
+ text?: string;
692
+ react?: React.ReactElement;
693
+ from?: {
694
+ email: string;
695
+ name?: string;
696
+ };
697
+ replyTo?: string;
698
+ }
699
+
700
+ declare class ResendProvider implements EmailProvider {
701
+ private client;
702
+ private audienceIds;
703
+ private fromAddress;
704
+ private fromName;
705
+ private isDevelopment;
706
+ constructor(config: ResendProviderConfig & {
707
+ fromAddress: string;
708
+ fromName: string;
709
+ });
710
+ getProvider(): string;
711
+ send(params: SendEmailParams): Promise<void>;
712
+ addContact(contact: Subscriber): Promise<void>;
713
+ updateContact(contact: Subscriber): Promise<void>;
714
+ removeContact(email: string): Promise<void>;
715
+ private getAudienceId;
716
+ }
717
+
718
+ declare class BroadcastProvider implements EmailProvider {
719
+ private apiUrl;
720
+ private token;
721
+ private fromAddress;
722
+ private fromName;
723
+ private replyTo?;
724
+ constructor(config: BroadcastProviderConfig & {
725
+ fromAddress: string;
726
+ fromName: string;
727
+ });
728
+ getProvider(): string;
729
+ send(params: SendEmailParams): Promise<void>;
730
+ addContact(contact: Subscriber): Promise<void>;
731
+ updateContact(contact: Subscriber): Promise<void>;
732
+ removeContact(email: string): Promise<void>;
733
+ }
734
+
735
+ export { BroadcastProvider, type NewsletterPluginConfig, ResendProvider, type Subscriber, createBroadcastsCollection, createNewsletterSettingsGlobal, createPreferencesEndpoint, createSubscribeEndpoint, createSubscribersCollection, createUnsubscribeEndpoint, createVerifyMagicLinkEndpoint, newsletterPlugin };