UNPKG

unemail

Version:

A modern TypeScript email library with zero dependencies, supporting multiple providers including AWS SES, Resend, MailCrab, and HTTP APIs

202 lines (199 loc) 5.63 kB
import { makeRequest, validateEmailOptions, createError, generateMessageId } from 'unemail/utils'; import { defineProvider } from './base.mjs'; const PROVIDER_NAME = "http"; const DEFAULT_METHOD = "POST"; const DEFAULT_TIMEOUT = 3e4; const httpProvider = defineProvider((opts = {}) => { if (!opts.endpoint) { throw new Error("Missing required option: endpoint"); } const options = { endpoint: opts.endpoint, apiKey: opts.apiKey || "", method: opts.method || DEFAULT_METHOD, headers: opts.headers || {} }; const getStandardHeaders = () => { const headers = { "Content-Type": "application/json", ...options.headers }; if (options.apiKey) { headers.Authorization = `Bearer ${options.apiKey}`; } return headers; }; const formatRequest = (emailOpts) => { const payload = { from: emailOpts.from.email, from_name: emailOpts.from.name, to: Array.isArray(emailOpts.to) ? emailOpts.to.map((r) => r.email) : emailOpts.to.email, subject: emailOpts.subject, text: emailOpts.text, html: emailOpts.html }; if (emailOpts.cc) { payload.cc = Array.isArray(emailOpts.cc) ? emailOpts.cc.map((r) => r.email) : emailOpts.cc.email; } if (emailOpts.bcc) { payload.bcc = Array.isArray(emailOpts.bcc) ? emailOpts.bcc.map((r) => r.email) : emailOpts.bcc.email; } if (emailOpts.customParams) { Object.assign(payload, emailOpts.customParams); } return payload; }; let isInitialized = false; return { name: PROVIDER_NAME, features: { attachments: false, html: true, templates: false, tracking: false, customHeaders: true, batchSending: false, tagging: false, scheduling: false, replyTo: false }, options, /** * Initialize the HTTP provider */ async initialize() { if (isInitialized) { return; } if (!await this.isAvailable()) { throw new Error("API endpoint not available"); } isInitialized = true; }, /** * Check if the HTTP endpoint is available */ async isAvailable() { try { const result = await makeRequest( options.endpoint, { method: "OPTIONS", headers: getStandardHeaders(), timeout: DEFAULT_TIMEOUT } ); if (result.success) { return true; } if (result.data?.statusCode && result.data.statusCode >= 400 && result.data.statusCode < 500) { return true; } return false; } catch (error) { if (error instanceof Error) { const errorMsg = error.message; if (errorMsg.includes("status 4") || errorMsg.includes("401") || errorMsg.includes("403")) { return true; } } return false; } }, /** * Send email via HTTP API */ async sendEmail(emailOpts) { try { const validationErrors = validateEmailOptions(emailOpts); if (validationErrors.length > 0) { return { success: false, error: createError( PROVIDER_NAME, `Invalid email options: ${validationErrors.join(", ")}` ) }; } if (!isInitialized) { await this.initialize(); } const headers = getStandardHeaders(); if (emailOpts.headers) { Object.assign(headers, emailOpts.headers); } const payload = formatRequest(emailOpts); const endpoint = emailOpts.endpointOverride || options.endpoint; const method = emailOpts.methodOverride || options.method; const result = await makeRequest( endpoint, { method, headers, timeout: DEFAULT_TIMEOUT }, JSON.stringify(payload) ); if (!result.success) { return { success: false, error: createError( PROVIDER_NAME, `Failed to send email: ${result.error?.message || "Unknown error"}`, { cause: result.error } ) }; } let messageId; const responseBody = result.data?.body; if (responseBody) { messageId = responseBody.id || responseBody.messageId || responseBody.data && (responseBody.data.id || responseBody.data.messageId); } if (!messageId) { messageId = generateMessageId(); } return { success: true, data: { messageId, sent: true, timestamp: /* @__PURE__ */ new Date(), provider: PROVIDER_NAME, response: result.data?.body } }; } catch (error) { return { success: false, error: createError( PROVIDER_NAME, `Failed to send email: ${error.message}`, { cause: error } ) }; } }, /** * Validate configuration */ async validateCredentials() { try { const result = await makeRequest( options.endpoint, { method: "GET", headers: getStandardHeaders(), timeout: DEFAULT_TIMEOUT } ); if (result.data?.statusCode && result.data.statusCode >= 200 && result.data.statusCode < 300) { return true; } return false; } catch { return false; } } }; }); export { httpProvider as default };