@kumix/email 0.1.1 → 0.1.2

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.
package/dist/index.js CHANGED
@@ -190,9 +190,14 @@ var NodemailerProvider = class {
190
190
  }
191
191
  async send(options) {
192
192
  try {
193
+ const transporter = await this.getTransporter();
194
+ const headers = {};
195
+ if (options.headers) for (const [k, v] of Object.entries(options.headers)) headers[k] = String(v).replace(/[\r\n]/g, "");
196
+ if (options.tags) for (const [name, value] of Object.entries(options.tags)) headers[`X-Tag-${name.replace(/[\r\n]/g, "")}`] = String(value).replace(/[\r\n]/g, "");
197
+ if (options.priority) headers["X-Priority"] = String(options.priority);
193
198
  return {
194
199
  success: true,
195
- messageId: (await (await this.getTransporter()).sendMail({
200
+ messageId: (await transporter.sendMail({
196
201
  from: formatEmailAddress(this.config.from.name, this.config.from.email),
197
202
  to: (Array.isArray(options.to) ? options.to : [options.to]).map(stripCrlf).join(", "),
198
203
  cc: options.cc ? (Array.isArray(options.cc) ? options.cc : [options.cc]).map(stripCrlf).join(", ") : void 0,
@@ -202,10 +207,7 @@ var NodemailerProvider = class {
202
207
  text: options.text,
203
208
  priority: options.priority === void 0 ? void 0 : options.priority <= 2 ? "high" : options.priority >= 4 ? "low" : "normal",
204
209
  replyTo: this.config.replyTo ? stripCrlf(this.config.replyTo) : void 0,
205
- headers: options.headers ? {
206
- ...Object.fromEntries(Object.entries(options.headers).map(([k, v]) => [k, String(v).replace(/[\r\n]/g, "")])),
207
- ...options.priority ? { "X-Priority": String(options.priority) } : {}
208
- } : options.priority ? { "X-Priority": String(options.priority) } : void 0,
210
+ headers: Object.keys(headers).length > 0 ? headers : void 0,
209
211
  attachments: options.attachments?.map((a) => ({
210
212
  filename: a.filename,
211
213
  content: a.content,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_exhaustive"],"sources":["../src/config.ts","../src/providers/nodemailer.ts","../src/providers/resend.ts","../src/services.ts","../src/factory.ts"],"sourcesContent":["import type { ConfigValidationResult, EmailConfig, NodemailerConfig, ResendConfig } from \"./types\";\n\n/**\n * Cross-runtime environment record.\n * Pass `process.env` on Node.js, `ctx.env` on Cloudflare Workers,\n * `Deno.env.toObject()` on Deno, or a plain object in the browser.\n */\nexport type EnvRecord = Record<string, string | undefined>;\n\nfunction getDefaultEnv(): EnvRecord | undefined {\n if (typeof process !== \"undefined\" && process?.env) {\n return process.env as EnvRecord;\n }\n return undefined;\n}\n\nfunction resolveEnv(env?: EnvRecord): EnvRecord | undefined {\n return env ?? getDefaultEnv();\n}\n\nexport function loadResendConfig(env?: EnvRecord): ResendConfig | null {\n const e = resolveEnv(env);\n if (!e) return null;\n\n // Trim before the presence check so whitespace-only values are not accepted\n // as valid (previously `\" \"` passed and produced a config with empty\n // credentials). Do NOT trim `apiKey`/`pass` — leading/trailing characters\n // can be meaningful in tokens.\n const apiKey = e.KUMIX_EMAIL_RESEND_API_KEY ?? e.RESEND_API_KEY;\n const fromName = (e.KUMIX_EMAIL_FROM_NAME ?? e.EMAIL_FROM_NAME)?.trim();\n const fromEmail = (e.KUMIX_EMAIL_FROM_EMAIL ?? e.EMAIL_FROM_EMAIL)?.trim();\n const replyTo = (e.KUMIX_EMAIL_REPLY_TO ?? e.EMAIL_REPLY_TO)?.trim();\n\n if (!apiKey || !fromName || !fromEmail) return null;\n\n return { provider: \"resend\", apiKey, from: { name: fromName, email: fromEmail }, replyTo };\n}\n\nexport function loadNodemailerConfig(env?: EnvRecord): NodemailerConfig | null {\n const e = resolveEnv(env);\n if (!e) return null;\n\n const host = (e.KUMIX_EMAIL_SMTP_HOST ?? e.SMTP_HOST)?.trim();\n const port = e.KUMIX_EMAIL_SMTP_PORT ?? e.SMTP_PORT;\n const secure = e.KUMIX_EMAIL_SMTP_SECURE ?? e.SMTP_SECURE;\n const user = e.KUMIX_EMAIL_SMTP_USER ?? e.SMTP_USER;\n const pass = e.KUMIX_EMAIL_SMTP_PASS ?? e.SMTP_PASS;\n const fromName = (e.KUMIX_EMAIL_FROM_NAME ?? e.EMAIL_FROM_NAME)?.trim();\n const fromEmail = (e.KUMIX_EMAIL_FROM_EMAIL ?? e.EMAIL_FROM_EMAIL)?.trim();\n const replyTo = (e.KUMIX_EMAIL_REPLY_TO ?? e.EMAIL_REPLY_TO)?.trim();\n\n if (!host || !port || !user || !pass || !fromName || !fromEmail) return null;\n\n const parsedPort = parseInt(port, 10);\n if (Number.isNaN(parsedPort) || parsedPort < 1 || parsedPort > 65535) return null;\n\n // Lowercase once so capitalized spellings (\"True\", \"TRUE\") also enable TLS.\n const secureNorm = typeof secure === \"string\" ? secure.toLowerCase() : \"\";\n\n return {\n provider: \"nodemailer\",\n from: { name: fromName, email: fromEmail },\n replyTo,\n smtp: {\n host,\n port: parsedPort,\n secure: secureNorm === \"true\" || secureNorm === \"1\" || secureNorm === \"yes\",\n auth: { user, pass },\n },\n };\n}\n\nexport function loadEmailConfig(env?: EnvRecord): EmailConfig | null {\n const e = resolveEnv(env);\n if (!e) return null;\n return loadResendConfig(e) || loadNodemailerConfig(e);\n}\n\nexport function hasEmailConfig(env?: EnvRecord): boolean {\n return loadEmailConfig(env) !== null;\n}\n\nexport function getEmailEnvVars(env?: EnvRecord): Record<string, string | undefined> {\n const e = resolveEnv(env);\n if (!e) return {};\n\n return {\n KUMIX_EMAIL_FROM_NAME: e.KUMIX_EMAIL_FROM_NAME,\n KUMIX_EMAIL_FROM_EMAIL: e.KUMIX_EMAIL_FROM_EMAIL,\n KUMIX_EMAIL_REPLY_TO: e.KUMIX_EMAIL_REPLY_TO,\n KUMIX_EMAIL_RESEND_API_KEY: e.KUMIX_EMAIL_RESEND_API_KEY ? \"***\" : undefined,\n KUMIX_EMAIL_SMTP_HOST: e.KUMIX_EMAIL_SMTP_HOST,\n KUMIX_EMAIL_SMTP_PORT: e.KUMIX_EMAIL_SMTP_PORT,\n KUMIX_EMAIL_SMTP_SECURE: e.KUMIX_EMAIL_SMTP_SECURE,\n KUMIX_EMAIL_SMTP_USER: e.KUMIX_EMAIL_SMTP_USER,\n KUMIX_EMAIL_SMTP_PASS: e.KUMIX_EMAIL_SMTP_PASS ? \"***\" : undefined,\n };\n}\n\nexport function validateResendEnvVars(env?: EnvRecord): ConfigValidationResult {\n const e = resolveEnv(env);\n if (!e) return { valid: false, missing: [\"env\"], errors: [\"No environment source available\"] };\n\n const missing: string[] = [];\n const errors: string[] = [];\n\n if (!e.KUMIX_EMAIL_RESEND_API_KEY && !e.RESEND_API_KEY) {\n missing.push(\"KUMIX_EMAIL_RESEND_API_KEY or RESEND_API_KEY\");\n }\n if (!e.KUMIX_EMAIL_FROM_NAME && !e.EMAIL_FROM_NAME) {\n missing.push(\"KUMIX_EMAIL_FROM_NAME or EMAIL_FROM_NAME\");\n }\n if (!e.KUMIX_EMAIL_FROM_EMAIL && !e.EMAIL_FROM_EMAIL) {\n missing.push(\"KUMIX_EMAIL_FROM_EMAIL or EMAIL_FROM_EMAIL\");\n }\n\n return { valid: missing.length === 0 && errors.length === 0, missing, errors };\n}\n\nexport function validateNodemailerEnvVars(env?: EnvRecord): ConfigValidationResult {\n const e = resolveEnv(env);\n if (!e) return { valid: false, missing: [\"env\"], errors: [\"No environment source available\"] };\n\n const missing: string[] = [];\n const errors: string[] = [];\n\n if (!e.KUMIX_EMAIL_SMTP_HOST && !e.SMTP_HOST) missing.push(\"KUMIX_EMAIL_SMTP_HOST or SMTP_HOST\");\n if (!e.KUMIX_EMAIL_SMTP_PORT && !e.SMTP_PORT) missing.push(\"KUMIX_EMAIL_SMTP_PORT or SMTP_PORT\");\n if (!e.KUMIX_EMAIL_SMTP_USER && !e.SMTP_USER) missing.push(\"KUMIX_EMAIL_SMTP_USER or SMTP_USER\");\n if (!e.KUMIX_EMAIL_SMTP_PASS && !e.SMTP_PASS) missing.push(\"KUMIX_EMAIL_SMTP_PASS or SMTP_PASS\");\n if (!e.KUMIX_EMAIL_FROM_NAME && !e.EMAIL_FROM_NAME)\n missing.push(\"KUMIX_EMAIL_FROM_NAME or EMAIL_FROM_NAME\");\n if (!e.KUMIX_EMAIL_FROM_EMAIL && !e.EMAIL_FROM_EMAIL)\n missing.push(\"KUMIX_EMAIL_FROM_EMAIL or EMAIL_FROM_EMAIL\");\n\n const port = e.KUMIX_EMAIL_SMTP_PORT || e.SMTP_PORT;\n if (port && (Number.isNaN(Number(port)) || Number(port) < 1 || Number(port) > 65535)) {\n errors.push(\"SMTP_PORT must be a valid port number (1-65535)\");\n }\n\n return { valid: missing.length === 0 && errors.length === 0, missing, errors };\n}\n\nexport function validateEmailEnvVars(env?: EnvRecord): ConfigValidationResult {\n const validations = [validateResendEnvVars(env), validateNodemailerEnvVars(env)];\n const validProvider = validations.find((v) => v.valid);\n if (validProvider) return validProvider;\n\n const missing = [...new Set(validations.flatMap((v) => v.missing))];\n const errors = [...new Set(validations.flatMap((v) => v.errors))];\n return { valid: false, missing, errors };\n}\n","/** biome-ignore-all lint/suspicious/noExplicitAny: We need to use any to avoid type errors. */\n\nimport { formatEmailAddress, stripCrlf } from \"../helpers\";\nimport type {\n EmailResult,\n EmailValidationResult,\n IEmailProvider,\n NodemailerConfig,\n SendEmailOptions,\n} from \"../types\";\n\nexport class NodemailerProvider implements IEmailProvider {\n private config: NodemailerConfig;\n private _transporter: any = null;\n\n constructor(config: NodemailerConfig) {\n this.config = config;\n if (!config.smtp) {\n throw new Error(\"SMTP configuration is required for Nodemailer\");\n }\n }\n\n private async getTransporter(): Promise<any> {\n if (this._transporter) return this._transporter;\n\n let nodemailer: any;\n try {\n nodemailer = await import(\"nodemailer\");\n } catch {\n throw new Error(\n \"nodemailer is not available in this runtime. \" +\n \"Use a Node.js environment or provide a Resend configuration instead.\",\n );\n }\n\n const mailer = nodemailer.default ?? nodemailer;\n this._transporter = mailer.createTransport({\n host: this.config.smtp.host,\n port: this.config.smtp.port,\n secure: this.config.smtp.secure,\n auth: {\n user: this.config.smtp.auth.user,\n pass: this.config.smtp.auth.pass,\n },\n });\n return this._transporter;\n }\n\n async validateConfig(): Promise<EmailValidationResult> {\n try {\n if (!this.config.smtp) {\n return { valid: false, error: \"SMTP configuration is required\" };\n }\n if (!this.config.smtp.host || !this.config.smtp.port) {\n return { valid: false, error: \"SMTP host and port are required\" };\n }\n if (!this.config.smtp.auth.user || !this.config.smtp.auth.pass) {\n return { valid: false, error: \"SMTP authentication credentials are required\" };\n }\n if (!this.config.from.email || !this.config.from.name) {\n return { valid: false, error: \"From email and name are required\" };\n }\n\n const transporter = await this.getTransporter();\n await transporter.verify();\n return { valid: true };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"SMTP connection failed\",\n };\n }\n }\n\n async send(options: SendEmailOptions): Promise<EmailResult> {\n try {\n const transporter = await this.getTransporter();\n const info: any = await transporter.sendMail({\n // Route `from` through formatEmailAddress so the display name is\n // quoted/escaped and CR/LF is stripped (header-injection hardening).\n from: formatEmailAddress(this.config.from.name, this.config.from.email),\n to: (Array.isArray(options.to) ? options.to : [options.to]).map(stripCrlf).join(\", \"),\n cc: options.cc\n ? (Array.isArray(options.cc) ? options.cc : [options.cc]).map(stripCrlf).join(\", \")\n : undefined,\n bcc: options.bcc\n ? (Array.isArray(options.bcc) ? options.bcc : [options.bcc]).map(stripCrlf).join(\", \")\n : undefined,\n subject: options.subject.replace(/[\\r\\n]/g, \"\"),\n html: options.html,\n text: options.text,\n // Forward priority (1=highest .. 5=lowest) — nodemailer supports both\n // `priority` (high/normal/low) and the X-Priority header.\n priority:\n options.priority === undefined\n ? undefined\n : options.priority <= 2\n ? \"high\"\n : options.priority >= 4\n ? \"low\"\n : \"normal\",\n replyTo: this.config.replyTo ? stripCrlf(this.config.replyTo) : undefined,\n headers: options.headers\n ? {\n ...Object.fromEntries(\n Object.entries(options.headers).map(([k, v]) => [\n k,\n String(v).replace(/[\\r\\n]/g, \"\"),\n ]),\n ),\n // Always also set the numeric X-Priority header for clients that\n // ignore the symbolic priority field.\n ...(options.priority ? { \"X-Priority\": String(options.priority) } : {}),\n }\n : options.priority\n ? { \"X-Priority\": String(options.priority) }\n : undefined,\n attachments: options.attachments?.map((a) => ({\n filename: a.filename,\n content: a.content as any,\n contentType: a.contentType,\n cid: a.cid,\n })),\n });\n\n return { success: true, messageId: info.messageId };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Unknown error occurred\",\n };\n }\n }\n}\n","/**\n * Resend email provider implementation\n * Provides email sending functionality using the Resend service\n */\n\nimport type { CreateEmailOptions, Resend } from \"resend\";\n\nimport { formatEmailAddress, stripCrlf } from \"../helpers\";\nimport type {\n EmailResult,\n EmailValidationResult,\n IEmailProvider,\n ResendConfig,\n SendEmailOptions,\n} from \"../types\";\n\n/**\n * Resend email provider implementation\n * @public\n *\n * @example\n * ```typescript\n * import { ResendProvider } from '@kumix/email';\n *\n * const provider = new ResendProvider({\n * provider: 'resend',\n * apiKey: 'your-resend-api-key',\n * from: {\n * name: 'Your App',\n * email: 'noreply@yourapp.com'\n * }\n * });\n *\n * const result = await provider.send({\n * to: 'user@example.com',\n * subject: 'Hello',\n * html: '<h1>Hello World</h1>'\n * });\n *\n * if (result.success) {\n * console.log('Email sent with ID:', result.messageId);\n * } else {\n * console.error('Failed to send email:', result.error);\n * }\n * ```\n */\nexport class ResendProvider implements IEmailProvider {\n private _client: Resend | null = null;\n private config: ResendConfig;\n\n /**\n * Create a new Resend provider instance\n * @param config Resend configuration including API key and sender info\n * @throws Error if API key is missing\n */\n constructor(config: ResendConfig) {\n this.config = config;\n\n if (!config.apiKey) {\n throw new Error(\"Resend API key is required\");\n }\n }\n\n // The SDK is loaded lazily so the `resend` peer dep isn't required at module\n // load time. This lets consumers who only use Nodemailer import the package\n // without installing `resend`.\n private async getClient(): Promise<Resend> {\n if (this._client) return this._client;\n const { Resend } = await import(\"resend\");\n this._client = new Resend(this.config.apiKey);\n return this._client;\n }\n\n /**\n * Validate Resend configuration\n * @returns Promise resolving to validation result\n * @public\n *\n * @example\n * ```typescript\n * const validation = await provider.validateConfig();\n * if (!validation.valid) {\n * console.error('Configuration error:', validation.error);\n * } else {\n * console.log('Resend configuration is valid');\n * }\n * ```\n */\n async validateConfig(): Promise<EmailValidationResult> {\n try {\n if (!this.config.apiKey) {\n return {\n valid: false,\n error: \"Resend API key is required\",\n };\n }\n\n if (!this.config.from.email || !this.config.from.name) {\n return {\n valid: false,\n error: \"From email and name are required\",\n };\n }\n\n // Test API key by making a simple request\n // Note: Resend doesn't have a dedicated validation endpoint,\n // so we'll just check if the API key format is valid\n if (!this.config.apiKey.startsWith(\"re_\")) {\n return {\n valid: false,\n error: \"Invalid Resend API key format\",\n };\n }\n\n return { valid: true };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Unknown validation error\",\n };\n }\n }\n\n /**\n * Send an email using Resend\n * @param options Email sending options\n * @returns Promise resolving to email sending result\n * @public\n *\n * @example\n * ```typescript\n * const result = await provider.send({\n * to: ['user1@example.com', 'user2@example.com'],\n * cc: 'manager@example.com',\n * subject: 'Important Update',\n * html: '<h1>Hello</h1><p>This is an important update.</p>',\n * text: 'Hello\\n\\nThis is an important update.',\n * attachments: [{\n * filename: 'document.pdf',\n * content: pdfBuffer,\n * contentType: 'application/pdf'\n * }],\n * tags: {\n * category: 'notification',\n * priority: 'high'\n * }\n * });\n *\n * if (result.success) {\n * console.log('Email sent with ID:', result.messageId);\n * } else {\n * console.error('Failed to send email:', result.error);\n * }\n * ```\n */\n async send(options: SendEmailOptions): Promise<EmailResult> {\n try {\n // Build the message body. Resend requires at least one of html/text/react/template,\n // and forcing `text: \"\"` would suppress its automatic text generation from html.\n // - When `html` is provided, send `html` (Resend derives the text part). If the\n // caller also provided an explicit `text`, send that too instead of the derived one.\n // - When no `html` is provided, fall back to `text` (empty string as the last resort).\n const base = {\n // Route `from` through formatEmailAddress so the display name is\n // quoted/escaped and CR/LF is stripped (header-injection hardening).\n from: formatEmailAddress(this.config.from.name, this.config.from.email),\n to: (Array.isArray(options.to) ? options.to : [options.to]).map(stripCrlf),\n subject: options.subject.replace(/[\\r\\n]/g, \"\"),\n };\n\n const emailData: CreateEmailOptions = options.html\n ? { ...base, html: options.html, ...(options.text ? { text: options.text } : {}) }\n : { ...base, text: options.text || \"\" };\n\n if (options.cc) {\n emailData.cc = (Array.isArray(options.cc) ? options.cc : [options.cc]).map(stripCrlf);\n }\n\n if (options.bcc) {\n emailData.bcc = (Array.isArray(options.bcc) ? options.bcc : [options.bcc]).map(stripCrlf);\n }\n\n if (this.config.replyTo) {\n emailData.replyTo = stripCrlf(this.config.replyTo);\n }\n\n if (options.headers) {\n const sanitizedHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(options.headers)) {\n sanitizedHeaders[key] = String(value).replace(/[\\r\\n]/g, \"\");\n }\n emailData.headers = sanitizedHeaders;\n }\n\n if (options.tags) {\n emailData.tags = Object.entries(options.tags).map(([name, value]) => ({\n name,\n value: String(value),\n }));\n }\n\n if (options.attachments) {\n emailData.attachments = options.attachments.map((attachment) => ({\n filename: attachment.filename,\n content: attachment.content as string | Buffer,\n content_type: attachment.contentType,\n cid: attachment.cid,\n }));\n }\n\n // Forward scheduled delivery to Resend (`scheduled_at`). Previously this\n // option was silently dropped and emails were sent immediately even when\n // the caller asked for a future delivery time.\n if (options.scheduledAt) {\n emailData.scheduledAt = options.scheduledAt.toISOString();\n }\n\n // Forward priority (1=highest .. 5=lowest) via the standard X-Priority\n // header, since Resend has no dedicated field for it.\n if (options.priority) {\n const headers = emailData.headers ?? {};\n headers[\"X-Priority\"] = String(options.priority);\n emailData.headers = headers;\n }\n\n const resend = await this.getClient();\n const { data, error } = await resend.emails.send(emailData);\n\n if (error) {\n return {\n success: false,\n error: error.message,\n };\n }\n\n return {\n success: true,\n messageId: data?.id,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Unknown error occurred\",\n };\n }\n }\n}\n","import type React from \"react\";\n\nimport { htmlToText, renderEmailTemplate } from \"./helpers\";\nimport { NodemailerProvider } from \"./providers/nodemailer\";\nimport { ResendProvider } from \"./providers/resend\";\nimport type {\n EmailConfig,\n EmailResult,\n EmailTemplateData,\n EmailValidationResult,\n IEmailProvider,\n SendEmailOptions,\n} from \"./types\";\n\nexport class EmailService {\n private provider: IEmailProvider;\n private config: EmailConfig;\n\n constructor(config: EmailConfig) {\n this.config = config;\n this.provider = this.createProvider(config);\n }\n\n private createProvider(config: EmailConfig): IEmailProvider {\n // The `switch` narrows `config` to the concrete variant; no cast needed.\n switch (config.provider) {\n case \"resend\":\n return new ResendProvider(config);\n case \"nodemailer\":\n return new NodemailerProvider(config);\n default: {\n // Exhaustiveness check — if a new provider is added to the union\n // without a case here, this assignment fails to type-check.\n const _exhaustive: never = config;\n throw new Error(`Unsupported email provider: ${(_exhaustive as EmailConfig).provider}`);\n }\n }\n }\n\n async sendEmail(options: SendEmailOptions): Promise<EmailResult> {\n return this.provider.send(options);\n }\n\n async sendTemplate<T extends EmailTemplateData>(\n Template: React.ComponentType<T>,\n props: T,\n options: Omit<SendEmailOptions, \"html\" | \"text\">,\n ): Promise<EmailResult> {\n try {\n const html = await renderEmailTemplate(Template, props);\n const text = htmlToText(html);\n\n return this.sendEmail({ ...options, html, text });\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Failed to render template\",\n };\n }\n }\n\n /**\n * Returns a deep copy of the current configuration. A shallow clone would\n * share the nested `from` / `smtp` / `auth` objects (which contain SMTP\n * credentials), letting callers accidentally mutate the service's config.\n */\n getConfig(): EmailConfig {\n return structuredClone(this.config);\n }\n\n updateConfig(config: EmailConfig): void {\n this.config = config;\n this.provider = this.createProvider(config);\n }\n\n async validateConfig(): Promise<EmailValidationResult> {\n if (this.provider.validateConfig) {\n return this.provider.validateConfig();\n }\n return { valid: true };\n }\n}\n","import { type EnvRecord, hasEmailConfig, loadEmailConfig } from \"./config\";\nimport { EmailService } from \"./services\";\nimport type { EmailConfig, EmailProvider, NodemailerConfig, ResendConfig } from \"./types\";\n\nexport function createEmail(config?: EmailConfig, env?: EnvRecord): EmailService {\n if (config) return new EmailService(config);\n\n const envConfig = loadEmailConfig(env);\n if (!envConfig) {\n throw new Error(\n \"No email configuration provided and none found in environment variables. \" +\n \"Please provide a config object or set environment variables like KUMIX_EMAIL_RESEND_API_KEY, etc.\",\n );\n }\n return new EmailService(envConfig);\n}\n\nexport function createResend(\n config?: Omit<ResendConfig, \"provider\"> & { provider?: \"resend\" },\n env?: EnvRecord,\n): EmailService {\n if (config) return createEmail({ ...config, provider: \"resend\" });\n\n const envConfig = loadEmailConfig(env);\n if (envConfig?.provider === \"resend\") return createEmail(envConfig);\n\n throw new Error(\n \"No Resend configuration found. Please provide config or set environment variables: \" +\n \"KUMIX_EMAIL_RESEND_API_KEY, KUMIX_EMAIL_FROM_NAME, KUMIX_EMAIL_FROM_EMAIL\",\n );\n}\n\nexport function createNodemailer(\n config?: Omit<NodemailerConfig, \"provider\"> & { provider?: \"nodemailer\" },\n env?: EnvRecord,\n): EmailService {\n if (config) return createEmail({ ...config, provider: \"nodemailer\" });\n\n const envConfig = loadEmailConfig(env);\n if (envConfig?.provider === \"nodemailer\") return createEmail(envConfig);\n\n throw new Error(\n \"No Nodemailer configuration found. Please provide config or set environment variables: \" +\n \"KUMIX_EMAIL_SMTP_HOST, KUMIX_EMAIL_SMTP_PORT, KUMIX_EMAIL_SMTP_USER, KUMIX_EMAIL_SMTP_PASS, KUMIX_EMAIL_FROM_NAME, KUMIX_EMAIL_FROM_EMAIL\",\n );\n}\n\nexport function isEmailConfigured(env?: EnvRecord): boolean {\n return hasEmailConfig(env);\n}\n\nexport function getConfiguredProvider(env?: EnvRecord): EmailProvider | null {\n const config = loadEmailConfig(env);\n return config?.provider || null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["_exhaustive"],"sources":["../src/config.ts","../src/providers/nodemailer.ts","../src/providers/resend.ts","../src/services.ts","../src/factory.ts"],"sourcesContent":["import type { ConfigValidationResult, EmailConfig, NodemailerConfig, ResendConfig } from \"./types\";\n\n/**\n * Cross-runtime environment record.\n * Pass `process.env` on Node.js, `ctx.env` on Cloudflare Workers,\n * `Deno.env.toObject()` on Deno, or a plain object in the browser.\n */\nexport type EnvRecord = Record<string, string | undefined>;\n\nfunction getDefaultEnv(): EnvRecord | undefined {\n if (typeof process !== \"undefined\" && process?.env) {\n return process.env as EnvRecord;\n }\n return undefined;\n}\n\nfunction resolveEnv(env?: EnvRecord): EnvRecord | undefined {\n return env ?? getDefaultEnv();\n}\n\nexport function loadResendConfig(env?: EnvRecord): ResendConfig | null {\n const e = resolveEnv(env);\n if (!e) return null;\n\n // Trim before the presence check so whitespace-only values are not accepted\n // as valid (previously `\" \"` passed and produced a config with empty\n // credentials). Do NOT trim `apiKey`/`pass` — leading/trailing characters\n // can be meaningful in tokens.\n const apiKey = e.KUMIX_EMAIL_RESEND_API_KEY ?? e.RESEND_API_KEY;\n const fromName = (e.KUMIX_EMAIL_FROM_NAME ?? e.EMAIL_FROM_NAME)?.trim();\n const fromEmail = (e.KUMIX_EMAIL_FROM_EMAIL ?? e.EMAIL_FROM_EMAIL)?.trim();\n const replyTo = (e.KUMIX_EMAIL_REPLY_TO ?? e.EMAIL_REPLY_TO)?.trim();\n\n if (!apiKey || !fromName || !fromEmail) return null;\n\n return { provider: \"resend\", apiKey, from: { name: fromName, email: fromEmail }, replyTo };\n}\n\nexport function loadNodemailerConfig(env?: EnvRecord): NodemailerConfig | null {\n const e = resolveEnv(env);\n if (!e) return null;\n\n const host = (e.KUMIX_EMAIL_SMTP_HOST ?? e.SMTP_HOST)?.trim();\n const port = e.KUMIX_EMAIL_SMTP_PORT ?? e.SMTP_PORT;\n const secure = e.KUMIX_EMAIL_SMTP_SECURE ?? e.SMTP_SECURE;\n const user = e.KUMIX_EMAIL_SMTP_USER ?? e.SMTP_USER;\n const pass = e.KUMIX_EMAIL_SMTP_PASS ?? e.SMTP_PASS;\n const fromName = (e.KUMIX_EMAIL_FROM_NAME ?? e.EMAIL_FROM_NAME)?.trim();\n const fromEmail = (e.KUMIX_EMAIL_FROM_EMAIL ?? e.EMAIL_FROM_EMAIL)?.trim();\n const replyTo = (e.KUMIX_EMAIL_REPLY_TO ?? e.EMAIL_REPLY_TO)?.trim();\n\n if (!host || !port || !user || !pass || !fromName || !fromEmail) return null;\n\n const parsedPort = parseInt(port, 10);\n if (Number.isNaN(parsedPort) || parsedPort < 1 || parsedPort > 65535) return null;\n\n // Lowercase once so capitalized spellings (\"True\", \"TRUE\") also enable TLS.\n const secureNorm = typeof secure === \"string\" ? secure.toLowerCase() : \"\";\n\n return {\n provider: \"nodemailer\",\n from: { name: fromName, email: fromEmail },\n replyTo,\n smtp: {\n host,\n port: parsedPort,\n secure: secureNorm === \"true\" || secureNorm === \"1\" || secureNorm === \"yes\",\n auth: { user, pass },\n },\n };\n}\n\nexport function loadEmailConfig(env?: EnvRecord): EmailConfig | null {\n const e = resolveEnv(env);\n if (!e) return null;\n return loadResendConfig(e) || loadNodemailerConfig(e);\n}\n\nexport function hasEmailConfig(env?: EnvRecord): boolean {\n return loadEmailConfig(env) !== null;\n}\n\nexport function getEmailEnvVars(env?: EnvRecord): Record<string, string | undefined> {\n const e = resolveEnv(env);\n if (!e) return {};\n\n return {\n KUMIX_EMAIL_FROM_NAME: e.KUMIX_EMAIL_FROM_NAME,\n KUMIX_EMAIL_FROM_EMAIL: e.KUMIX_EMAIL_FROM_EMAIL,\n KUMIX_EMAIL_REPLY_TO: e.KUMIX_EMAIL_REPLY_TO,\n KUMIX_EMAIL_RESEND_API_KEY: e.KUMIX_EMAIL_RESEND_API_KEY ? \"***\" : undefined,\n KUMIX_EMAIL_SMTP_HOST: e.KUMIX_EMAIL_SMTP_HOST,\n KUMIX_EMAIL_SMTP_PORT: e.KUMIX_EMAIL_SMTP_PORT,\n KUMIX_EMAIL_SMTP_SECURE: e.KUMIX_EMAIL_SMTP_SECURE,\n KUMIX_EMAIL_SMTP_USER: e.KUMIX_EMAIL_SMTP_USER,\n KUMIX_EMAIL_SMTP_PASS: e.KUMIX_EMAIL_SMTP_PASS ? \"***\" : undefined,\n };\n}\n\nexport function validateResendEnvVars(env?: EnvRecord): ConfigValidationResult {\n const e = resolveEnv(env);\n if (!e) return { valid: false, missing: [\"env\"], errors: [\"No environment source available\"] };\n\n const missing: string[] = [];\n const errors: string[] = [];\n\n if (!e.KUMIX_EMAIL_RESEND_API_KEY && !e.RESEND_API_KEY) {\n missing.push(\"KUMIX_EMAIL_RESEND_API_KEY or RESEND_API_KEY\");\n }\n if (!e.KUMIX_EMAIL_FROM_NAME && !e.EMAIL_FROM_NAME) {\n missing.push(\"KUMIX_EMAIL_FROM_NAME or EMAIL_FROM_NAME\");\n }\n if (!e.KUMIX_EMAIL_FROM_EMAIL && !e.EMAIL_FROM_EMAIL) {\n missing.push(\"KUMIX_EMAIL_FROM_EMAIL or EMAIL_FROM_EMAIL\");\n }\n\n return { valid: missing.length === 0 && errors.length === 0, missing, errors };\n}\n\nexport function validateNodemailerEnvVars(env?: EnvRecord): ConfigValidationResult {\n const e = resolveEnv(env);\n if (!e) return { valid: false, missing: [\"env\"], errors: [\"No environment source available\"] };\n\n const missing: string[] = [];\n const errors: string[] = [];\n\n if (!e.KUMIX_EMAIL_SMTP_HOST && !e.SMTP_HOST) missing.push(\"KUMIX_EMAIL_SMTP_HOST or SMTP_HOST\");\n if (!e.KUMIX_EMAIL_SMTP_PORT && !e.SMTP_PORT) missing.push(\"KUMIX_EMAIL_SMTP_PORT or SMTP_PORT\");\n if (!e.KUMIX_EMAIL_SMTP_USER && !e.SMTP_USER) missing.push(\"KUMIX_EMAIL_SMTP_USER or SMTP_USER\");\n if (!e.KUMIX_EMAIL_SMTP_PASS && !e.SMTP_PASS) missing.push(\"KUMIX_EMAIL_SMTP_PASS or SMTP_PASS\");\n if (!e.KUMIX_EMAIL_FROM_NAME && !e.EMAIL_FROM_NAME)\n missing.push(\"KUMIX_EMAIL_FROM_NAME or EMAIL_FROM_NAME\");\n if (!e.KUMIX_EMAIL_FROM_EMAIL && !e.EMAIL_FROM_EMAIL)\n missing.push(\"KUMIX_EMAIL_FROM_EMAIL or EMAIL_FROM_EMAIL\");\n\n const port = e.KUMIX_EMAIL_SMTP_PORT || e.SMTP_PORT;\n if (port && (Number.isNaN(Number(port)) || Number(port) < 1 || Number(port) > 65535)) {\n errors.push(\"SMTP_PORT must be a valid port number (1-65535)\");\n }\n\n return { valid: missing.length === 0 && errors.length === 0, missing, errors };\n}\n\nexport function validateEmailEnvVars(env?: EnvRecord): ConfigValidationResult {\n const validations = [validateResendEnvVars(env), validateNodemailerEnvVars(env)];\n const validProvider = validations.find((v) => v.valid);\n if (validProvider) return validProvider;\n\n const missing = [...new Set(validations.flatMap((v) => v.missing))];\n const errors = [...new Set(validations.flatMap((v) => v.errors))];\n return { valid: false, missing, errors };\n}\n","/** biome-ignore-all lint/suspicious/noExplicitAny: We need to use any to avoid type errors. */\n\nimport { formatEmailAddress, stripCrlf } from \"../helpers\";\nimport type {\n EmailResult,\n EmailValidationResult,\n IEmailProvider,\n NodemailerConfig,\n SendEmailOptions,\n} from \"../types\";\n\nexport class NodemailerProvider implements IEmailProvider {\n private config: NodemailerConfig;\n private _transporter: any = null;\n\n constructor(config: NodemailerConfig) {\n this.config = config;\n if (!config.smtp) {\n throw new Error(\"SMTP configuration is required for Nodemailer\");\n }\n }\n\n private async getTransporter(): Promise<any> {\n if (this._transporter) return this._transporter;\n\n let nodemailer: any;\n try {\n nodemailer = await import(\"nodemailer\");\n } catch {\n throw new Error(\n \"nodemailer is not available in this runtime. \" +\n \"Use a Node.js environment or provide a Resend configuration instead.\",\n );\n }\n\n const mailer = nodemailer.default ?? nodemailer;\n this._transporter = mailer.createTransport({\n host: this.config.smtp.host,\n port: this.config.smtp.port,\n secure: this.config.smtp.secure,\n auth: {\n user: this.config.smtp.auth.user,\n pass: this.config.smtp.auth.pass,\n },\n });\n return this._transporter;\n }\n\n async validateConfig(): Promise<EmailValidationResult> {\n try {\n if (!this.config.smtp) {\n return { valid: false, error: \"SMTP configuration is required\" };\n }\n if (!this.config.smtp.host || !this.config.smtp.port) {\n return { valid: false, error: \"SMTP host and port are required\" };\n }\n if (!this.config.smtp.auth.user || !this.config.smtp.auth.pass) {\n return { valid: false, error: \"SMTP authentication credentials are required\" };\n }\n if (!this.config.from.email || !this.config.from.name) {\n return { valid: false, error: \"From email and name are required\" };\n }\n\n const transporter = await this.getTransporter();\n await transporter.verify();\n return { valid: true };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"SMTP connection failed\",\n };\n }\n }\n\n async send(options: SendEmailOptions): Promise<EmailResult> {\n try {\n const transporter = await this.getTransporter();\n\n // Build the outgoing headers up front so tags/priority can be merged in a\n // single place. SMTP has no native tag concept, so forward each tag as an\n // `X-Tag-<name>` header instead of silently dropping it (Resend forwards\n // tags natively).\n const headers: Record<string, string> = {};\n if (options.headers) {\n for (const [k, v] of Object.entries(options.headers)) {\n headers[k] = String(v).replace(/[\\r\\n]/g, \"\");\n }\n }\n if (options.tags) {\n for (const [name, value] of Object.entries(options.tags)) {\n headers[`X-Tag-${name.replace(/[\\r\\n]/g, \"\")}`] = String(value).replace(/[\\r\\n]/g, \"\");\n }\n }\n // Always also set the numeric X-Priority header for clients that ignore\n // the symbolic priority field.\n if (options.priority) {\n headers[\"X-Priority\"] = String(options.priority);\n }\n\n const info: any = await transporter.sendMail({\n // Route `from` through formatEmailAddress so the display name is\n // quoted/escaped and CR/LF is stripped (header-injection hardening).\n from: formatEmailAddress(this.config.from.name, this.config.from.email),\n to: (Array.isArray(options.to) ? options.to : [options.to]).map(stripCrlf).join(\", \"),\n cc: options.cc\n ? (Array.isArray(options.cc) ? options.cc : [options.cc]).map(stripCrlf).join(\", \")\n : undefined,\n bcc: options.bcc\n ? (Array.isArray(options.bcc) ? options.bcc : [options.bcc]).map(stripCrlf).join(\", \")\n : undefined,\n subject: options.subject.replace(/[\\r\\n]/g, \"\"),\n html: options.html,\n text: options.text,\n // Forward priority (1=highest .. 5=lowest) — nodemailer supports both\n // `priority` (high/normal/low) and the X-Priority header.\n priority:\n options.priority === undefined\n ? undefined\n : options.priority <= 2\n ? \"high\"\n : options.priority >= 4\n ? \"low\"\n : \"normal\",\n replyTo: this.config.replyTo ? stripCrlf(this.config.replyTo) : undefined,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n attachments: options.attachments?.map((a) => ({\n filename: a.filename,\n content: a.content as any,\n contentType: a.contentType,\n cid: a.cid,\n })),\n });\n\n return { success: true, messageId: info.messageId };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Unknown error occurred\",\n };\n }\n }\n}\n","/**\n * Resend email provider implementation\n * Provides email sending functionality using the Resend service\n */\n\nimport type { CreateEmailOptions, Resend } from \"resend\";\n\nimport { formatEmailAddress, stripCrlf } from \"../helpers\";\nimport type {\n EmailResult,\n EmailValidationResult,\n IEmailProvider,\n ResendConfig,\n SendEmailOptions,\n} from \"../types\";\n\n/**\n * Resend email provider implementation\n * @public\n *\n * @example\n * ```typescript\n * import { ResendProvider } from '@kumix/email';\n *\n * const provider = new ResendProvider({\n * provider: 'resend',\n * apiKey: 'your-resend-api-key',\n * from: {\n * name: 'Your App',\n * email: 'noreply@yourapp.com'\n * }\n * });\n *\n * const result = await provider.send({\n * to: 'user@example.com',\n * subject: 'Hello',\n * html: '<h1>Hello World</h1>'\n * });\n *\n * if (result.success) {\n * console.log('Email sent with ID:', result.messageId);\n * } else {\n * console.error('Failed to send email:', result.error);\n * }\n * ```\n */\nexport class ResendProvider implements IEmailProvider {\n private _client: Resend | null = null;\n private config: ResendConfig;\n\n /**\n * Create a new Resend provider instance\n * @param config Resend configuration including API key and sender info\n * @throws Error if API key is missing\n */\n constructor(config: ResendConfig) {\n this.config = config;\n\n if (!config.apiKey) {\n throw new Error(\"Resend API key is required\");\n }\n }\n\n // The SDK is loaded lazily so the `resend` peer dep isn't required at module\n // load time. This lets consumers who only use Nodemailer import the package\n // without installing `resend`.\n private async getClient(): Promise<Resend> {\n if (this._client) return this._client;\n const { Resend } = await import(\"resend\");\n this._client = new Resend(this.config.apiKey);\n return this._client;\n }\n\n /**\n * Validate Resend configuration\n * @returns Promise resolving to validation result\n * @public\n *\n * @example\n * ```typescript\n * const validation = await provider.validateConfig();\n * if (!validation.valid) {\n * console.error('Configuration error:', validation.error);\n * } else {\n * console.log('Resend configuration is valid');\n * }\n * ```\n */\n async validateConfig(): Promise<EmailValidationResult> {\n try {\n if (!this.config.apiKey) {\n return {\n valid: false,\n error: \"Resend API key is required\",\n };\n }\n\n if (!this.config.from.email || !this.config.from.name) {\n return {\n valid: false,\n error: \"From email and name are required\",\n };\n }\n\n // Resend has no dedicated credential-verification endpoint (unlike SMTP's\n // `transporter.verify()`), so validation is limited to a format check on\n // the API key. This intentionally does NOT contact the Resend API.\n if (!this.config.apiKey.startsWith(\"re_\")) {\n return {\n valid: false,\n error: \"Invalid Resend API key format\",\n };\n }\n\n return { valid: true };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Unknown validation error\",\n };\n }\n }\n\n /**\n * Send an email using Resend\n * @param options Email sending options\n * @returns Promise resolving to email sending result\n * @public\n *\n * @example\n * ```typescript\n * const result = await provider.send({\n * to: ['user1@example.com', 'user2@example.com'],\n * cc: 'manager@example.com',\n * subject: 'Important Update',\n * html: '<h1>Hello</h1><p>This is an important update.</p>',\n * text: 'Hello\\n\\nThis is an important update.',\n * attachments: [{\n * filename: 'document.pdf',\n * content: pdfBuffer,\n * contentType: 'application/pdf'\n * }],\n * tags: {\n * category: 'notification',\n * priority: 'high'\n * }\n * });\n *\n * if (result.success) {\n * console.log('Email sent with ID:', result.messageId);\n * } else {\n * console.error('Failed to send email:', result.error);\n * }\n * ```\n */\n async send(options: SendEmailOptions): Promise<EmailResult> {\n try {\n // Build the message body. Resend requires at least one of html/text/react/template,\n // and forcing `text: \"\"` would suppress its automatic text generation from html.\n // - When `html` is provided, send `html` (Resend derives the text part). If the\n // caller also provided an explicit `text`, send that too instead of the derived one.\n // - When no `html` is provided, fall back to `text` (empty string as the last resort).\n const base = {\n // Route `from` through formatEmailAddress so the display name is\n // quoted/escaped and CR/LF is stripped (header-injection hardening).\n from: formatEmailAddress(this.config.from.name, this.config.from.email),\n to: (Array.isArray(options.to) ? options.to : [options.to]).map(stripCrlf),\n subject: options.subject.replace(/[\\r\\n]/g, \"\"),\n };\n\n const emailData: CreateEmailOptions = options.html\n ? { ...base, html: options.html, ...(options.text ? { text: options.text } : {}) }\n : { ...base, text: options.text || \"\" };\n\n if (options.cc) {\n emailData.cc = (Array.isArray(options.cc) ? options.cc : [options.cc]).map(stripCrlf);\n }\n\n if (options.bcc) {\n emailData.bcc = (Array.isArray(options.bcc) ? options.bcc : [options.bcc]).map(stripCrlf);\n }\n\n if (this.config.replyTo) {\n emailData.replyTo = stripCrlf(this.config.replyTo);\n }\n\n if (options.headers) {\n const sanitizedHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(options.headers)) {\n sanitizedHeaders[key] = String(value).replace(/[\\r\\n]/g, \"\");\n }\n emailData.headers = sanitizedHeaders;\n }\n\n if (options.tags) {\n emailData.tags = Object.entries(options.tags).map(([name, value]) => ({\n name,\n value: String(value),\n }));\n }\n\n if (options.attachments) {\n emailData.attachments = options.attachments.map((attachment) => ({\n filename: attachment.filename,\n content: attachment.content as string | Buffer,\n content_type: attachment.contentType,\n cid: attachment.cid,\n }));\n }\n\n // Forward scheduled delivery to Resend (`scheduled_at`). Previously this\n // option was silently dropped and emails were sent immediately even when\n // the caller asked for a future delivery time.\n if (options.scheduledAt) {\n emailData.scheduledAt = options.scheduledAt.toISOString();\n }\n\n // Forward priority (1=highest .. 5=lowest) via the standard X-Priority\n // header, since Resend has no dedicated field for it.\n if (options.priority) {\n const headers = emailData.headers ?? {};\n headers[\"X-Priority\"] = String(options.priority);\n emailData.headers = headers;\n }\n\n const resend = await this.getClient();\n const { data, error } = await resend.emails.send(emailData);\n\n if (error) {\n return {\n success: false,\n error: error.message,\n };\n }\n\n return {\n success: true,\n messageId: data?.id,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Unknown error occurred\",\n };\n }\n }\n}\n","import type React from \"react\";\n\nimport { htmlToText, renderEmailTemplate } from \"./helpers\";\nimport { NodemailerProvider } from \"./providers/nodemailer\";\nimport { ResendProvider } from \"./providers/resend\";\nimport type {\n EmailConfig,\n EmailResult,\n EmailTemplateData,\n EmailValidationResult,\n IEmailProvider,\n SendEmailOptions,\n} from \"./types\";\n\nexport class EmailService {\n private provider: IEmailProvider;\n private config: EmailConfig;\n\n constructor(config: EmailConfig) {\n this.config = config;\n this.provider = this.createProvider(config);\n }\n\n private createProvider(config: EmailConfig): IEmailProvider {\n // The `switch` narrows `config` to the concrete variant; no cast needed.\n switch (config.provider) {\n case \"resend\":\n return new ResendProvider(config);\n case \"nodemailer\":\n return new NodemailerProvider(config);\n default: {\n // Exhaustiveness check — if a new provider is added to the union\n // without a case here, this assignment fails to type-check.\n const _exhaustive: never = config;\n throw new Error(`Unsupported email provider: ${(_exhaustive as EmailConfig).provider}`);\n }\n }\n }\n\n async sendEmail(options: SendEmailOptions): Promise<EmailResult> {\n return this.provider.send(options);\n }\n\n async sendTemplate<T extends EmailTemplateData>(\n Template: React.ComponentType<T>,\n props: T,\n options: Omit<SendEmailOptions, \"html\" | \"text\">,\n ): Promise<EmailResult> {\n try {\n const html = await renderEmailTemplate(Template, props);\n const text = htmlToText(html);\n\n return this.sendEmail({ ...options, html, text });\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Failed to render template\",\n };\n }\n }\n\n /**\n * Returns a deep copy of the current configuration. A shallow clone would\n * share the nested `from` / `smtp` / `auth` objects (which contain SMTP\n * credentials), letting callers accidentally mutate the service's config.\n */\n getConfig(): EmailConfig {\n return structuredClone(this.config);\n }\n\n updateConfig(config: EmailConfig): void {\n this.config = config;\n this.provider = this.createProvider(config);\n }\n\n async validateConfig(): Promise<EmailValidationResult> {\n if (this.provider.validateConfig) {\n return this.provider.validateConfig();\n }\n return { valid: true };\n }\n}\n","import { type EnvRecord, hasEmailConfig, loadEmailConfig } from \"./config\";\nimport { EmailService } from \"./services\";\nimport type { EmailConfig, EmailProvider, NodemailerConfig, ResendConfig } from \"./types\";\n\nexport function createEmail(config?: EmailConfig, env?: EnvRecord): EmailService {\n if (config) return new EmailService(config);\n\n const envConfig = loadEmailConfig(env);\n if (!envConfig) {\n throw new Error(\n \"No email configuration provided and none found in environment variables. \" +\n \"Please provide a config object or set environment variables like KUMIX_EMAIL_RESEND_API_KEY, etc.\",\n );\n }\n return new EmailService(envConfig);\n}\n\nexport function createResend(\n config?: Omit<ResendConfig, \"provider\"> & { provider?: \"resend\" },\n env?: EnvRecord,\n): EmailService {\n if (config) return createEmail({ ...config, provider: \"resend\" });\n\n const envConfig = loadEmailConfig(env);\n if (envConfig?.provider === \"resend\") return createEmail(envConfig);\n\n throw new Error(\n \"No Resend configuration found. Please provide config or set environment variables: \" +\n \"KUMIX_EMAIL_RESEND_API_KEY, KUMIX_EMAIL_FROM_NAME, KUMIX_EMAIL_FROM_EMAIL\",\n );\n}\n\nexport function createNodemailer(\n config?: Omit<NodemailerConfig, \"provider\"> & { provider?: \"nodemailer\" },\n env?: EnvRecord,\n): EmailService {\n if (config) return createEmail({ ...config, provider: \"nodemailer\" });\n\n const envConfig = loadEmailConfig(env);\n if (envConfig?.provider === \"nodemailer\") return createEmail(envConfig);\n\n throw new Error(\n \"No Nodemailer configuration found. Please provide config or set environment variables: \" +\n \"KUMIX_EMAIL_SMTP_HOST, KUMIX_EMAIL_SMTP_PORT, KUMIX_EMAIL_SMTP_USER, KUMIX_EMAIL_SMTP_PASS, KUMIX_EMAIL_FROM_NAME, KUMIX_EMAIL_FROM_EMAIL\",\n );\n}\n\nexport function isEmailConfigured(env?: EnvRecord): boolean {\n return hasEmailConfig(env);\n}\n\nexport function getConfiguredProvider(env?: EnvRecord): EmailProvider | null {\n const config = loadEmailConfig(env);\n return config?.provider || null;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kumix/email",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Email templates and sending utilities for SaaS applications.",
5
5
  "author": "Kumix Labs <hai@kumix.io>",
6
6
  "license": "MIT",