payload-plugin-newsletter
Version:
Complete newsletter management plugin for Payload CMS with subscriber management, magic link authentication, and email service integration
1 lines • 210 kB
Source Map (JSON)
{"version":3,"sources":["../src/types/newsletter.ts","../src/types/broadcast.ts","../src/types/providers.ts","../src/types/index.ts","../src/providers/broadcast/broadcast.ts","../src/exports/collections.ts","../src/collections/Broadcasts.ts","../src/fields/emailContent.ts","../src/utils/blockValidation.ts","../src/fields/broadcastInlinePreview.ts","../src/utils/emailSafeHtml.ts","../src/utils/getBroadcastConfig.ts","../src/endpoints/broadcasts/send.ts","../src/utils/access.ts","../src/utils/auth.ts","../src/endpoints/broadcasts/schedule.ts","../src/endpoints/broadcasts/test.ts","../src/endpoints/broadcasts/preview.ts","../src/emails/render.tsx","../src/emails/MagicLink.tsx","../src/emails/styles.ts","../src/emails/Welcome.tsx","../src/emails/SignIn.tsx","../src/collections/Subscribers.ts"],"sourcesContent":["/**\n * Core types for newsletter management functionality\n */\n\n/**\n * Represents a newsletter/broadcast in the system\n */\nexport interface Newsletter {\n id: string;\n name: string;\n subject: string;\n preheader?: string;\n content: string; // HTML content\n status: NewsletterStatus;\n trackOpens: boolean;\n trackClicks: boolean;\n replyTo?: string;\n recipientCount?: number;\n sentAt?: Date;\n scheduledAt?: Date;\n createdAt: Date;\n updatedAt: Date;\n // Provider-specific data stored here\n providerData?: Record<string, any>;\n // Provider information\n providerId?: string;\n providerType?: 'broadcast' | 'resend';\n}\n\n/**\n * Possible statuses for a newsletter\n */\nexport enum NewsletterStatus {\n DRAFT = 'draft',\n SCHEDULED = 'scheduled',\n SENDING = 'sending',\n SENT = 'sent',\n FAILED = 'failed',\n PAUSED = 'paused',\n CANCELED = 'canceled'\n}\n\n/**\n * Options for listing newsletters\n */\nexport interface ListNewsletterOptions {\n limit?: number;\n offset?: number;\n status?: NewsletterStatus;\n sortBy?: 'createdAt' | 'updatedAt' | 'sentAt' | 'name';\n sortOrder?: 'asc' | 'desc';\n}\n\n/**\n * Response from listing newsletters\n */\nexport interface ListNewsletterResponse<T = Newsletter> {\n items: T[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n/**\n * Input for creating a new newsletter\n */\nexport interface CreateNewsletterInput {\n name: string;\n subject: string;\n preheader?: string;\n content: string;\n trackOpens?: boolean;\n trackClicks?: boolean;\n replyTo?: string;\n audienceIds?: string[]; // Maps to segments/audiences\n}\n\n/**\n * Input for updating an existing newsletter\n */\nexport interface UpdateNewsletterInput {\n name?: string;\n subject?: string;\n preheader?: string;\n content?: string;\n trackOpens?: boolean;\n trackClicks?: boolean;\n replyTo?: string;\n audienceIds?: string[];\n}\n\n/**\n * Options for sending a newsletter\n */\nexport interface SendNewsletterOptions {\n audienceIds?: string[]; // Target specific audiences\n testMode?: boolean; // Send test email\n testRecipients?: string[]; // Email addresses for test send\n}\n\n/**\n * Analytics data for a newsletter\n */\nexport interface NewsletterAnalytics {\n sent: number;\n delivered: number;\n opened: number;\n clicked: number;\n bounced: number;\n complained: number;\n unsubscribed: number;\n deliveryRate?: number;\n openRate?: number;\n clickRate?: number;\n bounceRate?: number;\n}\n\n/**\n * Capabilities that a newsletter provider supports\n */\nexport interface NewsletterProviderCapabilities {\n supportsScheduling: boolean;\n supportsSegmentation: boolean;\n supportsAnalytics: boolean;\n supportsABTesting: boolean;\n supportsTemplates: boolean;\n supportsPersonalization: boolean;\n maxRecipientsPerSend?: number;\n editableStatuses: NewsletterStatus[];\n supportedContentTypes: ('html' | 'text' | 'react')[];\n}\n\n/**\n * Error types specific to newsletter operations\n */\nexport class NewsletterProviderError extends Error {\n constructor(\n message: string,\n public code: NewsletterErrorCode,\n public provider: string,\n public details?: any\n ) {\n super(message);\n this.name = 'NewsletterProviderError';\n }\n}\n\nexport enum NewsletterErrorCode {\n NOT_SUPPORTED = 'NOT_SUPPORTED',\n INVALID_STATUS = 'INVALID_STATUS',\n PROVIDER_ERROR = 'PROVIDER_ERROR',\n VALIDATION_ERROR = 'VALIDATION_ERROR',\n NOT_FOUND = 'NOT_FOUND',\n PERMISSION_DENIED = 'PERMISSION_DENIED',\n RATE_LIMITED = 'RATE_LIMITED',\n CONFIGURATION_ERROR = 'CONFIGURATION_ERROR'\n}\n\n/**\n * Newsletter template for reusable content\n */\nexport interface NewsletterTemplate {\n id: string;\n name: string;\n description?: string;\n content: string;\n variables?: NewsletterTemplateVariable[];\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface NewsletterTemplateVariable {\n name: string;\n type: 'text' | 'html' | 'image' | 'url';\n defaultValue?: string;\n required?: boolean;\n}","/**\n * Core types for broadcast management functionality\n */\n\n/**\n * Represents a broadcast (individual email campaign) in the system\n */\nexport interface Broadcast {\n id: string;\n name: string;\n subject: string;\n preheader?: string;\n content: string; // HTML content\n sendStatus: BroadcastStatus;\n trackOpens: boolean;\n trackClicks: boolean;\n replyTo?: string;\n recipientCount?: number;\n sentAt?: Date;\n scheduledAt?: Date;\n createdAt: Date;\n updatedAt: Date;\n // Provider-specific data stored here\n providerData?: Record<string, any>;\n // Provider information\n providerId?: string;\n providerType?: 'broadcast' | 'resend';\n}\n\n/**\n * Possible statuses for a broadcast\n */\nexport enum BroadcastStatus {\n DRAFT = 'draft',\n SCHEDULED = 'scheduled',\n SENDING = 'sending',\n SENT = 'sent',\n FAILED = 'failed',\n PAUSED = 'paused',\n CANCELED = 'canceled'\n}\n\n/**\n * Options for listing broadcasts\n */\nexport interface ListBroadcastOptions {\n limit?: number;\n offset?: number;\n status?: BroadcastStatus;\n sortBy?: 'createdAt' | 'updatedAt' | 'sentAt' | 'name';\n sortOrder?: 'asc' | 'desc';\n}\n\n/**\n * Response from listing broadcasts\n */\nexport interface ListBroadcastResponse<T = Broadcast> {\n items: T[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n}\n\n/**\n * Input for creating a new broadcast\n */\nexport interface CreateBroadcastInput {\n name: string;\n subject: string;\n preheader?: string;\n content: string;\n trackOpens?: boolean;\n trackClicks?: boolean;\n replyTo?: string;\n audienceIds?: string[]; // Maps to segments/audiences\n}\n\n/**\n * Input for updating an existing broadcast\n */\nexport interface UpdateBroadcastInput {\n name?: string;\n subject?: string;\n preheader?: string;\n content?: string;\n trackOpens?: boolean;\n trackClicks?: boolean;\n replyTo?: string;\n audienceIds?: string[];\n}\n\n/**\n * Options for sending a broadcast\n */\nexport interface SendBroadcastOptions {\n audienceIds?: string[]; // Target specific audiences\n testMode?: boolean; // Send test email\n testRecipients?: string[]; // Email addresses for test send\n}\n\n/**\n * Analytics data for a broadcast\n */\nexport interface BroadcastAnalytics {\n sent: number;\n delivered: number;\n opened: number;\n clicked: number;\n bounced: number;\n complained: number;\n unsubscribed: number;\n deliveryRate?: number;\n openRate?: number;\n clickRate?: number;\n bounceRate?: number;\n}\n\n/**\n * Capabilities that a broadcast provider supports\n */\nexport interface BroadcastProviderCapabilities {\n supportsScheduling: boolean;\n supportsSegmentation: boolean;\n supportsAnalytics: boolean;\n supportsABTesting: boolean;\n supportsTemplates: boolean;\n supportsPersonalization: boolean;\n maxRecipientsPerSend?: number;\n editableStatuses: BroadcastStatus[];\n supportedContentTypes: ('html' | 'text' | 'react')[];\n supportsMultipleChannels: boolean;\n supportsChannelSegmentation: boolean;\n}\n\n/**\n * Error types specific to broadcast operations\n */\nexport class BroadcastProviderError extends Error {\n constructor(\n message: string,\n public code: BroadcastErrorCode,\n public provider: string,\n public details?: any\n ) {\n super(message);\n this.name = 'BroadcastProviderError';\n }\n}\n\nexport enum BroadcastErrorCode {\n NOT_SUPPORTED = 'NOT_SUPPORTED',\n INVALID_STATUS = 'INVALID_STATUS',\n PROVIDER_ERROR = 'PROVIDER_ERROR',\n VALIDATION_ERROR = 'VALIDATION_ERROR',\n NOT_FOUND = 'NOT_FOUND',\n PERMISSION_DENIED = 'PERMISSION_DENIED',\n RATE_LIMITED = 'RATE_LIMITED',\n CONFIGURATION_ERROR = 'CONFIGURATION_ERROR'\n}\n\n/**\n * Broadcast template for reusable content\n */\nexport interface BroadcastTemplate {\n id: string;\n name: string;\n description?: string;\n content: string;\n variables?: BroadcastTemplateVariable[];\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface BroadcastTemplateVariable {\n name: string;\n type: 'text' | 'html' | 'image' | 'url';\n defaultValue?: string;\n required?: boolean;\n}\n\n// Re-export newsletter types with deprecation notice for backwards compatibility\nexport {\n NewsletterStatus,\n type ListNewsletterOptions,\n type ListNewsletterResponse,\n type CreateNewsletterInput,\n type UpdateNewsletterInput,\n type SendNewsletterOptions,\n type NewsletterAnalytics,\n type NewsletterProviderCapabilities,\n NewsletterProviderError,\n NewsletterErrorCode,\n type NewsletterTemplate,\n type NewsletterTemplateVariable\n} from './newsletter';","/**\n * Provider interfaces for broadcast management\n */\n\n// Import broadcast types\nimport type {\n Broadcast,\n BroadcastStatus,\n ListBroadcastOptions,\n ListBroadcastResponse,\n CreateBroadcastInput,\n UpdateBroadcastInput,\n SendBroadcastOptions,\n BroadcastAnalytics,\n BroadcastProviderCapabilities\n} from './broadcast'\n\nimport {\n BroadcastProviderError,\n BroadcastErrorCode\n} from './broadcast'\n\n\n// Import legacy newsletter types for backwards compatibility\nimport type {\n Newsletter,\n NewsletterStatus,\n ListNewsletterOptions,\n ListNewsletterResponse,\n CreateNewsletterInput,\n UpdateNewsletterInput,\n SendNewsletterOptions,\n NewsletterAnalytics,\n NewsletterProviderCapabilities\n} from './newsletter'\n\nimport {\n NewsletterProviderError,\n NewsletterErrorCode\n} from './newsletter'\n\n/**\n * Main interface for broadcast providers\n */\nexport interface BroadcastProvider {\n /**\n * Get the provider name\n */\n readonly name: string;\n\n // Broadcast management methods\n /**\n * List broadcasts with pagination\n */\n list(options?: ListBroadcastOptions): Promise<ListBroadcastResponse<Broadcast>>;\n \n /**\n * Get a specific broadcast by ID\n */\n get(id: string): Promise<Broadcast>;\n \n /**\n * Create a new broadcast\n */\n create(data: CreateBroadcastInput): Promise<Broadcast>;\n \n /**\n * Update an existing broadcast\n */\n update(id: string, data: UpdateBroadcastInput): Promise<Broadcast>;\n \n /**\n * Delete a broadcast\n */\n delete(id: string): Promise<void>;\n \n /**\n * Send a broadcast immediately or to test recipients\n */\n send(id: string, options?: SendBroadcastOptions): Promise<Broadcast>;\n \n /**\n * Schedule a broadcast for future sending\n */\n schedule(id: string, scheduledAt: Date): Promise<Broadcast>;\n \n /**\n * Cancel a scheduled broadcast\n */\n cancelSchedule(id: string): Promise<Broadcast>;\n \n /**\n * Get analytics for a broadcast\n */\n getAnalytics(id: string): Promise<BroadcastAnalytics>;\n \n /**\n * Get provider capabilities\n */\n getCapabilities(): BroadcastProviderCapabilities;\n \n /**\n * Validate that the provider is properly configured\n */\n validateConfiguration(): Promise<boolean>;\n}\n\n/**\n * Legacy newsletter provider interface for backwards compatibility\n * @deprecated Use BroadcastProvider instead\n */\nexport interface NewsletterProvider {\n /**\n * Get the provider name\n */\n readonly name: string;\n\n /**\n * List newsletters with pagination\n */\n list(options?: ListNewsletterOptions): Promise<ListNewsletterResponse<Newsletter>>;\n \n /**\n * Get a specific newsletter by ID\n */\n get(id: string): Promise<Newsletter>;\n \n /**\n * Create a new newsletter\n */\n create(data: CreateNewsletterInput): Promise<Newsletter>;\n \n /**\n * Update an existing newsletter\n */\n update(id: string, data: UpdateNewsletterInput): Promise<Newsletter>;\n \n /**\n * Delete a newsletter\n */\n delete(id: string): Promise<void>;\n \n /**\n * Send a newsletter immediately or to test recipients\n */\n send(id: string, options?: SendNewsletterOptions): Promise<Newsletter>;\n \n /**\n * Schedule a newsletter for future sending\n */\n schedule(id: string, scheduledAt: Date): Promise<Newsletter>;\n \n /**\n * Cancel a scheduled newsletter\n */\n cancelSchedule(id: string): Promise<Newsletter>;\n \n /**\n * Get analytics for a newsletter\n */\n getAnalytics(id: string): Promise<NewsletterAnalytics>;\n \n /**\n * Get provider capabilities\n */\n getCapabilities(): NewsletterProviderCapabilities;\n \n /**\n * Validate that the provider is properly configured\n */\n validateConfiguration(): Promise<boolean>;\n}\n\n/**\n * Base abstract class for broadcast providers\n */\nexport abstract class BaseBroadcastProvider implements BroadcastProvider {\n abstract readonly name: string;\n \n constructor(protected config: any) {}\n \n // Broadcast management - abstract methods\n abstract list(options?: ListBroadcastOptions): Promise<ListBroadcastResponse<Broadcast>>;\n abstract get(id: string): Promise<Broadcast>;\n abstract create(data: CreateBroadcastInput): Promise<Broadcast>;\n abstract update(id: string, data: UpdateBroadcastInput): Promise<Broadcast>;\n abstract delete(id: string): Promise<void>;\n abstract send(id: string, options?: SendBroadcastOptions): Promise<Broadcast>;\n abstract getCapabilities(): BroadcastProviderCapabilities;\n abstract validateConfiguration(): Promise<boolean>;\n \n /**\n * Schedule a broadcast - default implementation throws not supported\n */\n async schedule(_id: string, _scheduledAt: Date): Promise<Broadcast> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsScheduling) {\n throw new BroadcastProviderError(\n 'Scheduling is not supported by this provider',\n BroadcastErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n throw new Error('Method not implemented');\n }\n \n /**\n * Cancel scheduled broadcast - default implementation throws not supported\n */\n async cancelSchedule(_id: string): Promise<Broadcast> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsScheduling) {\n throw new BroadcastProviderError(\n 'Scheduling is not supported by this provider',\n BroadcastErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n throw new Error('Method not implemented');\n }\n \n /**\n * Get analytics - default implementation returns zeros\n */\n async getAnalytics(_id: string): Promise<BroadcastAnalytics> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsAnalytics) {\n throw new BroadcastProviderError(\n 'Analytics are not supported by this provider',\n BroadcastErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n \n return {\n sent: 0,\n delivered: 0,\n opened: 0,\n clicked: 0,\n bounced: 0,\n complained: 0,\n unsubscribed: 0\n };\n }\n \n /**\n * Helper method to validate required fields\n */\n protected validateRequiredFields(data: any, fields: string[]): void {\n const missing = fields.filter(field => !data[field]);\n if (missing.length > 0) {\n throw new BroadcastProviderError(\n `Missing required fields: ${missing.join(', ')}`,\n BroadcastErrorCode.VALIDATION_ERROR,\n this.name\n );\n }\n }\n \n /**\n * Helper method to check if a status transition is allowed\n */\n protected canEditInStatus(status: BroadcastStatus): boolean {\n const capabilities = this.getCapabilities();\n return capabilities.editableStatuses.includes(status);\n }\n \n /**\n * Helper to build pagination response\n */\n protected buildListResponse<T>(\n items: T[],\n total: number,\n options: ListBroadcastOptions = {}\n ): ListBroadcastResponse<T> {\n const limit = options.limit || 20;\n const offset = options.offset || 0;\n \n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + items.length < total\n };\n }\n}\n\n/**\n * Base abstract class for newsletter providers\n * @deprecated Use BaseBroadcastProvider instead\n */\nexport abstract class BaseNewsletterProvider implements NewsletterProvider {\n abstract readonly name: string;\n \n constructor(protected config: any) {}\n \n abstract list(options?: ListNewsletterOptions): Promise<ListNewsletterResponse<Newsletter>>;\n abstract get(id: string): Promise<Newsletter>;\n abstract create(data: CreateNewsletterInput): Promise<Newsletter>;\n abstract update(id: string, data: UpdateNewsletterInput): Promise<Newsletter>;\n abstract delete(id: string): Promise<void>;\n abstract send(id: string, options?: SendNewsletterOptions): Promise<Newsletter>;\n abstract getCapabilities(): NewsletterProviderCapabilities;\n abstract validateConfiguration(): Promise<boolean>;\n \n /**\n * Schedule a newsletter - default implementation throws not supported\n */\n async schedule(_id: string, _scheduledAt: Date): Promise<Newsletter> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsScheduling) {\n throw new NewsletterProviderError(\n 'Scheduling is not supported by this provider',\n NewsletterErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n throw new Error('Method not implemented');\n }\n \n /**\n * Cancel scheduled newsletter - default implementation throws not supported\n */\n async cancelSchedule(_id: string): Promise<Newsletter> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsScheduling) {\n throw new NewsletterProviderError(\n 'Scheduling is not supported by this provider',\n NewsletterErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n throw new Error('Method not implemented');\n }\n \n /**\n * Get analytics - default implementation returns zeros\n */\n async getAnalytics(_id: string): Promise<NewsletterAnalytics> {\n const capabilities = this.getCapabilities();\n if (!capabilities.supportsAnalytics) {\n throw new NewsletterProviderError(\n 'Analytics are not supported by this provider',\n NewsletterErrorCode.NOT_SUPPORTED,\n this.name\n );\n }\n \n return {\n sent: 0,\n delivered: 0,\n opened: 0,\n clicked: 0,\n bounced: 0,\n complained: 0,\n unsubscribed: 0\n };\n }\n \n /**\n * Helper method to validate required fields\n */\n protected validateRequiredFields(data: any, fields: string[]): void {\n const missing = fields.filter(field => !data[field]);\n if (missing.length > 0) {\n throw new NewsletterProviderError(\n `Missing required fields: ${missing.join(', ')}`,\n NewsletterErrorCode.VALIDATION_ERROR,\n this.name\n );\n }\n }\n \n /**\n * Helper method to check if a status transition is allowed\n */\n protected canEditInStatus(status: NewsletterStatus): boolean {\n const capabilities = this.getCapabilities();\n return capabilities.editableStatuses.includes(status);\n }\n \n /**\n * Helper to build pagination response\n */\n protected buildListResponse<T>(\n items: T[],\n total: number,\n options: ListNewsletterOptions = {}\n ): ListNewsletterResponse<T> {\n const limit = options.limit || 20;\n const offset = options.offset || 0;\n \n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + items.length < total\n };\n }\n}","import type { Field, Block, RichTextField } from 'payload'\nimport type { BroadcastProvider } from './providers'\n\n// Export broadcast types\nexport * from './broadcast'\nexport * from './providers'\n// Export legacy newsletter types for backwards compatibility\nexport * from './newsletter'\n\n// Email wrapper options interface\nexport interface EmailWrapperOptions {\n preheader?: string\n subject?: string\n documentData?: Record<string, any> // Generic document data\n}\n\n// Re-export for convenience\nexport type CustomEmailWrapper = (\n content: string, \n options?: EmailWrapperOptions\n) => string | Promise<string>\n\n// Add new interface for broadcast customizations\nexport interface BroadcastCustomizations {\n additionalFields?: Field[]\n customBlocks?: Block[]\n fieldOverrides?: {\n content?: (defaultField: RichTextField) => RichTextField\n }\n /**\n * Custom block email converter\n * @param node - The block node from Lexical editor state\n * @param mediaUrl - Base URL for media files\n * @returns Promise<string> - The email-safe HTML for the block\n */\n customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>\n \n /**\n * Fields to populate in custom blocks before email conversion\n * Can be an array of field names or a function that returns field names based on block type\n * This is useful for upload fields that need to be populated with full media objects\n * \n * @example\n * // Array of field names to always populate\n * populateFields: ['bannerImage', 'sponsorLogo']\n * \n * @example\n * // Function to return fields based on block type\n * populateFields: (blockType) => {\n * if (blockType === 'newsletter-hero') return ['bannerImage', 'sponsorLogo']\n * if (blockType === 'content-section') return ['featuredImage']\n * return []\n * }\n */\n populateFields?: string[] | ((blockType: string) => string[])\n \n /**\n * Email preview customization options\n */\n emailPreview?: {\n /**\n * Whether to wrap preview content in default email template\n * @default true\n */\n wrapInTemplate?: boolean\n \n /**\n * Custom wrapper function for preview content\n * Receives the converted HTML and should return wrapped HTML\n */\n customWrapper?: (content: string, options?: EmailWrapperOptions) => string | Promise<string>\n \n /**\n * Custom preview component to replace the default one entirely\n * If provided, this component will be used instead of the default EmailPreview\n */\n customPreviewComponent?: string // Path to custom component for import map\n }\n}\n\nexport interface NewsletterPluginConfig {\n /**\n * Enable or disable the plugin\n * @default true\n */\n enabled?: boolean\n\n /**\n * Slug for the subscribers collection\n * @default 'subscribers'\n */\n subscribersSlug?: string\n \n /**\n * Slug for the newsletter settings global\n * @default 'newsletter-settings'\n */\n settingsSlug?: string\n\n /**\n * Authentication configuration for magic links\n */\n auth?: {\n /**\n * Enable magic link authentication\n * @default true\n */\n enabled?: boolean\n \n /**\n * Token expiration time\n * @default '7d'\n */\n tokenExpiration?: string\n \n /**\n * Path where magic link redirects\n * @default '/newsletter/verify'\n */\n magicLinkPath?: string\n \n /**\n * Allow unsubscribed users to sign in\n * @default false\n */\n allowUnsubscribedSignin?: boolean\n \n /**\n * Allow unsubscribed users to resubscribe\n * @default false\n */\n allowResubscribe?: boolean\n }\n\n /**\n * Access control configuration\n */\n access?: {\n /**\n * Custom function to determine if a user is an admin\n * @param user - The authenticated user object\n * @returns true if the user should have admin access\n */\n isAdmin?: (user: any) => boolean\n }\n\n /**\n * Email provider configuration\n */\n providers: {\n /**\n * Default provider to use\n */\n default: 'resend' | 'broadcast' | string\n \n /**\n * Resend provider configuration\n */\n resend?: ResendProviderConfig\n \n /**\n * Broadcast provider configuration\n */\n broadcast?: BroadcastProviderConfig\n }\n\n /**\n * Field customization options\n */\n fields?: {\n /**\n * Override default fields\n */\n overrides?: (args: { defaultFields: Field[] }) => Field[]\n \n /**\n * Additional custom fields\n */\n additional?: Field[]\n }\n\n /**\n * Email template components\n */\n templates?: {\n /**\n * Welcome email template\n */\n welcome?: React.ComponentType<WelcomeEmailProps>\n \n /**\n * Magic link email template\n */\n magicLink?: React.ComponentType<MagicLinkEmailProps>\n }\n\n /**\n * Plugin hooks\n */\n hooks?: {\n beforeSubscribe?: (args: BeforeSubscribeArgs) => void | Promise<void>\n afterSubscribe?: (args: AfterSubscribeArgs) => void | Promise<void>\n beforeUnsubscribe?: (args: BeforeUnsubscribeArgs) => void | Promise<void>\n afterUnsubscribe?: (args: AfterUnsubscribeArgs) => void | Promise<void>\n afterUnsubscribeSync?: (args: AfterUnsubscribeSyncArgs) => void | Promise<void>\n }\n\n /**\n * UI component overrides\n */\n components?: {\n signupForm?: React.ComponentType<SignupFormProps>\n preferencesForm?: React.ComponentType<PreferencesFormProps>\n }\n\n /**\n * Feature flags\n */\n features?: {\n /**\n * Lead magnet configuration\n */\n leadMagnets?: {\n enabled?: boolean\n collection?: string\n }\n \n /**\n * Post-signup survey configuration\n */\n surveys?: {\n enabled?: boolean\n questions?: SurveyQuestion[]\n }\n \n /**\n * UTM tracking configuration\n */\n utmTracking?: {\n enabled?: boolean\n fields?: string[]\n }\n \n /**\n * Newsletter scheduling configuration\n */\n newsletterScheduling?: {\n enabled?: boolean\n /**\n * Collections to add newsletter fields to\n * Can be a string for single collection or array for multiple\n * @example 'articles' or ['articles', 'posts', 'updates']\n */\n collections?: string | string[]\n /**\n * Field configuration\n */\n fields?: {\n /**\n * Group name for newsletter fields\n * @default 'newsletterScheduling'\n */\n groupName?: string\n /**\n * Rich text field name to use for content\n * @default 'content'\n */\n contentField?: string\n /**\n * Whether to create a markdown companion field\n * @default true\n */\n createMarkdownField?: boolean\n }\n }\n \n /**\n * Unsubscribe sync configuration\n */\n unsubscribeSync?: {\n /**\n * Enable sync of unsubscribes from email service to Payload\n * @default false\n */\n enabled?: boolean\n /**\n * Cron schedule for sync job (e.g., '0 * * * *' for hourly)\n * If not provided, job must be triggered manually\n */\n schedule?: string\n /**\n * Queue name for the sync job\n * @default 'newsletter-sync'\n */\n queue?: string\n }\n \n /**\n * Newsletter management configuration\n */\n newsletterManagement?: {\n /**\n * Enable newsletter management features\n * @default false\n */\n enabled?: boolean\n /**\n * Collection names for broadcast management\n */\n collections?: {\n /**\n * Broadcasts collection slug\n * @default 'broadcasts'\n */\n broadcasts?: string\n }\n /**\n * Optional: Custom broadcast provider implementation\n * If not provided, will use the default email provider\n */\n provider?: BroadcastProvider\n }\n }\n\n /**\n * Internationalization configuration\n */\n i18n?: {\n defaultLocale?: string\n locales?: string[]\n }\n\n /**\n * Custom email templates\n */\n customTemplates?: {\n [key: string]: React.ComponentType<any>\n }\n\n /**\n * Customization options for plugin collections\n */\n customizations?: {\n broadcasts?: BroadcastCustomizations\n }\n}\n\nexport interface ResendProviderConfig {\n apiKey: string\n fromEmail?: string\n fromAddress?: string // Alias for fromEmail\n fromName?: string\n replyTo?: string\n audienceIds?: {\n [locale: string]: {\n production?: string\n development?: string\n }\n }\n}\n\nexport interface BroadcastProviderConfig {\n apiUrl: string\n token: string\n fromEmail?: string\n fromAddress?: string // Alias for fromEmail\n fromName?: string\n replyTo?: string\n}\n\nexport interface EmailProvider {\n send(params: SendEmailParams): Promise<void>\n addContact(contact: Subscriber): Promise<void>\n updateContact(contact: Subscriber): Promise<void>\n removeContact(email: string): Promise<void>\n}\n\nexport interface SendEmailParams {\n to: string | string[]\n subject: string\n html?: string\n text?: string\n react?: React.ReactElement\n}\n\nexport interface Subscriber {\n id: string\n email: string\n name?: string\n locale?: string\n subscriptionStatus: 'active' | 'unsubscribed' | 'pending'\n emailPreferences?: {\n newsletter?: boolean\n announcements?: boolean\n [key: string]: boolean | undefined\n }\n source?: string\n utmParameters?: {\n source?: string\n medium?: string\n campaign?: string\n content?: string\n term?: string\n }\n // Additional fields that may exist in the database\n signupMetadata?: {\n ipAddress?: string\n userAgent?: string\n referrer?: string\n signupPage?: string\n }\n leadMagnet?: string\n unsubscribedAt?: string\n magicLinkToken?: string\n magicLinkTokenExpiry?: string\n createdAt: string\n updatedAt: string\n}\n\nexport interface WelcomeEmailProps {\n subscriber: Subscriber\n unsubscribeUrl: string\n preferencesUrl: string\n}\n\nexport interface MagicLinkEmailProps {\n magicLinkUrl: string\n subscriber: Subscriber\n}\n\nexport interface SignupFormProps {\n onSuccess?: (subscriber: Subscriber) => void\n onError?: (error: Error) => void\n showName?: boolean\n showPreferences?: boolean\n leadMagnet?: {\n id: string\n title: string\n description?: string\n }\n className?: string\n styles?: {\n form?: React.CSSProperties\n inputGroup?: React.CSSProperties\n label?: React.CSSProperties\n input?: React.CSSProperties\n button?: React.CSSProperties\n buttonDisabled?: React.CSSProperties\n error?: React.CSSProperties\n success?: React.CSSProperties\n checkbox?: React.CSSProperties\n checkboxInput?: React.CSSProperties\n checkboxLabel?: React.CSSProperties\n }\n apiEndpoint?: string\n buttonText?: string\n loadingText?: string\n successMessage?: string\n placeholders?: {\n email?: string\n name?: string\n }\n labels?: {\n email?: string\n name?: string\n newsletter?: string\n announcements?: string\n }\n}\n\nexport interface PreferencesFormProps {\n subscriber?: Subscriber\n onSuccess?: (subscriber: Subscriber) => void\n onError?: (error: Error) => void\n className?: string\n styles?: {\n container?: React.CSSProperties\n heading?: React.CSSProperties\n form?: React.CSSProperties\n section?: React.CSSProperties\n sectionTitle?: React.CSSProperties\n inputGroup?: React.CSSProperties\n label?: React.CSSProperties\n input?: React.CSSProperties\n select?: React.CSSProperties\n checkbox?: React.CSSProperties\n checkboxInput?: React.CSSProperties\n checkboxLabel?: React.CSSProperties\n buttonGroup?: React.CSSProperties\n button?: React.CSSProperties\n primaryButton?: React.CSSProperties\n secondaryButton?: React.CSSProperties\n dangerButton?: React.CSSProperties\n error?: React.CSSProperties\n success?: React.CSSProperties\n info?: React.CSSProperties\n }\n sessionToken?: string\n apiEndpoint?: string\n showUnsubscribe?: boolean\n locales?: string[]\n labels?: {\n title?: string\n personalInfo?: string\n emailPreferences?: string\n name?: string\n language?: string\n newsletter?: string\n announcements?: string\n saveButton?: string\n unsubscribeButton?: string\n saving?: string\n saved?: string\n unsubscribeConfirm?: string\n }\n}\n\nexport interface BeforeSubscribeArgs {\n data: Partial<Subscriber>\n req: any\n}\n\nexport interface AfterSubscribeArgs {\n doc: Subscriber\n req: any\n}\n\nexport interface BeforeUnsubscribeArgs {\n email: string\n req: any\n}\n\nexport interface AfterUnsubscribeArgs {\n doc: Subscriber\n req: any\n}\n\nexport interface AfterUnsubscribeSyncArgs {\n req: any\n syncedCount: number\n}\n\nexport interface SurveyQuestion {\n id: string\n question: string\n type: 'text' | 'select' | 'multiselect' | 'radio'\n options?: string[]\n required?: boolean\n}\n\n// Request data interfaces for endpoints\nexport interface SubscribeRequestData {\n email: string\n name?: string\n source?: string\n preferences?: { [key: string]: boolean }\n leadMagnet?: string\n surveyResponses?: { [key: string]: string | string[] }\n metadata?: {\n locale?: string\n signupPage?: string\n [key: string]: unknown\n }\n}\n\nexport interface UnsubscribeRequestData {\n email?: string\n token?: string\n}\n\nexport interface VerifyMagicLinkRequestData {\n token: string\n}\n\nexport interface SigninRequestData {\n email: string\n}\n\nexport interface UpdatePreferencesRequestData {\n name?: string\n locale?: string\n emailPreferences?: { [key: string]: boolean }\n}\n\n// Extended request types with proper data typing\nexport interface ExtendedPayloadRequest extends Request {\n payload: any // TODO: Add proper payload type\n data?: unknown\n ip?: string\n connection?: {\n remoteAddress?: string\n }\n cookies?: {\n [key: string]: string\n }\n // Headers are inherited from Request, but we document common ones for reference\n // Access via: req.headers.get('authorization'), req.headers.get('referer'), etc.\n}","import type { \n Broadcast,\n ListBroadcastOptions,\n ListBroadcastResponse,\n CreateBroadcastInput,\n UpdateBroadcastInput,\n SendBroadcastOptions,\n BroadcastAnalytics,\n BroadcastProviderCapabilities\n} from '../../types'\nimport { \n BroadcastProviderError, \n BroadcastErrorCode,\n BroadcastStatus,\n BaseBroadcastProvider\n} from '../../types'\nimport type { BroadcastProviderConfig } from '../../types'\n\ninterface BroadcastApiResponse {\n id: number\n name: string\n subject: string\n preheader?: string\n body: string\n status: string\n track_opens: boolean\n track_clicks: boolean\n html_body: boolean\n reply_to?: string\n total_recipients: number\n sent_at?: string\n scheduled_send_at?: string\n created_at: string\n updated_at: string\n}\n\ninterface BroadcastListResponse {\n data: BroadcastApiResponse[]\n total: number\n}\n\n\nexport class BroadcastApiProvider extends BaseBroadcastProvider {\n readonly name = 'broadcast'\n private apiUrl: string\n private token: string\n\n constructor(config: BroadcastProviderConfig) {\n super(config)\n this.apiUrl = config.apiUrl.replace(/\\/$/, '') // Remove trailing slash\n this.token = config.token\n \n if (!this.token) {\n throw new BroadcastProviderError(\n 'Broadcast API token is required',\n BroadcastErrorCode.CONFIGURATION_ERROR,\n this.name\n )\n }\n }\n\n // Broadcast Management Methods\n async list(options?: ListBroadcastOptions): Promise<ListBroadcastResponse<Broadcast>> {\n try {\n const params = new URLSearchParams()\n if (options?.limit) params.append('limit', options.limit.toString())\n if (options?.offset) params.append('offset', options.offset.toString())\n\n const response = await fetch(`${this.apiUrl}/api/v1/broadcasts?${params}`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Broadcast API error: ${response.status} - ${error}`)\n }\n\n const data: BroadcastListResponse = await response.json()\n \n const broadcasts = data.data.map(broadcast => this.transformBroadcastFromApi(broadcast))\n \n return this.buildListResponse(broadcasts, data.total, options)\n } catch (error: unknown) {\n throw new BroadcastProviderError(\n `Failed to list broadcasts: ${error instanceof Error ? error.message : 'Unknown error'}`,\n BroadcastErrorCode.PROVIDER_ERROR,\n this.name,\n error\n )\n }\n }\n\n async get(id: string): Promise<Broadcast> {\n try {\n const response = await fetch(`${this.apiUrl}/api/v1/broadcasts/${id}`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new BroadcastProviderError(\n `Broadcast not found: ${id}`,\n BroadcastErrorCode.NOT_FOUND,\n this.name\n )\n }\n const error = await response.text()\n throw new Error(`Broadcast API error: ${response.status} - ${error}`)\n }\n\n const broadcast: BroadcastApiResponse = await response.json()\n return this.transformBroadcastFromApi(broadcast)\n } catch (error: unknown) {\n if (error instanceof BroadcastProviderError) throw error\n \n throw new BroadcastProviderError(\n `Failed to get broadcast: ${error instanceof Error ? error.message : 'Unknown error'}`,\n BroadcastErrorCode.PROVIDER_ERROR,\n this.name,\n error\n )\n }\n }\n\n async create(data: CreateBroadcastInput): Promise<Broadcast> {\n try {\n this.validateRequiredFields(data, ['name', 'subject', 'content'])\n\n const requestBody = {\n broadcast: {\n name: data.name,\n subject: data.subject,\n preheader: data.preheader,\n body: data.content,\n html_body: true,\n track_opens: data.trackOpens ?? true,\n track_clicks: data.trackClicks ?? true,\n reply_to: data.replyTo,\n segment_ids: data.audienceIds,\n }\n }\n\n // Log the request details (without exposing the token)\n console.log('[BroadcastApiProvider] Creating broadcast:', {\n url: `${this.apiUrl}/api/v1/broadcasts`,\n method: 'POST',\n hasToken: !!this.token,\n tokenLength: this.token?.length,\n body: JSON.stringify(requestBody, null, 2),\n })\n\n const response = await fetch(`${this.apiUrl}/api/v1/broadcasts`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody),\n })\n\n console.log('[BroadcastApiProvider] Response status:', response.status)\n console.log('[BroadcastApiProvider] Response headers:', Object.fromEntries(response.headers.entries()))\n\n if (!response.ok) {\n const errorText = await response.text()\n console.error('[BroadcastApiProvider] Error response body:', errorText)\n \n // Try to parse as JSON if possible\n let errorDetails\n try {\n errorDetails = JSON.parse(errorText)\n console.error('[BroadcastApiProvider] Parsed error:', errorDetails)\n } catch {\n // Not JSON, use as is\n }\n \n throw new Error(`Broadcast API error: ${response.status} - ${errorText}`)\n }\n\n const result = await response.json()\n // Broadcast API returns just {id: 123}, so we need to fetch the full object\n return this.get(result.id.toString())\n } catch (error: unknown) {\n if (error instanceof BroadcastProviderError) throw error\n \n throw new BroadcastProviderError(\n `Failed to create broadcast: ${error instanceof Error ? error.message : 'Unknown error'}`,\n BroadcastErrorCode.PROVIDER_ERROR,\n this.name,\n error\n )\n }\n }\n\n async update(id: string, data: UpdateBroadcastInput): Promise<Broadcast> {\n try {\n // First check if the broadcast can be edited\n const existing = await this.get(id)\n if (!this.canEditInStatus(existing.sendStatus)) {\n throw new BroadcastProviderError(\n `Cannot update broadcast in status: ${existing.sendStatus}`,\n BroadcastErrorCode.INVALID_STATUS,\n this.name\n )\n }\n\n const response = await fetch(`${this.apiUrl}/api/v1/broadcasts/${id}`, {\n method: 'PATCH',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n broadcast: {\n name: data.name,\n subject: data.subject,\n preheader: data.preheader,\n body: data.content,\n track_opens: data.trackOpens,\n track_clicks: data.trackClicks,\n reply_to: data.replyTo,\n segment_ids: data.audienceIds,\n }\n }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Broadcast API error: ${response.status} - ${error}`)\n }\n\n const broadcast: BroadcastApiResponse = await response.json()\n return this.transformBroadcastFromApi(broadcast)\n } catch (error: unknown) {\n if (error instanceof BroadcastProviderError) throw error\n \n throw new BroadcastProviderError(\n `Failed to update broadcast: ${error instanceof Error ? error.message : 'Unknown error'}`,\n BroadcastErrorCode.PROVIDER_ERROR,\n this.name,\n error\n )\n }\n }\n\n async delete(id: string): Promise<void> {\n try {\n // First check if the broadcast can be deleted\n const existing = await this.get(id)\n if (!this.canEditInStatus(existing.sendStatus)) {\n throw new BroadcastProviderError(\n `Cannot delete broadcast in status: ${existing.sendStatus}`,\n BroadcastErrorCode.INVALID_STATUS,\n this.name\n )\n }\n\n const response = await fetch(`${this.apiUrl}/api/v1/broadcasts/${id}`, {\n method: 'DELETE',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n },\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Broadcast API error: ${response.status} - ${error}`)\n }\n } catch (error: unknown) {\n if (error instanceof BroadcastProviderError) throw error\n \n throw new BroadcastProviderError(\n `Failed to delete broadcast: ${error instanceof Error ? error.message : 'Unknown error'}`,\n BroadcastErrorCode.PROVIDER_ERROR,\n this.name,\n error\n )\n }\n }\n\n async send(id: string, options?: SendBroadcastOptions): Promise<Broadcast> {\n try {\n // Check if we're in test mode\n if (options?.testMode && options.testRecipients?.length) {\n // TODO: Broadcast doesn't have a documented test send API\n // For now, we'll throw an error\n throw new BroadcastProviderError(\n 'Test send is not yet implemented for Broadcast provider',\n BroadcastErrorCode.NOT_SUPPORTED,\n this.name\n )\n }\n\n const response = await fetch(`${this.apiUrl}/api/v1/broadcasts/${id}/send_broadcast`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n segment_ids: options?.audienceIds\n }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Broadcast API error: ${response.status} - ${error}`)\n }\n\n // Response includes updated status\n const result = await response.json()\n return this.get(result.id.toString())\n } catch (error: unknown) {\n if (error instanceof BroadcastProviderError) throw error\n \n throw new BroadcastProviderError(\n `Failed to send broadcast: ${error instanceof Error ? error.message : 'Unknown error'}`,\n BroadcastErrorCode.PROVIDER_ERROR,\n this.name,\n error\n )\n }\n }\n\n async schedule(id: string, scheduledAt: Date): Promise<Broadcast> {\n try {\n // Update the newsletter with scheduled time\n const response = await fetch(`${this.apiUrl}/api/v1/broadcasts/${id}`, {\n method: 'PATCH',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n broadcast: {\n scheduled_send_at: scheduledAt.toISOString(),\n // TODO: Handle timezone properly\n scheduled_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone\n }\n }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Broadcast API error: ${response.status} - ${error}`)\n }\n\n const broadcast: BroadcastApiResponse = await response.json()\n return this.transformBroadcastFromApi(broadcast)\n } catch (error: unknown) {\n throw new BroadcastProviderError(\n `Failed to schedule broadcast: ${error instanceof Error ? error.message : 'Unknown error'}`,\n BroadcastErrorCode.PROVIDER_ERROR,\n this.name,\n error\n )\n }\n }\n\n async cancelSchedule(id: string): Promise<Broadcast> {\n try {\n // Clear the scheduled time\n const response = await fetch(`${this.apiUrl}/api/v1/broadcasts/${id}`, {\n method: 'PATCH',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n broadcast: {\n scheduled_send_at: null,\n scheduled_timezone: null\n }\n }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Broadcast API error: ${response.status} - ${error}`)\n }\n\n const broadcast: BroadcastApiResponse = await response.json()\n return this.transformBroadcastFromApi(broadcast)\n } catch (error: unknown) {\n throw new BroadcastProviderError(\n `Failed to cancel scheduled broadcast: ${error instanceof Error ? error.message : 'Unknown error'}`,\n BroadcastErrorCode.PROVIDER_ERROR,\n this.name,\n error\n )\n }\n }\n\n async getAnalytics(_id: string): Promise<BroadcastAnalytics> {\n // TODO: Broadcast analytics API is not documented in the CRUD API\n // This would need additional API documentation\n throw new BroadcastProviderError(\n 'Analytics API not yet implemented for Broadcast provider',\n BroadcastErrorCode.NOT_SUPPORTED,\n this.name\n )\n }\n\n getCapabilities(): BroadcastProviderCapabilities {\n return {\n supportsScheduling: true,\n supportsSegmentation: true,\n supportsAnalytics: false, // Not documented yet\n supportsABTesting: false,\n supportsTemplates: false,\n supportsPersonalization: true,\n supportsMultipleChannels: false,\n supportsChannelSegmentation: false,\n editableStatuses: [BroadcastStatus.DRAFT, BroadcastStatus.SCHEDULED],\n supportedContentTypes: ['html', 'text']\n }\n }\n\n async validateConfiguration(): Promise<boolean> {\n try {\n // Try to list broadcasts with limit 1 to validate API access\n await this.list({ limit: 1 })\n return true\n } catch {\n return false\n }\n }\n\n private transformBroadcastFromApi(broadcast: BroadcastApiResponse): Broadcast {\n return {\n id: broadcast.id.toString(),\n name: broadcast.name,\n subject: broadcast.subject,\n preheader: broadcast.preheader,\n content: broadcast.body,\n sendStatus: this.mapBroadcastStatus(broadcast.status),\n trackOpens: broadcast.track_opens,\n trackClicks: broadcast.track_clicks,\n replyTo: broadcast.reply_to,\n recipientCount: broadcast.total_recipients,\n sentAt: broadcast.sent_at ? new Date(broadcast.sent_at) : undefined,\n scheduledAt: broadcast.scheduled_send_at ? new Date(broadcast.scheduled_send_at) : undefined,\n createdAt: new Date(broadcast.created_at),\n updatedAt: new Date(broadcast.updated_at),\n providerData: { broadcast },\n providerId: broadcast.id.toString(),\n providerType: 'broadcast'\n }\n }\n\n private mapBroadcastStatus(status: string): BroadcastStatus {\n const statusMap: Record<string, BroadcastStatus> = {\n 'draft': BroadcastStatus.DRAFT,\n 'scheduled': BroadcastStatus.SCHEDULED,\n 'queueing': BroadcastStatus.SENDING,\n 'sending': BroadcastStatus.SENDING,\n 'sent': BroadcastStatus.SENT,\n 'failed': BroadcastStatus.FAILED,\n 'partial_failure': BroadcastStatus.FAILED,\n 'paused': BroadcastStatus.PAUSED,\n 'aborted': BroadcastStatus.CANCELED\n }\n return statusMap[status] || BroadcastStatus.DRAFT\n }\n}","// Collection factories\nexport { createBroadcastsCollection } from '../collections/Broadcasts'\nexport { createSubscribersCollection } from '../collections/Subscribers'","import type { CollectionConfig } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\nimport { BroadcastStatus } from '../types'\nimport { createEmailContentField, createEmailLexicalEditor } from '../fields/emailContent'\nimport { createBroadcastInlinePreviewField } from '../fields/broadcastInlinePreview'\nimport { convertToEmailSafeHtml } from '../utils/emailSafeHtml'\nimport { getBroadcastConfig } from '../utils/getBroadcastConfig'\nimport { createSendBroadcastEndpoint } from '../endpoints/broadcasts/send'\nimport { createScheduleBroadcastEndpoint } from '../endpoints/broadcasts/schedule'\nimport { createTestBroadcastEndpoint } from '../endpoints/broadcasts/test'\nimport { createBroadcastPreviewEndpoint, populateMediaFields } from '../endpoints/broadcasts/preview'\n\nexport const createBroadcastsCollection = (pluginConfig: NewsletterPluginConfig): CollectionConfig => {\n const hasProviders = !!(pluginConfig.providers?.broadcast || pluginConfig.providers?.resend)\n const customizations = pluginConfig.customizations?.broadcasts\n\n const collectionSlug = 'broadcasts'\n\n return {\n slug: collectionSlug,\n access: {\n read: () => true, // Public read access\n create: ({ req: { user } }) => {\n // Allow authenticated users to create\n return Boolean(user)\n },\n update: ({ req: { user } }) => {\n // Allow authenticated users to update\n return Boolean(user)\n },\n delete: ({ req: { user } }) => {\n // Allow authenticated users to delete\n return Boolean(user)\n },\n },\n versions: {\n drafts: {\n autosave: true,\n schedulePublish: true,\n }\n },\n labels: {\n singular: 'Broadcast',\n plural: 'Broadcasts',\n },\n admin: {\n useAsTitle: 'subject',\n description: 'Individual email campaigns sent to subscribers',\n defaultColumns: ['subject', '_status', 'sendStatus', 'sentAt', 'recipientCount'],\n },\n endpoints: [\n createSendBroadcastEndpoint(pluginConfig, collectionSlug),\n createScheduleBroadcastEndpoint(pluginConfig, collectionSlug),\n createTestBroadcastEndpoint(pluginConfig, collectionSlug),\n createBroadcastPreviewEndpoint(pluginConfig, collectionSlug),\n ],\n fields: [\n {\n name: 'subject',\n type: 'text',\n required: true,\n admin: {\n description: 'Email subject line'\n },\n },\n // Add any additional fields from customizations after subject\n ...(customizations?.additionalFields || []),\n {\n type: 'row',\n fields: [\n {\n name: 'contentSection',\n type: 'group',\n label: false,\n admin: {\n width: '50%',\n style: {\n paddingRight: '1rem',\n },\n },\n fields: [\n {\n name: 'preheader',\n type: 'text',\n admin: {\n description: 'Preview text shown in email clients'\n },\n },\n // Apply content field customization if provided\n // Process blocks server-side to avoid client serialization issues\n (() => {\n // Create email editor with custom blocks processed server-side\n const emailEditor = createEmailLexicalEditor(customizations?.customBlocks)\n \n // Create base field with pre-processed editor\n const baseField = createEmailContentField({\n admin: { description: 'Email content' },\n editor: emailEditor\n })\n \n // Apply field overrides if provided\n return customizations?.fieldOverrides?.content\n ? customizations.fieldOverrides.content(baseField)\n : baseField\n })(),\n ],\n },\n {\n name: 'previewSection',\n type: 'group',\n label: false,\n admin: {\n width: '50%',\n },\n fields: [\n createBroadcastInlinePreviewField(),\n ],\n },\n ],\n },\n {\n name: 'sendStatus',\n type: 'select',\n label: 'Send Status',\n required: true,\n defaultValue: BroadcastStatus.DRAFT,\n options: [\n { label: 'Draft', value: BroadcastStatus.DRAFT },\n { label: 'Scheduled', value: BroadcastStatus.SCHEDULED },\n { label: 'Sending', value: BroadcastStatus.SENDING },\n { label: 'Sent', value: BroadcastStatus.SENT },\n { label: 'Failed', value: BroadcastStatus.FAILED },\n { label: 'Paused', value: BroadcastStatus.PAUSED },\n { label: 'Canceled', value: BroadcastStatus.CANCELED },\n ],\n admin: {\n readOnly: true,\n description: 'The status of the email send operation',\n components: {\n Cell: 'payload-plugin-newsletter/components#StatusBadge',\n },\n },\n },\n {\n name: 'settings',\n type: 'group',\n fields: [\n {\n name: 'trackOpens',\n type: 'checkbox',\n defaultValue: true,\n admin: {\n description: 'Track when recipients open this email'\n },\n },\n {\n name: 'trackClicks',\n type: 'checkbox',\n defaultValue: true,\n admin: {\n description: 'Track when recipients click links'\n },\n },\n {\n name: 'replyTo',\n type: 'email',\n admin: {\n description: 'Override the channel reply-to address for this broadcast'\n },\n },\n ],\n },\n {\n name: 'audienceIds',\n type: 'array',\n fields: [\n {\n name: 'audienceId',\n type: 'text',\n required: true,\n },\n ],\n admin: {\n description: 'Target specific audience segments',\n condition: () => {\n // Only show if the provider supports segmentation\n return hasProviders\n },\n },\n },\n {\n name: 'analytics',\n type: 'group',\n admin: {\n readOnly: true,\n condition: (data) => data?.sendStatus === BroadcastStatus.SENT,\n },\n fields: [\n {\n name: 'recipientCount',\n type: 'number',\n defaultValue: 0,\n },\n {\n name: 'sent',\n type: 'number',\n defaultValue: 0,\n },\n {\n name: 'delivered',\n type: 'number',\n defaultValue: 0,\n },\n {\n name: 'opened',\n type: 'number',\n defaultValue: 0,\n },\n {\n name: 'clicked',\n type: 'number',\n defaultValue: 0,\n },\n {\n name: 'bounced',\n type: 'number',\n defaultValue: 0,\n },\n {\n name: 'complained',\n type: 'number',\n defaultValue: 0,\n },\n {\n name: 'unsubscribed',\n type: 'number',\n defaultValue: 0,\n },\n ],\n },\n {\n name: 'sentAt',\n type: 'date',\n admin: {\n readOnly: true,\n date: {\n displayFormat: 'MMM d, yyyy h:mm a',\n },\n },\n },\n {\n name: 'scheduledAt',\n type: 'date',\n admin: {\n condition: (data) => data?.sendStatus === BroadcastStatus.SCHEDULED,\n date: {\n displayFormat: 'MMM d, yyyy h:mm a',\n },\n },\n },\n {\n name: 'providerId',\n type: 'text',\n admin: {\n readOnly: true,\n description: 'ID from the email provider',\n condition: (data) => hasProviders && data?.providerId,\n },\n },\n {\n name: 'providerData',\n type: 'json',\n admin: {\n readOnly: true,\n condition: () => false, // Hidden by default\n },\n },\n ],\n hooks: {\n // Sync with provider on create and update\n afterChange: [\n async ({ doc, operation, req, previousDoc }) => {\n if (!hasProviders) return doc\n\n // Handle create operation\n if (operation === 'create') {\n // Skip provider sync if essential fields are missing\n // Broadcast API requires both subject and body\n if (!doc.subject || !doc.contentSection?.content) {\n req.payload.logger.info('Skipping provider sync - broadcast has no subject or content yet')\n return doc\n }\n \n try {\n // Get provider config from settings first, then fall back to env vars\n const providerConfig = await getBroadcastConfig(req, pluginConfig)\n if (!providerConfig || !providerConfig.token) {\n req.payload.logger.error('Broadcast provider not configured in Newsletter Settings or environment variables')\n return doc\n }\n\n const { BroadcastApiProvider } = await import('../providers/broadcast/broadcast')\n const provider = new BroadcastApiProvider(providerConfig)\n\n // Populate media fields and convert rich text to HTML\n req.payload.logger.info('Populating media fields and converting content to HTML...')\n const populatedContent = await populateMediaFields(doc.contentSection?.content, req.payload, pluginConfig)\n \n // Get email preview customization options\n const emailPreviewConfig = pluginConfig.customizations?.broadcasts?.emailPreview\n \n const htmlContent = await convertToEmailSafeHtml(populatedContent, {\n wrapInTemplate: emailPreviewConfig?.wrapInTemplate ?? true,\n customWrapper: emailPreviewConfig?.customWrapper,\n preheader: doc.contentSection?.preheader,\n subject: doc.subject,\n documentData: doc, // Pass entire document\n customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter\n })\n \n // Skip if content is empty after conversion\n if (!htmlContent || htmlContent.trim() === '') {\n req.payload.logger.info('Skipping provider sync - content is empty after conversion')\n return doc\n }\n \n // Log what we're about to send\n const createData = {\n name: doc.subject, // Use subject as name since we removed the name field\n subject: doc.subject,\n preheader: doc.contentSection?.preheader,\n content: htmlContent,\n trackOpens: doc.settings?.trackOpens,\n trackClicks: doc.settings?.trackClicks,\n replyTo: doc.settings?.replyTo || providerConfig.replyTo,\n audienceIds: doc.audienceIds?.map((a: any) => a.audienceId),\n }\n \n req.payload.logger.info('Creating broadcast with data:', {\n name: createData.name,\n subject: createData.subject,\n preheader: createData.preheader || 'NONE',\n contentLength: htmlContent ? htmlContent.length : 0,\n contentPreview: htmlContent ? htmlContent.substring(0, 100) + '...' : 'EMPTY',\n trackOpens: createData.trackOpens,\n trackClicks: createData.trackClicks,\n replyTo: createData.replyTo,\n audienceIds: createData.audienceIds || [],\n apiUrl: providerConfig.apiUrl,\n hasToken: !!providerConfig.token,\n })\n\n // Create broadcast in provider\n const providerBroadcast = await provider.create(createData)\n\n // Update with provider ID\n await req.payload.update({\n collection: 'broadcasts',\n id: doc.id,\n data: {\n providerId: providerBroadcast.id,\n providerData: providerBroadcast.providerData,\n },\n req,\n })\n\n return {\n ...doc,\n providerId: providerBroadcast.id,\n providerData: providerBroadcast.providerData,\n }\n } catch (error: unknown) {\n // Log the raw error first to see what we're dealing with\n req.payload.logger.error('Raw error from broadcast provider:')\n req.payload.logger.error(error)\n \n // Try different error formats\n if (error instanceof Error) {\n req.payload.logger.error('Error is instance of Error:', {\n message: error.message,\n stack: error.stack,\n name: error.name,\n // If it's a BroadcastProviderError, it might have additional details\n ...(error as any).details,\n // Check if it's a fetch response error\n ...(error as any).response,\n ...(error as any).data,\n ...(error as any).status,\n ...(error as any).statusText,\n })\n } else if (typeof error === 'string') {\n req.payload.logger.error('Error is a string:', error)\n } else if (error && typeof error === 'object') {\n req.payload.logger.error('Error is an object:', JSON.stringify(error, null, 2))\n } else {\n req.payload.logger.error('Unknown error type:', typeof error)\n }\n \n // Also log the doc info for context\n req.payload.logger.error('Failed broadcast document:', {\n id: doc.id,\n subject: doc.subject,\n hasContent: !!doc.contentSection?.content,\n contentType: doc.contentSection?.content ? typeof doc.contentSection.content : 'none',\n })\n \n return doc\n }\n }\n \n // Handle update operation\n if (operation === 'update') {\n req.payload.logger.info('Broadcast afterChange update hook triggered', {\n operation,\n hasProviderId: !!doc.providerId,\n sendStatus: doc.sendStatus,\n publishStatus: doc._status\n })\n\n try {\n // Get provider config from settings first, then fall back to env vars\n const providerConfig = await getBroadcastConfig(req, pluginConfig)\n if (!providerConfig || !providerConfig.token) {\n req.payload.logger.error('Broadcast provider not configured in Newsletter Settings or environment variables')\n return doc\n }\n\n const { BroadcastApiProvider } = await import('../providers/broadcast/broadcast')\n const provider = new BroadcastApiProvider(providerConfig)\n\n // If no providerId exists yet, we need to create in provider first (deferred from initial create)\n if (!doc.providerId) {\n // Check if we have minimum required fields now\n if (!doc.subject || !doc.contentSection?.content) {\n req.payload.logger.info('Still missing required fields for provider sync')\n return doc\n }\n\n // Populate media fields and convert rich text to HTML\n req.payload.logger.info('Creating broadcast in provider (deferred from initial create)...')\n const populatedContent = await populateMediaFields(doc.contentSection?.content, req.payload, pluginConfig)\n \n // Get email preview customization options\n const emailPreviewConfig = pluginConfig.customizations?.broadcasts?.emailPreview\n \n const htmlContent = await convertToEmailSafeHtml(populatedContent, {\n wrapInTemplate: emailPreviewConfig?.wrapInTemplate ?? true,\n customWrapper: emailPreviewConfig?.customWrapper,\n preheader: doc.contentSection?.preheader,\n subject: doc.subject,\n documentData: doc, // Pass entire document\n customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter\n })\n\n // Skip if content is empty after conversion\n if (!htmlContent || htmlContent.trim() === '') {\n req.payload.logger.info('Skipping provider sync - content is empty after conversion')\n return doc\n }\n\n // Create broadcast in provider\n const createData = {\n name: doc.subject,\n subject: doc.subject,\n preheader: doc.contentSection?.preheader,\n content: htmlContent,\n trackOpens: doc.settings?.trackOpens,\n trackClicks: doc.settings?.trackClicks,\n replyTo: doc.settings?.replyTo || providerConfig.replyTo,\n audienceIds: doc.audienceIds?.map((a: any) => a.audienceId),\n }\n\n req.payload.logger.info('Creating broadcast with data:', {\n name: createData.name,\n subject: createData.subject,\n preheader: createData.preheader || 'NONE',\n contentLength: htmlContent ? htmlContent.length : 0,\n contentPreview: htmlContent ? htmlContent.substring(0, 100) + '...' : 'EMPTY',\n apiUrl: providerConfig.apiUrl,\n hasToken: !!providerConfig.token,\n })\n\n const providerBroadcast = await provider.create(createData)\n\n // Update with provider ID\n await req.payload.update({\n collection: 'broadcasts',\n id: doc.id,\n data: {\n providerId: providerBroadcast.id,\n providerData: providerBroadcast.providerData,\n },\n req,\n })\n\n req.payload.logger.info(`Broadcast ${doc.id} created in provider successfully (deferred)`)\n\n return {\n ...doc,\n providerId: providerBroadcast.id,\n providerData: providerBroadcast.providerData,\n }\n }\n\n // Handle normal updates to existing broadcasts (only if providerId exists)\n if (doc.providerId) {\n // Only sync if broadcast is still editable\n const capabilities = provider.getCapabilities()\n const sendStatus = doc.sendStatus || BroadcastStatus.DRAFT\n if (!capabilities.editableStatuses.includes(sendStatus)) {\n req.payload.logger.info(`Skipping sync for broadcast in status: ${sendStatus}`)\n return doc\n }\n\n // Check what has changed\n const contentChanged = \n doc.subject !== previousDoc?.subject ||\n doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader ||\n JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content) ||\n doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens ||\n doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks ||\n doc.settings?.replyTo !== previousDoc?.settings?.replyTo ||\n JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds)\n\n if (contentChanged) {\n // Build update data\n const updates: any = {}\n if (doc.subject !== previousDoc?.subject) {\n updates.name = doc.subject // Use subject as name in the provider\n updates.subject = doc.subject\n }\n if (doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader) {\n updates.preheader = doc.contentSection?.preheader\n }\n if (JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content)) {\n const populatedContent = await populateMediaFields(doc.contentSection?.content, req.payload, pluginConfig)\n \n // Get email preview customization options\n const emailPreviewConfig = pluginConfig.customizations?.broadcasts?.emailPreview\n \n updates.content = await convertToEmailSafeHtml(populatedContent, {\n wrapInTemplate: emailPreviewConfig?.wrapInTemplate ?? true,\n customWrapper: emailPreviewConfig?.customWrapper,\n preheader: doc.contentSection?.preheader,\n subject: doc.subject,\n documentData: doc, // Pass entire document\n customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter\n })\n }\n if (doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens) {\n updates.trackOpens = doc.settings.trackOpens\n }\n if (doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks) {\n updates.trackClicks = doc.settings.trackClicks\n }\n if (doc.settings?.replyTo !== previousDoc?.settings?.replyTo) {\n updates.replyTo = doc.settings.replyTo || providerConfig.replyTo\n }\n if (JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds)) {\n updates.audienceIds = doc.audienceIds?.map((a: any) => a.audienceId)\n }\n\n req.payload.logger.info('Syncing broadcast updates to provider', {\n providerId: doc.providerId,\n updates\n })\n \n await provider.update(doc.providerId, updates)\n req.payload.logger.info(`Broadcast ${doc.id} synced to provider successfully`)\n } else {\n req.payload.logger.info('No content changes to sync to provider')\n }\n }\n } catch (error) {\n // Enhanced error logging for debugging\n req.payload.logger.error('Raw error from broadcast update operation:')\n req.payload.logger.error(error)\n \n if (error instanceof Error) {\n req.payload.logger.error('Error is instance of Error:', {\n message: error.message,\n stack: error.stack,\n name: error.name,\n ...(error as any).details,\n ...(error as any).response,\n ...(error as any).data,\n ...(error as any).status,\n ...(error as any).statusText,\n })\n } else if (typeof error === 'string') {\n req.payload.logger.error('Error is a string:', error)\n } else if (error && typeof error === 'object') {\n req.payload.logger.error('Error is an object:', JSON.stringify(error, null, 2))\n } else {\n req.payload.logger.error('Unknown error type:', typeof error)\n }\n \n req.payload.logger.error('Failed broadcast document (update operation):', {\n id: doc.id,\n subject: doc.subject,\n hasContent: !!doc.contentSection?.content,\n contentType: doc.contentSection?.content ? typeof doc.contentSection.content : 'none',\n })\n \n // Don't throw - allow Payload update to succeed even if provider sync fails\n }\n }\n\n return doc\n },\n // Hook to send when published\n async ({ doc, operation, previousDoc, req }) => {\n // Only run on updates when transitioning to published\n if (operation !== 'update') return doc\n \n const wasUnpublished = !previousDoc?._status || previousDoc._status === 'draft'\n const isNowPublished = doc._status === 'published'\n \n if (wasUnpublished && isNowPublished && doc.providerId) {\n // Check if already sent\n if (doc.sendStatus === BroadcastStatus.SENT || doc.sendStatus === BroadcastStatus.SENDING) {\n return doc\n }\n \n try {\n // Get provider config from settings first, then fall back to env vars\n const broadcastConfig = await getBroadcastConfig(req, pluginConfig)\n const resendConfig = pluginConfig.providers?.resend // TODO: Add getResendConfig utility\n \n if (!broadcastConfig && !resendConfig) {\n req.payload.logger.error('No provider configured for sending in Newsletter Settings or environment variables')\n return doc\n }\n \n // Send via provider\n if (broadcastConfig && broadcastConfig.token) {\n const { BroadcastApiProvider } = await import('../providers/broadcast/broadcast')\n const provider = new BroadcastApiProvider(broadcastConfig)\n await provider.send(doc.providerId)\n }\n // Add resend provider support here when needed\n \n // Update status\n await req.payload.update({\n collection: 'broadcasts',\n id: doc.id,\n data: {\n sendStatus: BroadcastStatus.SENDING,\n sentAt: new Date().toISOString(),\n },\n req,\n })\n \n req.payload.logger.info(`Broadcast ${doc.id} sent successfully`)\n \n } catch (error) {\n // Log full error details for debugging\n if (error instanceof Error) {\n req.payload.logger.error(`Failed to send broadcast ${doc.id}:`, {\n message: error.message,\n stack: error.stack,\n name: error.name,\n // If it's a BroadcastProviderError, it might have additional details\n ...(error as any).details\n })\n } else {\n req.payload.logger.error(`Failed to send broadcast ${doc.id}:`, error)\n }\n \n // Update status to failed\n await req.payload.update({\n collection: 'broadcasts',\n id: doc.id,\n data: {\n sendStatus: BroadcastStatus.FAILED,\n },\n req,\n })\n }\n }\n \n return doc\n },\n ],\n // beforeChange hooks can be added here if needed\n beforeChange: [],\n // Handle deletion\n afterDelete: [\n async ({ doc, req }) => {\n if (!hasProviders || !doc?.providerId) return doc\n\n try {\n // Get provider config from settings first, then fall back to env vars\n const providerConfig = await getBroadcastConfig(req, pluginConfig)\n if (!providerConfig || !providerConfig.token) {\n req.payload.logger.error('Broadcast provider not configured in Newsletter Settings or environment variables')\n return doc\n }\n\n const { BroadcastApiProvider } = await import('../providers/broadcast/broadcast')\n const provider = new BroadcastApiProvider(providerConfig)\n\n // Only delete if broadcast is still editable\n const capabilities = provider.getCapabilities()\n if (capabilities.editableStatuses.includes(doc.sendStatus)) {\n await provider.delete(doc.providerId)\n }\n } catch (error) {\n // Log full error details for debugging\n if (error instanceof Error) {\n req.payload.logger.error('Failed to delete broadcast from provider:', {\n message: error.message,\n stack: error.stack,\n name: error.name,\n // If it's a BroadcastProviderError, it might have additional details\n ...(error as any).details\n })\n } else {\n req.payload.logger.error('Failed to delete broadcast from provider:', error)\n }\n }\n\n return doc\n },\n ],\n },\n }\n}\n\n\n","import { \n BoldFeature,\n ItalicFeature,\n UnderlineFeature,\n StrikethroughFeature,\n LinkFeature,\n OrderedListFeature,\n UnorderedListFeature,\n HeadingFeature,\n ParagraphFeature,\n AlignFeature,\n BlockquoteFeature,\n BlocksFeature,\n UploadFeature,\n FixedToolbarFeature,\n InlineToolbarFeature,\n lexicalEditor,\n} from '@payloadcms/richtext-lexical'\nimport type { RichTextField, Block } from 'payload'\nimport { createEmailSafeBlocks } from '../utils/blockValidation'\n\n/**\n * Creates email-safe features for Lexical editor with optional additional blocks\n * Only includes features that render consistently across email clients\n */\n// Using any[] here because Payload's FeatureProviderServer type is complex\n// and varies between versions. The features are properly typed by Payload internally.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const createEmailSafeFeatures = (additionalBlocks?: Block[]): any[] => {\n // Base email-safe blocks\n const baseBlocks = [\n {\n slug: 'button',\n fields: [\n {\n name: 'text',\n type: 'text',\n label: 'Button Text',\n required: true,\n },\n {\n name: 'url',\n type: 'text',\n label: 'Button URL',\n required: true,\n admin: {\n description: 'Enter the full URL (including https://)',\n },\n },\n {\n name: 'style',\n type: 'select',\n label: 'Button Style',\n defaultValue: 'primary',\n options: [\n { label: 'Primary', value: 'primary' },\n { label: 'Secondary', value: 'secondary' },\n { label: 'Outline', value: 'outline' },\n ],\n },\n ],\n interfaceName: 'EmailButton',\n labels: {\n singular: 'Button',\n plural: 'Buttons',\n },\n },\n {\n slug: 'divider',\n fields: [\n {\n name: 'style',\n type: 'select',\n label: 'Divider Style',\n defaultValue: 'solid',\n options: [\n { label: 'Solid', value: 'solid' },\n { label: 'Dashed', value: 'dashed' },\n { label: 'Dotted', value: 'dotted' },\n ],\n },\n ],\n interfaceName: 'EmailDivider',\n labels: {\n singular: 'Divider',\n plural: 'Dividers',\n },\n },\n ] as Block[]\n\n // Merge additional blocks if provided\n const allBlocks = [\n ...baseBlocks,\n ...(additionalBlocks || [])\n ]\n\n return [\n // Toolbars\n FixedToolbarFeature(), // Fixed toolbar at the top\n InlineToolbarFeature(), // Floating toolbar when text is selected\n \n // Basic text formatting\n BoldFeature(),\n ItalicFeature(),\n UnderlineFeature(),\n StrikethroughFeature(),\n \n // Links with enhanced configuration\n LinkFeature({\n fields: [\n {\n name: 'url',\n type: 'text',\n required: true,\n admin: {\n description: 'Enter the full URL (including https://)',\n },\n },\n {\n name: 'newTab',\n type: 'checkbox',\n label: 'Open in new tab',\n defaultValue: false,\n },\n ],\n }),\n \n // Lists\n OrderedListFeature(),\n UnorderedListFeature(),\n \n // Headings - limited to h1, h2, h3 for email compatibility\n HeadingFeature({\n enabledHeadingSizes: ['h1', 'h2', 'h3'],\n }),\n \n // Basic paragraph and alignment\n ParagraphFeature(),\n AlignFeature(),\n \n // Blockquotes\n BlockquoteFeature(),\n \n // Upload feature for images\n UploadFeature({\n collections: {\n media: {\n fields: [\n {\n name: 'caption',\n type: 'text',\n admin: {\n description: 'Optional caption for the image',\n },\n },\n {\n name: 'altText',\n type: 'text',\n label: 'Alt Text',\n required: true,\n admin: {\n description: 'Alternative text for accessibility and when image cannot be displayed',\n },\n },\n ],\n },\n },\n }),\n \n // Custom blocks for email-specific content\n BlocksFeature({\n blocks: allBlocks,\n }),\n ]\n}\n\n/**\n * Creates a Lexical editor configured specifically for email content\n * Processes blocks server-side to avoid client serialization issues\n */\nexport const createEmailLexicalEditor = (customBlocks: Block[] = []): any => {\n const emailSafeBlocks = createEmailSafeBlocks(customBlocks)\n \n return lexicalEditor({\n features: [\n // Toolbars\n FixedToolbarFeature(),\n InlineToolbarFeature(),\n \n // Basic text formatting\n BoldFeature(),\n ItalicFeature(),\n UnderlineFeature(),\n StrikethroughFeature(),\n \n // Links with enhanced configuration\n LinkFeature({\n fields: [\n {\n name: 'url',\n type: 'text',\n required: true,\n admin: {\n description: 'Enter the full URL (including https://)',\n },\n },\n {\n name: 'newTab',\n type: 'checkbox',\n label: 'Open in new tab',\n defaultValue: false,\n },\n ],\n }),\n \n // Lists\n OrderedListFeature(),\n UnorderedListFeature(),\n \n // Headings - limited to h1, h2, h3 for email compatibility\n HeadingFeature({\n enabledHeadingSizes: ['h1', 'h2', 'h3'],\n }),\n \n // Basic paragraph and alignment\n ParagraphFeature(),\n AlignFeature(),\n \n // Blockquotes\n BlockquoteFeature(),\n \n // Upload feature for images\n UploadFeature({\n collections: {\n media: {\n fields: [\n {\n name: 'caption',\n type: 'text',\n admin: {\n description: 'Optional caption for the image',\n },\n },\n {\n name: 'altText',\n type: 'text',\n label: 'Alt Text',\n required: true,\n admin: {\n description: 'Alternative text for accessibility and when image cannot be displayed',\n },\n },\n ],\n },\n },\n }),\n \n // Email-safe blocks (processed server-side)\n BlocksFeature({\n blocks: emailSafeBlocks,\n }),\n ],\n })\n}\n\n/**\n * Legacy export for backward compatibility\n */\nexport const emailSafeFeatures = createEmailSafeFeatures()\n\n/**\n * Creates an email-safe rich text field configuration\n */\nexport const createEmailContentField = (\n overrides?: Partial<RichTextField> & {\n additionalBlocks?: Block[]\n editor?: any // Lexical editor instance\n }\n): RichTextField => {\n // Use provided editor or create one with blocks\n const editor = overrides?.editor || createEmailLexicalEditor(overrides?.additionalBlocks)\n\n return {\n name: 'content',\n type: 'richText',\n required: true,\n editor,\n admin: {\n description: 'Email content with limited formatting for compatibility',\n ...overrides?.admin,\n },\n ...overrides,\n }\n}","import type { Block } from 'payload'\n\n/**\n * Email-incompatible block types that should be warned about\n */\nconst EMAIL_INCOMPATIBLE_TYPES = [\n 'chart',\n 'dataTable', \n 'interactive',\n 'streamable',\n 'video',\n 'iframe',\n 'form',\n 'carousel',\n 'tabs',\n 'accordion',\n 'map'\n]\n\n/**\n * Validates that blocks are email-compatible and warns about potential issues\n */\nexport const validateEmailBlocks = (blocks: Block[]): void => {\n blocks.forEach(block => {\n if (EMAIL_INCOMPATIBLE_TYPES.includes(block.slug)) {\n console.warn(`⚠️ Block \"${block.slug}\" may not be email-compatible. Consider creating an email-specific version.`)\n }\n \n // Check for complex field types that might not work in emails\n const hasComplexFields = block.fields?.some(field => {\n const complexTypes = ['code', 'json', 'richText', 'blocks', 'array']\n return complexTypes.includes(field.type)\n })\n \n if (hasComplexFields) {\n console.warn(`⚠️ Block \"${block.slug}\" contains complex field types that may not render consistently in email clients.`)\n }\n })\n}\n\n/**\n * Creates email-safe block configurations by filtering and validating blocks\n */\nexport const createEmailSafeBlocks = (customBlocks: Block[] = []): Block[] => {\n // Validate blocks\n validateEmailBlocks(customBlocks)\n \n // Base email-safe blocks that come with the plugin\n const baseBlocks: Block[] = [\n {\n slug: 'button',\n fields: [\n {\n name: 'text',\n type: 'text',\n label: 'Button Text',\n required: true,\n },\n {\n name: 'url',\n type: 'text',\n label: 'Button URL',\n required: true,\n admin: {\n description: 'Enter the full URL (including https://)',\n },\n },\n {\n name: 'style',\n type: 'select',\n label: 'Button Style',\n defaultValue: 'primary',\n options: [\n { label: 'Primary', value: 'primary' },\n { label: 'Secondary', value: 'secondary' },\n { label: 'Outline', value: 'outline' },\n ],\n },\n ],\n interfaceName: 'EmailButton',\n labels: {\n singular: 'Button',\n plural: 'Buttons',\n },\n },\n {\n slug: 'divider',\n fields: [\n {\n name: 'style',\n type: 'select',\n label: 'Divider Style',\n defaultValue: 'solid',\n options: [\n { label: 'Solid', value: 'solid' },\n { label: 'Dashed', value: 'dashed' },\n { label: 'Dotted', value: 'dotted' },\n ],\n },\n ],\n interfaceName: 'EmailDivider',\n labels: {\n singular: 'Divider',\n plural: 'Dividers',\n },\n },\n ]\n \n // Combine base blocks with custom blocks\n return [\n ...baseBlocks,\n ...customBlocks\n ]\n}","import type { Field } from 'payload'\n\nexport const createBroadcastInlinePreviewField = (): Field => {\n return {\n name: 'broadcastInlinePreview',\n type: 'ui',\n admin: {\n components: {\n Field: 'payload-plugin-newsletter/components#BroadcastInlinePreview',\n },\n },\n }\n}","import DOMPurify from 'isomorphic-dompurify'\nimport type { SerializedEditorState } from 'lexical'\n\n/**\n * DOMPurify configuration for email-safe HTML\n */\nexport const EMAIL_SAFE_CONFIG = {\n ALLOWED_TAGS: [\n 'p', 'br', 'strong', 'b', 'em', 'i', 'u', 'strike', 's', 'span',\n 'a', 'h1', 'h2', 'h3', 'ul', 'ol', 'li', 'blockquote', 'hr',\n 'img', 'div', 'table', 'tr', 'td', 'th', 'tbody', 'thead'\n ],\n ALLOWED_ATTR: ['href', 'style', 'target', 'rel', 'align', 'src', 'alt', 'width', 'height', 'border', 'cellpadding', 'cellspacing'],\n ALLOWED_STYLES: {\n '*': [\n 'color', 'background-color', 'font-size', 'font-weight',\n 'font-style', 'text-decoration', 'text-align', 'margin',\n 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',\n 'padding', 'padding-top', 'padding-right', 'padding-bottom', \n 'padding-left', 'line-height', 'border-left', 'border-left-width',\n 'border-left-style', 'border-left-color'\n ],\n },\n FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed', 'form', 'input'],\n FORBID_ATTR: ['class', 'id', 'onclick', 'onload', 'onerror'],\n}\n\n/**\n * Converts Lexical editor state to email-safe HTML\n */\nexport async function convertToEmailSafeHtml(\n editorState: SerializedEditorState | undefined | null,\n options?: {\n wrapInTemplate?: boolean\n preheader?: string\n mediaUrl?: string // Base URL for media files\n customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>\n payload?: any // Payload instance for populating relationships\n populateFields?: string[] | ((blockType: string) => string[]) // Fields to populate\n customWrapper?: (content: string, options?: { preheader?: string; subject?: string; documentData?: Record<string, any> }) => string | Promise<string>\n subject?: string // Email subject for custom wrapper\n documentData?: Record<string, any> // Generic document data for custom wrapper\n }\n): Promise<string> {\n // Handle empty content\n if (!editorState) {\n return ''\n }\n \n // First, convert Lexical state to HTML using custom converters\n const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl, options?.customBlockConverter)\n \n // Sanitize the HTML\n const sanitizedHtml = DOMPurify.sanitize(rawHtml, EMAIL_SAFE_CONFIG)\n \n // Optionally wrap in email template\n if (options?.wrapInTemplate) {\n if (options.customWrapper) {\n return await Promise.resolve(options.customWrapper(sanitizedHtml, { \n preheader: options.preheader,\n subject: options.subject,\n documentData: options.documentData\n }))\n }\n return wrapInEmailTemplate(sanitizedHtml, options.preheader)\n }\n \n return sanitizedHtml\n}\n\n/**\n * Custom Lexical to HTML converter for email\n */\nasync function lexicalToEmailHtml(\n editorState: SerializedEditorState, \n mediaUrl?: string,\n customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>\n): Promise<string> {\n const { root } = editorState\n \n if (!root || !root.children) {\n return ''\n }\n \n // Convert nodes asynchronously to support custom converters\n const htmlParts = await Promise.all(\n root.children.map((node: any) => convertNode(node, mediaUrl, customBlockConverter))\n )\n \n return htmlParts.join('')\n}\n\n/**\n * Convert individual Lexical nodes to email-safe HTML\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function convertNode(\n node: any, \n mediaUrl?: string,\n customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>\n): Promise<string> {\n switch (node.type) {\n case 'paragraph':\n return convertParagraph(node, mediaUrl, customBlockConverter)\n case 'heading':\n return convertHeading(node, mediaUrl, customBlockConverter)\n case 'list':\n return convertList(node, mediaUrl, customBlockConverter)\n case 'listitem':\n return convertListItem(node, mediaUrl, customBlockConverter)\n case 'blockquote':\n return convertBlockquote(node, mediaUrl, customBlockConverter)\n case 'text':\n return convertText(node)\n case 'link':\n return convertLink(node, mediaUrl, customBlockConverter)\n case 'linebreak':\n return '<br>'\n case 'upload':\n return convertUpload(node, mediaUrl)\n case 'block':\n return await convertBlock(node, mediaUrl, customBlockConverter)\n default:\n // Unknown node type - convert children if any\n if (node.children) {\n const childParts = await Promise.all(\n node.children.map((child: any) => convertNode(child, mediaUrl, customBlockConverter))\n )\n return childParts.join('')\n }\n return ''\n }\n}\n\n/**\n * Convert paragraph node\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function convertParagraph(\n node: any, \n mediaUrl?: string,\n customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>\n): Promise<string> {\n const align = getAlignment(node.format)\n const childParts = await Promise.all(\n (node.children || []).map((child: any) => convertNode(child, mediaUrl, customBlockConverter))\n )\n const children = childParts.join('')\n \n if (!children.trim()) {\n return '<p class=\"mobile-margin-bottom-16\" style=\"margin: 0 0 16px 0; min-height: 1em;\"> </p>'\n }\n \n return `<p class=\"mobile-margin-bottom-16\" style=\"margin: 0 0 16px 0; text-align: ${align}; font-size: 16px; line-height: 1.5;\">${children}</p>`\n}\n\n/**\n * Convert heading node\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function convertHeading(\n node: any, \n mediaUrl?: string,\n customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>\n): Promise<string> {\n const tag = node.tag || 'h1'\n const align = getAlignment(node.format)\n const childParts = await Promise.all(\n (node.children || []).map((child: any) => convertNode(child, mediaUrl, customBlockConverter))\n )\n const children = childParts.join('')\n \n const styles: Record<string, string> = {\n h1: 'font-size: 32px; font-weight: 700; margin: 0 0 24px 0; line-height: 1.2;',\n h2: 'font-size: 24px; font-weight: 600; margin: 0 0 16px 0; line-height: 1.3;',\n h3: 'font-size: 20px; font-weight: 600; margin: 0 0 12px 0; line-height: 1.4;',\n }\n \n const mobileClasses: Record<string, string> = {\n h1: 'mobile-font-size-24',\n h2: 'mobile-font-size-20',\n h3: 'mobile-font-size-16',\n }\n \n const style = `${styles[tag] || styles.h3} text-align: ${align};`\n const mobileClass = mobileClasses[tag] || mobileClasses.h3\n \n return `<${tag} class=\"${mobileClass}\" style=\"${style}\">${children}</${tag}>`\n}\n\n/**\n * Convert list node\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function convertList(\n node: any, \n mediaUrl?: string,\n customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>\n): Promise<string> {\n const tag = node.listType === 'number' ? 'ol' : 'ul'\n const childParts = await Promise.all(\n (node.children || []).map((child: any) => convertNode(child, mediaUrl, customBlockConverter))\n )\n const children = childParts.join('')\n \n const style = tag === 'ul' \n ? 'margin: 0 0 16px 0; padding-left: 24px; list-style-type: disc; font-size: 16px; line-height: 1.5;'\n : 'margin: 0 0 16px 0; padding-left: 24px; list-style-type: decimal; font-size: 16px; line-height: 1.5;'\n \n return `<${tag} class=\"mobile-margin-bottom-16\" style=\"${style}\">${children}</${tag}>`\n}\n\n/**\n * Convert list item node\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function convertListItem(\n node: any, \n mediaUrl?: string,\n customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>\n): Promise<string> {\n const childParts = await Promise.all(\n (node.children || []).map((child: any) => convertNode(child, mediaUrl, customBlockConverter))\n )\n const children = childParts.join('')\n return `<li style=\"margin: 0 0 8px 0;\">${children}</li>`\n}\n\n/**\n * Convert blockquote node\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function convertBlockquote(\n node: any, \n mediaUrl?: string,\n customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>\n): Promise<string> {\n const childParts = await Promise.all(\n (node.children || []).map((child: any) => convertNode(child, mediaUrl, customBlockConverter))\n )\n const children = childParts.join('')\n const style = 'margin: 0 0 16px 0; padding-left: 16px; border-left: 4px solid #e5e7eb; color: #6b7280;'\n \n return `<blockquote style=\"${style}\">${children}</blockquote>`\n}\n\n/**\n * Convert text node\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction convertText(node: any): string {\n let text = escapeHtml(node.text || '')\n \n // Apply formatting\n if (node.format & 1) { // Bold\n text = `<strong>${text}</strong>`\n }\n if (node.format & 2) { // Italic\n text = `<em>${text}</em>`\n }\n if (node.format & 8) { // Underline\n text = `<u>${text}</u>`\n }\n if (node.format & 4) { // Strikethrough\n text = `<strike>${text}</strike>`\n }\n \n return text\n}\n\n/**\n * Convert link node\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function convertLink(\n node: any, \n mediaUrl?: string,\n customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>\n): Promise<string> {\n const childParts = await Promise.all(\n (node.children || []).map((child: any) => convertNode(child, mediaUrl, customBlockConverter))\n )\n const children = childParts.join('')\n const url = node.fields?.url || '#'\n const newTab = node.fields?.newTab ?? false\n \n // Add target and rel attributes based on newTab setting\n const targetAttr = newTab ? ' target=\"_blank\"' : ''\n const relAttr = newTab ? ' rel=\"noopener noreferrer\"' : ''\n \n return `<a href=\"${escapeHtml(url)}\"${targetAttr}${relAttr} style=\"color: #2563eb; text-decoration: underline;\">${children}</a>`\n}\n\n/**\n * Convert upload (image) node\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction convertUpload(node: any, mediaUrl?: string): string {\n const upload = node.value\n if (!upload) return ''\n \n // Get image URL - handle both direct URL and media object\n let src = ''\n if (typeof upload === 'string') {\n src = upload\n } else if (upload.url) {\n src = upload.url\n } else if (upload.filename && mediaUrl) {\n // Construct URL from media URL and filename\n src = `${mediaUrl}/${upload.filename}`\n }\n \n const alt = node.fields?.altText || upload.alt || ''\n const caption = node.fields?.caption || ''\n \n // Responsive email-safe image\n const imgHtml = `<img src=\"${escapeHtml(src)}\" alt=\"${escapeHtml(alt)}\" class=\"mobile-width-100\" style=\"max-width: 100%; height: auto; display: block; margin: 0 auto; border-radius: 6px;\" />`\n \n if (caption) {\n return `\n <div style=\"margin: 0 0 16px 0; text-align: center;\" class=\"mobile-margin-bottom-16\">\n ${imgHtml}\n <p style=\"margin: 8px 0 0 0; font-size: 14px; color: #6b7280; font-style: italic; text-align: center;\" class=\"mobile-font-size-14\">${escapeHtml(caption)}</p>\n </div>\n `\n }\n \n return `<div style=\"margin: 0 0 16px 0; text-align: center;\" class=\"mobile-margin-bottom-16\">${imgHtml}</div>`\n}\n\n/**\n * Convert custom block node\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function convertBlock(\n node: any, \n mediaUrl?: string,\n customBlockConverter?: (node: any, mediaUrl?: string) => Promise<string>\n): Promise<string> {\n const blockType = node.fields?.blockName || node.blockName\n \n // First, check if there's a custom converter for this block\n if (customBlockConverter) {\n try {\n const customHtml = await customBlockConverter(node, mediaUrl)\n if (customHtml) {\n return customHtml\n }\n } catch (error) {\n console.error(`Custom block converter error for ${blockType}:`, error)\n // Fall through to default handling\n }\n }\n \n // Default handling for built-in blocks\n switch (blockType) {\n case 'button':\n return convertButtonBlock(node.fields)\n case 'divider':\n return convertDividerBlock(node.fields)\n default:\n // Unknown block type - try to convert children\n if (node.children) {\n const childParts = await Promise.all(\n node.children.map((child: any) => convertNode(child, mediaUrl, customBlockConverter))\n )\n return childParts.join('')\n }\n return ''\n }\n}\n\n/**\n * Convert button block\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction convertButtonBlock(fields: any): string {\n const text = fields?.text || 'Click here'\n const url = fields?.url || '#'\n const style = fields?.style || 'primary'\n \n const styles: Record<string, string> = {\n primary: 'background-color: #2563eb; color: #ffffff; border: 2px solid #2563eb;',\n secondary: 'background-color: #6b7280; color: #ffffff; border: 2px solid #6b7280;',\n outline: 'background-color: transparent; color: #2563eb; border: 2px solid #2563eb;',\n }\n \n const buttonStyle = `${styles[style] || styles.primary} display: inline-block; padding: 12px 24px; font-size: 16px; font-weight: 600; text-decoration: none; border-radius: 6px; text-align: center;`\n \n return `\n <div style=\"margin: 0 0 16px 0; text-align: center;\">\n <a href=\"${escapeHtml(url)}\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"${buttonStyle}\">${escapeHtml(text)}</a>\n </div>\n `\n}\n\n/**\n * Convert divider block\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction convertDividerBlock(fields: any): string {\n const style = fields?.style || 'solid'\n \n const styles: Record<string, string> = {\n solid: 'border-top: 1px solid #e5e7eb;',\n dashed: 'border-top: 1px dashed #e5e7eb;',\n dotted: 'border-top: 1px dotted #e5e7eb;',\n }\n \n return `<hr style=\"${styles[style] || styles.solid} margin: 24px 0; border-bottom: none; border-left: none; border-right: none;\" />`\n}\n\n/**\n * Get text alignment from format number\n */\nfunction getAlignment(format?: number): string {\n if (!format) return 'left'\n \n // Lexical alignment format values\n if (format & 2) return 'center'\n if (format & 3) return 'right'\n if (format & 4) return 'justify'\n \n return 'left'\n}\n\n/**\n * Escape HTML special characters\n */\nfunction escapeHtml(text: string): string {\n const map: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''\n }\n \n return text.replace(/[&<>\"']/g, m => map[m])\n}\n\n/**\n * Wrap content in a responsive email template\n */\nfunction wrapInEmailTemplate(content: string, preheader?: string): string {\n return `<!DOCTYPE html>\n<html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"x-apple-disable-message-reformatting\">\n <title>Newsletter</title>\n \n <!--[if mso]>\n <noscript>\n <xml>\n <o:OfficeDocumentSettings>\n <o:PixelsPerInch>96</o:PixelsPerInch>\n </o:OfficeDocumentSettings>\n </xml>\n </noscript>\n <![endif]-->\n \n <style>\n /* Reset and base styles */\n * {\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n }\n \n body {\n margin: 0 !important;\n padding: 0 !important;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;\n font-size: 16px;\n line-height: 1.5;\n color: #1A1A1A;\n background-color: #f8f9fa;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n \n table {\n border-spacing: 0 !important;\n border-collapse: collapse !important;\n table-layout: fixed !important;\n margin: 0 auto !important;\n }\n \n table table table {\n table-layout: auto;\n }\n \n img {\n -ms-interpolation-mode: bicubic;\n max-width: 100%;\n height: auto;\n border: 0;\n outline: none;\n text-decoration: none;\n }\n \n /* Responsive styles */\n @media only screen and (max-width: 640px) {\n .mobile-hide {\n display: none !important;\n }\n \n .mobile-center {\n text-align: center !important;\n }\n \n .mobile-width-100 {\n width: 100% !important;\n max-width: 100% !important;\n }\n \n .mobile-padding {\n padding: 20px !important;\n }\n \n .mobile-padding-sm {\n padding: 16px !important;\n }\n \n .mobile-font-size-14 {\n font-size: 14px !important;\n }\n \n .mobile-font-size-16 {\n font-size: 16px !important;\n }\n \n .mobile-font-size-20 {\n font-size: 20px !important;\n line-height: 1.3 !important;\n }\n \n .mobile-font-size-24 {\n font-size: 24px !important;\n line-height: 1.2 !important;\n }\n \n /* Stack sections on mobile */\n .mobile-stack {\n display: block !important;\n width: 100% !important;\n }\n \n /* Mobile-specific spacing */\n .mobile-margin-bottom-16 {\n margin-bottom: 16px !important;\n }\n \n .mobile-margin-bottom-20 {\n margin-bottom: 20px !important;\n }\n }\n \n /* Dark mode support */\n @media (prefers-color-scheme: dark) {\n .dark-mode-bg {\n background-color: #1a1a1a !important;\n }\n \n .dark-mode-text {\n color: #ffffff !important;\n }\n \n .dark-mode-border {\n border-color: #333333 !important;\n }\n }\n \n /* Outlook-specific fixes */\n <!--[if mso]>\n <style>\n table {\n border-collapse: collapse;\n border-spacing: 0;\n border: none;\n margin: 0;\n }\n \n div, p {\n margin: 0;\n }\n </style>\n <![endif]-->\n </style>\n</head>\n<body style=\"margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 16px; line-height: 1.5; color: #1A1A1A; background-color: #f8f9fa;\">\n ${preheader ? `\n <!-- Preheader text -->\n <div style=\"display: none; max-height: 0; overflow: hidden; font-size: 1px; line-height: 1px; color: transparent;\">\n ${escapeHtml(preheader)}\n </div>\n ` : ''}\n \n <!-- Main container -->\n <table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"margin: 0; padding: 0; background-color: #f8f9fa;\">\n <tr>\n <td align=\"center\" style=\"padding: 20px 10px;\">\n <!-- Email wrapper -->\n <table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" width=\"600\" class=\"mobile-width-100\" style=\"margin: 0 auto; max-width: 600px;\">\n <tr>\n <td class=\"mobile-padding\" style=\"padding: 0;\">\n <!-- Content area with light background -->\n <div style=\"background-color: #ffffff; padding: 40px 30px; border-radius: 8px;\" class=\"mobile-padding\">\n ${content}\n </div>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n</body>\n</html>`\n}\n\n/**\n * Extract personalization tags from content\n */\nexport function extractPersonalizationTags(html: string): string[] {\n const regex = /\\{\\{([^}]+)\\}\\}/g\n const tags: string[] = []\n let match\n \n while ((match = regex.exec(html)) !== null) {\n tags.push(match[1].trim())\n }\n \n return [...new Set(tags)]\n}\n\n/**\n * Replace personalization tags with sample data\n */\nexport function replacePersonalizationTags(\n html: string, \n sampleData: Record<string, string>\n): string {\n return html.replace(/\\{\\{([^}]+)\\}\\}/g, (match, tag) => {\n const trimmedTag = tag.trim()\n return sampleData[trimmedTag] || match\n })\n}","import type { PayloadRequest } from 'payload'\nimport type { NewsletterPluginConfig, BroadcastProviderConfig } from '../types'\n\nexport async function getBroadcastConfig(\n req: PayloadRequest,\n pluginConfig: NewsletterPluginConfig\n): Promise<BroadcastProviderConfig | null> {\n try {\n // Get settings from Newsletter Settings collection\n const settings = await req.payload.findGlobal({\n slug: pluginConfig.settingsSlug || 'newsletter-settings',\n req,\n })\n\n // Build provider config from settings, falling back to env vars\n if (settings?.provider === 'broadcast' && settings?.broadcastSettings) {\n return {\n apiUrl: settings.broadcastSettings.apiUrl || pluginConfig.providers?.broadcast?.apiUrl || '',\n token: settings.broadcastSettings.token || pluginConfig.providers?.broadcast?.token || '',\n fromAddress: settings.fromAddress || pluginConfig.providers?.broadcast?.fromAddress || '',\n fromName: settings.fromName || pluginConfig.providers?.broadcast?.fromName || '',\n replyTo: settings.replyTo || pluginConfig.providers?.broadcast?.replyTo,\n }\n }\n\n // Fall back to env var config\n return pluginConfig.providers?.broadcast || null\n } catch (error) {\n req.payload.logger.error('Failed to get broadcast config from settings:', error)\n // Fall back to env var config on error\n return pluginConfig.providers?.broadcast || null\n }\n}","import type { Endpoint, PayloadHandler, PayloadRequest } from 'payload'\nimport type { NewsletterPluginConfig, SendBroadcastOptions } from '../../types'\nimport { NewsletterProviderError, NewsletterStatus } from '../../types'\nimport { requireAdmin } from '../../utils/auth'\nimport { getBroadcastConfig } from '../../utils/getBroadcastConfig'\n\nexport const createSendBroadcastEndpoint = (\n config: NewsletterPluginConfig,\n collectionSlug: string\n): Endpoint => {\n return {\n path: '/:id/send',\n method: 'post',\n handler: (async (req: PayloadRequest) => {\n try {\n // Check authentication\n const auth = await requireAdmin(req, config)\n if (!auth.authorized) {\n return Response.json({\n success: false,\n error: auth.error,\n }, { status: 401 })\n }\n\n // Check if broadcast management is enabled\n if (!config.features?.newsletterManagement?.enabled) {\n return Response.json({\n success: false,\n error: 'Broadcast management is not enabled',\n }, { status: 400 })\n }\n\n // Get ID from URL\n const url = new URL(req.url || '', `http://localhost`)\n const pathParts = url.pathname.split('/')\n const id = pathParts[pathParts.length - 2] // -2 because last part is 'send'\n\n if (!id) {\n return Response.json({\n success: false,\n error: 'Broadcast ID is required',\n }, { status: 400 })\n }\n\n // Parse request body\n const data = await (req.json?.() || Promise.resolve({})) as SendBroadcastOptions\n\n // Get the broadcast document\n const broadcastDoc = await req.payload.findByID({\n collection: collectionSlug,\n id,\n user: auth.user,\n })\n\n if (!broadcastDoc || !broadcastDoc.providerId) {\n return Response.json({\n success: false,\n error: 'Broadcast not found or not synced with provider',\n }, { status: 404 })\n }\n\n // Get provider config from settings first, then fall back to env vars\n const providerConfig = await getBroadcastConfig(req, config)\n if (!providerConfig || !providerConfig.token) {\n return Response.json({\n success: false,\n error: 'Broadcast provider not configured in Newsletter Settings or environment variables',\n }, { status: 500 })\n }\n\n const { BroadcastApiProvider } = await import('../../providers/broadcast/broadcast')\n const provider = new BroadcastApiProvider(providerConfig)\n\n // Send broadcast using provider ID\n const broadcast = await provider.send(broadcastDoc.providerId, data)\n\n // Update status in Payload collection\n await req.payload.update({\n collection: collectionSlug,\n id,\n data: {\n sendStatus: NewsletterStatus.SENDING,\n sentAt: new Date().toISOString(),\n },\n user: auth.user,\n })\n\n return Response.json({\n success: true,\n message: 'Broadcast sent successfully',\n broadcast,\n })\n } catch (error) {\n console.error('Failed to send broadcast:', error)\n \n if (error instanceof NewsletterProviderError) {\n return Response.json({\n success: false,\n error: error.message,\n code: error.code,\n }, { status: error.code === 'NOT_SUPPORTED' ? 501 : 500 })\n }\n\n return Response.json({\n success: false,\n error: 'Failed to send broadcast',\n }, { status: 500 })\n }\n }) as PayloadHandler,\n }\n}","import type { Access, AccessArgs } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\n\n/**\n * Check if a user is an admin based on the plugin configuration\n */\nexport const isAdmin = (user: any, config?: NewsletterPluginConfig): boolean => {\n if (!user || user.collection !== 'users') {\n return false\n }\n\n // If custom admin check is provided, use it\n if (config?.access?.isAdmin) {\n return config.access.isAdmin(user)\n }\n\n // Default checks for common admin patterns\n // 1. Check for admin role\n if (user.roles?.includes('admin')) {\n return true\n }\n\n // 2. Check for isAdmin boolean field\n if (user.isAdmin === true) {\n return true\n }\n\n // 3. Check for role field with admin value\n if (user.role === 'admin') {\n return true\n }\n\n // 4. Check for admin collection relationship\n if (user.admin === true) {\n return true\n }\n\n return false\n}\n\n/**\n * Create admin-only access control\n */\nexport const adminOnly = (config?: NewsletterPluginConfig): Access => \n ({ req }: AccessArgs) => {\n const user = req.user\n return isAdmin(user, config)\n }\n\n/**\n * Create admin or owner access control\n */\nexport const adminOrSelf = (config?: NewsletterPluginConfig): Access => \n ({ req, id }: AccessArgs) => {\n const user = req.user\n \n // No user = no access\n if (!user) {\n // For list operations without ID, return impossible condition\n if (!id) {\n return {\n id: {\n equals: 'unauthorized-no-access',\n },\n }\n }\n return false\n }\n \n // Admins can access everything\n if (isAdmin(user, config)) {\n return true\n }\n \n // Synthetic users (subscribers from magic link) can access their own data\n if (user.collection === 'subscribers') {\n // For list operations, scope to their own data\n if (!id) {\n return {\n id: {\n equals: user.id,\n },\n }\n }\n // For specific document access, check if it's their own\n return id === user.id\n }\n \n // Regular users cannot access subscriber data\n if (!id) {\n return {\n id: {\n equals: 'unauthorized-no-access',\n },\n }\n }\n return false\n }","import type { PayloadRequest } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\nimport { isAdmin } from './access'\n\n/**\n * Get authenticated user from request\n * In Payload v3, authentication is handled differently than v2\n */\nexport async function getAuthenticatedUser(req: PayloadRequest): Promise<any | null> {\n try {\n // Try to get the current user using Payload's auth\n // This requires the request to have proper authentication headers/cookies\n const me = await req.payload.find({\n collection: 'users',\n where: {\n id: {\n equals: 'me', // Special value in Payload to get current user\n },\n },\n limit: 1,\n depth: 0,\n })\n \n return me.docs[0] || null\n } catch {\n return null\n }\n}\n\n/**\n * Check if request has admin access\n */\nexport async function requireAdmin(\n req: PayloadRequest,\n config: NewsletterPluginConfig\n): Promise<{ authorized: true; user: any } | { authorized: false; error: string }> {\n const user = await getAuthenticatedUser(req)\n \n if (!user) {\n return {\n authorized: false,\n error: 'Authentication required',\n }\n }\n \n if (!isAdmin(user, config)) {\n return {\n authorized: false,\n error: 'Admin access required',\n }\n }\n \n return {\n authorized: true,\n user,\n }\n}","import type { Endpoint, PayloadHandler, PayloadRequest } from 'payload'\nimport type { NewsletterPluginConfig } from '../../types'\nimport { NewsletterProviderError, NewsletterStatus } from '../../types'\nimport { requireAdmin } from '../../utils/auth'\n\nexport const createScheduleBroadcastEndpoint = (\n config: NewsletterPluginConfig,\n collectionSlug: string\n): Endpoint => {\n return {\n path: '/:id/schedule',\n method: 'post',\n handler: (async (req: PayloadRequest) => {\n try {\n // Check authentication\n const auth = await requireAdmin(req, config)\n if (!auth.authorized) {\n return Response.json({\n success: false,\n error: auth.error,\n }, { status: 401 })\n }\n\n // Check if broadcast management is enabled\n if (!config.features?.newsletterManagement?.enabled) {\n return Response.json({\n success: false,\n error: 'Broadcast management is not enabled',\n }, { status: 400 })\n }\n\n // Get ID from URL\n const url = new URL(req.url || '', `http://localhost`)\n const pathParts = url.pathname.split('/')\n const id = pathParts[pathParts.length - 2] // -2 because last part is 'schedule'\n\n if (!id) {\n return Response.json({\n success: false,\n error: 'Broadcast ID is required',\n }, { status: 400 })\n }\n\n // Parse request body\n const data = await (req.json?.() || Promise.resolve({}))\n const { scheduledAt } = data\n\n if (!scheduledAt) {\n return Response.json({\n success: false,\n error: 'scheduledAt is required',\n }, { status: 400 })\n }\n\n // Parse and validate date\n const scheduledDate = new Date(scheduledAt)\n if (isNaN(scheduledDate.getTime())) {\n return Response.json({\n success: false,\n error: 'Invalid scheduledAt date',\n }, { status: 400 })\n }\n\n // Ensure scheduled date is in the future\n if (scheduledDate <= new Date()) {\n return Response.json({\n success: false,\n error: 'scheduledAt must be in the future',\n }, { status: 400 })\n }\n\n // Get the broadcast document\n const broadcastDoc = await req.payload.findByID({\n collection: collectionSlug,\n id,\n user: auth.user,\n })\n\n if (!broadcastDoc || !broadcastDoc.providerId) {\n return Response.json({\n success: false,\n error: 'Broadcast not found or not synced with provider',\n }, { status: 404 })\n }\n\n // Get provider from config\n const providerConfig = config.providers?.broadcast\n if (!providerConfig) {\n return Response.json({\n success: false,\n error: 'Broadcast provider not configured',\n }, { status: 500 })\n }\n\n const { BroadcastApiProvider } = await import('../../providers/broadcast/broadcast')\n const provider = new BroadcastApiProvider(providerConfig)\n\n // Schedule broadcast using provider ID\n const broadcast = await provider.schedule(broadcastDoc.providerId, scheduledDate)\n\n // Update status in Payload collection\n await req.payload.update({\n collection: collectionSlug,\n id,\n data: {\n sendStatus: NewsletterStatus.SCHEDULED,\n scheduledAt: scheduledDate.toISOString(),\n },\n user: auth.user,\n })\n\n return Response.json({\n success: true,\n message: `Broadcast scheduled for ${scheduledDate.toISOString()}`,\n broadcast,\n })\n } catch (error) {\n console.error('Failed to schedule broadcast:', error)\n \n if (error instanceof NewsletterProviderError) {\n return Response.json({\n success: false,\n error: error.message,\n code: error.code,\n }, { status: error.code === 'NOT_SUPPORTED' ? 501 : 500 })\n }\n\n return Response.json({\n success: false,\n error: 'Failed to schedule broadcast',\n }, { status: 500 })\n }\n }) as PayloadHandler,\n }\n}","import type { Endpoint, PayloadHandler, PayloadRequest } from 'payload'\nimport type { NewsletterPluginConfig } from '../../types'\nimport { requireAdmin } from '../../utils/auth'\nimport { convertToEmailSafeHtml } from '../../utils/emailSafeHtml'\n\nexport const createTestBroadcastEndpoint = (\n config: NewsletterPluginConfig,\n collectionSlug: string\n): Endpoint => {\n return {\n path: '/:id/test',\n method: 'post',\n handler: (async (req: PayloadRequest) => {\n try {\n // Check authentication\n const auth = await requireAdmin(req, config)\n if (!auth.authorized) {\n return Response.json({\n success: false,\n error: auth.error,\n }, { status: 401 })\n }\n\n // Get ID from URL\n const url = new URL(req.url || '', `http://localhost`)\n const pathParts = url.pathname.split('/')\n const id = pathParts[pathParts.length - 2] // -2 because last part is 'test'\n\n if (!id) {\n return Response.json({\n success: false,\n error: 'Broadcast ID is required',\n }, { status: 400 })\n }\n\n // Parse request body for optional test email\n const data = await (req.json?.() || Promise.resolve({}))\n const testEmail = data.email || auth.user.email\n\n if (!testEmail) {\n return Response.json({\n success: false,\n error: 'No email address available for test send',\n }, { status: 400 })\n }\n\n // Get the broadcast document\n const broadcast = await req.payload.findByID({\n collection: collectionSlug,\n id,\n user: auth.user,\n })\n\n if (!broadcast) {\n return Response.json({\n success: false,\n error: 'Broadcast not found',\n }, { status: 404 })\n }\n\n // Convert content to email-safe HTML\n const htmlContent = await convertToEmailSafeHtml(broadcast.content, {\n wrapInTemplate: true,\n preheader: broadcast.preheader,\n customBlockConverter: config.customizations?.broadcasts?.customBlockConverter,\n })\n\n // Get email service\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const emailService = (req.payload as any).newsletterEmailService\n if (!emailService) {\n return Response.json({\n success: false,\n error: 'Email service is not configured',\n }, { status: 500 })\n }\n\n // Get sender info from provider config\n const providerConfig = config.providers.default === 'resend' \n ? config.providers.resend \n : config.providers.broadcast\n const fromEmail = providerConfig?.fromAddress || providerConfig?.fromEmail || 'noreply@example.com'\n const fromName = providerConfig?.fromName || 'Newsletter'\n const replyTo = broadcast.settings?.replyTo || providerConfig?.replyTo\n\n // Send test email\n await emailService.send({\n to: testEmail,\n from: fromEmail,\n fromName: fromName,\n replyTo: replyTo,\n subject: `[TEST] ${broadcast.subject}`,\n html: htmlContent,\n trackOpens: false,\n trackClicks: false,\n })\n\n return Response.json({\n success: true,\n message: `Test email sent to ${testEmail}`,\n })\n } catch (error) {\n console.error('Failed to send test broadcast:', error)\n \n return Response.json({\n success: false,\n error: 'Failed to send test email',\n }, { status: 500 })\n }\n }) as PayloadHandler,\n }\n}","import type { Endpoint, PayloadHandler, PayloadRequest, Payload } from 'payload'\nimport type { NewsletterPluginConfig } from '../../types'\nimport { convertToEmailSafeHtml } from '../../utils/emailSafeHtml'\n\n// Helper function to recursively find and populate media fields in content\nexport async function populateMediaFields(content: any, payload: Payload, config: NewsletterPluginConfig): Promise<any> {\n if (!content || typeof content !== 'object') return content\n \n // Handle Lexical editor state\n if (content.root?.children) {\n for (const child of content.root.children) {\n await populateBlockMediaFields(child, payload, config)\n }\n }\n \n return content\n}\n\n// Helper function to populate media fields in individual blocks\nasync function populateBlockMediaFields(node: any, payload: Payload, config: NewsletterPluginConfig): Promise<void> {\n // Check if this is a block node\n if (node.type === 'block' && node.fields) {\n const blockType = node.fields.blockType || node.fields.blockName\n \n // Get custom blocks configuration\n const customBlocks = config.customizations?.broadcasts?.customBlocks || []\n const blockConfig = customBlocks.find((b: any) => b.slug === blockType)\n \n if (blockConfig && blockConfig.fields) {\n // Find all upload fields in the block\n for (const field of blockConfig.fields) {\n if (field.type === 'upload' && field.relationTo && node.fields[field.name]) {\n const fieldValue = node.fields[field.name]\n \n // If it's just an ID string, populate it\n if (typeof fieldValue === 'string' && fieldValue.match(/^[a-f0-9]{24}$/i)) {\n try {\n const media = await payload.findByID({\n collection: field.relationTo,\n id: fieldValue,\n depth: 0,\n })\n \n if (media) {\n node.fields[field.name] = media\n payload.logger?.info(`Populated ${field.name} for block ${blockType}:`, {\n mediaId: fieldValue,\n mediaUrl: media.url,\n filename: media.filename\n })\n }\n } catch (error) {\n payload.logger?.error(`Failed to populate ${field.name} for block ${blockType}:`, error)\n }\n }\n }\n \n // Also handle arrays of uploads\n if (field.type === 'array' && field.fields) {\n const arrayValue = node.fields[field.name]\n if (Array.isArray(arrayValue)) {\n for (const arrayItem of arrayValue) {\n if (arrayItem && typeof arrayItem === 'object') {\n // Recursively process array items for upload fields\n for (const arrayField of field.fields) {\n if (arrayField.type === 'upload' && arrayField.relationTo && arrayItem[arrayField.name]) {\n const arrayFieldValue = arrayItem[arrayField.name]\n \n if (typeof arrayFieldValue === 'string' && arrayFieldValue.match(/^[a-f0-9]{24}$/i)) {\n try {\n const media = await payload.findByID({\n collection: arrayField.relationTo,\n id: arrayFieldValue,\n depth: 0,\n })\n \n if (media) {\n arrayItem[arrayField.name] = media\n payload.logger?.info(`Populated array ${arrayField.name} for block ${blockType}:`, {\n mediaId: arrayFieldValue,\n mediaUrl: media.url,\n filename: media.filename\n })\n }\n } catch (error) {\n payload.logger?.error(`Failed to populate array ${arrayField.name} for block ${blockType}:`, error)\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n \n // Recursively process children\n if (node.children) {\n for (const child of node.children) {\n await populateBlockMediaFields(child, payload, config)\n }\n }\n}\n\nexport const createBroadcastPreviewEndpoint = (\n config: NewsletterPluginConfig,\n _collectionSlug: string\n): Endpoint => {\n return {\n path: '/preview',\n method: 'post',\n handler: (async (req: PayloadRequest) => {\n try {\n // Parse request body\n const data = await (req.json?.() || Promise.resolve({}))\n const { content, preheader, subject, documentData } = data\n\n if (!content) {\n return Response.json({\n success: false,\n error: 'Content is required for preview',\n }, { status: 400 })\n }\n\n // Get media URL from payload config or use default\n const mediaUrl = req.payload.config.serverURL \n ? `${req.payload.config.serverURL}/api/media`\n : '/api/media'\n\n // Populate media fields in custom blocks before conversion\n req.payload.logger?.info('Populating media fields for email preview...')\n const populatedContent = await populateMediaFields(content, req.payload, config)\n\n // Get email preview customization options\n const emailPreviewConfig = config.customizations?.broadcasts?.emailPreview\n \n // Convert content to email-safe HTML with customization options\n const htmlContent = await convertToEmailSafeHtml(populatedContent, {\n wrapInTemplate: emailPreviewConfig?.wrapInTemplate ?? true,\n preheader: preheader,\n subject: subject,\n mediaUrl: mediaUrl,\n documentData, // Pass all document data\n customBlockConverter: config.customizations?.broadcasts?.customBlockConverter,\n customWrapper: emailPreviewConfig?.customWrapper,\n })\n\n return Response.json({\n success: true,\n html: htmlContent,\n preview: {\n subject: subject || 'Preview',\n preheader: preheader || '',\n html: htmlContent,\n },\n })\n } catch (error) {\n console.error('Failed to generate email preview:', error)\n \n return Response.json({\n success: false,\n error: 'Failed to generate email preview',\n }, { status: 500 })\n }\n }) as PayloadHandler,\n }\n}","import { render } from '@react-email/render'\nimport { MagicLinkEmail } from './MagicLink'\nimport { WelcomeEmail } from './Welcome'\nimport { SignInEmail } from './SignIn'\nimport type { NewsletterPluginConfig } from '../types'\n\nexport type EmailTemplate = 'magic-link' | 'welcome' | 'signin'\n\nexport interface BaseEmailData {\n email?: string\n siteName?: string\n [key: string]: any\n}\n\nexport interface MagicLinkData extends BaseEmailData {\n magicLink?: string\n verificationUrl?: string\n magicLinkUrl?: string\n expiresIn?: string\n}\n\nexport interface WelcomeData extends BaseEmailData {\n preferencesUrl?: string\n}\n\nexport async function renderEmail(\n template: EmailTemplate, \n data: MagicLinkData | WelcomeData,\n config?: NewsletterPluginConfig\n): Promise<string> {\n try {\n // Check for custom templates if config provided\n if (config?.customTemplates) {\n const customTemplate = config.customTemplates[template]\n if (customTemplate) {\n const CustomComponent = customTemplate\n return render(<CustomComponent {...data} />)\n }\n }\n \n // Fall back to built-in templates\n switch (template) {\n case 'magic-link': {\n const magicLinkData = data as MagicLinkData\n return render(\n <MagicLinkEmail\n magicLink={\n magicLinkData.magicLink || \n magicLinkData.verificationUrl || \n magicLinkData.magicLinkUrl || \n ''\n }\n email={magicLinkData.email || ''}\n siteName={magicLinkData.siteName}\n expiresIn={magicLinkData.expiresIn}\n />\n )\n }\n \n case 'signin': {\n const signinData = data as MagicLinkData\n return render(\n <SignInEmail\n magicLink={\n signinData.magicLink || \n signinData.verificationUrl || \n signinData.magicLinkUrl || \n ''\n }\n email={signinData.email || ''}\n siteName={signinData.siteName}\n expiresIn={signinData.expiresIn}\n />\n )\n }\n \n case 'welcome': {\n const welcomeData = data as WelcomeData\n return render(\n <WelcomeEmail\n email={welcomeData.email || ''}\n siteName={welcomeData.siteName}\n preferencesUrl={welcomeData.preferencesUrl}\n />\n )\n }\n \n default:\n throw new Error(`Unknown email template: ${template}`)\n }\n } catch (error) {\n console.error(`Failed to render email template ${template}:`, error)\n throw error\n }\n}\n\n// Export for custom template rendering\nexport { MagicLinkEmail, WelcomeEmail, SignInEmail }","import React from 'react'\nimport {\n Body,\n Button,\n Container,\n Head,\n Hr,\n Html,\n Preview,\n Text,\n} from '@react-email/components'\nimport { styles } from './styles'\n\nexport interface MagicLinkEmailProps {\n magicLink: string\n email: string\n siteName?: string\n expiresIn?: string\n}\n\nexport const MagicLinkEmail: React.FC<MagicLinkEmailProps> = ({\n magicLink,\n email,\n siteName = 'Newsletter',\n expiresIn = '24 hours',\n}) => {\n const previewText = `Sign in to ${siteName}`\n \n return (\n <Html>\n <Head />\n <Preview>{previewText}</Preview>\n <Body style={styles.main}>\n <Container style={styles.container}>\n <Text style={styles.heading}>Sign in to {siteName}</Text>\n \n <Text style={styles.text}>\n Hi {email.split('@')[0]},\n </Text>\n \n <Text style={styles.text}>\n We received a request to sign in to your {siteName} account. \n Click the button below to complete your sign in:\n </Text>\n \n <Button href={magicLink} style={styles.button}>\n Sign in to {siteName}\n </Button>\n \n <Text style={styles.text}>\n Or copy and paste this URL into your browser:\n </Text>\n \n <code style={styles.code}>{magicLink}</code>\n \n <Hr style={styles.hr} />\n \n <Text style={styles.footer}>\n This link will expire in {expiresIn}. If you didn't request this email, \n you can safely ignore it.\n </Text>\n </Container>\n </Body>\n </Html>\n )\n}\n\nexport default MagicLinkEmail","export const styles = {\n main: {\n backgroundColor: '#f6f9fc',\n fontFamily:\n '-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Ubuntu,sans-serif',\n },\n container: {\n backgroundColor: '#ffffff',\n border: '1px solid #f0f0f0',\n borderRadius: '5px',\n margin: '0 auto',\n padding: '45px',\n marginBottom: '64px',\n maxWidth: '500px',\n },\n heading: {\n fontSize: '24px',\n letterSpacing: '-0.5px',\n lineHeight: '1.3',\n fontWeight: '600',\n color: '#484848',\n margin: '0 0 20px',\n padding: '0',\n },\n text: {\n fontSize: '16px',\n lineHeight: '26px',\n fontWeight: '400',\n color: '#484848',\n margin: '16px 0',\n },\n button: {\n backgroundColor: '#000000',\n borderRadius: '5px',\n color: '#fff',\n fontSize: '16px',\n fontWeight: 'bold',\n textDecoration: 'none',\n textAlign: 'center' as const,\n display: 'block',\n width: '100%',\n padding: '14px 20px',\n margin: '30px 0',\n },\n link: {\n color: '#2754C5',\n fontSize: '14px',\n textDecoration: 'underline',\n wordBreak: 'break-all' as const,\n },\n hr: {\n borderColor: '#e6ebf1',\n margin: '30px 0',\n },\n footer: {\n fontSize: '14px',\n lineHeight: '24px',\n color: '#9ca2ac',\n textAlign: 'center' as const,\n margin: '0',\n },\n code: {\n display: 'inline-block',\n padding: '16px',\n width: '100%',\n backgroundColor: '#f4f4f4',\n borderRadius: '5px',\n border: '1px solid #eee',\n fontSize: '14px',\n fontFamily: 'monospace',\n textAlign: 'center' as const,\n margin: '24px 0',\n },\n}","import React from 'react'\nimport {\n Body,\n Button,\n Container,\n Head,\n Hr,\n Html,\n Preview,\n Text,\n} from '@react-email/components'\nimport { styles } from './styles'\n\nexport interface WelcomeEmailProps {\n email: string\n siteName?: string\n preferencesUrl?: string\n}\n\nexport const WelcomeEmail: React.FC<WelcomeEmailProps> = ({\n email,\n siteName = 'Newsletter',\n preferencesUrl,\n}) => {\n const previewText = `Welcome to ${siteName}!`\n const firstName = email.split('@')[0]\n \n return (\n <Html>\n <Head />\n <Preview>{previewText}</Preview>\n <Body style={styles.main}>\n <Container style={styles.container}>\n <Text style={styles.heading}>Welcome to {siteName}! 🎉</Text>\n \n <Text style={styles.text}>\n Hi {firstName},\n </Text>\n \n <Text style={styles.text}>\n Thanks for subscribing to {siteName}! We're excited to have you as part \n of our community.\n </Text>\n \n <Text style={styles.text}>\n You'll receive our newsletter based on your preferences. Speaking of which, \n you can update your preferences anytime:\n </Text>\n \n {preferencesUrl && (\n <Button href={preferencesUrl} style={styles.button}>\n Manage Preferences\n </Button>\n )}\n \n <Text style={styles.text}>\n Here's what you can expect from us:\n </Text>\n \n <Text style={styles.text}>\n • Regular updates based on your chosen frequency<br />\n • Content tailored to your interests<br />\n • Easy unsubscribe options in every email<br />\n • Your privacy respected always\n </Text>\n \n <Hr style={styles.hr} />\n \n <Text style={styles.footer}>\n If you have any questions, feel free to reply to this email. \n We're here to help!\n </Text>\n </Container>\n </Body>\n </Html>\n )\n}\n\nexport default WelcomeEmail","// SignIn is just an alias for MagicLink with slightly different defaults\nimport React from 'react'\nimport { MagicLinkEmail, MagicLinkEmailProps } from './MagicLink'\n\nexport const SignInEmail: React.FC<MagicLinkEmailProps> = (props) => {\n return <MagicLinkEmail {...props} />\n}\n\nexport default SignInEmail","import type { CollectionConfig, Field, CollectionAfterChangeHook, CollectionBeforeDeleteHook } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\nimport { adminOnly, adminOrSelf } from '../utils/access'\nimport { renderEmail } from '../emails/render'\n\nexport const createSubscribersCollection = (\n pluginConfig: NewsletterPluginConfig\n): CollectionConfig => {\n const slug = pluginConfig.subscribersSlug || 'subscribers'\n \n // Default fields for the subscribers collection\n const defaultFields: Field[] = [\n // Core fields\n {\n name: 'email',\n type: 'email',\n required: true,\n unique: true,\n admin: {\n description: 'Subscriber email address',\n },\n },\n {\n name: 'name',\n type: 'text',\n admin: {\n description: 'Subscriber full name',\n },\n },\n {\n name: 'locale',\n type: 'select',\n options: pluginConfig.i18n?.locales?.map(locale => ({\n label: locale.toUpperCase(),\n value: locale,\n })) || [\n { label: 'EN', value: 'en' },\n ],\n defaultValue: pluginConfig.i18n?.defaultLocale || 'en',\n admin: {\n description: 'Preferred language for communications',\n },\n },\n \n // Authentication fields (hidden from admin UI)\n {\n name: 'magicLinkToken',\n type: 'text',\n hidden: true,\n },\n {\n name: 'magicLinkTokenExpiry',\n type: 'date',\n hidden: true,\n },\n \n // Subscription status\n {\n name: 'subscriptionStatus',\n type: 'select',\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Unsubscribed', value: 'unsubscribed' },\n { label: 'Pending', value: 'pending' },\n ],\n defaultValue: 'pending',\n required: true,\n admin: {\n description: 'Current subscription status',\n },\n },\n {\n name: 'unsubscribedAt',\n type: 'date',\n admin: {\n condition: (data) => data?.subscriptionStatus === 'unsubscribed',\n description: 'When the user unsubscribed',\n readOnly: true,\n },\n },\n \n // Email preferences\n {\n name: 'emailPreferences',\n type: 'group',\n fields: [\n {\n name: 'newsletter',\n type: 'checkbox',\n defaultValue: true,\n label: 'Newsletter',\n admin: {\n description: 'Receive regular newsletter updates',\n },\n },\n {\n name: 'announcements',\n type: 'checkbox',\n defaultValue: true,\n label: 'Announcements',\n admin: {\n description: 'Receive important announcements',\n },\n },\n ],\n admin: {\n description: 'Email communication preferences',\n },\n },\n \n // Source tracking\n {\n name: 'source',\n type: 'text',\n admin: {\n description: 'Where the subscriber signed up from',\n },\n },\n ]\n\n // Add UTM tracking fields if enabled\n if (pluginConfig.features?.utmTracking?.enabled) {\n const utmFields = pluginConfig.features.utmTracking.fields || [\n 'source',\n 'medium',\n 'campaign',\n 'content',\n 'term',\n ]\n \n defaultFields.push({\n name: 'utmParameters',\n type: 'group',\n fields: utmFields.map(field => ({\n name: field,\n type: 'text',\n admin: {\n description: `UTM ${field} parameter`,\n },\n })),\n admin: {\n description: 'UTM tracking parameters',\n },\n })\n }\n\n // Add signup metadata\n defaultFields.push({\n name: 'signupMetadata',\n type: 'group',\n fields: [\n {\n name: 'ipAddress',\n type: 'text',\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'userAgent',\n type: 'text',\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'referrer',\n type: 'text',\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'signupPage',\n type: 'text',\n admin: {\n readOnly: true,\n },\n },\n ],\n admin: {\n description: 'Technical information about signup',\n },\n })\n\n // Add lead magnet field if enabled\n if (pluginConfig.features?.leadMagnets?.enabled) {\n defaultFields.push({\n name: 'leadMagnet',\n type: 'relationship',\n relationTo: pluginConfig.features.leadMagnets.collection || 'media',\n admin: {\n description: 'Lead magnet downloaded at signup',\n },\n })\n }\n\n // Allow field customization\n let fields = defaultFields\n if (pluginConfig.fields?.overrides) {\n fields = pluginConfig.fields.overrides({ defaultFields })\n }\n if (pluginConfig.fields?.additional) {\n fields = [...fields, ...pluginConfig.fields.additional]\n }\n\n const subscribersCollection: CollectionConfig = {\n slug,\n labels: {\n singular: 'Subscriber',\n plural: 'Subscribers',\n },\n admin: {\n useAsTitle: 'email',\n defaultColumns: ['email', 'name', 'subscriptionStatus', 'createdAt'],\n group: 'Newsletter',\n },\n fields,\n hooks: {\n afterChange: [\n async ({ doc, req, operation, previousDoc }) => {\n // After create logic\n if (operation === 'create') {\n // Add to email service\n const emailService = (req.payload as any).newsletterEmailService // TODO: Add proper type for newsletter email service\n console.log('[Newsletter Plugin] Creating subscriber:', {\n email: doc.email,\n hasEmailService: !!emailService\n })\n \n if (emailService) {\n try {\n await emailService.addContact(doc)\n console.log('[Newsletter Plugin] Successfully added contact to email service')\n } catch (error) {\n console.error('[Newsletter Plugin] Failed to add contact to email service:', error)\n }\n } else {\n console.warn('[Newsletter Plugin] No email service configured for subscriber creation')\n }\n\n // Send welcome email if active\n if (doc.subscriptionStatus === 'active' && emailService) {\n try {\n // Get settings for site name\n const settings = await req.payload.findGlobal({\n slug: pluginConfig.settingsSlug || 'newsletter-settings',\n })\n \n // Render welcome email\n const serverURL = req.payload.config.serverURL || process.env.PAYLOAD_PUBLIC_SERVER_URL || ''\n const html = await renderEmail('welcome', {\n email: doc.email,\n siteName: settings?.brandSettings?.siteName || 'Newsletter',\n preferencesUrl: `${serverURL}/account/preferences`, // This could be customized\n }, pluginConfig)\n \n // Send email\n await emailService.send({\n to: doc.email,\n subject: settings?.brandSettings?.siteName ? `Welcome to ${settings.brandSettings.siteName}!` : 'Welcome!',\n html,\n })\n \n console.warn(`Welcome email sent to: ${doc.email}`)\n } catch (error) {\n console.error('Failed to send welcome email:', error)\n // Don't fail the subscription if welcome email fails\n }\n }\n\n // Custom after subscribe hook\n if (pluginConfig.hooks?.afterSubscribe) {\n await pluginConfig.hooks.afterSubscribe({ doc, req })\n }\n }\n \n // After update logic\n if (operation === 'update' && previousDoc) {\n // Update email service if status changed\n const emailService = (req.payload as any).newsletterEmailService // TODO: Add proper type for newsletter email service\n \n if (doc.subscriptionStatus !== previousDoc.subscriptionStatus) {\n console.log('[Newsletter Plugin] Subscription status changed:', {\n email: doc.email,\n from: previousDoc.subscriptionStatus,\n to: doc.subscriptionStatus,\n hasEmailService: !!emailService\n })\n \n if (emailService) {\n try {\n await emailService.updateContact(doc)\n console.log('[Newsletter Plugin] Successfully updated contact in email service')\n } catch (error) {\n console.error('[Newsletter Plugin] Failed to update contact in email service:', error)\n }\n } else {\n console.warn('[Newsletter Plugin] No email service configured')\n }\n }\n\n // Handle unsubscribe\n if (\n doc.subscriptionStatus === 'unsubscribed' &&\n previousDoc.subscriptionStatus !== 'unsubscribed'\n ) {\n // Set unsubscribed timestamp\n doc.unsubscribedAt = new Date().toISOString()\n \n // Custom after unsubscribe hook\n if (pluginConfig.hooks?.afterUnsubscribe) {\n await pluginConfig.hooks.afterUnsubscribe({ doc, req })\n }\n }\n }\n },\n ] as CollectionAfterChangeHook[],\n beforeDelete: [\n async ({ id, req }) => {\n // Remove from email service\n const emailService = (req.payload as any).newsletterEmailService // TODO: Add proper type for newsletter email service\n if (emailService) {\n try {\n const doc = await req.payload.findByID({\n collection: slug,\n id,\n })\n await emailService.removeContact(doc.email)\n } catch {\n // Failed to remove contact from email service\n }\n }\n },\n ] as CollectionBeforeDeleteHook[],\n },\n access: {\n create: () => true, // Public can subscribe\n read: adminOrSelf(pluginConfig),\n update: adminOrSelf(pluginConfig),\n delete: adminOnly(pluginConfig),\n },\n timestamps: true,\n }\n\n return subscribersCollection\n}"],"mappings":"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","names":["init_broadcast","DOMPurify","styles","BroadcastApiProvider","BroadcastApiProvider","BroadcastApiProvider","import_components","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}