@shogo-ai/email 1.4.1

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,109 @@
1
+ import { b as EmailConfig, a as EmailAddress, g as IEmailService } from './types-CCHryvHe.cjs';
2
+ export { f as EmailResult, E as EmailTemplate, S as SendEmailParams, h as SendTemplateParams } from './types-CCHryvHe.cjs';
3
+
4
+ /**
5
+ * Email Service - Server Side
6
+ *
7
+ * Main entry point for server-side email functionality.
8
+ * Use this in server functions, API routes, etc.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { createEmail } from '@shogo-ai/sdk/email/server'
13
+ *
14
+ * // Auto-configured from environment variables
15
+ * const email = createEmail()
16
+ *
17
+ * // Send a templated email
18
+ * await email.sendTemplate({
19
+ * to: 'user@example.com',
20
+ * template: 'welcome',
21
+ * data: { name: 'Alice', appName: 'MyApp' },
22
+ * })
23
+ *
24
+ * // Send raw email
25
+ * await email.send({
26
+ * to: 'user@example.com',
27
+ * subject: 'Hello!',
28
+ * html: '<h1>Hello World</h1>',
29
+ * })
30
+ * ```
31
+ */
32
+
33
+ /**
34
+ * Options for creating an email service
35
+ */
36
+ interface CreateEmailOptions {
37
+ /**
38
+ * Email provider configuration.
39
+ * If not provided, auto-detects from environment variables.
40
+ */
41
+ config?: EmailConfig;
42
+ /**
43
+ * Default sender address.
44
+ * Can also be set via EMAIL_FROM or SMTP_FROM_EMAIL env var.
45
+ */
46
+ defaultFrom?: EmailAddress;
47
+ /**
48
+ * Include built-in templates (welcome, password-reset, invitation, notification).
49
+ * Default: true
50
+ */
51
+ includeBuiltins?: boolean;
52
+ }
53
+ /**
54
+ * Create an email service instance.
55
+ *
56
+ * If no config is provided, auto-detects provider from environment variables:
57
+ * - SMTP: SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD
58
+ * - SES: AWS_REGION or SES_REGION
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * // Auto-configured from env
63
+ * const email = createEmail()
64
+ *
65
+ * // With explicit SMTP config
66
+ * const email = createEmail({
67
+ * config: {
68
+ * provider: 'smtp',
69
+ * defaultFrom: 'noreply@myapp.com',
70
+ * smtp: {
71
+ * host: 'smtp.example.com',
72
+ * port: 587,
73
+ * user: 'user',
74
+ * password: 'pass',
75
+ * },
76
+ * },
77
+ * })
78
+ *
79
+ * // With SES config
80
+ * const email = createEmail({
81
+ * config: {
82
+ * provider: 'ses',
83
+ * defaultFrom: 'noreply@myapp.com',
84
+ * ses: {
85
+ * region: 'us-east-1',
86
+ * },
87
+ * },
88
+ * })
89
+ * ```
90
+ */
91
+ declare function createEmail(options?: CreateEmailOptions): IEmailService;
92
+ /**
93
+ * Create an email service, returning null if not configured.
94
+ * Useful for optional email functionality.
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * const email = createEmailOptional()
99
+ *
100
+ * if (email) {
101
+ * await email.send({ ... })
102
+ * } else {
103
+ * console.log('Email not configured, skipping')
104
+ * }
105
+ * ```
106
+ */
107
+ declare function createEmailOptional(options?: CreateEmailOptions): IEmailService | null;
108
+
109
+ export { type CreateEmailOptions, EmailConfig, IEmailService, createEmail, createEmailOptional };
@@ -0,0 +1,109 @@
1
+ import { b as EmailConfig, a as EmailAddress, g as IEmailService } from './types-CCHryvHe.js';
2
+ export { f as EmailResult, E as EmailTemplate, S as SendEmailParams, h as SendTemplateParams } from './types-CCHryvHe.js';
3
+
4
+ /**
5
+ * Email Service - Server Side
6
+ *
7
+ * Main entry point for server-side email functionality.
8
+ * Use this in server functions, API routes, etc.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { createEmail } from '@shogo-ai/sdk/email/server'
13
+ *
14
+ * // Auto-configured from environment variables
15
+ * const email = createEmail()
16
+ *
17
+ * // Send a templated email
18
+ * await email.sendTemplate({
19
+ * to: 'user@example.com',
20
+ * template: 'welcome',
21
+ * data: { name: 'Alice', appName: 'MyApp' },
22
+ * })
23
+ *
24
+ * // Send raw email
25
+ * await email.send({
26
+ * to: 'user@example.com',
27
+ * subject: 'Hello!',
28
+ * html: '<h1>Hello World</h1>',
29
+ * })
30
+ * ```
31
+ */
32
+
33
+ /**
34
+ * Options for creating an email service
35
+ */
36
+ interface CreateEmailOptions {
37
+ /**
38
+ * Email provider configuration.
39
+ * If not provided, auto-detects from environment variables.
40
+ */
41
+ config?: EmailConfig;
42
+ /**
43
+ * Default sender address.
44
+ * Can also be set via EMAIL_FROM or SMTP_FROM_EMAIL env var.
45
+ */
46
+ defaultFrom?: EmailAddress;
47
+ /**
48
+ * Include built-in templates (welcome, password-reset, invitation, notification).
49
+ * Default: true
50
+ */
51
+ includeBuiltins?: boolean;
52
+ }
53
+ /**
54
+ * Create an email service instance.
55
+ *
56
+ * If no config is provided, auto-detects provider from environment variables:
57
+ * - SMTP: SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD
58
+ * - SES: AWS_REGION or SES_REGION
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * // Auto-configured from env
63
+ * const email = createEmail()
64
+ *
65
+ * // With explicit SMTP config
66
+ * const email = createEmail({
67
+ * config: {
68
+ * provider: 'smtp',
69
+ * defaultFrom: 'noreply@myapp.com',
70
+ * smtp: {
71
+ * host: 'smtp.example.com',
72
+ * port: 587,
73
+ * user: 'user',
74
+ * password: 'pass',
75
+ * },
76
+ * },
77
+ * })
78
+ *
79
+ * // With SES config
80
+ * const email = createEmail({
81
+ * config: {
82
+ * provider: 'ses',
83
+ * defaultFrom: 'noreply@myapp.com',
84
+ * ses: {
85
+ * region: 'us-east-1',
86
+ * },
87
+ * },
88
+ * })
89
+ * ```
90
+ */
91
+ declare function createEmail(options?: CreateEmailOptions): IEmailService;
92
+ /**
93
+ * Create an email service, returning null if not configured.
94
+ * Useful for optional email functionality.
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * const email = createEmailOptional()
99
+ *
100
+ * if (email) {
101
+ * await email.send({ ... })
102
+ * } else {
103
+ * console.log('Email not configured, skipping')
104
+ * }
105
+ * ```
106
+ */
107
+ declare function createEmailOptional(options?: CreateEmailOptions): IEmailService | null;
108
+
109
+ export { type CreateEmailOptions, EmailConfig, IEmailService, createEmail, createEmailOptional };
package/dist/server.js ADDED
@@ -0,0 +1,404 @@
1
+ import {
2
+ EmailError,
3
+ EmailTemplateRegistry,
4
+ createShogoTemplateRegistry,
5
+ formatEmailAddress
6
+ } from "./chunk-VPWVZHAC.js";
7
+
8
+ // src/providers/smtp.ts
9
+ var SmtpProvider = class {
10
+ // nodemailer.Transporter - lazy loaded
11
+ constructor(config) {
12
+ this.validateConfig(config);
13
+ this.config = config;
14
+ }
15
+ /**
16
+ * Validate required config fields
17
+ */
18
+ validateConfig(config) {
19
+ const required = ["host", "port", "user", "password"];
20
+ for (const field of required) {
21
+ if (!config[field]) {
22
+ throw EmailError.configMissing(`smtp.${field}`);
23
+ }
24
+ }
25
+ }
26
+ /**
27
+ * Lazy-load nodemailer and create transporter
28
+ */
29
+ async getTransporter() {
30
+ if (this.transporter) {
31
+ return this.transporter;
32
+ }
33
+ const nodemailer = await import("nodemailer");
34
+ this.transporter = nodemailer.createTransport({
35
+ host: this.config.host,
36
+ port: this.config.port,
37
+ secure: this.config.secure ?? this.config.port === 465,
38
+ auth: {
39
+ user: this.config.user,
40
+ pass: this.config.password
41
+ }
42
+ });
43
+ return this.transporter;
44
+ }
45
+ /**
46
+ * Format recipients for nodemailer
47
+ */
48
+ formatRecipients(addresses) {
49
+ const list = Array.isArray(addresses) ? addresses : [addresses];
50
+ return list.map(formatEmailAddress).join(", ");
51
+ }
52
+ /**
53
+ * Strip HTML tags to create plain text version
54
+ */
55
+ stripHtml(html) {
56
+ return html.replace(/<style[^>]*>.*?<\/style>/gi, "").replace(/<script[^>]*>.*?<\/script>/gi, "").replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
57
+ }
58
+ /**
59
+ * Check if provider is configured
60
+ */
61
+ isConfigured() {
62
+ return !!(this.config.host && this.config.port && this.config.user && this.config.password);
63
+ }
64
+ /**
65
+ * Send an email via SMTP
66
+ */
67
+ async send(params) {
68
+ const { to, subject, html, text, from, replyTo, cc, bcc } = params;
69
+ if (!from) {
70
+ throw EmailError.configMissing("from address");
71
+ }
72
+ try {
73
+ const transporter = await this.getTransporter();
74
+ const mailOptions = {
75
+ from: formatEmailAddress(from),
76
+ to: this.formatRecipients(to),
77
+ subject,
78
+ html,
79
+ text: text || this.stripHtml(html)
80
+ };
81
+ if (replyTo) {
82
+ mailOptions.replyTo = formatEmailAddress(replyTo);
83
+ }
84
+ if (cc) {
85
+ mailOptions.cc = this.formatRecipients(cc);
86
+ }
87
+ if (bcc) {
88
+ mailOptions.bcc = this.formatRecipients(bcc);
89
+ }
90
+ const info = await transporter.sendMail(mailOptions);
91
+ return {
92
+ success: true,
93
+ messageId: info.messageId
94
+ };
95
+ } catch (error) {
96
+ const errorMessage = error.message || "Failed to send email";
97
+ const errorCode = this.mapErrorCode(error);
98
+ return {
99
+ success: false,
100
+ error: `[${errorCode}] ${errorMessage}`
101
+ };
102
+ }
103
+ }
104
+ /**
105
+ * Map nodemailer error to error code
106
+ */
107
+ mapErrorCode(error) {
108
+ const code = error.code || error.errno || "";
109
+ const message = (error.message || "").toLowerCase();
110
+ if (code === "ECONNREFUSED" || code === "ENOTFOUND" || message.includes("connection")) {
111
+ return "connection_error";
112
+ }
113
+ if (code === "EAUTH" || message.includes("auth") || message.includes("credentials")) {
114
+ return "authentication_error";
115
+ }
116
+ if (message.includes("recipient") || message.includes("mailbox") || message.includes("not found")) {
117
+ return "invalid_recipient";
118
+ }
119
+ return "send_failed";
120
+ }
121
+ };
122
+ function createSmtpProviderFromEnv() {
123
+ const host = process.env.SMTP_HOST;
124
+ const port = process.env.SMTP_PORT;
125
+ const user = process.env.SMTP_USER;
126
+ const password = process.env.SMTP_PASSWORD;
127
+ if (!host || !port || !user || !password) {
128
+ return null;
129
+ }
130
+ return new SmtpProvider({
131
+ host,
132
+ port: parseInt(port, 10),
133
+ user,
134
+ password,
135
+ secure: process.env.SMTP_SECURE === "true" ? true : void 0
136
+ });
137
+ }
138
+
139
+ // src/providers/ses.ts
140
+ var SesProvider = class {
141
+ // SESClient - lazy loaded
142
+ constructor(config) {
143
+ this.validateConfig(config);
144
+ this.config = config;
145
+ }
146
+ /**
147
+ * Validate required config fields
148
+ */
149
+ validateConfig(config) {
150
+ if (!config.region) {
151
+ throw EmailError.configMissing("ses.region");
152
+ }
153
+ }
154
+ /**
155
+ * Lazy-load AWS SDK and create client
156
+ */
157
+ async getClient() {
158
+ if (this.client) {
159
+ return this.client;
160
+ }
161
+ const { SESClient } = await import("@aws-sdk/client-ses");
162
+ const clientConfig = {
163
+ region: this.config.region
164
+ };
165
+ if (this.config.accessKeyId && this.config.secretAccessKey) {
166
+ clientConfig.credentials = {
167
+ accessKeyId: this.config.accessKeyId,
168
+ secretAccessKey: this.config.secretAccessKey
169
+ };
170
+ }
171
+ this.client = new SESClient(clientConfig);
172
+ return this.client;
173
+ }
174
+ /**
175
+ * Format recipients as array of strings
176
+ */
177
+ formatRecipients(addresses) {
178
+ const list = Array.isArray(addresses) ? addresses : [addresses];
179
+ return list.map(
180
+ (addr) => typeof addr === "string" ? addr : addr.email
181
+ );
182
+ }
183
+ /**
184
+ * Strip HTML tags to create plain text version
185
+ */
186
+ stripHtml(html) {
187
+ return html.replace(/<style[^>]*>.*?<\/style>/gi, "").replace(/<script[^>]*>.*?<\/script>/gi, "").replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
188
+ }
189
+ /**
190
+ * Check if provider is configured
191
+ */
192
+ isConfigured() {
193
+ return !!this.config.region;
194
+ }
195
+ /**
196
+ * Send an email via SES
197
+ */
198
+ async send(params) {
199
+ const { to, subject, html, text, from, replyTo, cc, bcc } = params;
200
+ if (!from) {
201
+ throw EmailError.configMissing("from address");
202
+ }
203
+ try {
204
+ const client = await this.getClient();
205
+ const { SendEmailCommand } = await import("@aws-sdk/client-ses");
206
+ const destination = {
207
+ ToAddresses: this.formatRecipients(to)
208
+ };
209
+ if (cc) {
210
+ destination.CcAddresses = this.formatRecipients(cc);
211
+ }
212
+ if (bcc) {
213
+ destination.BccAddresses = this.formatRecipients(bcc);
214
+ }
215
+ const command = new SendEmailCommand({
216
+ Source: formatEmailAddress(from),
217
+ Destination: destination,
218
+ Message: {
219
+ Subject: {
220
+ Data: subject,
221
+ Charset: "UTF-8"
222
+ },
223
+ Body: {
224
+ Html: {
225
+ Data: html,
226
+ Charset: "UTF-8"
227
+ },
228
+ Text: {
229
+ Data: text || this.stripHtml(html),
230
+ Charset: "UTF-8"
231
+ }
232
+ }
233
+ },
234
+ ReplyToAddresses: replyTo ? [formatEmailAddress(replyTo)] : void 0
235
+ });
236
+ const response = await client.send(command);
237
+ return {
238
+ success: true,
239
+ messageId: response.MessageId
240
+ };
241
+ } catch (error) {
242
+ const errorMessage = error.message || "Failed to send email";
243
+ return {
244
+ success: false,
245
+ error: `[ses_error] ${errorMessage}`
246
+ };
247
+ }
248
+ }
249
+ };
250
+ function createSesProviderFromEnv() {
251
+ const region = process.env.SES_REGION || process.env.AWS_REGION;
252
+ if (!region) {
253
+ return null;
254
+ }
255
+ return new SesProvider({
256
+ region,
257
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
258
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
259
+ });
260
+ }
261
+
262
+ // src/providers/oci-email.ts
263
+ var OciEmailProvider = class {
264
+ constructor(config) {
265
+ this.fromAddress = config.fromAddress;
266
+ this.smtp = new SmtpProvider({
267
+ host: config.host,
268
+ port: config.port,
269
+ user: config.user,
270
+ password: config.password,
271
+ secure: config.port === 465
272
+ });
273
+ }
274
+ isConfigured() {
275
+ return this.smtp.isConfigured();
276
+ }
277
+ async send(params) {
278
+ const effective = {
279
+ ...params,
280
+ from: params.from ?? this.fromAddress
281
+ };
282
+ return this.smtp.send(effective);
283
+ }
284
+ };
285
+ function createOciEmailProviderFromEnv() {
286
+ const host = process.env.OCI_EMAIL_SMTP_HOST;
287
+ const user = process.env.OCI_EMAIL_SMTP_USER;
288
+ const password = process.env.OCI_EMAIL_SMTP_PASS;
289
+ if (!host || !user || !password) {
290
+ return null;
291
+ }
292
+ const port = parseInt(process.env.OCI_EMAIL_SMTP_PORT || "587", 10);
293
+ return new OciEmailProvider({
294
+ host,
295
+ port,
296
+ user,
297
+ password,
298
+ fromAddress: process.env.OCI_EMAIL_FROM_ADDRESS
299
+ });
300
+ }
301
+
302
+ // src/providers/index.ts
303
+ function createProviderFromEnv() {
304
+ const explicitProvider = process.env.EMAIL_PROVIDER;
305
+ if (explicitProvider === "smtp") {
306
+ return createSmtpProviderFromEnv();
307
+ }
308
+ if (explicitProvider === "ses") {
309
+ return createSesProviderFromEnv();
310
+ }
311
+ if (explicitProvider === "oci-email") {
312
+ return createOciEmailProviderFromEnv();
313
+ }
314
+ if (process.env.OCI_EMAIL_SMTP_HOST) {
315
+ return createOciEmailProviderFromEnv();
316
+ }
317
+ if (process.env.SMTP_HOST) {
318
+ return createSmtpProviderFromEnv();
319
+ }
320
+ if (process.env.SES_REGION || process.env.AWS_REGION) {
321
+ return createSesProviderFromEnv();
322
+ }
323
+ return null;
324
+ }
325
+
326
+ // src/server.ts
327
+ var EmailService = class {
328
+ constructor(provider, templates, defaultFrom) {
329
+ this.provider = provider;
330
+ this.templates = templates;
331
+ this.defaultFrom = defaultFrom;
332
+ }
333
+ /**
334
+ * Check if the service is configured and ready
335
+ */
336
+ isConfigured() {
337
+ return this.provider.isConfigured();
338
+ }
339
+ /**
340
+ * Register a custom email template
341
+ */
342
+ registerTemplate(template) {
343
+ this.templates.register(template);
344
+ }
345
+ /**
346
+ * Send a raw email
347
+ */
348
+ async send(params) {
349
+ const emailParams = {
350
+ ...params,
351
+ from: params.from ?? this.defaultFrom
352
+ };
353
+ return this.provider.send(emailParams);
354
+ }
355
+ /**
356
+ * Send a templated email
357
+ */
358
+ async sendTemplate(params) {
359
+ const { template: templateName, data, ...rest } = params;
360
+ const { subject, html, text } = this.templates.render(templateName, data);
361
+ return this.send({
362
+ ...rest,
363
+ subject,
364
+ html,
365
+ text
366
+ });
367
+ }
368
+ };
369
+ function createEmail(options = {}) {
370
+ const { config, defaultFrom, includeBuiltins = true } = options;
371
+ let provider = null;
372
+ if (config) {
373
+ if (config.provider === "smtp" && config.smtp) {
374
+ provider = new SmtpProvider(config.smtp);
375
+ } else if (config.provider === "ses" && config.ses) {
376
+ provider = new SesProvider(config.ses);
377
+ } else if (config.provider === "oci-email" && config.ociEmail) {
378
+ provider = new OciEmailProvider(config.ociEmail);
379
+ }
380
+ } else {
381
+ provider = createProviderFromEnv();
382
+ }
383
+ if (!provider) {
384
+ throw EmailError.providerNotConfigured();
385
+ }
386
+ const from = defaultFrom ?? config?.defaultFrom ?? process.env.EMAIL_FROM ?? process.env.SMTP_FROM_EMAIL ?? process.env.SES_FROM_EMAIL ?? process.env.OCI_EMAIL_FROM_ADDRESS;
387
+ if (!from) {
388
+ throw EmailError.configMissing("defaultFrom (or EMAIL_FROM env var)");
389
+ }
390
+ const templates = includeBuiltins ? createShogoTemplateRegistry() : new EmailTemplateRegistry();
391
+ return new EmailService(provider, templates, from);
392
+ }
393
+ function createEmailOptional(options = {}) {
394
+ try {
395
+ return createEmail(options);
396
+ } catch {
397
+ return null;
398
+ }
399
+ }
400
+ export {
401
+ createEmail,
402
+ createEmailOptional
403
+ };
404
+ //# sourceMappingURL=server.js.map