@kumix/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.
package/dist/index.js ADDED
@@ -0,0 +1,454 @@
1
+ import { htmlToText, renderEmailTemplate } from "./helpers.js";
2
+ import { Resend } from "resend";
3
+ //#region src/config.ts
4
+ function getDefaultEnv() {
5
+ if (typeof process !== "undefined" && process?.env) return process.env;
6
+ }
7
+ function resolveEnv(env) {
8
+ return env ?? getDefaultEnv();
9
+ }
10
+ function loadResendConfig(env) {
11
+ const e = resolveEnv(env);
12
+ if (!e) return null;
13
+ const apiKey = e.KUMIX_EMAIL_RESEND_API_KEY;
14
+ const fromName = e.KUMIX_EMAIL_FROM_NAME;
15
+ const fromEmail = e.KUMIX_EMAIL_FROM_EMAIL;
16
+ const replyTo = e.KUMIX_EMAIL_REPLY_TO;
17
+ if (!apiKey || !fromName || !fromEmail) return null;
18
+ return {
19
+ provider: "resend",
20
+ apiKey,
21
+ from: {
22
+ name: fromName,
23
+ email: fromEmail
24
+ },
25
+ replyTo
26
+ };
27
+ }
28
+ function loadNodemailerConfig(env) {
29
+ const e = resolveEnv(env);
30
+ if (!e) return null;
31
+ const host = e.KUMIX_EMAIL_SMTP_HOST;
32
+ const port = e.KUMIX_EMAIL_SMTP_PORT;
33
+ const secure = e.KUMIX_EMAIL_SMTP_SECURE;
34
+ const user = e.KUMIX_EMAIL_SMTP_USER;
35
+ const pass = e.KUMIX_EMAIL_SMTP_PASS;
36
+ const fromName = e.KUMIX_EMAIL_FROM_NAME;
37
+ const fromEmail = e.KUMIX_EMAIL_FROM_EMAIL;
38
+ const replyTo = e.KUMIX_EMAIL_REPLY_TO;
39
+ if (!host || !port || !user || !pass || !fromName || !fromEmail) return null;
40
+ return {
41
+ provider: "nodemailer",
42
+ from: {
43
+ name: fromName,
44
+ email: fromEmail
45
+ },
46
+ replyTo,
47
+ smtp: {
48
+ host,
49
+ port: parseInt(port, 10),
50
+ secure: secure === "true",
51
+ auth: {
52
+ user,
53
+ pass
54
+ }
55
+ }
56
+ };
57
+ }
58
+ function loadEmailConfig(env) {
59
+ const e = resolveEnv(env);
60
+ if (!e) return null;
61
+ return loadResendConfig(e) || loadNodemailerConfig(e);
62
+ }
63
+ function hasEmailConfig(env) {
64
+ return loadEmailConfig(env) !== null;
65
+ }
66
+ function getEmailEnvVars(env) {
67
+ const e = resolveEnv(env);
68
+ if (!e) return {};
69
+ return {
70
+ KUMIX_EMAIL_FROM_NAME: e.KUMIX_EMAIL_FROM_NAME,
71
+ KUMIX_EMAIL_FROM_EMAIL: e.KUMIX_EMAIL_FROM_EMAIL,
72
+ KUMIX_EMAIL_REPLY_TO: e.KUMIX_EMAIL_REPLY_TO,
73
+ KUMIX_EMAIL_RESEND_API_KEY: e.KUMIX_EMAIL_RESEND_API_KEY ? "***" : void 0,
74
+ KUMIX_EMAIL_SMTP_HOST: e.KUMIX_EMAIL_SMTP_HOST,
75
+ KUMIX_EMAIL_SMTP_PORT: e.KUMIX_EMAIL_SMTP_PORT,
76
+ KUMIX_EMAIL_SMTP_SECURE: e.KUMIX_EMAIL_SMTP_SECURE,
77
+ KUMIX_EMAIL_SMTP_USER: e.KUMIX_EMAIL_SMTP_USER,
78
+ KUMIX_EMAIL_SMTP_PASS: e.KUMIX_EMAIL_SMTP_PASS ? "***" : void 0
79
+ };
80
+ }
81
+ function validateResendEnvVars(env) {
82
+ const e = resolveEnv(env);
83
+ if (!e) return {
84
+ valid: false,
85
+ missing: ["env"],
86
+ errors: ["No environment source available"]
87
+ };
88
+ const missing = [];
89
+ const errors = [];
90
+ if (!e.KUMIX_EMAIL_RESEND_API_KEY && !e.RESEND_API_KEY) missing.push("KUMIX_EMAIL_RESEND_API_KEY or RESEND_API_KEY");
91
+ if (!e.KUMIX_EMAIL_FROM_NAME && !e.EMAIL_FROM_NAME) missing.push("KUMIX_EMAIL_FROM_NAME or EMAIL_FROM_NAME");
92
+ if (!e.KUMIX_EMAIL_FROM_EMAIL && !e.EMAIL_FROM_EMAIL) missing.push("KUMIX_EMAIL_FROM_EMAIL or EMAIL_FROM_EMAIL");
93
+ return {
94
+ valid: missing.length === 0 && errors.length === 0,
95
+ missing,
96
+ errors
97
+ };
98
+ }
99
+ function validateNodemailerEnvVars(env) {
100
+ const e = resolveEnv(env);
101
+ if (!e) return {
102
+ valid: false,
103
+ missing: ["env"],
104
+ errors: ["No environment source available"]
105
+ };
106
+ const missing = [];
107
+ const errors = [];
108
+ if (!e.KUMIX_EMAIL_SMTP_HOST && !e.SMTP_HOST) missing.push("KUMIX_EMAIL_SMTP_HOST or SMTP_HOST");
109
+ if (!e.KUMIX_EMAIL_SMTP_PORT && !e.SMTP_PORT) missing.push("KUMIX_EMAIL_SMTP_PORT or SMTP_PORT");
110
+ if (!e.KUMIX_EMAIL_SMTP_USER && !e.SMTP_USER) missing.push("KUMIX_EMAIL_SMTP_USER or SMTP_USER");
111
+ if (!e.KUMIX_EMAIL_SMTP_PASS && !e.SMTP_PASS) missing.push("KUMIX_EMAIL_SMTP_PASS or SMTP_PASS");
112
+ if (!e.KUMIX_EMAIL_FROM_NAME && !e.EMAIL_FROM_NAME) missing.push("KUMIX_EMAIL_FROM_NAME or EMAIL_FROM_NAME");
113
+ if (!e.KUMIX_EMAIL_FROM_EMAIL && !e.EMAIL_FROM_EMAIL) missing.push("KUMIX_EMAIL_FROM_EMAIL or EMAIL_FROM_EMAIL");
114
+ const port = e.KUMIX_EMAIL_SMTP_PORT || e.SMTP_PORT;
115
+ if (port && (Number.isNaN(Number(port)) || Number(port) < 1 || Number(port) > 65535)) errors.push("SMTP_PORT must be a valid port number (1-65535)");
116
+ return {
117
+ valid: missing.length === 0 && errors.length === 0,
118
+ missing,
119
+ errors
120
+ };
121
+ }
122
+ function validateEmailEnvVars(env) {
123
+ const validations = [validateResendEnvVars(env), validateNodemailerEnvVars(env)];
124
+ const validProvider = validations.find((v) => v.valid);
125
+ if (validProvider) return validProvider;
126
+ return {
127
+ valid: false,
128
+ missing: [...new Set(validations.flatMap((v) => v.missing))],
129
+ errors: [...new Set(validations.flatMap((v) => v.errors))]
130
+ };
131
+ }
132
+ //#endregion
133
+ //#region src/providers/nodemailer.ts
134
+ var NodemailerProvider = class {
135
+ config;
136
+ _transporter = null;
137
+ constructor(config) {
138
+ this.config = config;
139
+ if (!config.smtp) throw new Error("SMTP configuration is required for Nodemailer");
140
+ }
141
+ async getTransporter() {
142
+ if (this._transporter) return this._transporter;
143
+ let nodemailer;
144
+ try {
145
+ nodemailer = await import("nodemailer");
146
+ } catch {
147
+ throw new Error("nodemailer is not available in this runtime. Use a Node.js environment or provide a Resend configuration instead.");
148
+ }
149
+ const mailer = nodemailer.default ?? nodemailer;
150
+ this._transporter = mailer.createTransport({
151
+ host: this.config.smtp.host,
152
+ port: this.config.smtp.port,
153
+ secure: this.config.smtp.secure,
154
+ auth: {
155
+ user: this.config.smtp.auth.user,
156
+ pass: this.config.smtp.auth.pass
157
+ }
158
+ });
159
+ return this._transporter;
160
+ }
161
+ async validateConfig() {
162
+ try {
163
+ if (!this.config.smtp) return {
164
+ valid: false,
165
+ error: "SMTP configuration is required"
166
+ };
167
+ if (!this.config.smtp.host || !this.config.smtp.port) return {
168
+ valid: false,
169
+ error: "SMTP host and port are required"
170
+ };
171
+ if (!this.config.smtp.auth.user || !this.config.smtp.auth.pass) return {
172
+ valid: false,
173
+ error: "SMTP authentication credentials are required"
174
+ };
175
+ if (!this.config.from.email || !this.config.from.name) return {
176
+ valid: false,
177
+ error: "From email and name are required"
178
+ };
179
+ await (await this.getTransporter()).verify();
180
+ return { valid: true };
181
+ } catch (error) {
182
+ return {
183
+ valid: false,
184
+ error: error instanceof Error ? error.message : "SMTP connection failed"
185
+ };
186
+ }
187
+ }
188
+ async send(options) {
189
+ try {
190
+ return {
191
+ success: true,
192
+ messageId: (await (await this.getTransporter()).sendMail({
193
+ from: `${this.config.from.name} <${this.config.from.email}>`,
194
+ to: Array.isArray(options.to) ? options.to.join(", ") : options.to,
195
+ cc: options.cc ? Array.isArray(options.cc) ? options.cc.join(", ") : options.cc : void 0,
196
+ bcc: options.bcc ? Array.isArray(options.bcc) ? options.bcc.join(", ") : options.bcc : void 0,
197
+ subject: options.subject,
198
+ html: options.html,
199
+ text: options.text,
200
+ replyTo: this.config.replyTo,
201
+ headers: options.headers,
202
+ attachments: options.attachments?.map((a) => ({
203
+ filename: a.filename,
204
+ content: a.content,
205
+ contentType: a.contentType,
206
+ cid: a.cid
207
+ }))
208
+ })).messageId
209
+ };
210
+ } catch (error) {
211
+ return {
212
+ success: false,
213
+ error: error instanceof Error ? error.message : "Unknown error occurred"
214
+ };
215
+ }
216
+ }
217
+ };
218
+ //#endregion
219
+ //#region src/providers/resend.ts
220
+ /**
221
+ * Resend email provider implementation
222
+ * @public
223
+ *
224
+ * @example
225
+ * ```typescript
226
+ * import { ResendProvider } from '@kumix/email';
227
+ *
228
+ * const provider = new ResendProvider({
229
+ * provider: 'resend',
230
+ * apiKey: 'your-resend-api-key',
231
+ * from: {
232
+ * name: 'Your App',
233
+ * email: 'noreply@yourapp.com'
234
+ * }
235
+ * });
236
+ *
237
+ * const result = await provider.send({
238
+ * to: 'user@example.com',
239
+ * subject: 'Hello',
240
+ * html: '<h1>Hello World</h1>'
241
+ * });
242
+ *
243
+ * if (result.success) {
244
+ * console.log('Email sent with ID:', result.messageId);
245
+ * } else {
246
+ * console.error('Failed to send email:', result.error);
247
+ * }
248
+ * ```
249
+ */
250
+ var ResendProvider = class {
251
+ resend;
252
+ config;
253
+ /**
254
+ * Create a new Resend provider instance
255
+ * @param config Resend configuration including API key and sender info
256
+ * @throws Error if API key is missing
257
+ */
258
+ constructor(config) {
259
+ this.config = config;
260
+ if (!config.apiKey) throw new Error("Resend API key is required");
261
+ this.resend = new Resend(config.apiKey);
262
+ }
263
+ /**
264
+ * Validate Resend configuration
265
+ * @returns Promise resolving to validation result
266
+ * @public
267
+ *
268
+ * @example
269
+ * ```typescript
270
+ * const validation = await provider.validateConfig();
271
+ * if (!validation.valid) {
272
+ * console.error('Configuration error:', validation.error);
273
+ * } else {
274
+ * console.log('Resend configuration is valid');
275
+ * }
276
+ * ```
277
+ */
278
+ async validateConfig() {
279
+ try {
280
+ if (!this.config.apiKey) return {
281
+ valid: false,
282
+ error: "Resend API key is required"
283
+ };
284
+ if (!this.config.from.email || !this.config.from.name) return {
285
+ valid: false,
286
+ error: "From email and name are required"
287
+ };
288
+ if (!this.config.apiKey.startsWith("re_")) return {
289
+ valid: false,
290
+ error: "Invalid Resend API key format"
291
+ };
292
+ return { valid: true };
293
+ } catch (error) {
294
+ return {
295
+ valid: false,
296
+ error: error instanceof Error ? error.message : "Unknown validation error"
297
+ };
298
+ }
299
+ }
300
+ /**
301
+ * Send an email using Resend
302
+ * @param options Email sending options
303
+ * @returns Promise resolving to email sending result
304
+ * @public
305
+ *
306
+ * @example
307
+ * ```typescript
308
+ * const result = await provider.send({
309
+ * to: ['user1@example.com', 'user2@example.com'],
310
+ * cc: 'manager@example.com',
311
+ * subject: 'Important Update',
312
+ * html: '<h1>Hello</h1><p>This is an important update.</p>',
313
+ * text: 'Hello\n\nThis is an important update.',
314
+ * attachments: [{
315
+ * filename: 'document.pdf',
316
+ * content: pdfBuffer,
317
+ * contentType: 'application/pdf'
318
+ * }],
319
+ * tags: {
320
+ * category: 'notification',
321
+ * priority: 'high'
322
+ * }
323
+ * });
324
+ *
325
+ * if (result.success) {
326
+ * console.log('Email sent with ID:', result.messageId);
327
+ * } else {
328
+ * console.error('Failed to send email:', result.error);
329
+ * }
330
+ * ```
331
+ */
332
+ async send(options) {
333
+ try {
334
+ const emailData = {
335
+ from: `${this.config.from.name} <${this.config.from.email}>`,
336
+ to: Array.isArray(options.to) ? options.to : [options.to],
337
+ subject: options.subject,
338
+ text: options.text || ""
339
+ };
340
+ if (options.cc) emailData.cc = Array.isArray(options.cc) ? options.cc : [options.cc];
341
+ if (options.bcc) emailData.bcc = Array.isArray(options.bcc) ? options.bcc : [options.bcc];
342
+ if (options.html) emailData.html = options.html;
343
+ if (this.config.replyTo) emailData.replyTo = this.config.replyTo;
344
+ if (options.headers) emailData.headers = options.headers;
345
+ if (options.tags) emailData.tags = Object.entries(options.tags).map(([name, value]) => ({
346
+ name,
347
+ value: String(value)
348
+ }));
349
+ if (options.attachments) emailData.attachments = options.attachments.map((attachment) => ({
350
+ filename: attachment.filename,
351
+ content: attachment.content,
352
+ content_type: attachment.contentType,
353
+ cid: attachment.cid
354
+ }));
355
+ const { data, error } = await this.resend.emails.send(emailData);
356
+ if (error) return {
357
+ success: false,
358
+ error: error.message
359
+ };
360
+ return {
361
+ success: true,
362
+ messageId: data?.id
363
+ };
364
+ } catch (error) {
365
+ return {
366
+ success: false,
367
+ error: error instanceof Error ? error.message : "Unknown error occurred"
368
+ };
369
+ }
370
+ }
371
+ };
372
+ //#endregion
373
+ //#region src/services.ts
374
+ var EmailService = class {
375
+ provider;
376
+ config;
377
+ constructor(config) {
378
+ this.config = config;
379
+ this.provider = this.createProvider(config);
380
+ }
381
+ createProvider(config) {
382
+ switch (config.provider) {
383
+ case "resend": return new ResendProvider(config);
384
+ case "nodemailer": return new NodemailerProvider(config);
385
+ default: throw new Error(`Unsupported email provider: ${config.provider}`);
386
+ }
387
+ }
388
+ async sendEmail(options) {
389
+ return this.provider.send(options);
390
+ }
391
+ async sendTemplate(Template, props, options) {
392
+ try {
393
+ const html = await renderEmailTemplate(Template, props);
394
+ const text = htmlToText(html);
395
+ return this.sendEmail({
396
+ ...options,
397
+ html,
398
+ text
399
+ });
400
+ } catch (error) {
401
+ return {
402
+ success: false,
403
+ error: error instanceof Error ? error.message : "Failed to render template"
404
+ };
405
+ }
406
+ }
407
+ getConfig() {
408
+ return { ...this.config };
409
+ }
410
+ updateConfig(config) {
411
+ this.config = config;
412
+ this.provider = this.createProvider(config);
413
+ }
414
+ async validateConfig() {
415
+ if (this.provider.validateConfig) return this.provider.validateConfig();
416
+ return { valid: true };
417
+ }
418
+ };
419
+ //#endregion
420
+ //#region src/factory.ts
421
+ function createEmail(config, env) {
422
+ if (config) return new EmailService(config);
423
+ const envConfig = loadEmailConfig(env);
424
+ if (!envConfig) throw new Error("No email configuration provided and none found in environment variables. Please provide a config object or set environment variables like KUMIX_EMAIL_RESEND_API_KEY, etc.");
425
+ return new EmailService(envConfig);
426
+ }
427
+ function createResend(config, env) {
428
+ if (config) return createEmail({
429
+ ...config,
430
+ provider: "resend"
431
+ });
432
+ const envConfig = loadEmailConfig(env);
433
+ if (envConfig?.provider === "resend") return createEmail(envConfig);
434
+ throw new Error("No Resend configuration found. Please provide config or set environment variables: KUMIX_EMAIL_RESEND_API_KEY, KUMIX_EMAIL_FROM_NAME, KUMIX_EMAIL_FROM_EMAIL");
435
+ }
436
+ function createNodemailer(config, env) {
437
+ if (config) return createEmail({
438
+ ...config,
439
+ provider: "nodemailer"
440
+ });
441
+ const envConfig = loadEmailConfig(env);
442
+ if (envConfig?.provider === "nodemailer") return createEmail(envConfig);
443
+ throw new Error("No Nodemailer configuration found. Please provide config or set environment variables: KUMIX_EMAIL_SMTP_HOST, KUMIX_EMAIL_SMTP_PORT, KUMIX_EMAIL_SMTP_USER, KUMIX_EMAIL_SMTP_PASS, KUMIX_EMAIL_FROM_NAME, KUMIX_EMAIL_FROM_EMAIL");
444
+ }
445
+ function isEmailConfigured(env) {
446
+ return hasEmailConfig(env);
447
+ }
448
+ function getConfiguredProvider(env) {
449
+ return loadEmailConfig(env)?.provider || null;
450
+ }
451
+ //#endregion
452
+ export { EmailService, NodemailerProvider, ResendProvider, createEmail, createNodemailer, createResend, getConfiguredProvider, getEmailEnvVars, hasEmailConfig, isEmailConfigured, loadEmailConfig, loadNodemailerConfig, loadResendConfig, validateEmailEnvVars, validateNodemailerEnvVars, validateResendEnvVars };
453
+
454
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"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 const apiKey = e.KUMIX_EMAIL_RESEND_API_KEY;\n const fromName = e.KUMIX_EMAIL_FROM_NAME;\n const fromEmail = e.KUMIX_EMAIL_FROM_EMAIL;\n const replyTo = e.KUMIX_EMAIL_REPLY_TO;\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;\n const port = e.KUMIX_EMAIL_SMTP_PORT;\n const secure = e.KUMIX_EMAIL_SMTP_SECURE;\n const user = e.KUMIX_EMAIL_SMTP_USER;\n const pass = e.KUMIX_EMAIL_SMTP_PASS;\n const fromName = e.KUMIX_EMAIL_FROM_NAME;\n const fromEmail = e.KUMIX_EMAIL_FROM_EMAIL;\n const replyTo = e.KUMIX_EMAIL_REPLY_TO;\n\n if (!host || !port || !user || !pass || !fromName || !fromEmail) return null;\n\n return {\n provider: \"nodemailer\",\n from: { name: fromName, email: fromEmail },\n replyTo,\n smtp: { host, port: parseInt(port, 10), secure: secure === \"true\", auth: { user, pass } },\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 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 from: `${this.config.from.name} <${this.config.from.email}>`,\n to: Array.isArray(options.to) ? options.to.join(\", \") : options.to,\n cc: options.cc\n ? Array.isArray(options.cc)\n ? options.cc.join(\", \")\n : options.cc\n : undefined,\n bcc: options.bcc\n ? Array.isArray(options.bcc)\n ? options.bcc.join(\", \")\n : options.bcc\n : undefined,\n subject: options.subject,\n html: options.html,\n text: options.text,\n replyTo: this.config.replyTo,\n headers: options.headers,\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 } from \"resend\";\nimport { Resend } from \"resend\";\n\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 resend: Resend;\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 this.resend = new Resend(config.apiKey);\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 const emailData: CreateEmailOptions = {\n from: `${this.config.from.name} <${this.config.from.email}>`,\n to: Array.isArray(options.to) ? options.to : [options.to],\n subject: options.subject,\n text: options.text || \"\",\n };\n\n if (options.cc) {\n emailData.cc = Array.isArray(options.cc) ? options.cc : [options.cc];\n }\n\n if (options.bcc) {\n emailData.bcc = Array.isArray(options.bcc) ? options.bcc : [options.bcc];\n }\n\n if (options.html) {\n emailData.html = options.html;\n }\n\n if (this.config.replyTo) {\n emailData.replyTo = this.config.replyTo;\n }\n\n if (options.headers) {\n emailData.headers = options.headers;\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 const { data, error } = await this.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 IEmailProvider,\n NodemailerConfig,\n ResendConfig,\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 switch (config.provider) {\n case \"resend\":\n return new ResendProvider(config as ResendConfig);\n case \"nodemailer\":\n return new NodemailerProvider(config as NodemailerConfig);\n default:\n throw new Error(`Unsupported email provider: ${(config as EmailConfig).provider}`);\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 getConfig(): EmailConfig {\n return { ...this.config };\n }\n\n updateConfig(config: EmailConfig): void {\n this.config = config;\n this.provider = this.createProvider(config);\n }\n\n async validateConfig() {\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 type { EnvRecord } from \"./config\";\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 ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@kumix/email",
3
+ "version": "0.1.0",
4
+ "description": "Email templates and sending utilities for SaaS applications.",
5
+ "author": "Kumix Labs <hai@kumix.io>",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "sideEffects": false,
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./components": {
18
+ "types": "./dist/components.d.ts",
19
+ "import": "./dist/components.js"
20
+ },
21
+ "./helpers": {
22
+ "types": "./dist/helpers.d.ts",
23
+ "import": "./dist/helpers.js"
24
+ }
25
+ },
26
+ "scripts": {
27
+ "clean": "rimraf dist",
28
+ "clean:all": "rimraf .turbo coverage dist node_modules",
29
+ "dev": "tsdown --watch",
30
+ "build": "tsdown",
31
+ "types:check": "tsc --noEmit",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "test:coverage": "vitest run --coverage"
35
+ },
36
+ "homepage": "https://github.com/kumixlabs/toolkits",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/kumixlabs/toolkits.git",
40
+ "directory": "packages/email"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/kumixlabs/toolkits/issues"
44
+ },
45
+ "files": [
46
+ "dist"
47
+ ],
48
+ "keywords": [
49
+ "kumix",
50
+ "email",
51
+ "smtp",
52
+ "nodemailer",
53
+ "resend"
54
+ ],
55
+ "devDependencies": {
56
+ "@kumix/tsconfig": "^0.1.0",
57
+ "@types/node": "^26.1.0",
58
+ "@types/nodemailer": "^8.0.1",
59
+ "@types/react": "^19.2.17",
60
+ "nodemailer": "^9.0.3",
61
+ "react": "^19.2.7",
62
+ "react-email": "^6.6.6",
63
+ "resend": "^6.17.1",
64
+ "tsdown": "^0.22.3",
65
+ "typescript": "^6.0.3"
66
+ },
67
+ "peerDependencies": {
68
+ "nodemailer": ">=9.0.0",
69
+ "react": ">=18.0.0 || >=19.0.0-rc.0",
70
+ "react-email": ">=6.6.0",
71
+ "resend": ">=6.15.0"
72
+ },
73
+ "publishConfig": {
74
+ "registry": "https://registry.npmjs.org/",
75
+ "access": "public"
76
+ }
77
+ }