@parsrun/email 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types.ts","../../src/providers/resend.ts","../../src/providers/sendgrid.ts","../../src/providers/postmark.ts","../../src/providers/console.ts"],"sourcesContent":["/**\n * @parsrun/email - Type Definitions\n * Email types and interfaces\n */\n\n// Re-export types from @parsrun/types for convenience\nexport {\n type,\n emailAddress,\n emailAttachment,\n sendEmailOptions,\n sendTemplateEmailOptions,\n emailSendResult,\n smtpConfig,\n resendConfig,\n sendgridConfig,\n sesConfig,\n postmarkConfig,\n emailConfig,\n type EmailAddress as ParsEmailAddress,\n type EmailAttachment as ParsEmailAttachment,\n type SendEmailOptions,\n type SendTemplateEmailOptions,\n type EmailSendResult,\n type SmtpConfig,\n type ResendConfig,\n type SendgridConfig,\n type SesConfig,\n type PostmarkConfig,\n type EmailConfig,\n} from \"@parsrun/types\";\n\n/**\n * Email provider type\n */\nexport type EmailProviderType = \"resend\" | \"sendgrid\" | \"postmark\" | \"ses\" | \"console\" | \"mailgun\";\n\n/**\n * Email address with optional name\n */\nexport interface EmailAddress {\n email: string;\n name?: string | undefined;\n}\n\n/**\n * Email attachment\n */\nexport interface EmailAttachment {\n /** File name */\n filename: string;\n /** File content (base64 encoded or Buffer) */\n content: string | Uint8Array;\n /** Content type (MIME type) */\n contentType?: string | undefined;\n /** Content ID for inline attachments */\n contentId?: string | undefined;\n}\n\n/**\n * Email options\n */\nexport interface EmailOptions {\n /** Recipient email address(es) */\n to: string | string[] | EmailAddress | EmailAddress[];\n /** Email subject */\n subject: string;\n /** HTML content */\n html?: string | undefined;\n /** Plain text content */\n text?: string | undefined;\n /** From address (overrides default) */\n from?: string | EmailAddress | undefined;\n /** Reply-to address */\n replyTo?: string | EmailAddress | undefined;\n /** CC recipients */\n cc?: string | string[] | EmailAddress | EmailAddress[] | undefined;\n /** BCC recipients */\n bcc?: string | string[] | EmailAddress | EmailAddress[] | undefined;\n /** Attachments */\n attachments?: EmailAttachment[] | undefined;\n /** Custom headers */\n headers?: Record<string, string> | undefined;\n /** Tags for tracking */\n tags?: Record<string, string> | undefined;\n /** Schedule send time */\n scheduledAt?: Date | undefined;\n}\n\n/**\n * Email send result\n */\nexport interface EmailResult {\n /** Whether send was successful */\n success: boolean;\n /** Message ID from provider */\n messageId?: string | undefined;\n /** Error message if failed */\n error?: string | undefined;\n /** Provider-specific response data */\n data?: unknown;\n}\n\n/**\n * Batch email options\n */\nexport interface BatchEmailOptions {\n /** List of emails to send */\n emails: EmailOptions[];\n /** Whether to stop on first error */\n stopOnError?: boolean | undefined;\n}\n\n/**\n * Batch email result\n */\nexport interface BatchEmailResult {\n /** Total emails attempted */\n total: number;\n /** Successful sends */\n successful: number;\n /** Failed sends */\n failed: number;\n /** Individual results */\n results: EmailResult[];\n}\n\n/**\n * Email provider configuration\n */\nexport interface EmailProviderConfig {\n /** API key for the provider */\n apiKey: string;\n /** Default from email */\n fromEmail: string;\n /** Default from name */\n fromName?: string | undefined;\n /** Provider-specific options */\n options?: Record<string, unknown> | undefined;\n}\n\n/**\n * Email provider interface\n */\nexport interface EmailProvider {\n /** Provider type */\n readonly type: EmailProviderType;\n\n /**\n * Send a single email\n */\n send(options: EmailOptions): Promise<EmailResult>;\n\n /**\n * Send multiple emails\n */\n sendBatch?(options: BatchEmailOptions): Promise<BatchEmailResult>;\n\n /**\n * Verify provider configuration\n */\n verify?(): Promise<boolean>;\n}\n\n/**\n * Email service configuration\n */\nexport interface EmailServiceConfig {\n /** Provider type */\n provider: EmailProviderType;\n /** API key */\n apiKey: string;\n /** Default from email */\n fromEmail: string;\n /** Default from name */\n fromName?: string | undefined;\n /** Enable debug logging */\n debug?: boolean | undefined;\n /** Provider-specific options */\n providerOptions?: Record<string, unknown> | undefined;\n}\n\n/**\n * Template data for email templates\n */\nexport interface TemplateData {\n [key: string]: string | number | boolean | undefined | null | TemplateData | TemplateData[];\n}\n\n/**\n * Email template\n */\nexport interface EmailTemplate {\n /** Template name */\n name: string;\n /** Subject template */\n subject: string;\n /** HTML template */\n html: string;\n /** Plain text template */\n text?: string | undefined;\n}\n\n/**\n * Email error\n */\nexport class EmailError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly cause?: unknown\n ) {\n super(message);\n this.name = \"EmailError\";\n }\n}\n\n/**\n * Common email error codes\n */\nexport const EmailErrorCodes = {\n INVALID_CONFIG: \"INVALID_CONFIG\",\n INVALID_RECIPIENT: \"INVALID_RECIPIENT\",\n INVALID_CONTENT: \"INVALID_CONTENT\",\n SEND_FAILED: \"SEND_FAILED\",\n RATE_LIMITED: \"RATE_LIMITED\",\n PROVIDER_ERROR: \"PROVIDER_ERROR\",\n TEMPLATE_ERROR: \"TEMPLATE_ERROR\",\n ATTACHMENT_ERROR: \"ATTACHMENT_ERROR\",\n} as const;\n","/**\n * @parsrun/email - Resend Provider\n * Edge-compatible Resend email provider\n */\n\nimport type {\n BatchEmailOptions,\n BatchEmailResult,\n EmailAddress,\n EmailOptions,\n EmailProvider,\n EmailProviderConfig,\n EmailResult,\n} from \"../types.js\";\nimport { EmailError, EmailErrorCodes } from \"../types.js\";\n\n/**\n * Resend Email Provider\n * Uses fetch API for edge compatibility\n *\n * @example\n * ```typescript\n * const resend = new ResendProvider({\n * apiKey: process.env.RESEND_API_KEY,\n * fromEmail: 'hello@example.com',\n * fromName: 'My App',\n * });\n *\n * await resend.send({\n * to: 'user@example.com',\n * subject: 'Hello',\n * html: '<p>Hello World!</p>',\n * });\n * ```\n */\nexport class ResendProvider implements EmailProvider {\n readonly type = \"resend\" as const;\n\n private apiKey: string;\n private fromEmail: string;\n private fromName: string | undefined;\n private baseUrl = \"https://api.resend.com\";\n\n constructor(config: EmailProviderConfig) {\n this.apiKey = config.apiKey;\n this.fromEmail = config.fromEmail;\n this.fromName = config.fromName;\n }\n\n private formatAddress(address: string | EmailAddress): string {\n if (typeof address === \"string\") {\n return address;\n }\n if (address.name) {\n return `${address.name} <${address.email}>`;\n }\n return address.email;\n }\n\n private formatAddresses(\n addresses: string | string[] | EmailAddress | EmailAddress[]\n ): string[] {\n if (Array.isArray(addresses)) {\n return addresses.map((a) => this.formatAddress(a));\n }\n return [this.formatAddress(addresses)];\n }\n\n async send(options: EmailOptions): Promise<EmailResult> {\n const from = options.from\n ? this.formatAddress(options.from)\n : this.fromName\n ? `${this.fromName} <${this.fromEmail}>`\n : this.fromEmail;\n\n const payload: Record<string, unknown> = {\n from,\n to: this.formatAddresses(options.to),\n subject: options.subject,\n };\n\n if (options.html) payload[\"html\"] = options.html;\n if (options.text) payload[\"text\"] = options.text;\n if (options.replyTo) payload[\"reply_to\"] = this.formatAddress(options.replyTo);\n if (options.cc) payload[\"cc\"] = this.formatAddresses(options.cc);\n if (options.bcc) payload[\"bcc\"] = this.formatAddresses(options.bcc);\n if (options.headers) payload[\"headers\"] = options.headers;\n if (options.tags) payload[\"tags\"] = Object.entries(options.tags).map(([name, value]) => ({ name, value }));\n if (options.scheduledAt) payload[\"scheduled_at\"] = options.scheduledAt.toISOString();\n\n if (options.attachments && options.attachments.length > 0) {\n payload[\"attachments\"] = options.attachments.map((att) => ({\n filename: att.filename,\n content: typeof att.content === \"string\"\n ? att.content\n : this.uint8ArrayToBase64(att.content),\n content_type: att.contentType,\n }));\n }\n\n try {\n const response = await fetch(`${this.baseUrl}/emails`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n const data = await response.json() as { id?: string; message?: string; statusCode?: number };\n\n if (!response.ok) {\n return {\n success: false,\n error: data.message || `HTTP ${response.status}`,\n data,\n };\n }\n\n return {\n success: true,\n messageId: data.id,\n data,\n };\n } catch (err) {\n throw new EmailError(\n `Resend send failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n EmailErrorCodes.SEND_FAILED,\n err\n );\n }\n }\n\n async sendBatch(options: BatchEmailOptions): Promise<BatchEmailResult> {\n const results: EmailResult[] = [];\n let successful = 0;\n let failed = 0;\n\n for (const email of options.emails) {\n try {\n const result = await this.send(email);\n results.push(result);\n\n if (result.success) {\n successful++;\n } else {\n failed++;\n if (options.stopOnError) break;\n }\n } catch (err) {\n failed++;\n results.push({\n success: false,\n error: err instanceof Error ? err.message : \"Unknown error\",\n });\n\n if (options.stopOnError) break;\n }\n }\n\n return {\n total: options.emails.length,\n successful,\n failed,\n results,\n };\n }\n\n async verify(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/domains`, {\n headers: {\n \"Authorization\": `Bearer ${this.apiKey}`,\n },\n });\n\n return response.ok;\n } catch {\n return false;\n }\n }\n\n private uint8ArrayToBase64(data: Uint8Array): string {\n // Edge-compatible base64 encoding\n let binary = \"\";\n for (let i = 0; i < data.length; i++) {\n const byte = data[i];\n if (byte !== undefined) {\n binary += String.fromCharCode(byte);\n }\n }\n return btoa(binary);\n }\n}\n\n/**\n * Create a Resend provider\n */\nexport function createResendProvider(config: EmailProviderConfig): ResendProvider {\n return new ResendProvider(config);\n}\n","/**\n * @parsrun/email - SendGrid Provider\n * Edge-compatible SendGrid email provider\n */\n\nimport type {\n BatchEmailOptions,\n BatchEmailResult,\n EmailAddress,\n EmailOptions,\n EmailProvider,\n EmailProviderConfig,\n EmailResult,\n} from \"../types.js\";\nimport { EmailError, EmailErrorCodes } from \"../types.js\";\n\n/**\n * SendGrid Email Provider\n * Uses fetch API for edge compatibility\n *\n * @example\n * ```typescript\n * const sendgrid = new SendGridProvider({\n * apiKey: process.env.SENDGRID_API_KEY,\n * fromEmail: 'hello@example.com',\n * fromName: 'My App',\n * });\n *\n * await sendgrid.send({\n * to: 'user@example.com',\n * subject: 'Hello',\n * html: '<p>Hello World!</p>',\n * });\n * ```\n */\nexport class SendGridProvider implements EmailProvider {\n readonly type = \"sendgrid\" as const;\n\n private apiKey: string;\n private fromEmail: string;\n private fromName: string | undefined;\n private baseUrl = \"https://api.sendgrid.com/v3\";\n\n constructor(config: EmailProviderConfig) {\n this.apiKey = config.apiKey;\n this.fromEmail = config.fromEmail;\n this.fromName = config.fromName;\n }\n\n private formatAddress(address: string | EmailAddress): { email: string; name?: string | undefined } {\n if (typeof address === \"string\") {\n return { email: address };\n }\n return address.name ? { email: address.email, name: address.name } : { email: address.email };\n }\n\n private formatAddresses(\n addresses: string | string[] | EmailAddress | EmailAddress[]\n ): Array<{ email: string; name?: string | undefined }> {\n if (Array.isArray(addresses)) {\n return addresses.map((a) => this.formatAddress(a));\n }\n return [this.formatAddress(addresses)];\n }\n\n async send(options: EmailOptions): Promise<EmailResult> {\n const from = options.from\n ? this.formatAddress(options.from)\n : this.fromName\n ? { email: this.fromEmail, name: this.fromName }\n : { email: this.fromEmail };\n\n const personalization: {\n to: Array<{ email: string; name?: string | undefined }>;\n cc?: Array<{ email: string; name?: string | undefined }> | undefined;\n bcc?: Array<{ email: string; name?: string | undefined }> | undefined;\n headers?: Record<string, string> | undefined;\n } = {\n to: this.formatAddresses(options.to),\n };\n\n if (options.cc) {\n personalization.cc = this.formatAddresses(options.cc);\n }\n if (options.bcc) {\n personalization.bcc = this.formatAddresses(options.bcc);\n }\n if (options.headers) {\n personalization.headers = options.headers;\n }\n\n const payload: Record<string, unknown> = {\n personalizations: [personalization],\n from,\n subject: options.subject,\n content: [],\n };\n\n const content: Array<{ type: string; value: string }> = [];\n if (options.text) {\n content.push({ type: \"text/plain\", value: options.text });\n }\n if (options.html) {\n content.push({ type: \"text/html\", value: options.html });\n }\n payload[\"content\"] = content;\n\n if (options.replyTo) {\n payload[\"reply_to\"] = this.formatAddress(options.replyTo);\n }\n\n if (options.attachments && options.attachments.length > 0) {\n payload[\"attachments\"] = options.attachments.map((att) => ({\n filename: att.filename,\n content: typeof att.content === \"string\"\n ? att.content\n : this.uint8ArrayToBase64(att.content),\n type: att.contentType,\n content_id: att.contentId,\n disposition: att.contentId ? \"inline\" : \"attachment\",\n }));\n }\n\n if (options.scheduledAt) {\n payload[\"send_at\"] = Math.floor(options.scheduledAt.getTime() / 1000);\n }\n\n try {\n const response = await fetch(`${this.baseUrl}/mail/send`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n // SendGrid returns 202 for success with no body\n if (response.status === 202) {\n const messageId = response.headers.get(\"x-message-id\");\n return {\n success: true,\n messageId: messageId ?? undefined,\n };\n }\n\n const data = await response.json().catch(() => ({})) as { errors?: Array<{ message: string }> };\n const errorMessage = data.errors?.[0]?.message || `HTTP ${response.status}`;\n\n return {\n success: false,\n error: errorMessage,\n data,\n };\n } catch (err) {\n throw new EmailError(\n `SendGrid send failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n EmailErrorCodes.SEND_FAILED,\n err\n );\n }\n }\n\n async sendBatch(options: BatchEmailOptions): Promise<BatchEmailResult> {\n const results: EmailResult[] = [];\n let successful = 0;\n let failed = 0;\n\n for (const email of options.emails) {\n try {\n const result = await this.send(email);\n results.push(result);\n\n if (result.success) {\n successful++;\n } else {\n failed++;\n if (options.stopOnError) break;\n }\n } catch (err) {\n failed++;\n results.push({\n success: false,\n error: err instanceof Error ? err.message : \"Unknown error\",\n });\n\n if (options.stopOnError) break;\n }\n }\n\n return {\n total: options.emails.length,\n successful,\n failed,\n results,\n };\n }\n\n async verify(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/scopes`, {\n headers: {\n \"Authorization\": `Bearer ${this.apiKey}`,\n },\n });\n\n return response.ok;\n } catch {\n return false;\n }\n }\n\n private uint8ArrayToBase64(data: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < data.length; i++) {\n const byte = data[i];\n if (byte !== undefined) {\n binary += String.fromCharCode(byte);\n }\n }\n return btoa(binary);\n }\n}\n\n/**\n * Create a SendGrid provider\n */\nexport function createSendGridProvider(config: EmailProviderConfig): SendGridProvider {\n return new SendGridProvider(config);\n}\n","/**\n * @parsrun/email - Postmark Provider\n * Edge-compatible Postmark email provider\n */\n\nimport type {\n BatchEmailOptions,\n BatchEmailResult,\n EmailAddress,\n EmailOptions,\n EmailProvider,\n EmailProviderConfig,\n EmailResult,\n} from \"../types.js\";\nimport { EmailError, EmailErrorCodes } from \"../types.js\";\n\n/**\n * Postmark Email Provider\n * Uses fetch API for edge compatibility\n *\n * @example\n * ```typescript\n * const postmark = new PostmarkProvider({\n * apiKey: process.env.POSTMARK_API_KEY,\n * fromEmail: 'hello@example.com',\n * fromName: 'My App',\n * });\n *\n * await postmark.send({\n * to: 'user@example.com',\n * subject: 'Hello',\n * html: '<p>Hello World!</p>',\n * });\n * ```\n */\nexport class PostmarkProvider implements EmailProvider {\n readonly type = \"postmark\" as const;\n\n private apiKey: string;\n private fromEmail: string;\n private fromName: string | undefined;\n private baseUrl = \"https://api.postmarkapp.com\";\n\n constructor(config: EmailProviderConfig) {\n this.apiKey = config.apiKey;\n this.fromEmail = config.fromEmail;\n this.fromName = config.fromName;\n }\n\n private formatAddress(address: string | EmailAddress): string {\n if (typeof address === \"string\") {\n return address;\n }\n if (address.name) {\n return `${address.name} <${address.email}>`;\n }\n return address.email;\n }\n\n private formatAddresses(\n addresses: string | string[] | EmailAddress | EmailAddress[]\n ): string {\n if (Array.isArray(addresses)) {\n return addresses.map((a) => this.formatAddress(a)).join(\",\");\n }\n return this.formatAddress(addresses);\n }\n\n async send(options: EmailOptions): Promise<EmailResult> {\n const from = options.from\n ? this.formatAddress(options.from)\n : this.fromName\n ? `${this.fromName} <${this.fromEmail}>`\n : this.fromEmail;\n\n const payload: Record<string, unknown> = {\n From: from,\n To: this.formatAddresses(options.to),\n Subject: options.subject,\n };\n\n if (options.html) payload[\"HtmlBody\"] = options.html;\n if (options.text) payload[\"TextBody\"] = options.text;\n if (options.replyTo) payload[\"ReplyTo\"] = this.formatAddress(options.replyTo);\n if (options.cc) payload[\"Cc\"] = this.formatAddresses(options.cc);\n if (options.bcc) payload[\"Bcc\"] = this.formatAddresses(options.bcc);\n\n if (options.headers) {\n payload[\"Headers\"] = Object.entries(options.headers).map(([Name, Value]) => ({ Name, Value }));\n }\n\n if (options.tags) {\n // Postmark uses Tag field (single tag) and Metadata for custom data\n const tagEntries = Object.entries(options.tags);\n if (tagEntries.length > 0 && tagEntries[0]) {\n payload[\"Tag\"] = tagEntries[0][1];\n }\n payload[\"Metadata\"] = options.tags;\n }\n\n if (options.attachments && options.attachments.length > 0) {\n payload[\"Attachments\"] = options.attachments.map((att) => ({\n Name: att.filename,\n Content: typeof att.content === \"string\"\n ? att.content\n : this.uint8ArrayToBase64(att.content),\n ContentType: att.contentType || \"application/octet-stream\",\n ContentID: att.contentId,\n }));\n }\n\n try {\n const response = await fetch(`${this.baseUrl}/email`, {\n method: \"POST\",\n headers: {\n \"X-Postmark-Server-Token\": this.apiKey,\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n const data = await response.json() as {\n MessageID?: string;\n ErrorCode?: number;\n Message?: string;\n };\n\n if (!response.ok || data.ErrorCode) {\n return {\n success: false,\n error: data.Message || `HTTP ${response.status}`,\n data,\n };\n }\n\n return {\n success: true,\n messageId: data.MessageID,\n data,\n };\n } catch (err) {\n throw new EmailError(\n `Postmark send failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n EmailErrorCodes.SEND_FAILED,\n err\n );\n }\n }\n\n async sendBatch(options: BatchEmailOptions): Promise<BatchEmailResult> {\n // Postmark supports batch sending up to 500 emails\n const batchPayload = options.emails.map((email) => {\n const from = email.from\n ? this.formatAddress(email.from)\n : this.fromName\n ? `${this.fromName} <${this.fromEmail}>`\n : this.fromEmail;\n\n const item: Record<string, unknown> = {\n From: from,\n To: this.formatAddresses(email.to),\n Subject: email.subject,\n };\n\n if (email.html) item[\"HtmlBody\"] = email.html;\n if (email.text) item[\"TextBody\"] = email.text;\n if (email.replyTo) item[\"ReplyTo\"] = this.formatAddress(email.replyTo);\n if (email.cc) item[\"Cc\"] = this.formatAddresses(email.cc);\n if (email.bcc) item[\"Bcc\"] = this.formatAddresses(email.bcc);\n\n return item;\n });\n\n try {\n const response = await fetch(`${this.baseUrl}/email/batch`, {\n method: \"POST\",\n headers: {\n \"X-Postmark-Server-Token\": this.apiKey,\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n },\n body: JSON.stringify(batchPayload),\n });\n\n const data = await response.json() as Array<{\n MessageID?: string;\n ErrorCode?: number;\n Message?: string;\n }>;\n\n const results: EmailResult[] = data.map((item) => ({\n success: !item.ErrorCode,\n messageId: item.MessageID,\n error: item.ErrorCode ? item.Message : undefined,\n data: item,\n }));\n\n const successful = results.filter((r) => r.success).length;\n const failed = results.filter((r) => !r.success).length;\n\n return {\n total: options.emails.length,\n successful,\n failed,\n results,\n };\n } catch (err) {\n throw new EmailError(\n `Postmark batch send failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n EmailErrorCodes.SEND_FAILED,\n err\n );\n }\n }\n\n async verify(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/server`, {\n headers: {\n \"X-Postmark-Server-Token\": this.apiKey,\n \"Accept\": \"application/json\",\n },\n });\n\n return response.ok;\n } catch {\n return false;\n }\n }\n\n private uint8ArrayToBase64(data: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < data.length; i++) {\n const byte = data[i];\n if (byte !== undefined) {\n binary += String.fromCharCode(byte);\n }\n }\n return btoa(binary);\n }\n}\n\n/**\n * Create a Postmark provider\n */\nexport function createPostmarkProvider(config: EmailProviderConfig): PostmarkProvider {\n return new PostmarkProvider(config);\n}\n","/**\n * @parsrun/email - Console Provider\n * Development/testing email provider that logs to console\n */\n\nimport type {\n BatchEmailOptions,\n BatchEmailResult,\n EmailAddress,\n EmailOptions,\n EmailProvider,\n EmailProviderConfig,\n EmailResult,\n} from \"../types.js\";\n\n/**\n * Console Email Provider\n * Logs emails to console instead of sending them.\n * Useful for development and testing.\n *\n * @example\n * ```typescript\n * const console = new ConsoleProvider({\n * apiKey: 'not-needed',\n * fromEmail: 'test@example.com',\n * fromName: 'Test App',\n * });\n *\n * await console.send({\n * to: 'user@example.com',\n * subject: 'Hello',\n * html: '<p>Hello World!</p>',\n * });\n * // Logs email details to console\n * ```\n */\nexport class ConsoleProvider implements EmailProvider {\n readonly type = \"console\" as const;\n\n private fromEmail: string;\n private fromName: string | undefined;\n private messageCounter = 0;\n\n constructor(config: EmailProviderConfig) {\n this.fromEmail = config.fromEmail;\n this.fromName = config.fromName;\n }\n\n private formatAddress(address: string | EmailAddress): string {\n if (typeof address === \"string\") {\n return address;\n }\n if (address.name) {\n return `${address.name} <${address.email}>`;\n }\n return address.email;\n }\n\n private formatAddresses(\n addresses: string | string[] | EmailAddress | EmailAddress[]\n ): string {\n if (Array.isArray(addresses)) {\n return addresses.map((a) => this.formatAddress(a)).join(\", \");\n }\n return this.formatAddress(addresses);\n }\n\n async send(options: EmailOptions): Promise<EmailResult> {\n this.messageCounter++;\n const messageId = `console-${Date.now()}-${this.messageCounter}`;\n\n const from = options.from\n ? this.formatAddress(options.from)\n : this.fromName\n ? `${this.fromName} <${this.fromEmail}>`\n : this.fromEmail;\n\n const separator = \"─\".repeat(60);\n\n console.log(`\\n${separator}`);\n console.log(\"šŸ“§ EMAIL (Console Provider)\");\n console.log(separator);\n console.log(`Message ID: ${messageId}`);\n console.log(`From: ${from}`);\n console.log(`To: ${this.formatAddresses(options.to)}`);\n if (options.cc) {\n console.log(`CC: ${this.formatAddresses(options.cc)}`);\n }\n if (options.bcc) {\n console.log(`BCC: ${this.formatAddresses(options.bcc)}`);\n }\n if (options.replyTo) {\n console.log(`Reply-To: ${this.formatAddress(options.replyTo)}`);\n }\n console.log(`Subject: ${options.subject}`);\n\n if (options.headers) {\n console.log(`Headers: ${JSON.stringify(options.headers)}`);\n }\n if (options.tags) {\n console.log(`Tags: ${JSON.stringify(options.tags)}`);\n }\n if (options.scheduledAt) {\n console.log(`Scheduled: ${options.scheduledAt.toISOString()}`);\n }\n if (options.attachments && options.attachments.length > 0) {\n console.log(`Attachments:`);\n for (const att of options.attachments) {\n const size = typeof att.content === \"string\"\n ? att.content.length\n : att.content.length;\n console.log(` - ${att.filename} (${att.contentType || \"unknown\"}, ${size} bytes)`);\n }\n }\n\n console.log(separator);\n if (options.text) {\n console.log(\"TEXT CONTENT:\");\n console.log(options.text);\n }\n if (options.html) {\n console.log(\"HTML CONTENT:\");\n console.log(options.html);\n }\n console.log(`${separator}\\n`);\n\n return {\n success: true,\n messageId,\n };\n }\n\n async sendBatch(options: BatchEmailOptions): Promise<BatchEmailResult> {\n const results: EmailResult[] = [];\n let successful = 0;\n let failed = 0;\n\n console.log(`\\nšŸ“¬ BATCH EMAIL (${options.emails.length} emails)`);\n\n for (const email of options.emails) {\n try {\n const result = await this.send(email);\n results.push(result);\n\n if (result.success) {\n successful++;\n } else {\n failed++;\n if (options.stopOnError) break;\n }\n } catch (err) {\n failed++;\n results.push({\n success: false,\n error: err instanceof Error ? err.message : \"Unknown error\",\n });\n\n if (options.stopOnError) break;\n }\n }\n\n console.log(`šŸ“¬ BATCH COMPLETE: ${successful} sent, ${failed} failed\\n`);\n\n return {\n total: options.emails.length,\n successful,\n failed,\n results,\n };\n }\n\n async verify(): Promise<boolean> {\n console.log(\"šŸ“§ Console email provider verified (always returns true)\");\n return true;\n }\n}\n\n/**\n * Create a Console provider\n */\nexport function createConsoleProvider(config: EmailProviderConfig): ConsoleProvider {\n return new ConsoleProvider(config);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,47 @@
1
+ import { EmailProvider, EmailProviderConfig, EmailOptions, EmailResult, BatchEmailOptions, BatchEmailResult } from '../types.js';
2
+ import '@parsrun/types';
3
+
4
+ /**
5
+ * @parsrun/email - Postmark Provider
6
+ * Edge-compatible Postmark email provider
7
+ */
8
+
9
+ /**
10
+ * Postmark Email Provider
11
+ * Uses fetch API for edge compatibility
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const postmark = new PostmarkProvider({
16
+ * apiKey: process.env.POSTMARK_API_KEY,
17
+ * fromEmail: 'hello@example.com',
18
+ * fromName: 'My App',
19
+ * });
20
+ *
21
+ * await postmark.send({
22
+ * to: 'user@example.com',
23
+ * subject: 'Hello',
24
+ * html: '<p>Hello World!</p>',
25
+ * });
26
+ * ```
27
+ */
28
+ declare class PostmarkProvider implements EmailProvider {
29
+ readonly type: "postmark";
30
+ private apiKey;
31
+ private fromEmail;
32
+ private fromName;
33
+ private baseUrl;
34
+ constructor(config: EmailProviderConfig);
35
+ private formatAddress;
36
+ private formatAddresses;
37
+ send(options: EmailOptions): Promise<EmailResult>;
38
+ sendBatch(options: BatchEmailOptions): Promise<BatchEmailResult>;
39
+ verify(): Promise<boolean>;
40
+ private uint8ArrayToBase64;
41
+ }
42
+ /**
43
+ * Create a Postmark provider
44
+ */
45
+ declare function createPostmarkProvider(config: EmailProviderConfig): PostmarkProvider;
46
+
47
+ export { PostmarkProvider, createPostmarkProvider };
@@ -0,0 +1,202 @@
1
+ // src/types.ts
2
+ import {
3
+ type,
4
+ emailAddress,
5
+ emailAttachment,
6
+ sendEmailOptions,
7
+ sendTemplateEmailOptions,
8
+ emailSendResult,
9
+ smtpConfig,
10
+ resendConfig,
11
+ sendgridConfig,
12
+ sesConfig,
13
+ postmarkConfig,
14
+ emailConfig
15
+ } from "@parsrun/types";
16
+ var EmailError = class extends Error {
17
+ constructor(message, code, cause) {
18
+ super(message);
19
+ this.code = code;
20
+ this.cause = cause;
21
+ this.name = "EmailError";
22
+ }
23
+ };
24
+ var EmailErrorCodes = {
25
+ INVALID_CONFIG: "INVALID_CONFIG",
26
+ INVALID_RECIPIENT: "INVALID_RECIPIENT",
27
+ INVALID_CONTENT: "INVALID_CONTENT",
28
+ SEND_FAILED: "SEND_FAILED",
29
+ RATE_LIMITED: "RATE_LIMITED",
30
+ PROVIDER_ERROR: "PROVIDER_ERROR",
31
+ TEMPLATE_ERROR: "TEMPLATE_ERROR",
32
+ ATTACHMENT_ERROR: "ATTACHMENT_ERROR"
33
+ };
34
+
35
+ // src/providers/postmark.ts
36
+ var PostmarkProvider = class {
37
+ type = "postmark";
38
+ apiKey;
39
+ fromEmail;
40
+ fromName;
41
+ baseUrl = "https://api.postmarkapp.com";
42
+ constructor(config) {
43
+ this.apiKey = config.apiKey;
44
+ this.fromEmail = config.fromEmail;
45
+ this.fromName = config.fromName;
46
+ }
47
+ formatAddress(address) {
48
+ if (typeof address === "string") {
49
+ return address;
50
+ }
51
+ if (address.name) {
52
+ return `${address.name} <${address.email}>`;
53
+ }
54
+ return address.email;
55
+ }
56
+ formatAddresses(addresses) {
57
+ if (Array.isArray(addresses)) {
58
+ return addresses.map((a) => this.formatAddress(a)).join(",");
59
+ }
60
+ return this.formatAddress(addresses);
61
+ }
62
+ async send(options) {
63
+ const from = options.from ? this.formatAddress(options.from) : this.fromName ? `${this.fromName} <${this.fromEmail}>` : this.fromEmail;
64
+ const payload = {
65
+ From: from,
66
+ To: this.formatAddresses(options.to),
67
+ Subject: options.subject
68
+ };
69
+ if (options.html) payload["HtmlBody"] = options.html;
70
+ if (options.text) payload["TextBody"] = options.text;
71
+ if (options.replyTo) payload["ReplyTo"] = this.formatAddress(options.replyTo);
72
+ if (options.cc) payload["Cc"] = this.formatAddresses(options.cc);
73
+ if (options.bcc) payload["Bcc"] = this.formatAddresses(options.bcc);
74
+ if (options.headers) {
75
+ payload["Headers"] = Object.entries(options.headers).map(([Name, Value]) => ({ Name, Value }));
76
+ }
77
+ if (options.tags) {
78
+ const tagEntries = Object.entries(options.tags);
79
+ if (tagEntries.length > 0 && tagEntries[0]) {
80
+ payload["Tag"] = tagEntries[0][1];
81
+ }
82
+ payload["Metadata"] = options.tags;
83
+ }
84
+ if (options.attachments && options.attachments.length > 0) {
85
+ payload["Attachments"] = options.attachments.map((att) => ({
86
+ Name: att.filename,
87
+ Content: typeof att.content === "string" ? att.content : this.uint8ArrayToBase64(att.content),
88
+ ContentType: att.contentType || "application/octet-stream",
89
+ ContentID: att.contentId
90
+ }));
91
+ }
92
+ try {
93
+ const response = await fetch(`${this.baseUrl}/email`, {
94
+ method: "POST",
95
+ headers: {
96
+ "X-Postmark-Server-Token": this.apiKey,
97
+ "Content-Type": "application/json",
98
+ "Accept": "application/json"
99
+ },
100
+ body: JSON.stringify(payload)
101
+ });
102
+ const data = await response.json();
103
+ if (!response.ok || data.ErrorCode) {
104
+ return {
105
+ success: false,
106
+ error: data.Message || `HTTP ${response.status}`,
107
+ data
108
+ };
109
+ }
110
+ return {
111
+ success: true,
112
+ messageId: data.MessageID,
113
+ data
114
+ };
115
+ } catch (err) {
116
+ throw new EmailError(
117
+ `Postmark send failed: ${err instanceof Error ? err.message : "Unknown error"}`,
118
+ EmailErrorCodes.SEND_FAILED,
119
+ err
120
+ );
121
+ }
122
+ }
123
+ async sendBatch(options) {
124
+ const batchPayload = options.emails.map((email) => {
125
+ const from = email.from ? this.formatAddress(email.from) : this.fromName ? `${this.fromName} <${this.fromEmail}>` : this.fromEmail;
126
+ const item = {
127
+ From: from,
128
+ To: this.formatAddresses(email.to),
129
+ Subject: email.subject
130
+ };
131
+ if (email.html) item["HtmlBody"] = email.html;
132
+ if (email.text) item["TextBody"] = email.text;
133
+ if (email.replyTo) item["ReplyTo"] = this.formatAddress(email.replyTo);
134
+ if (email.cc) item["Cc"] = this.formatAddresses(email.cc);
135
+ if (email.bcc) item["Bcc"] = this.formatAddresses(email.bcc);
136
+ return item;
137
+ });
138
+ try {
139
+ const response = await fetch(`${this.baseUrl}/email/batch`, {
140
+ method: "POST",
141
+ headers: {
142
+ "X-Postmark-Server-Token": this.apiKey,
143
+ "Content-Type": "application/json",
144
+ "Accept": "application/json"
145
+ },
146
+ body: JSON.stringify(batchPayload)
147
+ });
148
+ const data = await response.json();
149
+ const results = data.map((item) => ({
150
+ success: !item.ErrorCode,
151
+ messageId: item.MessageID,
152
+ error: item.ErrorCode ? item.Message : void 0,
153
+ data: item
154
+ }));
155
+ const successful = results.filter((r) => r.success).length;
156
+ const failed = results.filter((r) => !r.success).length;
157
+ return {
158
+ total: options.emails.length,
159
+ successful,
160
+ failed,
161
+ results
162
+ };
163
+ } catch (err) {
164
+ throw new EmailError(
165
+ `Postmark batch send failed: ${err instanceof Error ? err.message : "Unknown error"}`,
166
+ EmailErrorCodes.SEND_FAILED,
167
+ err
168
+ );
169
+ }
170
+ }
171
+ async verify() {
172
+ try {
173
+ const response = await fetch(`${this.baseUrl}/server`, {
174
+ headers: {
175
+ "X-Postmark-Server-Token": this.apiKey,
176
+ "Accept": "application/json"
177
+ }
178
+ });
179
+ return response.ok;
180
+ } catch {
181
+ return false;
182
+ }
183
+ }
184
+ uint8ArrayToBase64(data) {
185
+ let binary = "";
186
+ for (let i = 0; i < data.length; i++) {
187
+ const byte = data[i];
188
+ if (byte !== void 0) {
189
+ binary += String.fromCharCode(byte);
190
+ }
191
+ }
192
+ return btoa(binary);
193
+ }
194
+ };
195
+ function createPostmarkProvider(config) {
196
+ return new PostmarkProvider(config);
197
+ }
198
+ export {
199
+ PostmarkProvider,
200
+ createPostmarkProvider
201
+ };
202
+ //# sourceMappingURL=postmark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types.ts","../../src/providers/postmark.ts"],"sourcesContent":["/**\n * @parsrun/email - Type Definitions\n * Email types and interfaces\n */\n\n// Re-export types from @parsrun/types for convenience\nexport {\n type,\n emailAddress,\n emailAttachment,\n sendEmailOptions,\n sendTemplateEmailOptions,\n emailSendResult,\n smtpConfig,\n resendConfig,\n sendgridConfig,\n sesConfig,\n postmarkConfig,\n emailConfig,\n type EmailAddress as ParsEmailAddress,\n type EmailAttachment as ParsEmailAttachment,\n type SendEmailOptions,\n type SendTemplateEmailOptions,\n type EmailSendResult,\n type SmtpConfig,\n type ResendConfig,\n type SendgridConfig,\n type SesConfig,\n type PostmarkConfig,\n type EmailConfig,\n} from \"@parsrun/types\";\n\n/**\n * Email provider type\n */\nexport type EmailProviderType = \"resend\" | \"sendgrid\" | \"postmark\" | \"ses\" | \"console\" | \"mailgun\";\n\n/**\n * Email address with optional name\n */\nexport interface EmailAddress {\n email: string;\n name?: string | undefined;\n}\n\n/**\n * Email attachment\n */\nexport interface EmailAttachment {\n /** File name */\n filename: string;\n /** File content (base64 encoded or Buffer) */\n content: string | Uint8Array;\n /** Content type (MIME type) */\n contentType?: string | undefined;\n /** Content ID for inline attachments */\n contentId?: string | undefined;\n}\n\n/**\n * Email options\n */\nexport interface EmailOptions {\n /** Recipient email address(es) */\n to: string | string[] | EmailAddress | EmailAddress[];\n /** Email subject */\n subject: string;\n /** HTML content */\n html?: string | undefined;\n /** Plain text content */\n text?: string | undefined;\n /** From address (overrides default) */\n from?: string | EmailAddress | undefined;\n /** Reply-to address */\n replyTo?: string | EmailAddress | undefined;\n /** CC recipients */\n cc?: string | string[] | EmailAddress | EmailAddress[] | undefined;\n /** BCC recipients */\n bcc?: string | string[] | EmailAddress | EmailAddress[] | undefined;\n /** Attachments */\n attachments?: EmailAttachment[] | undefined;\n /** Custom headers */\n headers?: Record<string, string> | undefined;\n /** Tags for tracking */\n tags?: Record<string, string> | undefined;\n /** Schedule send time */\n scheduledAt?: Date | undefined;\n}\n\n/**\n * Email send result\n */\nexport interface EmailResult {\n /** Whether send was successful */\n success: boolean;\n /** Message ID from provider */\n messageId?: string | undefined;\n /** Error message if failed */\n error?: string | undefined;\n /** Provider-specific response data */\n data?: unknown;\n}\n\n/**\n * Batch email options\n */\nexport interface BatchEmailOptions {\n /** List of emails to send */\n emails: EmailOptions[];\n /** Whether to stop on first error */\n stopOnError?: boolean | undefined;\n}\n\n/**\n * Batch email result\n */\nexport interface BatchEmailResult {\n /** Total emails attempted */\n total: number;\n /** Successful sends */\n successful: number;\n /** Failed sends */\n failed: number;\n /** Individual results */\n results: EmailResult[];\n}\n\n/**\n * Email provider configuration\n */\nexport interface EmailProviderConfig {\n /** API key for the provider */\n apiKey: string;\n /** Default from email */\n fromEmail: string;\n /** Default from name */\n fromName?: string | undefined;\n /** Provider-specific options */\n options?: Record<string, unknown> | undefined;\n}\n\n/**\n * Email provider interface\n */\nexport interface EmailProvider {\n /** Provider type */\n readonly type: EmailProviderType;\n\n /**\n * Send a single email\n */\n send(options: EmailOptions): Promise<EmailResult>;\n\n /**\n * Send multiple emails\n */\n sendBatch?(options: BatchEmailOptions): Promise<BatchEmailResult>;\n\n /**\n * Verify provider configuration\n */\n verify?(): Promise<boolean>;\n}\n\n/**\n * Email service configuration\n */\nexport interface EmailServiceConfig {\n /** Provider type */\n provider: EmailProviderType;\n /** API key */\n apiKey: string;\n /** Default from email */\n fromEmail: string;\n /** Default from name */\n fromName?: string | undefined;\n /** Enable debug logging */\n debug?: boolean | undefined;\n /** Provider-specific options */\n providerOptions?: Record<string, unknown> | undefined;\n}\n\n/**\n * Template data for email templates\n */\nexport interface TemplateData {\n [key: string]: string | number | boolean | undefined | null | TemplateData | TemplateData[];\n}\n\n/**\n * Email template\n */\nexport interface EmailTemplate {\n /** Template name */\n name: string;\n /** Subject template */\n subject: string;\n /** HTML template */\n html: string;\n /** Plain text template */\n text?: string | undefined;\n}\n\n/**\n * Email error\n */\nexport class EmailError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly cause?: unknown\n ) {\n super(message);\n this.name = \"EmailError\";\n }\n}\n\n/**\n * Common email error codes\n */\nexport const EmailErrorCodes = {\n INVALID_CONFIG: \"INVALID_CONFIG\",\n INVALID_RECIPIENT: \"INVALID_RECIPIENT\",\n INVALID_CONTENT: \"INVALID_CONTENT\",\n SEND_FAILED: \"SEND_FAILED\",\n RATE_LIMITED: \"RATE_LIMITED\",\n PROVIDER_ERROR: \"PROVIDER_ERROR\",\n TEMPLATE_ERROR: \"TEMPLATE_ERROR\",\n ATTACHMENT_ERROR: \"ATTACHMENT_ERROR\",\n} as const;\n","/**\n * @parsrun/email - Postmark Provider\n * Edge-compatible Postmark email provider\n */\n\nimport type {\n BatchEmailOptions,\n BatchEmailResult,\n EmailAddress,\n EmailOptions,\n EmailProvider,\n EmailProviderConfig,\n EmailResult,\n} from \"../types.js\";\nimport { EmailError, EmailErrorCodes } from \"../types.js\";\n\n/**\n * Postmark Email Provider\n * Uses fetch API for edge compatibility\n *\n * @example\n * ```typescript\n * const postmark = new PostmarkProvider({\n * apiKey: process.env.POSTMARK_API_KEY,\n * fromEmail: 'hello@example.com',\n * fromName: 'My App',\n * });\n *\n * await postmark.send({\n * to: 'user@example.com',\n * subject: 'Hello',\n * html: '<p>Hello World!</p>',\n * });\n * ```\n */\nexport class PostmarkProvider implements EmailProvider {\n readonly type = \"postmark\" as const;\n\n private apiKey: string;\n private fromEmail: string;\n private fromName: string | undefined;\n private baseUrl = \"https://api.postmarkapp.com\";\n\n constructor(config: EmailProviderConfig) {\n this.apiKey = config.apiKey;\n this.fromEmail = config.fromEmail;\n this.fromName = config.fromName;\n }\n\n private formatAddress(address: string | EmailAddress): string {\n if (typeof address === \"string\") {\n return address;\n }\n if (address.name) {\n return `${address.name} <${address.email}>`;\n }\n return address.email;\n }\n\n private formatAddresses(\n addresses: string | string[] | EmailAddress | EmailAddress[]\n ): string {\n if (Array.isArray(addresses)) {\n return addresses.map((a) => this.formatAddress(a)).join(\",\");\n }\n return this.formatAddress(addresses);\n }\n\n async send(options: EmailOptions): Promise<EmailResult> {\n const from = options.from\n ? this.formatAddress(options.from)\n : this.fromName\n ? `${this.fromName} <${this.fromEmail}>`\n : this.fromEmail;\n\n const payload: Record<string, unknown> = {\n From: from,\n To: this.formatAddresses(options.to),\n Subject: options.subject,\n };\n\n if (options.html) payload[\"HtmlBody\"] = options.html;\n if (options.text) payload[\"TextBody\"] = options.text;\n if (options.replyTo) payload[\"ReplyTo\"] = this.formatAddress(options.replyTo);\n if (options.cc) payload[\"Cc\"] = this.formatAddresses(options.cc);\n if (options.bcc) payload[\"Bcc\"] = this.formatAddresses(options.bcc);\n\n if (options.headers) {\n payload[\"Headers\"] = Object.entries(options.headers).map(([Name, Value]) => ({ Name, Value }));\n }\n\n if (options.tags) {\n // Postmark uses Tag field (single tag) and Metadata for custom data\n const tagEntries = Object.entries(options.tags);\n if (tagEntries.length > 0 && tagEntries[0]) {\n payload[\"Tag\"] = tagEntries[0][1];\n }\n payload[\"Metadata\"] = options.tags;\n }\n\n if (options.attachments && options.attachments.length > 0) {\n payload[\"Attachments\"] = options.attachments.map((att) => ({\n Name: att.filename,\n Content: typeof att.content === \"string\"\n ? att.content\n : this.uint8ArrayToBase64(att.content),\n ContentType: att.contentType || \"application/octet-stream\",\n ContentID: att.contentId,\n }));\n }\n\n try {\n const response = await fetch(`${this.baseUrl}/email`, {\n method: \"POST\",\n headers: {\n \"X-Postmark-Server-Token\": this.apiKey,\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n const data = await response.json() as {\n MessageID?: string;\n ErrorCode?: number;\n Message?: string;\n };\n\n if (!response.ok || data.ErrorCode) {\n return {\n success: false,\n error: data.Message || `HTTP ${response.status}`,\n data,\n };\n }\n\n return {\n success: true,\n messageId: data.MessageID,\n data,\n };\n } catch (err) {\n throw new EmailError(\n `Postmark send failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n EmailErrorCodes.SEND_FAILED,\n err\n );\n }\n }\n\n async sendBatch(options: BatchEmailOptions): Promise<BatchEmailResult> {\n // Postmark supports batch sending up to 500 emails\n const batchPayload = options.emails.map((email) => {\n const from = email.from\n ? this.formatAddress(email.from)\n : this.fromName\n ? `${this.fromName} <${this.fromEmail}>`\n : this.fromEmail;\n\n const item: Record<string, unknown> = {\n From: from,\n To: this.formatAddresses(email.to),\n Subject: email.subject,\n };\n\n if (email.html) item[\"HtmlBody\"] = email.html;\n if (email.text) item[\"TextBody\"] = email.text;\n if (email.replyTo) item[\"ReplyTo\"] = this.formatAddress(email.replyTo);\n if (email.cc) item[\"Cc\"] = this.formatAddresses(email.cc);\n if (email.bcc) item[\"Bcc\"] = this.formatAddresses(email.bcc);\n\n return item;\n });\n\n try {\n const response = await fetch(`${this.baseUrl}/email/batch`, {\n method: \"POST\",\n headers: {\n \"X-Postmark-Server-Token\": this.apiKey,\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n },\n body: JSON.stringify(batchPayload),\n });\n\n const data = await response.json() as Array<{\n MessageID?: string;\n ErrorCode?: number;\n Message?: string;\n }>;\n\n const results: EmailResult[] = data.map((item) => ({\n success: !item.ErrorCode,\n messageId: item.MessageID,\n error: item.ErrorCode ? item.Message : undefined,\n data: item,\n }));\n\n const successful = results.filter((r) => r.success).length;\n const failed = results.filter((r) => !r.success).length;\n\n return {\n total: options.emails.length,\n successful,\n failed,\n results,\n };\n } catch (err) {\n throw new EmailError(\n `Postmark batch send failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n EmailErrorCodes.SEND_FAILED,\n err\n );\n }\n }\n\n async verify(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/server`, {\n headers: {\n \"X-Postmark-Server-Token\": this.apiKey,\n \"Accept\": \"application/json\",\n },\n });\n\n return response.ok;\n } catch {\n return false;\n }\n }\n\n private uint8ArrayToBase64(data: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < data.length; i++) {\n const byte = data[i];\n if (byte !== undefined) {\n binary += String.fromCharCode(byte);\n }\n }\n return btoa(binary);\n }\n}\n\n/**\n * Create a Postmark provider\n */\nexport function createPostmarkProvider(config: EmailProviderConfig): PostmarkProvider {\n return new PostmarkProvider(config);\n}\n"],"mappings":";AAMA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAYK;AAgLA,IAAM,aAAN,cAAyB,MAAM;AAAA,EACpC,YACE,SACgB,MACA,OAChB;AACA,UAAM,OAAO;AAHG;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,kBAAkB;AAAA,EAC7B,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,kBAAkB;AACpB;;;AClMO,IAAM,mBAAN,MAAgD;AAAA,EAC5C,OAAO;AAAA,EAER;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EAElB,YAAY,QAA6B;AACvC,SAAK,SAAS,OAAO;AACrB,SAAK,YAAY,OAAO;AACxB,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA,EAEQ,cAAc,SAAwC;AAC5D,QAAI,OAAO,YAAY,UAAU;AAC/B,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,MAAM;AAChB,aAAO,GAAG,QAAQ,IAAI,KAAK,QAAQ,KAAK;AAAA,IAC1C;AACA,WAAO,QAAQ;AAAA,EACjB;AAAA,EAEQ,gBACN,WACQ;AACR,QAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,aAAO,UAAU,IAAI,CAAC,MAAM,KAAK,cAAc,CAAC,CAAC,EAAE,KAAK,GAAG;AAAA,IAC7D;AACA,WAAO,KAAK,cAAc,SAAS;AAAA,EACrC;AAAA,EAEA,MAAM,KAAK,SAA6C;AACtD,UAAM,OAAO,QAAQ,OACjB,KAAK,cAAc,QAAQ,IAAI,IAC/B,KAAK,WACH,GAAG,KAAK,QAAQ,KAAK,KAAK,SAAS,MACnC,KAAK;AAEX,UAAM,UAAmC;AAAA,MACvC,MAAM;AAAA,MACN,IAAI,KAAK,gBAAgB,QAAQ,EAAE;AAAA,MACnC,SAAS,QAAQ;AAAA,IACnB;AAEA,QAAI,QAAQ,KAAM,SAAQ,UAAU,IAAI,QAAQ;AAChD,QAAI,QAAQ,KAAM,SAAQ,UAAU,IAAI,QAAQ;AAChD,QAAI,QAAQ,QAAS,SAAQ,SAAS,IAAI,KAAK,cAAc,QAAQ,OAAO;AAC5E,QAAI,QAAQ,GAAI,SAAQ,IAAI,IAAI,KAAK,gBAAgB,QAAQ,EAAE;AAC/D,QAAI,QAAQ,IAAK,SAAQ,KAAK,IAAI,KAAK,gBAAgB,QAAQ,GAAG;AAElE,QAAI,QAAQ,SAAS;AACnB,cAAQ,SAAS,IAAI,OAAO,QAAQ,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO,EAAE,MAAM,MAAM,EAAE;AAAA,IAC/F;AAEA,QAAI,QAAQ,MAAM;AAEhB,YAAM,aAAa,OAAO,QAAQ,QAAQ,IAAI;AAC9C,UAAI,WAAW,SAAS,KAAK,WAAW,CAAC,GAAG;AAC1C,gBAAQ,KAAK,IAAI,WAAW,CAAC,EAAE,CAAC;AAAA,MAClC;AACA,cAAQ,UAAU,IAAI,QAAQ;AAAA,IAChC;AAEA,QAAI,QAAQ,eAAe,QAAQ,YAAY,SAAS,GAAG;AACzD,cAAQ,aAAa,IAAI,QAAQ,YAAY,IAAI,CAAC,SAAS;AAAA,QACzD,MAAM,IAAI;AAAA,QACV,SAAS,OAAO,IAAI,YAAY,WAC5B,IAAI,UACJ,KAAK,mBAAmB,IAAI,OAAO;AAAA,QACvC,aAAa,IAAI,eAAe;AAAA,QAChC,WAAW,IAAI;AAAA,MACjB,EAAE;AAAA,IACJ;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,UAAU;AAAA,QACpD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,2BAA2B,KAAK;AAAA,UAChC,gBAAgB;AAAA,UAChB,UAAU;AAAA,QACZ;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,MAC9B,CAAC;AAED,YAAM,OAAO,MAAM,SAAS,KAAK;AAMjC,UAAI,CAAC,SAAS,MAAM,KAAK,WAAW;AAClC,eAAO;AAAA,UACL,SAAS;AAAA,UACT,OAAO,KAAK,WAAW,QAAQ,SAAS,MAAM;AAAA,UAC9C;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,QACL,SAAS;AAAA,QACT,WAAW,KAAK;AAAA,QAChB;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,IAAI;AAAA,QACR,yBAAyB,eAAe,QAAQ,IAAI,UAAU,eAAe;AAAA,QAC7E,gBAAgB;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,UAAU,SAAuD;AAErE,UAAM,eAAe,QAAQ,OAAO,IAAI,CAAC,UAAU;AACjD,YAAM,OAAO,MAAM,OACf,KAAK,cAAc,MAAM,IAAI,IAC7B,KAAK,WACH,GAAG,KAAK,QAAQ,KAAK,KAAK,SAAS,MACnC,KAAK;AAEX,YAAM,OAAgC;AAAA,QACpC,MAAM;AAAA,QACN,IAAI,KAAK,gBAAgB,MAAM,EAAE;AAAA,QACjC,SAAS,MAAM;AAAA,MACjB;AAEA,UAAI,MAAM,KAAM,MAAK,UAAU,IAAI,MAAM;AACzC,UAAI,MAAM,KAAM,MAAK,UAAU,IAAI,MAAM;AACzC,UAAI,MAAM,QAAS,MAAK,SAAS,IAAI,KAAK,cAAc,MAAM,OAAO;AACrE,UAAI,MAAM,GAAI,MAAK,IAAI,IAAI,KAAK,gBAAgB,MAAM,EAAE;AACxD,UAAI,MAAM,IAAK,MAAK,KAAK,IAAI,KAAK,gBAAgB,MAAM,GAAG;AAE3D,aAAO;AAAA,IACT,CAAC;AAED,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,gBAAgB;AAAA,QAC1D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,2BAA2B,KAAK;AAAA,UAChC,gBAAgB;AAAA,UAChB,UAAU;AAAA,QACZ;AAAA,QACA,MAAM,KAAK,UAAU,YAAY;AAAA,MACnC,CAAC;AAED,YAAM,OAAO,MAAM,SAAS,KAAK;AAMjC,YAAM,UAAyB,KAAK,IAAI,CAAC,UAAU;AAAA,QACjD,SAAS,CAAC,KAAK;AAAA,QACf,WAAW,KAAK;AAAA,QAChB,OAAO,KAAK,YAAY,KAAK,UAAU;AAAA,QACvC,MAAM;AAAA,MACR,EAAE;AAEF,YAAM,aAAa,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AACpD,YAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE;AAEjD,aAAO;AAAA,QACL,OAAO,QAAQ,OAAO;AAAA,QACtB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,IAAI;AAAA,QACR,+BAA+B,eAAe,QAAQ,IAAI,UAAU,eAAe;AAAA,QACnF,gBAAgB;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,SAA2B;AAC/B,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,WAAW;AAAA,QACrD,SAAS;AAAA,UACP,2BAA2B,KAAK;AAAA,UAChC,UAAU;AAAA,QACZ;AAAA,MACF,CAAC;AAED,aAAO,SAAS;AAAA,IAClB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,mBAAmB,MAA0B;AACnD,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,OAAO,KAAK,CAAC;AACnB,UAAI,SAAS,QAAW;AACtB,kBAAU,OAAO,aAAa,IAAI;AAAA,MACpC;AAAA,IACF;AACA,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;AAKO,SAAS,uBAAuB,QAA+C;AACpF,SAAO,IAAI,iBAAiB,MAAM;AACpC;","names":[]}
@@ -0,0 +1,47 @@
1
+ import { EmailProvider, EmailProviderConfig, EmailOptions, EmailResult, BatchEmailOptions, BatchEmailResult } from '../types.js';
2
+ import '@parsrun/types';
3
+
4
+ /**
5
+ * @parsrun/email - Resend Provider
6
+ * Edge-compatible Resend email provider
7
+ */
8
+
9
+ /**
10
+ * Resend Email Provider
11
+ * Uses fetch API for edge compatibility
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const resend = new ResendProvider({
16
+ * apiKey: process.env.RESEND_API_KEY,
17
+ * fromEmail: 'hello@example.com',
18
+ * fromName: 'My App',
19
+ * });
20
+ *
21
+ * await resend.send({
22
+ * to: 'user@example.com',
23
+ * subject: 'Hello',
24
+ * html: '<p>Hello World!</p>',
25
+ * });
26
+ * ```
27
+ */
28
+ declare class ResendProvider implements EmailProvider {
29
+ readonly type: "resend";
30
+ private apiKey;
31
+ private fromEmail;
32
+ private fromName;
33
+ private baseUrl;
34
+ constructor(config: EmailProviderConfig);
35
+ private formatAddress;
36
+ private formatAddresses;
37
+ send(options: EmailOptions): Promise<EmailResult>;
38
+ sendBatch(options: BatchEmailOptions): Promise<BatchEmailResult>;
39
+ verify(): Promise<boolean>;
40
+ private uint8ArrayToBase64;
41
+ }
42
+ /**
43
+ * Create a Resend provider
44
+ */
45
+ declare function createResendProvider(config: EmailProviderConfig): ResendProvider;
46
+
47
+ export { ResendProvider, createResendProvider };
@@ -0,0 +1,174 @@
1
+ // src/types.ts
2
+ import {
3
+ type,
4
+ emailAddress,
5
+ emailAttachment,
6
+ sendEmailOptions,
7
+ sendTemplateEmailOptions,
8
+ emailSendResult,
9
+ smtpConfig,
10
+ resendConfig,
11
+ sendgridConfig,
12
+ sesConfig,
13
+ postmarkConfig,
14
+ emailConfig
15
+ } from "@parsrun/types";
16
+ var EmailError = class extends Error {
17
+ constructor(message, code, cause) {
18
+ super(message);
19
+ this.code = code;
20
+ this.cause = cause;
21
+ this.name = "EmailError";
22
+ }
23
+ };
24
+ var EmailErrorCodes = {
25
+ INVALID_CONFIG: "INVALID_CONFIG",
26
+ INVALID_RECIPIENT: "INVALID_RECIPIENT",
27
+ INVALID_CONTENT: "INVALID_CONTENT",
28
+ SEND_FAILED: "SEND_FAILED",
29
+ RATE_LIMITED: "RATE_LIMITED",
30
+ PROVIDER_ERROR: "PROVIDER_ERROR",
31
+ TEMPLATE_ERROR: "TEMPLATE_ERROR",
32
+ ATTACHMENT_ERROR: "ATTACHMENT_ERROR"
33
+ };
34
+
35
+ // src/providers/resend.ts
36
+ var ResendProvider = class {
37
+ type = "resend";
38
+ apiKey;
39
+ fromEmail;
40
+ fromName;
41
+ baseUrl = "https://api.resend.com";
42
+ constructor(config) {
43
+ this.apiKey = config.apiKey;
44
+ this.fromEmail = config.fromEmail;
45
+ this.fromName = config.fromName;
46
+ }
47
+ formatAddress(address) {
48
+ if (typeof address === "string") {
49
+ return address;
50
+ }
51
+ if (address.name) {
52
+ return `${address.name} <${address.email}>`;
53
+ }
54
+ return address.email;
55
+ }
56
+ formatAddresses(addresses) {
57
+ if (Array.isArray(addresses)) {
58
+ return addresses.map((a) => this.formatAddress(a));
59
+ }
60
+ return [this.formatAddress(addresses)];
61
+ }
62
+ async send(options) {
63
+ const from = options.from ? this.formatAddress(options.from) : this.fromName ? `${this.fromName} <${this.fromEmail}>` : this.fromEmail;
64
+ const payload = {
65
+ from,
66
+ to: this.formatAddresses(options.to),
67
+ subject: options.subject
68
+ };
69
+ if (options.html) payload["html"] = options.html;
70
+ if (options.text) payload["text"] = options.text;
71
+ if (options.replyTo) payload["reply_to"] = this.formatAddress(options.replyTo);
72
+ if (options.cc) payload["cc"] = this.formatAddresses(options.cc);
73
+ if (options.bcc) payload["bcc"] = this.formatAddresses(options.bcc);
74
+ if (options.headers) payload["headers"] = options.headers;
75
+ if (options.tags) payload["tags"] = Object.entries(options.tags).map(([name, value]) => ({ name, value }));
76
+ if (options.scheduledAt) payload["scheduled_at"] = options.scheduledAt.toISOString();
77
+ if (options.attachments && options.attachments.length > 0) {
78
+ payload["attachments"] = options.attachments.map((att) => ({
79
+ filename: att.filename,
80
+ content: typeof att.content === "string" ? att.content : this.uint8ArrayToBase64(att.content),
81
+ content_type: att.contentType
82
+ }));
83
+ }
84
+ try {
85
+ const response = await fetch(`${this.baseUrl}/emails`, {
86
+ method: "POST",
87
+ headers: {
88
+ "Authorization": `Bearer ${this.apiKey}`,
89
+ "Content-Type": "application/json"
90
+ },
91
+ body: JSON.stringify(payload)
92
+ });
93
+ const data = await response.json();
94
+ if (!response.ok) {
95
+ return {
96
+ success: false,
97
+ error: data.message || `HTTP ${response.status}`,
98
+ data
99
+ };
100
+ }
101
+ return {
102
+ success: true,
103
+ messageId: data.id,
104
+ data
105
+ };
106
+ } catch (err) {
107
+ throw new EmailError(
108
+ `Resend send failed: ${err instanceof Error ? err.message : "Unknown error"}`,
109
+ EmailErrorCodes.SEND_FAILED,
110
+ err
111
+ );
112
+ }
113
+ }
114
+ async sendBatch(options) {
115
+ const results = [];
116
+ let successful = 0;
117
+ let failed = 0;
118
+ for (const email of options.emails) {
119
+ try {
120
+ const result = await this.send(email);
121
+ results.push(result);
122
+ if (result.success) {
123
+ successful++;
124
+ } else {
125
+ failed++;
126
+ if (options.stopOnError) break;
127
+ }
128
+ } catch (err) {
129
+ failed++;
130
+ results.push({
131
+ success: false,
132
+ error: err instanceof Error ? err.message : "Unknown error"
133
+ });
134
+ if (options.stopOnError) break;
135
+ }
136
+ }
137
+ return {
138
+ total: options.emails.length,
139
+ successful,
140
+ failed,
141
+ results
142
+ };
143
+ }
144
+ async verify() {
145
+ try {
146
+ const response = await fetch(`${this.baseUrl}/domains`, {
147
+ headers: {
148
+ "Authorization": `Bearer ${this.apiKey}`
149
+ }
150
+ });
151
+ return response.ok;
152
+ } catch {
153
+ return false;
154
+ }
155
+ }
156
+ uint8ArrayToBase64(data) {
157
+ let binary = "";
158
+ for (let i = 0; i < data.length; i++) {
159
+ const byte = data[i];
160
+ if (byte !== void 0) {
161
+ binary += String.fromCharCode(byte);
162
+ }
163
+ }
164
+ return btoa(binary);
165
+ }
166
+ };
167
+ function createResendProvider(config) {
168
+ return new ResendProvider(config);
169
+ }
170
+ export {
171
+ ResendProvider,
172
+ createResendProvider
173
+ };
174
+ //# sourceMappingURL=resend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types.ts","../../src/providers/resend.ts"],"sourcesContent":["/**\n * @parsrun/email - Type Definitions\n * Email types and interfaces\n */\n\n// Re-export types from @parsrun/types for convenience\nexport {\n type,\n emailAddress,\n emailAttachment,\n sendEmailOptions,\n sendTemplateEmailOptions,\n emailSendResult,\n smtpConfig,\n resendConfig,\n sendgridConfig,\n sesConfig,\n postmarkConfig,\n emailConfig,\n type EmailAddress as ParsEmailAddress,\n type EmailAttachment as ParsEmailAttachment,\n type SendEmailOptions,\n type SendTemplateEmailOptions,\n type EmailSendResult,\n type SmtpConfig,\n type ResendConfig,\n type SendgridConfig,\n type SesConfig,\n type PostmarkConfig,\n type EmailConfig,\n} from \"@parsrun/types\";\n\n/**\n * Email provider type\n */\nexport type EmailProviderType = \"resend\" | \"sendgrid\" | \"postmark\" | \"ses\" | \"console\" | \"mailgun\";\n\n/**\n * Email address with optional name\n */\nexport interface EmailAddress {\n email: string;\n name?: string | undefined;\n}\n\n/**\n * Email attachment\n */\nexport interface EmailAttachment {\n /** File name */\n filename: string;\n /** File content (base64 encoded or Buffer) */\n content: string | Uint8Array;\n /** Content type (MIME type) */\n contentType?: string | undefined;\n /** Content ID for inline attachments */\n contentId?: string | undefined;\n}\n\n/**\n * Email options\n */\nexport interface EmailOptions {\n /** Recipient email address(es) */\n to: string | string[] | EmailAddress | EmailAddress[];\n /** Email subject */\n subject: string;\n /** HTML content */\n html?: string | undefined;\n /** Plain text content */\n text?: string | undefined;\n /** From address (overrides default) */\n from?: string | EmailAddress | undefined;\n /** Reply-to address */\n replyTo?: string | EmailAddress | undefined;\n /** CC recipients */\n cc?: string | string[] | EmailAddress | EmailAddress[] | undefined;\n /** BCC recipients */\n bcc?: string | string[] | EmailAddress | EmailAddress[] | undefined;\n /** Attachments */\n attachments?: EmailAttachment[] | undefined;\n /** Custom headers */\n headers?: Record<string, string> | undefined;\n /** Tags for tracking */\n tags?: Record<string, string> | undefined;\n /** Schedule send time */\n scheduledAt?: Date | undefined;\n}\n\n/**\n * Email send result\n */\nexport interface EmailResult {\n /** Whether send was successful */\n success: boolean;\n /** Message ID from provider */\n messageId?: string | undefined;\n /** Error message if failed */\n error?: string | undefined;\n /** Provider-specific response data */\n data?: unknown;\n}\n\n/**\n * Batch email options\n */\nexport interface BatchEmailOptions {\n /** List of emails to send */\n emails: EmailOptions[];\n /** Whether to stop on first error */\n stopOnError?: boolean | undefined;\n}\n\n/**\n * Batch email result\n */\nexport interface BatchEmailResult {\n /** Total emails attempted */\n total: number;\n /** Successful sends */\n successful: number;\n /** Failed sends */\n failed: number;\n /** Individual results */\n results: EmailResult[];\n}\n\n/**\n * Email provider configuration\n */\nexport interface EmailProviderConfig {\n /** API key for the provider */\n apiKey: string;\n /** Default from email */\n fromEmail: string;\n /** Default from name */\n fromName?: string | undefined;\n /** Provider-specific options */\n options?: Record<string, unknown> | undefined;\n}\n\n/**\n * Email provider interface\n */\nexport interface EmailProvider {\n /** Provider type */\n readonly type: EmailProviderType;\n\n /**\n * Send a single email\n */\n send(options: EmailOptions): Promise<EmailResult>;\n\n /**\n * Send multiple emails\n */\n sendBatch?(options: BatchEmailOptions): Promise<BatchEmailResult>;\n\n /**\n * Verify provider configuration\n */\n verify?(): Promise<boolean>;\n}\n\n/**\n * Email service configuration\n */\nexport interface EmailServiceConfig {\n /** Provider type */\n provider: EmailProviderType;\n /** API key */\n apiKey: string;\n /** Default from email */\n fromEmail: string;\n /** Default from name */\n fromName?: string | undefined;\n /** Enable debug logging */\n debug?: boolean | undefined;\n /** Provider-specific options */\n providerOptions?: Record<string, unknown> | undefined;\n}\n\n/**\n * Template data for email templates\n */\nexport interface TemplateData {\n [key: string]: string | number | boolean | undefined | null | TemplateData | TemplateData[];\n}\n\n/**\n * Email template\n */\nexport interface EmailTemplate {\n /** Template name */\n name: string;\n /** Subject template */\n subject: string;\n /** HTML template */\n html: string;\n /** Plain text template */\n text?: string | undefined;\n}\n\n/**\n * Email error\n */\nexport class EmailError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly cause?: unknown\n ) {\n super(message);\n this.name = \"EmailError\";\n }\n}\n\n/**\n * Common email error codes\n */\nexport const EmailErrorCodes = {\n INVALID_CONFIG: \"INVALID_CONFIG\",\n INVALID_RECIPIENT: \"INVALID_RECIPIENT\",\n INVALID_CONTENT: \"INVALID_CONTENT\",\n SEND_FAILED: \"SEND_FAILED\",\n RATE_LIMITED: \"RATE_LIMITED\",\n PROVIDER_ERROR: \"PROVIDER_ERROR\",\n TEMPLATE_ERROR: \"TEMPLATE_ERROR\",\n ATTACHMENT_ERROR: \"ATTACHMENT_ERROR\",\n} as const;\n","/**\n * @parsrun/email - Resend Provider\n * Edge-compatible Resend email provider\n */\n\nimport type {\n BatchEmailOptions,\n BatchEmailResult,\n EmailAddress,\n EmailOptions,\n EmailProvider,\n EmailProviderConfig,\n EmailResult,\n} from \"../types.js\";\nimport { EmailError, EmailErrorCodes } from \"../types.js\";\n\n/**\n * Resend Email Provider\n * Uses fetch API for edge compatibility\n *\n * @example\n * ```typescript\n * const resend = new ResendProvider({\n * apiKey: process.env.RESEND_API_KEY,\n * fromEmail: 'hello@example.com',\n * fromName: 'My App',\n * });\n *\n * await resend.send({\n * to: 'user@example.com',\n * subject: 'Hello',\n * html: '<p>Hello World!</p>',\n * });\n * ```\n */\nexport class ResendProvider implements EmailProvider {\n readonly type = \"resend\" as const;\n\n private apiKey: string;\n private fromEmail: string;\n private fromName: string | undefined;\n private baseUrl = \"https://api.resend.com\";\n\n constructor(config: EmailProviderConfig) {\n this.apiKey = config.apiKey;\n this.fromEmail = config.fromEmail;\n this.fromName = config.fromName;\n }\n\n private formatAddress(address: string | EmailAddress): string {\n if (typeof address === \"string\") {\n return address;\n }\n if (address.name) {\n return `${address.name} <${address.email}>`;\n }\n return address.email;\n }\n\n private formatAddresses(\n addresses: string | string[] | EmailAddress | EmailAddress[]\n ): string[] {\n if (Array.isArray(addresses)) {\n return addresses.map((a) => this.formatAddress(a));\n }\n return [this.formatAddress(addresses)];\n }\n\n async send(options: EmailOptions): Promise<EmailResult> {\n const from = options.from\n ? this.formatAddress(options.from)\n : this.fromName\n ? `${this.fromName} <${this.fromEmail}>`\n : this.fromEmail;\n\n const payload: Record<string, unknown> = {\n from,\n to: this.formatAddresses(options.to),\n subject: options.subject,\n };\n\n if (options.html) payload[\"html\"] = options.html;\n if (options.text) payload[\"text\"] = options.text;\n if (options.replyTo) payload[\"reply_to\"] = this.formatAddress(options.replyTo);\n if (options.cc) payload[\"cc\"] = this.formatAddresses(options.cc);\n if (options.bcc) payload[\"bcc\"] = this.formatAddresses(options.bcc);\n if (options.headers) payload[\"headers\"] = options.headers;\n if (options.tags) payload[\"tags\"] = Object.entries(options.tags).map(([name, value]) => ({ name, value }));\n if (options.scheduledAt) payload[\"scheduled_at\"] = options.scheduledAt.toISOString();\n\n if (options.attachments && options.attachments.length > 0) {\n payload[\"attachments\"] = options.attachments.map((att) => ({\n filename: att.filename,\n content: typeof att.content === \"string\"\n ? att.content\n : this.uint8ArrayToBase64(att.content),\n content_type: att.contentType,\n }));\n }\n\n try {\n const response = await fetch(`${this.baseUrl}/emails`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n\n const data = await response.json() as { id?: string; message?: string; statusCode?: number };\n\n if (!response.ok) {\n return {\n success: false,\n error: data.message || `HTTP ${response.status}`,\n data,\n };\n }\n\n return {\n success: true,\n messageId: data.id,\n data,\n };\n } catch (err) {\n throw new EmailError(\n `Resend send failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n EmailErrorCodes.SEND_FAILED,\n err\n );\n }\n }\n\n async sendBatch(options: BatchEmailOptions): Promise<BatchEmailResult> {\n const results: EmailResult[] = [];\n let successful = 0;\n let failed = 0;\n\n for (const email of options.emails) {\n try {\n const result = await this.send(email);\n results.push(result);\n\n if (result.success) {\n successful++;\n } else {\n failed++;\n if (options.stopOnError) break;\n }\n } catch (err) {\n failed++;\n results.push({\n success: false,\n error: err instanceof Error ? err.message : \"Unknown error\",\n });\n\n if (options.stopOnError) break;\n }\n }\n\n return {\n total: options.emails.length,\n successful,\n failed,\n results,\n };\n }\n\n async verify(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/domains`, {\n headers: {\n \"Authorization\": `Bearer ${this.apiKey}`,\n },\n });\n\n return response.ok;\n } catch {\n return false;\n }\n }\n\n private uint8ArrayToBase64(data: Uint8Array): string {\n // Edge-compatible base64 encoding\n let binary = \"\";\n for (let i = 0; i < data.length; i++) {\n const byte = data[i];\n if (byte !== undefined) {\n binary += String.fromCharCode(byte);\n }\n }\n return btoa(binary);\n }\n}\n\n/**\n * Create a Resend provider\n */\nexport function createResendProvider(config: EmailProviderConfig): ResendProvider {\n return new ResendProvider(config);\n}\n"],"mappings":"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","names":[]}