From 6e4f75430656a31be76a73c5531bb81f7373808a Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Sat, 13 Sep 2025 21:57:02 +0200 Subject: [PATCH] Fix critical type safety and validation issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue 2 - Type Safety: - Remove dangerous 'as any' casts in sendEmail function - Use proper typing for payload.create() calls - Maintain type safety throughout email creation process Issue 3 - Email Validation: - Implement RFC 5322 compliant email regex - Add comprehensive validation for common invalid patterns - Check for consecutive dots, invalid domain formats - Prevent emails like 'test@.com' and 'test@domain.' Issue 4 - Error Message Logic: - Add contextual error messages for template vs direct email modes - Distinguish between template rendering failures and missing direct email content - Provide clearer guidance to developers on what went wrong Additional fixes: - Update imports to use generated Email type instead of BaseEmailData - Maintain compatibility with updated sendEmail interface 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/index.ts | 2 +- src/jobs/sendEmailTask.ts | 5 +++-- src/sendEmail.ts | 21 +++++++++++++++------ src/utils/helpers.ts | 17 ++++++++++++++--- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/index.ts b/src/index.ts index 3606914..bb6cd5b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,7 +16,7 @@ export { mailingJobs, sendEmailJob } from './jobs/index.js' export type { SendEmailTaskInput } from './jobs/sendEmailTask.js' // Main email sending function -export { sendEmail, type BaseEmailData, type SendEmailOptions } from './sendEmail.js' +export { sendEmail, type SendEmailOptions } from './sendEmail.js' export { default as sendEmailDefault } from './sendEmail.js' // Utility functions for developers diff --git a/src/jobs/sendEmailTask.ts b/src/jobs/sendEmailTask.ts index 474867a..55cd15c 100644 --- a/src/jobs/sendEmailTask.ts +++ b/src/jobs/sendEmailTask.ts @@ -1,4 +1,5 @@ -import { sendEmail, type BaseEmailData } from '../sendEmail.js' +import { sendEmail } from '../sendEmail.js' +import { Email } from '../payload-types.js' export interface SendEmailTaskInput { // Template mode fields @@ -153,7 +154,7 @@ export const sendEmailJob = { }) // Use the sendEmail helper to create the email - const email = await sendEmail(payload, sendEmailOptions) + const email = await sendEmail(payload, sendEmailOptions) return { output: { diff --git a/src/sendEmail.ts b/src/sendEmail.ts index 8aa12ce..aeb28bd 100644 --- a/src/sendEmail.ts +++ b/src/sendEmail.ts @@ -66,8 +66,17 @@ export const sendEmail = async ( throw new Error('Field "to" is required for sending emails') } - if (!emailData.subject || !emailData.html) { - throw new Error('Fields "subject" and "html" are required when not using a template') + // Validate required fields based on whether template was used + if (options.template) { + // When using template, subject and html should have been set by renderTemplate + if (!emailData.subject || !emailData.html) { + throw new Error(`Template rendering failed: template "${options.template.slug}" did not provide required subject and html content`) + } + } else { + // When not using template, user must provide subject and html directly + if (!emailData.subject || !emailData.html) { + throw new Error('Fields "subject" and "html" are required when sending direct emails without a template') + } } // Process email addresses using shared validation (handle null values) @@ -81,13 +90,13 @@ export const sendEmail = async ( emailData.bcc = parseAndValidateEmails(emailData.bcc as string | string[]) } - // Create the email in the collection + // Create the email in the collection with proper typing const email = await payload.create({ - collection: collectionSlug as any, - data: emailData as any + collection: collectionSlug, + data: emailData }) - return email as unknown as T + return email as T } export default sendEmail diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 4c6b005..2c0f192 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -15,9 +15,20 @@ export const parseAndValidateEmails = (emails: string | string[] | null | undefi emailList = emails.split(',').map(email => email.trim()).filter(Boolean) } - // Basic email validation - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ - const invalidEmails = emailList.filter(email => !emailRegex.test(email)) + // RFC 5322 compliant email validation + const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ + const invalidEmails = emailList.filter(email => { + // Check basic format + if (!emailRegex.test(email)) return true + // Check for common invalid patterns + if (email.includes('..') || email.startsWith('.') || email.endsWith('.')) return true + if (email.includes('@.') || email.includes('.@')) return true + // Check domain has at least one dot + const parts = email.split('@') + if (parts.length !== 2 || !parts[1].includes('.')) return true + return false + }) + if (invalidEmails.length > 0) { throw new Error(`Invalid email addresses: ${invalidEmails.join(', ')}`) }