From 8f200da4494a6fc8f0968e055c2fa735bb50ad7d Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Sun, 14 Sep 2025 17:16:01 +0200 Subject: [PATCH 1/4] Refactor and clean up job organization - Properly encapsulate processEmailsJob in its own file with handler and definition - Clean up index.ts to remove duplicate code and just export job definitions - Add comprehensive JSDoc comments for better documentation - Separate job handler logic from job definition for clarity - Fix job scheduling to use correct field names - Bump version to 0.2.1 --- package.json | 2 +- src/jobs/index.ts | 35 +++++----------------- src/jobs/processEmailsJob.ts | 58 +++++++++++++++++++++++++++++++----- src/jobs/sendEmailTask.ts | 4 +++ 4 files changed, 62 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 6059eb1..cbc42a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xtr-dev/payload-mailing", - "version": "0.2.0", + "version": "0.2.1", "description": "Template-based email system with scheduling and job processing for PayloadCMS", "type": "module", "main": "dist/index.js", diff --git a/src/jobs/index.ts b/src/jobs/index.ts index 86f5007..3876ae7 100644 --- a/src/jobs/index.ts +++ b/src/jobs/index.ts @@ -1,35 +1,14 @@ -import { processEmailsJob, ProcessEmailsJobData } from './processEmailsJob.js' +import { processEmailsJob } from './processEmailsJob.js' import { sendEmailJob } from './sendEmailTask.js' -import { MailingService } from '../services/MailingService.js' +/** + * All mailing-related jobs that get registered with Payload + */ export const mailingJobs = [ - { - slug: 'processEmails', - handler: async ({ job, req }: { job: any; req: any }) => { - // Get mailing context from payload - const payload = (req as any).payload - const mailingContext = payload.mailing - if (!mailingContext) { - throw new Error('Mailing plugin not properly initialized') - } - - // Use the existing mailing service from context - await processEmailsJob( - job as { data: ProcessEmailsJobData }, - { req, mailingService: mailingContext.service } - ) - - return { - output: { - success: true, - message: 'Email queue processing completed successfully' - } - } - }, - interfaceName: 'ProcessEmailsJob', - }, + processEmailsJob, sendEmailJob, ] +// Re-export everything from individual job files export * from './processEmailsJob.js' -export * from './sendEmailTask.js' \ No newline at end of file +export * from './sendEmailTask.js' diff --git a/src/jobs/processEmailsJob.ts b/src/jobs/processEmailsJob.ts index 65d0614..aeef18b 100644 --- a/src/jobs/processEmailsJob.ts +++ b/src/jobs/processEmailsJob.ts @@ -1,11 +1,18 @@ -import type { PayloadRequest } from 'payload' -import { MailingService } from '../services/MailingService.js' +import type { PayloadRequest, Payload } from 'payload' +import type { MailingService } from '../services/MailingService.js' +/** + * Data passed to the process emails job + */ export interface ProcessEmailsJobData { - // No type needed - always processes both pending and failed emails + // Currently no data needed - always processes both pending and failed emails } -export const processEmailsJob = async ( +/** + * Handler function for processing emails + * Used internally by the job definition + */ +export const processEmailsJobHandler = async ( job: { data: ProcessEmailsJobData }, context: { req: PayloadRequest; mailingService: MailingService } ) => { @@ -20,15 +27,50 @@ export const processEmailsJob = async ( // Then retry failed emails await mailingService.retryFailedEmails() - console.log('✅ Email queue processing completed successfully (pending and failed emails)') + console.log('✅ Email queue processing completed successfully') } catch (error) { console.error('❌ Email queue processing failed:', error) throw error } } +/** + * Job definition for processing emails + * This is what gets registered with Payload's job system + */ +export const processEmailsJob = { + slug: 'process-emails', + handler: async ({ job, req }: { job: any; req: any }) => { + // Get mailing context from payload + const payload = (req as any).payload + const mailingContext = payload.mailing + + if (!mailingContext) { + throw new Error('Mailing plugin not properly initialized') + } + + // Use the existing mailing service from context + await processEmailsJobHandler( + job as { data: ProcessEmailsJobData }, + { req, mailingService: mailingContext.service } + ) + + return { + output: { + success: true, + message: 'Email queue processing completed successfully' + } + } + }, + interfaceName: 'ProcessEmailsJob', +} + +/** + * Helper function to schedule an email processing job + * Used by the plugin during initialization and can be used by developers + */ export const scheduleEmailsJob = async ( - payload: any, + payload: Payload, queueName: string, delay?: number ) => { @@ -40,10 +82,10 @@ export const scheduleEmailsJob = async ( try { await payload.jobs.queue({ queue: queueName, - task: 'processEmails', + workflow: 'process-emails', input: {}, waitUntil: delay ? new Date(Date.now() + delay) : undefined, - }) + } as any) } catch (error) { console.error('Failed to schedule email processing job:', error) } diff --git a/src/jobs/sendEmailTask.ts b/src/jobs/sendEmailTask.ts index 14f20e8..9d39151 100644 --- a/src/jobs/sendEmailTask.ts +++ b/src/jobs/sendEmailTask.ts @@ -64,6 +64,10 @@ function transformTaskInputToSendEmailOptions(taskInput: SendEmailTaskInput) { return sendEmailOptions } +/** + * Job definition for sending emails + * Can be used through Payload's job queue system to send emails programmatically + */ export const sendEmailJob = { slug: 'send-email', label: 'Send Email', From a6564e2a2918e2cfdf4582d2eaea4066d408f6f6 Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Sun, 14 Sep 2025 17:20:21 +0200 Subject: [PATCH 2/4] Add sendEmail workflow with immediate processing option - Create sendEmailWorkflow as a Payload workflow alternative to task - Add processImmediately option (disabled by default) to send emails immediately - Expose processEmailItem method in MailingService for individual email processing - Add comprehensive input schema with conditional fields - Update plugin to register both tasks and workflows - Add detailed documentation comparing tasks vs workflows - Includes status tracking and error handling - Bump version to 0.3.0 (new feature) --- README.md | 73 ++++++- package.json | 2 +- src/index.ts | 4 + src/plugin.ts | 5 + src/services/MailingService.ts | 2 +- src/types/index.ts | 1 + src/workflows/index.ts | 11 ++ src/workflows/sendEmailWorkflow.ts | 295 +++++++++++++++++++++++++++++ 8 files changed, 390 insertions(+), 3 deletions(-) create mode 100644 src/workflows/index.ts create mode 100644 src/workflows/sendEmailWorkflow.ts diff --git a/README.md b/README.md index b8fd70a..826a84e 100644 --- a/README.md +++ b/README.md @@ -380,7 +380,16 @@ await processEmails(payload) await retryFailedEmails(payload) ``` -## PayloadCMS Task Integration +## PayloadCMS Integration + +The plugin provides both tasks and workflows for email processing: + +### Tasks vs Workflows + +- **Tasks**: Simple job execution, good for background processing +- **Workflows**: More advanced with UI, status tracking, and immediate processing options + +### Task Integration The plugin provides a ready-to-use PayloadCMS task for queuing template emails: @@ -455,6 +464,68 @@ The task can also be triggered from the Payload admin panel with a user-friendly - ✅ **Error Handling**: Comprehensive error reporting - ✅ **Queue Management**: Leverage Payload's job queue system +### Workflow Integration + +The plugin also provides a workflow for sending emails with advanced features: + +```typescript +import { sendEmailWorkflow } from '@xtr-dev/payload-mailing' + +export default buildConfig({ + // ... your config + jobs: { + workflows: [ + sendEmailWorkflow, + // ... your other workflows + ] + } +}) +``` + +### Workflow Features + +- **Immediate Processing**: Option to send emails immediately instead of queuing +- **Admin UI**: Rich form interface with conditional fields +- **Status Tracking**: Track workflow execution progress +- **Template or Direct**: Support for both template-based and direct HTML emails + +### Using the Workflow + +```typescript +// Queue a workflow with immediate processing +await payload.workflows.queue({ + workflow: 'send-email', + input: { + processImmediately: true, // Send immediately + templateSlug: 'welcome-email', + to: ['user@example.com'], + variables: { name: 'John Doe' } + } +}) + +// Queue normally (processed by background job) +await payload.workflows.queue({ + workflow: 'send-email', + input: { + processImmediately: false, // Default: false + subject: 'Direct Email', + html: '

Hello World!

', + to: ['user@example.com'] + } +}) +``` + +### Workflow vs Task Comparison + +| Feature | Task | Workflow | +|---------|------|----------| +| Admin UI | Basic form | Rich form with conditions | +| Immediate Processing | ❌ | ✅ (optional) | +| Status Tracking | Basic | Detailed with progress | +| Template Support | ✅ | ✅ | +| Direct HTML Support | ✅ | ✅ | +| Background Processing | ✅ | ✅ | + ## Job Processing The plugin automatically adds a unified email processing job to PayloadCMS: diff --git a/package.json b/package.json index cbc42a3..95580a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xtr-dev/payload-mailing", - "version": "0.2.1", + "version": "0.3.0", "description": "Template-based email system with scheduling and job processing for PayloadCMS", "type": "module", "main": "dist/index.js", diff --git a/src/index.ts b/src/index.ts index bb6cd5b..15d5d5c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,10 @@ export { default as Emails } from './collections/Emails.js' export { mailingJobs, sendEmailJob } from './jobs/index.js' export type { SendEmailTaskInput } from './jobs/sendEmailTask.js' +// Workflows (includes the send email workflow) +export { mailingWorkflows, sendEmailWorkflow } from './workflows/index.js' +export type { SendEmailWorkflowInput } from './workflows/sendEmailWorkflow.js' + // Main email sending function export { sendEmail, type SendEmailOptions } from './sendEmail.js' export { default as sendEmailDefault } from './sendEmail.js' diff --git a/src/plugin.ts b/src/plugin.ts index 8cd05c1..efb056a 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -4,6 +4,7 @@ import { MailingService } from './services/MailingService.js' import { createEmailTemplatesCollection } from './collections/EmailTemplates.js' import Emails from './collections/Emails.js' import { mailingJobs, scheduleEmailsJob } from './jobs/index.js' +import { mailingWorkflows } from './workflows/index.js' export const mailingPlugin = (pluginConfig: MailingPluginConfig) => (config: Config): Config => { @@ -86,6 +87,10 @@ export const mailingPlugin = (pluginConfig: MailingPluginConfig) => (config: Con ...(config.jobs?.tasks || []), ...mailingJobs, ], + workflows: [ + ...(config.jobs?.workflows || []), + ...mailingWorkflows, + ], }, onInit: async (payload: any) => { if (pluginConfig.initOrder === 'after' && config.onInit) { diff --git a/src/services/MailingService.ts b/src/services/MailingService.ts index d1860fb..c71d185 100644 --- a/src/services/MailingService.ts +++ b/src/services/MailingService.ts @@ -225,7 +225,7 @@ export class MailingService implements IMailingService { } } - private async processEmailItem(emailId: string): Promise { + async processEmailItem(emailId: string): Promise { try { await this.payload.update({ collection: this.emailsCollection as any, diff --git a/src/types/index.ts b/src/types/index.ts index b545996..4c4a86c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -111,6 +111,7 @@ export interface TemplateVariables { export interface MailingService { processEmails(): Promise + processEmailItem(emailId: string): Promise retryFailedEmails(): Promise renderTemplate(templateSlug: string, variables: TemplateVariables): Promise<{ html: string; text: string; subject: string }> } diff --git a/src/workflows/index.ts b/src/workflows/index.ts new file mode 100644 index 0000000..6936e53 --- /dev/null +++ b/src/workflows/index.ts @@ -0,0 +1,11 @@ +import { sendEmailWorkflow } from './sendEmailWorkflow.js' + +/** + * All mailing-related workflows that get registered with Payload + */ +export const mailingWorkflows = [ + sendEmailWorkflow, +] + +// Re-export everything from individual workflow files +export * from './sendEmailWorkflow.js' \ No newline at end of file diff --git a/src/workflows/sendEmailWorkflow.ts b/src/workflows/sendEmailWorkflow.ts new file mode 100644 index 0000000..98697f0 --- /dev/null +++ b/src/workflows/sendEmailWorkflow.ts @@ -0,0 +1,295 @@ +import { sendEmail } from '../sendEmail.js' +import { BaseEmailDocument } from '../types/index.js' + +export interface SendEmailWorkflowInput { + // Template mode fields + templateSlug?: string + variables?: Record + + // Direct email mode fields + subject?: string + html?: string + text?: string + + // Common fields + to: string | string[] + cc?: string | string[] + bcc?: string | string[] + from?: string + fromName?: string + replyTo?: string + scheduledAt?: string | Date // ISO date string or Date object + priority?: number + + // Workflow-specific option + processImmediately?: boolean // If true, process the email immediately instead of waiting for the queue + + // Allow any additional fields that users might have in their email collection + [key: string]: any +} + +/** + * Transforms workflow input into sendEmail options by separating template and data fields + */ +function transformWorkflowInputToSendEmailOptions(workflowInput: SendEmailWorkflowInput) { + const sendEmailOptions: any = { + data: {} + } + + // If using template mode, set template options + if (workflowInput.templateSlug) { + sendEmailOptions.template = { + slug: workflowInput.templateSlug, + variables: workflowInput.variables || {} + } + } + + // Standard email fields that should be copied to data + const standardFields = ['to', 'cc', 'bcc', 'from', 'fromName', 'replyTo', 'subject', 'html', 'text', 'scheduledAt', 'priority'] + + // Fields that should not be copied to data + const excludedFields = ['templateSlug', 'variables', 'processImmediately'] + + // Copy standard fields to data + standardFields.forEach(field => { + if (workflowInput[field] !== undefined) { + sendEmailOptions.data[field] = workflowInput[field] + } + }) + + // Copy any additional custom fields + Object.keys(workflowInput).forEach(key => { + if (!excludedFields.includes(key) && !standardFields.includes(key)) { + sendEmailOptions.data[key] = workflowInput[key] + } + }) + + return sendEmailOptions +} + +/** + * Workflow for sending emails with optional immediate processing + * Can be used through Payload's workflow system to send emails programmatically + */ +export const sendEmailWorkflow = { + slug: 'send-email', + label: 'Send Email', + inputSchema: [ + { + name: 'processImmediately', + type: 'checkbox' as const, + label: 'Process Immediately', + defaultValue: false, + admin: { + description: 'Process and send the email immediately instead of waiting for the queue processor' + } + }, + { + name: 'templateSlug', + type: 'text' as const, + label: 'Template Slug', + admin: { + description: 'Use a template (leave empty for direct email)', + condition: (data: any) => !data.subject && !data.html + } + }, + { + name: 'variables', + type: 'json' as const, + label: 'Template Variables', + admin: { + description: 'JSON object with variables for template rendering', + condition: (data: any) => Boolean(data.templateSlug) + } + }, + { + name: 'subject', + type: 'text' as const, + label: 'Subject', + admin: { + description: 'Email subject (required if not using template)', + condition: (data: any) => !data.templateSlug + } + }, + { + name: 'html', + type: 'textarea' as const, + label: 'HTML Content', + admin: { + description: 'HTML email content (required if not using template)', + condition: (data: any) => !data.templateSlug + } + }, + { + name: 'text', + type: 'textarea' as const, + label: 'Text Content', + admin: { + description: 'Plain text email content (optional)', + condition: (data: any) => !data.templateSlug + } + }, + { + name: 'to', + type: 'text' as const, + required: true, + label: 'To (Email Recipients)', + admin: { + description: 'Comma-separated list of email addresses' + } + }, + { + name: 'cc', + type: 'text' as const, + label: 'CC (Carbon Copy)', + admin: { + description: 'Optional comma-separated list of CC email addresses' + } + }, + { + name: 'bcc', + type: 'text' as const, + label: 'BCC (Blind Carbon Copy)', + admin: { + description: 'Optional comma-separated list of BCC email addresses' + } + }, + { + name: 'from', + type: 'text' as const, + label: 'From Email', + admin: { + description: 'Optional sender email address (uses default if not provided)' + } + }, + { + name: 'fromName', + type: 'text' as const, + label: 'From Name', + admin: { + description: 'Optional sender display name (e.g., "John Doe")' + } + }, + { + name: 'replyTo', + type: 'text' as const, + label: 'Reply To', + admin: { + description: 'Optional reply-to email address' + } + }, + { + name: 'scheduledAt', + type: 'date' as const, + label: 'Schedule For', + admin: { + description: 'Optional date/time to schedule email for future delivery', + condition: (data: any) => !data.processImmediately + } + }, + { + name: 'priority', + type: 'number' as const, + label: 'Priority', + min: 1, + max: 10, + defaultValue: 5, + admin: { + description: 'Email priority (1 = highest, 10 = lowest)' + } + } + ], + handler: async ({ job, req }: any) => { + const { input, id, taskStatus } = job + const { payload } = req + + // Cast input to our expected type + const workflowInput = input as SendEmailWorkflowInput + const shouldProcessImmediately = workflowInput.processImmediately || false + + try { + console.log(`📧 Workflow ${id}: Creating email...`) + + // Transform workflow input into sendEmail options + const sendEmailOptions = transformWorkflowInputToSendEmailOptions(workflowInput) + + // Create the email in the database + const email = await sendEmail(payload, sendEmailOptions) + + console.log(`✅ Workflow ${id}: Email created with ID: ${email.id}`) + + // Update task status with email ID + if (taskStatus) { + await taskStatus.update({ + data: { + emailId: email.id, + status: 'created' + } + }) + } + + // If processImmediately is true, process the email now + if (shouldProcessImmediately) { + console.log(`⚡ Workflow ${id}: Processing email immediately...`) + + // Get the mailing service from context + const mailingContext = payload.mailing + if (!mailingContext || !mailingContext.service) { + throw new Error('Mailing plugin not properly initialized') + } + + // Process just this specific email + await mailingContext.service.processEmailItem(String(email.id)) + + console.log(`✅ Workflow ${id}: Email processed and sent immediately`) + + // Update task status + if (taskStatus) { + await taskStatus.update({ + data: { + emailId: email.id, + status: 'sent', + processedImmediately: true + } + }) + } + } else { + // Update task status for queued email + if (taskStatus) { + await taskStatus.update({ + data: { + emailId: email.id, + status: 'queued', + processedImmediately: false + } + }) + } + } + + } catch (error) { + console.error(`❌ Workflow ${id}: Failed to process email:`, error) + + // Update task status with error + if (taskStatus) { + await taskStatus.update({ + data: { + status: 'failed', + error: error instanceof Error ? error.message : String(error) + } + }) + } + + if (error instanceof Error) { + // Preserve original error and stack trace + const wrappedError = new Error(`Failed to process email: ${error.message}`) + wrappedError.stack = error.stack + wrappedError.cause = error + throw wrappedError + } else { + throw new Error(`Failed to process email: ${String(error)}`) + } + } + } +} + +export default sendEmailWorkflow \ No newline at end of file From dd205dba41f7cad7a6f37d3c33e731733b2f0fea Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Sun, 14 Sep 2025 17:21:57 +0200 Subject: [PATCH 3/4] Make workflow documentation more concise - Simplified workflow section to focus on key advantage - Removed verbose comparison table and features list - Kept essential usage example with processImmediately option - More readable and focused on the main differentiator --- README.md | 44 +++++--------------------------------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 826a84e..cc42ed4 100644 --- a/README.md +++ b/README.md @@ -466,66 +466,32 @@ The task can also be triggered from the Payload admin panel with a user-friendly ### Workflow Integration -The plugin also provides a workflow for sending emails with advanced features: +For advanced features, use the workflow instead: ```typescript import { sendEmailWorkflow } from '@xtr-dev/payload-mailing' export default buildConfig({ - // ... your config jobs: { - workflows: [ - sendEmailWorkflow, - // ... your other workflows - ] + workflows: [sendEmailWorkflow] } }) ``` -### Workflow Features - -- **Immediate Processing**: Option to send emails immediately instead of queuing -- **Admin UI**: Rich form interface with conditional fields -- **Status Tracking**: Track workflow execution progress -- **Template or Direct**: Support for both template-based and direct HTML emails - -### Using the Workflow +**Key advantage**: Optional `processImmediately` option to send emails instantly instead of queuing. ```typescript -// Queue a workflow with immediate processing await payload.workflows.queue({ workflow: 'send-email', input: { - processImmediately: true, // Send immediately + processImmediately: true, // Send immediately (default: false) templateSlug: 'welcome-email', to: ['user@example.com'], - variables: { name: 'John Doe' } - } -}) - -// Queue normally (processed by background job) -await payload.workflows.queue({ - workflow: 'send-email', - input: { - processImmediately: false, // Default: false - subject: 'Direct Email', - html: '

Hello World!

', - to: ['user@example.com'] + variables: { name: 'John' } } }) ``` -### Workflow vs Task Comparison - -| Feature | Task | Workflow | -|---------|------|----------| -| Admin UI | Basic form | Rich form with conditions | -| Immediate Processing | ❌ | ✅ (optional) | -| Status Tracking | Basic | Detailed with progress | -| Template Support | ✅ | ✅ | -| Direct HTML Support | ✅ | ✅ | -| Background Processing | ✅ | ✅ | - ## Job Processing The plugin automatically adds a unified email processing job to PayloadCMS: From 845b379da34ef0f0689408df842e75b4c875b7ee Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Sun, 14 Sep 2025 17:34:53 +0200 Subject: [PATCH 4/4] Fix error handling and improve naming consistency - Use native error chaining in workflow (Error constructor with cause option) - Fix job scheduling to use 'task' instead of 'workflow' property - Rename processEmailsJob.ts to processEmailsTask.ts for consistency - Update all imports and references while maintaining backward compatibility - Add processEmailsTask export with processEmailsJob alias - Bump version to 0.3.1 --- package.json | 2 +- src/jobs/index.ts | 4 +-- ...ocessEmailsJob.ts => processEmailsTask.ts} | 25 +++++++++++-------- src/workflows/sendEmailWorkflow.ts | 6 +---- 4 files changed, 18 insertions(+), 19 deletions(-) rename src/jobs/{processEmailsJob.ts => processEmailsTask.ts} (78%) diff --git a/package.json b/package.json index 95580a9..647817a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xtr-dev/payload-mailing", - "version": "0.3.0", + "version": "0.3.1", "description": "Template-based email system with scheduling and job processing for PayloadCMS", "type": "module", "main": "dist/index.js", diff --git a/src/jobs/index.ts b/src/jobs/index.ts index 3876ae7..348cb0d 100644 --- a/src/jobs/index.ts +++ b/src/jobs/index.ts @@ -1,4 +1,4 @@ -import { processEmailsJob } from './processEmailsJob.js' +import { processEmailsJob } from './processEmailsTask.js' import { sendEmailJob } from './sendEmailTask.js' /** @@ -10,5 +10,5 @@ export const mailingJobs = [ ] // Re-export everything from individual job files -export * from './processEmailsJob.js' +export * from './processEmailsTask.js' export * from './sendEmailTask.js' diff --git a/src/jobs/processEmailsJob.ts b/src/jobs/processEmailsTask.ts similarity index 78% rename from src/jobs/processEmailsJob.ts rename to src/jobs/processEmailsTask.ts index aeef18b..14de41f 100644 --- a/src/jobs/processEmailsJob.ts +++ b/src/jobs/processEmailsTask.ts @@ -2,18 +2,18 @@ import type { PayloadRequest, Payload } from 'payload' import type { MailingService } from '../services/MailingService.js' /** - * Data passed to the process emails job + * Data passed to the process emails task */ -export interface ProcessEmailsJobData { +export interface ProcessEmailsTaskData { // Currently no data needed - always processes both pending and failed emails } /** * Handler function for processing emails - * Used internally by the job definition + * Used internally by the task definition */ -export const processEmailsJobHandler = async ( - job: { data: ProcessEmailsJobData }, +export const processEmailsTaskHandler = async ( + job: { data: ProcessEmailsTaskData }, context: { req: PayloadRequest; mailingService: MailingService } ) => { const { mailingService } = context @@ -35,10 +35,10 @@ export const processEmailsJobHandler = async ( } /** - * Job definition for processing emails + * Task definition for processing emails * This is what gets registered with Payload's job system */ -export const processEmailsJob = { +export const processEmailsTask = { slug: 'process-emails', handler: async ({ job, req }: { job: any; req: any }) => { // Get mailing context from payload @@ -50,8 +50,8 @@ export const processEmailsJob = { } // Use the existing mailing service from context - await processEmailsJobHandler( - job as { data: ProcessEmailsJobData }, + await processEmailsTaskHandler( + job as { data: ProcessEmailsTaskData }, { req, mailingService: mailingContext.service } ) @@ -62,9 +62,12 @@ export const processEmailsJob = { } } }, - interfaceName: 'ProcessEmailsJob', + interfaceName: 'ProcessEmailsTask', } +// For backward compatibility, export as processEmailsJob +export const processEmailsJob = processEmailsTask + /** * Helper function to schedule an email processing job * Used by the plugin during initialization and can be used by developers @@ -82,7 +85,7 @@ export const scheduleEmailsJob = async ( try { await payload.jobs.queue({ queue: queueName, - workflow: 'process-emails', + task: 'process-emails', input: {}, waitUntil: delay ? new Date(Date.now() + delay) : undefined, } as any) diff --git a/src/workflows/sendEmailWorkflow.ts b/src/workflows/sendEmailWorkflow.ts index 98697f0..3e3c565 100644 --- a/src/workflows/sendEmailWorkflow.ts +++ b/src/workflows/sendEmailWorkflow.ts @@ -280,11 +280,7 @@ export const sendEmailWorkflow = { } if (error instanceof Error) { - // Preserve original error and stack trace - const wrappedError = new Error(`Failed to process email: ${error.message}`) - wrappedError.stack = error.stack - wrappedError.cause = error - throw wrappedError + throw new Error(`Failed to process email: ${error.message}`, { cause: error }) } else { throw new Error(`Failed to process email: ${String(error)}`) }