@forinda/kickjs-mailer 2.0.1 → 2.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.d.mts +282 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +277 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +27 -11
- package/dist/adapter.d.ts +0 -28
- package/dist/adapter.d.ts.map +0 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -126
- package/dist/mailer.service.d.ts +0 -56
- package/dist/mailer.service.d.ts.map +0 -1
- package/dist/providers/console.provider.d.ts +0 -18
- package/dist/providers/console.provider.d.ts.map +0 -1
- package/dist/providers/index.d.ts +0 -3
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/smtp.provider.d.ts +0 -55
- package/dist/providers/smtp.provider.d.ts.map +0 -1
- package/dist/types.d.ts +0 -118
- package/dist/types.d.ts.map +0 -1
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
|
|
2
|
+
import { AdapterContext, AppAdapter } from "@forinda/kickjs";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
interface MailAddress {
|
|
6
|
+
name?: string;
|
|
7
|
+
address: string;
|
|
8
|
+
}
|
|
9
|
+
type MailRecipient = string | MailAddress;
|
|
10
|
+
interface MailAttachment {
|
|
11
|
+
filename: string;
|
|
12
|
+
content?: string | Buffer;
|
|
13
|
+
path?: string;
|
|
14
|
+
contentType?: string;
|
|
15
|
+
encoding?: string;
|
|
16
|
+
}
|
|
17
|
+
interface MailMessage {
|
|
18
|
+
/** Sender address */
|
|
19
|
+
from?: MailRecipient;
|
|
20
|
+
/** Recipient(s) */
|
|
21
|
+
to: MailRecipient | MailRecipient[];
|
|
22
|
+
/** CC recipient(s) */
|
|
23
|
+
cc?: MailRecipient | MailRecipient[];
|
|
24
|
+
/** BCC recipient(s) */
|
|
25
|
+
bcc?: MailRecipient | MailRecipient[];
|
|
26
|
+
/** Reply-to address */
|
|
27
|
+
replyTo?: MailRecipient;
|
|
28
|
+
/** Email subject */
|
|
29
|
+
subject: string;
|
|
30
|
+
/** Plain text body */
|
|
31
|
+
text?: string;
|
|
32
|
+
/** HTML body */
|
|
33
|
+
html?: string;
|
|
34
|
+
/** File attachments */
|
|
35
|
+
attachments?: MailAttachment[];
|
|
36
|
+
/** Custom headers */
|
|
37
|
+
headers?: Record<string, string>;
|
|
38
|
+
/** Provider-specific options (e.g. Resend tags, SES configuration set) */
|
|
39
|
+
metadata?: Record<string, any>;
|
|
40
|
+
}
|
|
41
|
+
interface MailResult {
|
|
42
|
+
/** Provider-assigned message ID */
|
|
43
|
+
messageId: string;
|
|
44
|
+
/** Whether the send was accepted (does not guarantee delivery) */
|
|
45
|
+
accepted: boolean;
|
|
46
|
+
/** Raw response from the provider */
|
|
47
|
+
raw?: any;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Template engine for rendering email bodies.
|
|
51
|
+
* Implement this to use EJS, Handlebars, Pug, or any template system.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* import Handlebars from 'handlebars'
|
|
56
|
+
*
|
|
57
|
+
* class HandlebarsEngine implements MailTemplateEngine {
|
|
58
|
+
* private templates = new Map<string, HandlebarsTemplateDelegate>()
|
|
59
|
+
*
|
|
60
|
+
* register(name: string, source: string) {
|
|
61
|
+
* this.templates.set(name, Handlebars.compile(source))
|
|
62
|
+
* }
|
|
63
|
+
*
|
|
64
|
+
* async render(template: string, data: any) {
|
|
65
|
+
* const fn = this.templates.get(template)
|
|
66
|
+
* if (!fn) throw new Error(`Template "${template}" not found`)
|
|
67
|
+
* return fn(data)
|
|
68
|
+
* }
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
interface MailTemplateEngine {
|
|
73
|
+
/** Render a named template with data. Returns HTML string. */
|
|
74
|
+
render(template: string, data: Record<string, any>): Promise<string> | string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Abstract mail provider. Implement this to use any email service:
|
|
78
|
+
* SMTP (nodemailer), Resend, AWS SES, SendGrid, Postmark, Mailgun, etc.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* class ResendProvider implements MailProvider {
|
|
83
|
+
* name = 'resend'
|
|
84
|
+
* private client: Resend
|
|
85
|
+
*
|
|
86
|
+
* constructor(apiKey: string) {
|
|
87
|
+
* this.client = new Resend(apiKey)
|
|
88
|
+
* }
|
|
89
|
+
*
|
|
90
|
+
* async send(message: MailMessage): Promise<MailResult> {
|
|
91
|
+
* const { data, error } = await this.client.emails.send({
|
|
92
|
+
* from: formatAddress(message.from),
|
|
93
|
+
* to: formatRecipients(message.to),
|
|
94
|
+
* subject: message.subject,
|
|
95
|
+
* html: message.html,
|
|
96
|
+
* text: message.text,
|
|
97
|
+
* })
|
|
98
|
+
* if (error) throw error
|
|
99
|
+
* return { messageId: data.id, accepted: true, raw: data }
|
|
100
|
+
* }
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
interface MailProvider {
|
|
105
|
+
/** Provider name for logging */
|
|
106
|
+
name: string;
|
|
107
|
+
/** Send an email message */
|
|
108
|
+
send(message: MailMessage): Promise<MailResult>;
|
|
109
|
+
/** Optional cleanup (close connections, etc.) */
|
|
110
|
+
shutdown?(): Promise<void>;
|
|
111
|
+
}
|
|
112
|
+
interface MailerOptions {
|
|
113
|
+
/** Mail provider to use */
|
|
114
|
+
provider: MailProvider;
|
|
115
|
+
/** Default "from" address for all emails */
|
|
116
|
+
defaultFrom?: MailRecipient;
|
|
117
|
+
/** Optional template engine for rendering HTML from templates */
|
|
118
|
+
templateEngine?: MailTemplateEngine;
|
|
119
|
+
/** Enable/disable sending (useful for testing — logs instead of sending) */
|
|
120
|
+
enabled?: boolean;
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/mailer.service.d.ts
|
|
124
|
+
/** DI token for resolving MailerService from the container */
|
|
125
|
+
declare const MAILER: unique symbol;
|
|
126
|
+
/**
|
|
127
|
+
* Central mail service — send emails through any provider.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* @Service()
|
|
132
|
+
* class UserService {
|
|
133
|
+
* constructor(@Inject(MAILER) private mailer: MailerService) {}
|
|
134
|
+
*
|
|
135
|
+
* async sendWelcome(user: User) {
|
|
136
|
+
* await this.mailer.send({
|
|
137
|
+
* to: user.email,
|
|
138
|
+
* subject: 'Welcome!',
|
|
139
|
+
* html: '<h1>Welcome to our app</h1>',
|
|
140
|
+
* })
|
|
141
|
+
* }
|
|
142
|
+
*
|
|
143
|
+
* // Or with templates:
|
|
144
|
+
* async sendInvoice(user: User, invoice: Invoice) {
|
|
145
|
+
* await this.mailer.sendTemplate('invoice', {
|
|
146
|
+
* to: user.email,
|
|
147
|
+
* subject: `Invoice #${invoice.number}`,
|
|
148
|
+
* }, { user, invoice })
|
|
149
|
+
* }
|
|
150
|
+
* }
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
declare class MailerService {
|
|
154
|
+
private provider;
|
|
155
|
+
private defaultFrom?;
|
|
156
|
+
private templateEngine?;
|
|
157
|
+
private enabled;
|
|
158
|
+
constructor(options: MailerOptions);
|
|
159
|
+
/**
|
|
160
|
+
* Send an email message.
|
|
161
|
+
* Applies defaultFrom if no from address is set.
|
|
162
|
+
*/
|
|
163
|
+
send(message: MailMessage): Promise<MailResult>;
|
|
164
|
+
/**
|
|
165
|
+
* Render a template and send the resulting HTML as an email.
|
|
166
|
+
* Requires a templateEngine to be configured.
|
|
167
|
+
*
|
|
168
|
+
* @param template - Template name (resolved by the engine)
|
|
169
|
+
* @param message - Mail message (html will be overwritten by the rendered template)
|
|
170
|
+
* @param data - Template variables
|
|
171
|
+
*/
|
|
172
|
+
sendTemplate(template: string, message: Omit<MailMessage, 'html'>, data: Record<string, any>): Promise<MailResult>;
|
|
173
|
+
/** Get the underlying provider (for advanced use) */
|
|
174
|
+
getProvider(): MailProvider;
|
|
175
|
+
/** Shutdown the provider */
|
|
176
|
+
shutdown(): Promise<void>;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/adapter.d.ts
|
|
180
|
+
/**
|
|
181
|
+
* Mailer adapter — registers MailerService in the DI container.
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```ts
|
|
185
|
+
* import { MailerAdapter, SmtpProvider } from '@forinda/kickjs-mailer'
|
|
186
|
+
*
|
|
187
|
+
* bootstrap({
|
|
188
|
+
* adapters: [
|
|
189
|
+
* new MailerAdapter({
|
|
190
|
+
* provider: new SmtpProvider({ host: 'smtp.gmail.com', port: 587, auth: { ... } }),
|
|
191
|
+
* defaultFrom: { name: 'My App', address: 'noreply@myapp.com' },
|
|
192
|
+
* }),
|
|
193
|
+
* ],
|
|
194
|
+
* })
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
declare class MailerAdapter implements AppAdapter {
|
|
198
|
+
private options;
|
|
199
|
+
name: string;
|
|
200
|
+
private mailer;
|
|
201
|
+
constructor(options: MailerOptions);
|
|
202
|
+
afterStart({
|
|
203
|
+
container
|
|
204
|
+
}: AdapterContext): void;
|
|
205
|
+
shutdown(): Promise<void>;
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region src/providers/smtp.provider.d.ts
|
|
209
|
+
interface SmtpOptions {
|
|
210
|
+
/** SMTP host (e.g. 'smtp.gmail.com', 'smtp.resend.com') */
|
|
211
|
+
host: string;
|
|
212
|
+
/** SMTP port (default: 587) */
|
|
213
|
+
port?: number;
|
|
214
|
+
/** Use TLS (default: true for port 465, false otherwise) */
|
|
215
|
+
secure?: boolean;
|
|
216
|
+
/** Authentication credentials */
|
|
217
|
+
auth?: {
|
|
218
|
+
user: string;
|
|
219
|
+
pass: string;
|
|
220
|
+
};
|
|
221
|
+
/** Connection timeout in ms (default: 10000) */
|
|
222
|
+
connectionTimeout?: number;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* SMTP mail provider using nodemailer.
|
|
226
|
+
*
|
|
227
|
+
* Requires `nodemailer` as a peer dependency:
|
|
228
|
+
* ```bash
|
|
229
|
+
* pnpm add nodemailer @types/nodemailer
|
|
230
|
+
* ```
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```ts
|
|
234
|
+
* // Gmail
|
|
235
|
+
* new SmtpProvider({
|
|
236
|
+
* host: 'smtp.gmail.com',
|
|
237
|
+
* port: 587,
|
|
238
|
+
* auth: { user: 'you@gmail.com', pass: 'app-password' },
|
|
239
|
+
* })
|
|
240
|
+
*
|
|
241
|
+
* // Resend via SMTP
|
|
242
|
+
* new SmtpProvider({
|
|
243
|
+
* host: 'smtp.resend.com',
|
|
244
|
+
* port: 465,
|
|
245
|
+
* secure: true,
|
|
246
|
+
* auth: { user: 'resend', pass: process.env.RESEND_API_KEY! },
|
|
247
|
+
* })
|
|
248
|
+
*
|
|
249
|
+
* // Mailpit (local dev)
|
|
250
|
+
* new SmtpProvider({ host: 'localhost', port: 1025 })
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
declare class SmtpProvider implements MailProvider {
|
|
254
|
+
private options;
|
|
255
|
+
name: string;
|
|
256
|
+
private transporter;
|
|
257
|
+
constructor(options: SmtpOptions);
|
|
258
|
+
private ensureTransporter;
|
|
259
|
+
send(message: MailMessage): Promise<MailResult>;
|
|
260
|
+
shutdown(): Promise<void>;
|
|
261
|
+
}
|
|
262
|
+
//#endregion
|
|
263
|
+
//#region src/providers/console.provider.d.ts
|
|
264
|
+
/**
|
|
265
|
+
* Console mail provider — logs emails instead of sending them.
|
|
266
|
+
* Perfect for development and testing.
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```ts
|
|
270
|
+
* new MailerAdapter({
|
|
271
|
+
* provider: new ConsoleProvider(),
|
|
272
|
+
* defaultFrom: 'dev@localhost',
|
|
273
|
+
* })
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
declare class ConsoleProvider implements MailProvider {
|
|
277
|
+
name: string;
|
|
278
|
+
send(message: MailMessage): Promise<MailResult>;
|
|
279
|
+
}
|
|
280
|
+
//#endregion
|
|
281
|
+
export { ConsoleProvider, MAILER, type MailAddress, type MailAttachment, type MailMessage, type MailProvider, type MailRecipient, type MailResult, type MailTemplateEngine, MailerAdapter, type MailerOptions, MailerService, type SmtpOptions, SmtpProvider };
|
|
282
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/mailer.service.ts","../src/adapter.ts","../src/providers/smtp.provider.ts","../src/providers/console.provider.ts"],"mappings":";;;;UAEiB,WAAA;EACf,IAAA;EACA,OAAA;AAAA;AAAA,KAGU,aAAA,YAAyB,WAAA;AAAA,UAEpB,cAAA;EACf,QAAA;EACA,OAAA,YAAmB,MAAA;EACnB,IAAA;EACA,WAAA;EACA,QAAA;AAAA;AAAA,UAGe,WAAA;EAV+B;EAY9C,IAAA,GAAO,aAAA;EAVsB;EAY7B,EAAA,EAAI,aAAA,GAAgB,aAAA;EAVK;EAYzB,EAAA,GAAK,aAAA,GAAgB,aAAA;EAZrB;EAcA,GAAA,GAAM,aAAA,GAAgB,aAAA;EAbtB;EAeA,OAAA,GAAU,aAAA;EAbV;EAeA,OAAA;EAfQ;EAiBR,IAAA;EAd0B;EAgB1B,IAAA;EAdO;EAgBP,WAAA,GAAc,cAAA;EAdM;EAgBpB,OAAA,GAAU,MAAA;EAdW;EAgBrB,QAAA,GAAW,MAAA;AAAA;AAAA,UAGI,UAAA;EAPD;EASd,SAAA;EALW;EAOX,QAAA;EAPiB;EASjB,GAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;AANF;UAkCiB,kBAAA;;EAEf,MAAA,CAAO,QAAA,UAAkB,IAAA,EAAM,MAAA,gBAAsB,OAAA;AAAA;;;;;AAFvD;;;;;;;;;;;AAmCA;;;;;;;;;;;;;UAAiB,YAAA;EAKqB;EAHpC,IAAA;EAMa;EAHb,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,UAAA;EAGhB;EAApB,QAAA,KAAa,OAAA;AAAA;AAAA,UAKE,aAAA;EAEL;EAAV,QAAA,EAAU,YAAA;EAMO;EAHjB,WAAA,GAAc,aAAA;EAGqB;EAAnC,cAAA,GAAiB,kBAAA;EANP;EASV,OAAA;AAAA;;;;cC1HW,MAAA;;ADXb;;;;;AAKA;;;;;AAEA;;;;;;;;;;;;AAQA;;;;cCyBa,aAAA;EAAA,QACH,QAAA;EAAA,QACA,WAAA;EAAA,QACA,cAAA;EAAA,QACA,OAAA;cAEI,OAAA,EAAS,aAAA;EDrBX;;;;ECgCJ,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,UAAA;EDpBzB;;;;;;;;ECmDX,YAAA,CACJ,QAAA,UACA,OAAA,EAAS,IAAA,CAAK,WAAA,WACd,IAAA,EAAM,MAAA,gBACL,OAAA,CAAQ,UAAA;EDrEX;ECkFA,WAAA,CAAA,GAAe,YAAA;EDlFO;ECuFhB,QAAA,CAAA,GAAY,OAAA;AAAA;;;;;AD9GpB;;;;;AAKA;;;;;AAEA;;;;;cEca,aAAA,YAAyB,UAAA;EAAA,QAIhB,OAAA;EAHpB,IAAA;EAAA,QACQ,MAAA;cAEY,OAAA,EAAS,aAAA;EAI7B,UAAA,CAAA;IAAa;EAAA,GAAa,cAAA;EAOpB,QAAA,CAAA,GAAY,OAAA;AAAA;;;UCpCH,WAAA;;EAEf,IAAA;EHFe;EGIf,IAAA;;EAEA,MAAA;EHJO;EGMP,IAAA;IACE,IAAA;IACA,IAAA;EAAA;EHL4C;EGQ9C,iBAAA;AAAA;;;;;;;;;;;AHEF;;;;;;;;;;;;;;;;;;;cG8Ba,YAAA,YAAwB,YAAA;EAAA,QAIf,OAAA;EAHpB,IAAA;EAAA,QACQ,WAAA;cAEY,OAAA,EAAS,WAAA;EAAA,QAEf,iBAAA;EAiBR,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,UAAA;EAuBpC,QAAA,CAAA,GAAY,OAAA;AAAA;;;;;;AH3FpB;;;;;AAKA;;;;cIYa,eAAA,YAA2B,YAAA;EACtC,IAAA;EAEM,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,UAAA;AAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @forinda/kickjs-mailer v2.1.0
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) Felix Orinda
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*
|
|
9
|
+
* @license MIT
|
|
10
|
+
*/
|
|
11
|
+
import "reflect-metadata";
|
|
12
|
+
import { Logger } from "@forinda/kickjs";
|
|
13
|
+
//#region src/mailer.service.ts
|
|
14
|
+
const log$2 = Logger.for("Mailer");
|
|
15
|
+
/** DI token for resolving MailerService from the container */
|
|
16
|
+
const MAILER = Symbol("MailerService");
|
|
17
|
+
/**
|
|
18
|
+
* Central mail service — send emails through any provider.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* @Service()
|
|
23
|
+
* class UserService {
|
|
24
|
+
* constructor(@Inject(MAILER) private mailer: MailerService) {}
|
|
25
|
+
*
|
|
26
|
+
* async sendWelcome(user: User) {
|
|
27
|
+
* await this.mailer.send({
|
|
28
|
+
* to: user.email,
|
|
29
|
+
* subject: 'Welcome!',
|
|
30
|
+
* html: '<h1>Welcome to our app</h1>',
|
|
31
|
+
* })
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* // Or with templates:
|
|
35
|
+
* async sendInvoice(user: User, invoice: Invoice) {
|
|
36
|
+
* await this.mailer.sendTemplate('invoice', {
|
|
37
|
+
* to: user.email,
|
|
38
|
+
* subject: `Invoice #${invoice.number}`,
|
|
39
|
+
* }, { user, invoice })
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
var MailerService = class {
|
|
45
|
+
provider;
|
|
46
|
+
defaultFrom;
|
|
47
|
+
templateEngine;
|
|
48
|
+
enabled;
|
|
49
|
+
constructor(options) {
|
|
50
|
+
this.provider = options.provider;
|
|
51
|
+
this.defaultFrom = options.defaultFrom;
|
|
52
|
+
this.templateEngine = options.templateEngine;
|
|
53
|
+
this.enabled = options.enabled ?? true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Send an email message.
|
|
57
|
+
* Applies defaultFrom if no from address is set.
|
|
58
|
+
*/
|
|
59
|
+
async send(message) {
|
|
60
|
+
const msg = { ...message };
|
|
61
|
+
if (!msg.from && this.defaultFrom) msg.from = this.defaultFrom;
|
|
62
|
+
if (!this.enabled) {
|
|
63
|
+
log$2.info(`[dry-run] → ${formatRecipient(msg.to)} | ${msg.subject}`);
|
|
64
|
+
return {
|
|
65
|
+
messageId: "dry-run",
|
|
66
|
+
accepted: true
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const result = await this.provider.send(msg);
|
|
71
|
+
log$2.info(`Sent → ${formatRecipient(msg.to)} | ${msg.subject} [${result.messageId}]`);
|
|
72
|
+
return result;
|
|
73
|
+
} catch (err) {
|
|
74
|
+
log$2.error({ err }, `Failed → ${formatRecipient(msg.to)} | ${msg.subject}`);
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Render a template and send the resulting HTML as an email.
|
|
80
|
+
* Requires a templateEngine to be configured.
|
|
81
|
+
*
|
|
82
|
+
* @param template - Template name (resolved by the engine)
|
|
83
|
+
* @param message - Mail message (html will be overwritten by the rendered template)
|
|
84
|
+
* @param data - Template variables
|
|
85
|
+
*/
|
|
86
|
+
async sendTemplate(template, message, data) {
|
|
87
|
+
if (!this.templateEngine) throw new Error("MailerService: templateEngine is required for sendTemplate(). Pass one in MailerOptions or use send() with raw HTML.");
|
|
88
|
+
const html = await this.templateEngine.render(template, data);
|
|
89
|
+
return this.send({
|
|
90
|
+
...message,
|
|
91
|
+
html
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/** Get the underlying provider (for advanced use) */
|
|
95
|
+
getProvider() {
|
|
96
|
+
return this.provider;
|
|
97
|
+
}
|
|
98
|
+
/** Shutdown the provider */
|
|
99
|
+
async shutdown() {
|
|
100
|
+
if (this.provider.shutdown) await this.provider.shutdown();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
function formatRecipient(to) {
|
|
104
|
+
if (Array.isArray(to)) return to.map((r) => typeof r === "string" ? r : r.address).join(", ");
|
|
105
|
+
return typeof to === "string" ? to : to.address;
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/adapter.ts
|
|
109
|
+
const log$1 = Logger.for("MailerAdapter");
|
|
110
|
+
/**
|
|
111
|
+
* Mailer adapter — registers MailerService in the DI container.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* import { MailerAdapter, SmtpProvider } from '@forinda/kickjs-mailer'
|
|
116
|
+
*
|
|
117
|
+
* bootstrap({
|
|
118
|
+
* adapters: [
|
|
119
|
+
* new MailerAdapter({
|
|
120
|
+
* provider: new SmtpProvider({ host: 'smtp.gmail.com', port: 587, auth: { ... } }),
|
|
121
|
+
* defaultFrom: { name: 'My App', address: 'noreply@myapp.com' },
|
|
122
|
+
* }),
|
|
123
|
+
* ],
|
|
124
|
+
* })
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
var MailerAdapter = class {
|
|
128
|
+
name = "MailerAdapter";
|
|
129
|
+
mailer;
|
|
130
|
+
constructor(options) {
|
|
131
|
+
this.options = options;
|
|
132
|
+
this.mailer = new MailerService(options);
|
|
133
|
+
}
|
|
134
|
+
afterStart({ container }) {
|
|
135
|
+
container.registerInstance(MAILER, this.mailer);
|
|
136
|
+
log$1.info(`Mail provider: ${this.options.provider.name}${this.options.enabled === false ? " (disabled)" : ""}`);
|
|
137
|
+
}
|
|
138
|
+
async shutdown() {
|
|
139
|
+
await this.mailer.shutdown();
|
|
140
|
+
log$1.info("Mailer shut down");
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/providers/smtp.provider.ts
|
|
145
|
+
/**
|
|
146
|
+
* SMTP mail provider using nodemailer.
|
|
147
|
+
*
|
|
148
|
+
* Requires `nodemailer` as a peer dependency:
|
|
149
|
+
* ```bash
|
|
150
|
+
* pnpm add nodemailer @types/nodemailer
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* // Gmail
|
|
156
|
+
* new SmtpProvider({
|
|
157
|
+
* host: 'smtp.gmail.com',
|
|
158
|
+
* port: 587,
|
|
159
|
+
* auth: { user: 'you@gmail.com', pass: 'app-password' },
|
|
160
|
+
* })
|
|
161
|
+
*
|
|
162
|
+
* // Resend via SMTP
|
|
163
|
+
* new SmtpProvider({
|
|
164
|
+
* host: 'smtp.resend.com',
|
|
165
|
+
* port: 465,
|
|
166
|
+
* secure: true,
|
|
167
|
+
* auth: { user: 'resend', pass: process.env.RESEND_API_KEY! },
|
|
168
|
+
* })
|
|
169
|
+
*
|
|
170
|
+
* // Mailpit (local dev)
|
|
171
|
+
* new SmtpProvider({ host: 'localhost', port: 1025 })
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
var SmtpProvider = class {
|
|
175
|
+
name = "smtp";
|
|
176
|
+
transporter;
|
|
177
|
+
constructor(options) {
|
|
178
|
+
this.options = options;
|
|
179
|
+
}
|
|
180
|
+
async ensureTransporter() {
|
|
181
|
+
if (this.transporter) return;
|
|
182
|
+
try {
|
|
183
|
+
const nodemailer = await import("nodemailer");
|
|
184
|
+
this.transporter = (nodemailer.createTransport ?? nodemailer.default?.createTransport)({
|
|
185
|
+
host: this.options.host,
|
|
186
|
+
port: this.options.port ?? 587,
|
|
187
|
+
secure: this.options.secure ?? this.options.port === 465,
|
|
188
|
+
auth: this.options.auth,
|
|
189
|
+
connectionTimeout: this.options.connectionTimeout ?? 1e4
|
|
190
|
+
});
|
|
191
|
+
} catch {
|
|
192
|
+
throw new Error("SmtpProvider requires \"nodemailer\" package. Install: pnpm add nodemailer");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async send(message) {
|
|
196
|
+
await this.ensureTransporter();
|
|
197
|
+
const result = await this.transporter.sendMail({
|
|
198
|
+
from: formatAddress(message.from),
|
|
199
|
+
to: formatRecipients(message.to),
|
|
200
|
+
cc: message.cc ? formatRecipients(message.cc) : void 0,
|
|
201
|
+
bcc: message.bcc ? formatRecipients(message.bcc) : void 0,
|
|
202
|
+
replyTo: message.replyTo ? formatAddress(message.replyTo) : void 0,
|
|
203
|
+
subject: message.subject,
|
|
204
|
+
text: message.text,
|
|
205
|
+
html: message.html,
|
|
206
|
+
attachments: message.attachments,
|
|
207
|
+
headers: message.headers
|
|
208
|
+
});
|
|
209
|
+
return {
|
|
210
|
+
messageId: result.messageId,
|
|
211
|
+
accepted: (result.accepted?.length ?? 0) > 0,
|
|
212
|
+
raw: result
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
async shutdown() {
|
|
216
|
+
if (this.transporter) this.transporter.close();
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
function formatAddress(addr) {
|
|
220
|
+
if (!addr) return void 0;
|
|
221
|
+
if (typeof addr === "string") return addr;
|
|
222
|
+
return addr.name ? `"${addr.name}" <${addr.address}>` : addr.address;
|
|
223
|
+
}
|
|
224
|
+
function formatRecipients(recipients) {
|
|
225
|
+
if (!recipients) return "";
|
|
226
|
+
if (typeof recipients === "string") return recipients;
|
|
227
|
+
if (Array.isArray(recipients)) return recipients.map((r) => formatAddress(r)).join(", ");
|
|
228
|
+
return formatAddress(recipients) ?? "";
|
|
229
|
+
}
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/providers/console.provider.ts
|
|
232
|
+
const log = Logger.for("ConsoleMail");
|
|
233
|
+
let counter = 0;
|
|
234
|
+
/**
|
|
235
|
+
* Console mail provider — logs emails instead of sending them.
|
|
236
|
+
* Perfect for development and testing.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* new MailerAdapter({
|
|
241
|
+
* provider: new ConsoleProvider(),
|
|
242
|
+
* defaultFrom: 'dev@localhost',
|
|
243
|
+
* })
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
var ConsoleProvider = class {
|
|
247
|
+
name = "console";
|
|
248
|
+
async send(message) {
|
|
249
|
+
const id = `console-${++counter}`;
|
|
250
|
+
const to = Array.isArray(message.to) ? message.to.map((r) => typeof r === "string" ? r : r.address).join(", ") : typeof message.to === "string" ? message.to : message.to.address;
|
|
251
|
+
log.info(`────────────────────────────────────────`);
|
|
252
|
+
log.info(`From: ${formatAddr(message.from)}`);
|
|
253
|
+
log.info(`To: ${to}`);
|
|
254
|
+
if (message.cc) log.info(`CC: ${formatAddr(message.cc)}`);
|
|
255
|
+
if (message.bcc) log.info(`BCC: ${formatAddr(message.bcc)}`);
|
|
256
|
+
log.info(`Subject: ${message.subject}`);
|
|
257
|
+
if (message.text) log.info(`Text: ${message.text.slice(0, 200)}${message.text.length > 200 ? "..." : ""}`);
|
|
258
|
+
if (message.html) log.info(`HTML: ${message.html.slice(0, 200)}${message.html.length > 200 ? "..." : ""}`);
|
|
259
|
+
if (message.attachments?.length) log.info(`Attach: ${message.attachments.map((a) => a.filename).join(", ")}`);
|
|
260
|
+
log.info(`ID: ${id}`);
|
|
261
|
+
log.info(`────────────────────────────────────────`);
|
|
262
|
+
return {
|
|
263
|
+
messageId: id,
|
|
264
|
+
accepted: true
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
function formatAddr(addr) {
|
|
269
|
+
if (!addr) return "(none)";
|
|
270
|
+
if (typeof addr === "string") return addr;
|
|
271
|
+
if (Array.isArray(addr)) return addr.map(formatAddr).join(", ");
|
|
272
|
+
return addr.name ? `"${addr.name}" <${addr.address}>` : addr.address;
|
|
273
|
+
}
|
|
274
|
+
//#endregion
|
|
275
|
+
export { ConsoleProvider, MAILER, MailerAdapter, MailerService, SmtpProvider };
|
|
276
|
+
|
|
277
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["log","log"],"sources":["../src/mailer.service.ts","../src/adapter.ts","../src/providers/smtp.provider.ts","../src/providers/console.provider.ts"],"sourcesContent":["import { Logger } from '@forinda/kickjs'\nimport type {\n MailProvider,\n MailMessage,\n MailResult,\n MailRecipient,\n MailTemplateEngine,\n MailerOptions,\n} from './types'\n\nconst log = Logger.for('Mailer')\n\n/** DI token for resolving MailerService from the container */\nexport const MAILER = Symbol('MailerService')\n\n/**\n * Central mail service — send emails through any provider.\n *\n * @example\n * ```ts\n * @Service()\n * class UserService {\n * constructor(@Inject(MAILER) private mailer: MailerService) {}\n *\n * async sendWelcome(user: User) {\n * await this.mailer.send({\n * to: user.email,\n * subject: 'Welcome!',\n * html: '<h1>Welcome to our app</h1>',\n * })\n * }\n *\n * // Or with templates:\n * async sendInvoice(user: User, invoice: Invoice) {\n * await this.mailer.sendTemplate('invoice', {\n * to: user.email,\n * subject: `Invoice #${invoice.number}`,\n * }, { user, invoice })\n * }\n * }\n * ```\n */\nexport class MailerService {\n private provider: MailProvider\n private defaultFrom?: MailRecipient\n private templateEngine?: MailTemplateEngine\n private enabled: boolean\n\n constructor(options: MailerOptions) {\n this.provider = options.provider\n this.defaultFrom = options.defaultFrom\n this.templateEngine = options.templateEngine\n this.enabled = options.enabled ?? true\n }\n\n /**\n * Send an email message.\n * Applies defaultFrom if no from address is set.\n */\n async send(message: MailMessage): Promise<MailResult> {\n const msg = { ...message }\n\n // Apply default from\n if (!msg.from && this.defaultFrom) {\n msg.from = this.defaultFrom\n }\n\n if (!this.enabled) {\n log.info(`[dry-run] → ${formatRecipient(msg.to)} | ${msg.subject}`)\n return { messageId: 'dry-run', accepted: true }\n }\n\n try {\n const result = await this.provider.send(msg)\n log.info(`Sent → ${formatRecipient(msg.to)} | ${msg.subject} [${result.messageId}]`)\n return result\n } catch (err: any) {\n log.error({ err }, `Failed → ${formatRecipient(msg.to)} | ${msg.subject}`)\n throw err\n }\n }\n\n /**\n * Render a template and send the resulting HTML as an email.\n * Requires a templateEngine to be configured.\n *\n * @param template - Template name (resolved by the engine)\n * @param message - Mail message (html will be overwritten by the rendered template)\n * @param data - Template variables\n */\n async sendTemplate(\n template: string,\n message: Omit<MailMessage, 'html'>,\n data: Record<string, any>,\n ): Promise<MailResult> {\n if (!this.templateEngine) {\n throw new Error(\n 'MailerService: templateEngine is required for sendTemplate(). ' +\n 'Pass one in MailerOptions or use send() with raw HTML.',\n )\n }\n\n const html = await this.templateEngine.render(template, data)\n return this.send({ ...message, html })\n }\n\n /** Get the underlying provider (for advanced use) */\n getProvider(): MailProvider {\n return this.provider\n }\n\n /** Shutdown the provider */\n async shutdown(): Promise<void> {\n if (this.provider.shutdown) {\n await this.provider.shutdown()\n }\n }\n}\n\nfunction formatRecipient(to: MailRecipient | MailRecipient[]): string {\n if (Array.isArray(to)) {\n return to.map((r) => (typeof r === 'string' ? r : r.address)).join(', ')\n }\n return typeof to === 'string' ? to : to.address\n}\n","import { Logger, type AppAdapter, type AdapterContext } from '@forinda/kickjs'\nimport { MailerService, MAILER } from './mailer.service'\nimport type { MailerOptions } from './types'\n\nconst log = Logger.for('MailerAdapter')\n\n/**\n * Mailer adapter — registers MailerService in the DI container.\n *\n * @example\n * ```ts\n * import { MailerAdapter, SmtpProvider } from '@forinda/kickjs-mailer'\n *\n * bootstrap({\n * adapters: [\n * new MailerAdapter({\n * provider: new SmtpProvider({ host: 'smtp.gmail.com', port: 587, auth: { ... } }),\n * defaultFrom: { name: 'My App', address: 'noreply@myapp.com' },\n * }),\n * ],\n * })\n * ```\n */\nexport class MailerAdapter implements AppAdapter {\n name = 'MailerAdapter'\n private mailer: MailerService\n\n constructor(private options: MailerOptions) {\n this.mailer = new MailerService(options)\n }\n\n afterStart({ container }: AdapterContext): void {\n container.registerInstance(MAILER, this.mailer)\n log.info(\n `Mail provider: ${this.options.provider.name}${this.options.enabled === false ? ' (disabled)' : ''}`,\n )\n }\n\n async shutdown(): Promise<void> {\n await this.mailer.shutdown()\n log.info('Mailer shut down')\n }\n}\n","import type { MailProvider, MailMessage, MailResult } from '../types'\n\nexport interface SmtpOptions {\n /** SMTP host (e.g. 'smtp.gmail.com', 'smtp.resend.com') */\n host: string\n /** SMTP port (default: 587) */\n port?: number\n /** Use TLS (default: true for port 465, false otherwise) */\n secure?: boolean\n /** Authentication credentials */\n auth?: {\n user: string\n pass: string\n }\n /** Connection timeout in ms (default: 10000) */\n connectionTimeout?: number\n}\n\n/**\n * SMTP mail provider using nodemailer.\n *\n * Requires `nodemailer` as a peer dependency:\n * ```bash\n * pnpm add nodemailer @types/nodemailer\n * ```\n *\n * @example\n * ```ts\n * // Gmail\n * new SmtpProvider({\n * host: 'smtp.gmail.com',\n * port: 587,\n * auth: { user: 'you@gmail.com', pass: 'app-password' },\n * })\n *\n * // Resend via SMTP\n * new SmtpProvider({\n * host: 'smtp.resend.com',\n * port: 465,\n * secure: true,\n * auth: { user: 'resend', pass: process.env.RESEND_API_KEY! },\n * })\n *\n * // Mailpit (local dev)\n * new SmtpProvider({ host: 'localhost', port: 1025 })\n * ```\n */\nexport class SmtpProvider implements MailProvider {\n name = 'smtp'\n private transporter: any\n\n constructor(private options: SmtpOptions) {}\n\n private async ensureTransporter(): Promise<void> {\n if (this.transporter) return\n try {\n const nodemailer: any = await import('nodemailer')\n const createTransport = nodemailer.createTransport ?? nodemailer.default?.createTransport\n this.transporter = createTransport({\n host: this.options.host,\n port: this.options.port ?? 587,\n secure: this.options.secure ?? this.options.port === 465,\n auth: this.options.auth,\n connectionTimeout: this.options.connectionTimeout ?? 10000,\n })\n } catch {\n throw new Error('SmtpProvider requires \"nodemailer\" package. Install: pnpm add nodemailer')\n }\n }\n\n async send(message: MailMessage): Promise<MailResult> {\n await this.ensureTransporter()\n\n const result = await this.transporter.sendMail({\n from: formatAddress(message.from),\n to: formatRecipients(message.to),\n cc: message.cc ? formatRecipients(message.cc) : undefined,\n bcc: message.bcc ? formatRecipients(message.bcc) : undefined,\n replyTo: message.replyTo ? formatAddress(message.replyTo) : undefined,\n subject: message.subject,\n text: message.text,\n html: message.html,\n attachments: message.attachments,\n headers: message.headers,\n })\n\n return {\n messageId: result.messageId,\n accepted: (result.accepted?.length ?? 0) > 0,\n raw: result,\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.transporter) {\n this.transporter.close()\n }\n }\n}\n\nfunction formatAddress(addr: any): string | undefined {\n if (!addr) return undefined\n if (typeof addr === 'string') return addr\n return addr.name ? `\"${addr.name}\" <${addr.address}>` : addr.address\n}\n\nfunction formatRecipients(recipients: any): string {\n if (!recipients) return ''\n if (typeof recipients === 'string') return recipients\n if (Array.isArray(recipients)) {\n return recipients.map((r: any) => formatAddress(r)).join(', ')\n }\n return formatAddress(recipients) ?? ''\n}\n","import { Logger } from '@forinda/kickjs'\nimport type { MailProvider, MailMessage, MailResult } from '../types'\n\nconst log = Logger.for('ConsoleMail')\n\nlet counter = 0\n\n/**\n * Console mail provider — logs emails instead of sending them.\n * Perfect for development and testing.\n *\n * @example\n * ```ts\n * new MailerAdapter({\n * provider: new ConsoleProvider(),\n * defaultFrom: 'dev@localhost',\n * })\n * ```\n */\nexport class ConsoleProvider implements MailProvider {\n name = 'console'\n\n async send(message: MailMessage): Promise<MailResult> {\n const id = `console-${++counter}`\n const to = Array.isArray(message.to)\n ? message.to.map((r) => (typeof r === 'string' ? r : r.address)).join(', ')\n : typeof message.to === 'string'\n ? message.to\n : message.to.address\n\n log.info(`────────────────────────────────────────`)\n log.info(`From: ${formatAddr(message.from)}`)\n log.info(`To: ${to}`)\n if (message.cc) log.info(`CC: ${formatAddr(message.cc)}`)\n if (message.bcc) log.info(`BCC: ${formatAddr(message.bcc)}`)\n log.info(`Subject: ${message.subject}`)\n if (message.text)\n log.info(`Text: ${message.text.slice(0, 200)}${message.text.length > 200 ? '...' : ''}`)\n if (message.html)\n log.info(`HTML: ${message.html.slice(0, 200)}${message.html.length > 200 ? '...' : ''}`)\n if (message.attachments?.length) {\n log.info(`Attach: ${message.attachments.map((a) => a.filename).join(', ')}`)\n }\n log.info(`ID: ${id}`)\n log.info(`────────────────────────────────────────`)\n\n return { messageId: id, accepted: true }\n }\n}\n\nfunction formatAddr(addr: any): string {\n if (!addr) return '(none)'\n if (typeof addr === 'string') return addr\n if (Array.isArray(addr)) return addr.map(formatAddr).join(', ')\n return addr.name ? `\"${addr.name}\" <${addr.address}>` : addr.address\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forinda/kickjs-mailer",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Pluggable email sending for KickJS — nodemailer, Resend, SES, and custom providers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kickjs",
|
|
@@ -39,20 +39,37 @@
|
|
|
39
39
|
"vite"
|
|
40
40
|
],
|
|
41
41
|
"type": "module",
|
|
42
|
-
"main": "dist/index.
|
|
43
|
-
"types": "dist/index.d.
|
|
42
|
+
"main": "dist/index.mjs",
|
|
43
|
+
"types": "dist/index.d.mts",
|
|
44
44
|
"exports": {
|
|
45
45
|
".": {
|
|
46
|
-
"import": "./dist/index.
|
|
47
|
-
"types": "./dist/index.d.
|
|
46
|
+
"import": "./dist/index.mjs",
|
|
47
|
+
"types": "./dist/index.d.mts"
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
51
51
|
"dist"
|
|
52
52
|
],
|
|
53
|
+
"wireit": {
|
|
54
|
+
"build": {
|
|
55
|
+
"command": "tsdown",
|
|
56
|
+
"files": [
|
|
57
|
+
"src/**/*.ts",
|
|
58
|
+
"tsdown.config.ts",
|
|
59
|
+
"tsconfig.json",
|
|
60
|
+
"package.json"
|
|
61
|
+
],
|
|
62
|
+
"output": [
|
|
63
|
+
"dist/**"
|
|
64
|
+
],
|
|
65
|
+
"dependencies": [
|
|
66
|
+
"../core:build"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
},
|
|
53
70
|
"dependencies": {
|
|
54
71
|
"reflect-metadata": "^0.2.2",
|
|
55
|
-
"@forinda/kickjs": "2.0
|
|
72
|
+
"@forinda/kickjs": "2.1.0"
|
|
56
73
|
},
|
|
57
74
|
"peerDependencies": {
|
|
58
75
|
"nodemailer": ">=7.0.11"
|
|
@@ -67,7 +84,7 @@
|
|
|
67
84
|
"@types/nodemailer": "^7.0.11",
|
|
68
85
|
"nodemailer": "^8.0.4",
|
|
69
86
|
"typescript": "^5.9.2",
|
|
70
|
-
"vitest": "^4.1.
|
|
87
|
+
"vitest": "^4.1.2"
|
|
71
88
|
},
|
|
72
89
|
"publishConfig": {
|
|
73
90
|
"access": "public"
|
|
@@ -87,11 +104,10 @@
|
|
|
87
104
|
"url": "https://github.com/forinda/kick-js/issues"
|
|
88
105
|
},
|
|
89
106
|
"scripts": {
|
|
90
|
-
"build": "
|
|
91
|
-
"
|
|
92
|
-
"dev": "vite build --watch",
|
|
107
|
+
"build": "wireit",
|
|
108
|
+
"dev": "tsdown --watch",
|
|
93
109
|
"test": "vitest run",
|
|
94
110
|
"typecheck": "tsc --noEmit",
|
|
95
|
-
"clean": "rm -rf dist .
|
|
111
|
+
"clean": "rm -rf dist .wireit"
|
|
96
112
|
}
|
|
97
113
|
}
|
package/dist/adapter.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { type AppAdapter, type AdapterContext } from '@forinda/kickjs';
|
|
2
|
-
import type { MailerOptions } from './types';
|
|
3
|
-
/**
|
|
4
|
-
* Mailer adapter — registers MailerService in the DI container.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* import { MailerAdapter, SmtpProvider } from '@forinda/kickjs-mailer'
|
|
9
|
-
*
|
|
10
|
-
* bootstrap({
|
|
11
|
-
* adapters: [
|
|
12
|
-
* new MailerAdapter({
|
|
13
|
-
* provider: new SmtpProvider({ host: 'smtp.gmail.com', port: 587, auth: { ... } }),
|
|
14
|
-
* defaultFrom: { name: 'My App', address: 'noreply@myapp.com' },
|
|
15
|
-
* }),
|
|
16
|
-
* ],
|
|
17
|
-
* })
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
export declare class MailerAdapter implements AppAdapter {
|
|
21
|
-
private options;
|
|
22
|
-
name: string;
|
|
23
|
-
private mailer;
|
|
24
|
-
constructor(options: MailerOptions);
|
|
25
|
-
afterStart({ container }: AdapterContext): void;
|
|
26
|
-
shutdown(): Promise<void>;
|
|
27
|
-
}
|
|
28
|
-
//# sourceMappingURL=adapter.d.ts.map
|
package/dist/adapter.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAE9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAI5C;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAc,YAAW,UAAU;IAIlC,OAAO,CAAC,OAAO;IAH3B,IAAI,SAAkB;IACtB,OAAO,CAAC,MAAM,CAAe;gBAET,OAAO,EAAE,aAAa;IAI1C,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,cAAc,GAAG,IAAI;IAOzC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAIhC"}
|
package/dist/index.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import 'reflect-metadata';
|
|
2
|
-
export { type MailProvider, type MailMessage, type MailResult, type MailAddress, type MailRecipient, type MailAttachment, type MailTemplateEngine, type MailerOptions, } from './types';
|
|
3
|
-
export { MailerService, MAILER } from './mailer.service';
|
|
4
|
-
export { MailerAdapter } from './adapter';
|
|
5
|
-
export { SmtpProvider, ConsoleProvider, type SmtpOptions } from './providers';
|
|
6
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAA;AAGzB,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,aAAa,GACnB,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAGxD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAGzC,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/index.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import "reflect-metadata";
|
|
2
|
-
import { Logger as f } from "@forinda/kickjs";
|
|
3
|
-
var s = f.for("Mailer"), p = /* @__PURE__ */ Symbol("MailerService"), u = class {
|
|
4
|
-
provider;
|
|
5
|
-
defaultFrom;
|
|
6
|
-
templateEngine;
|
|
7
|
-
enabled;
|
|
8
|
-
constructor(t) {
|
|
9
|
-
this.provider = t.provider, this.defaultFrom = t.defaultFrom, this.templateEngine = t.templateEngine, this.enabled = t.enabled ?? !0;
|
|
10
|
-
}
|
|
11
|
-
async send(t) {
|
|
12
|
-
const r = { ...t };
|
|
13
|
-
if (!r.from && this.defaultFrom && (r.from = this.defaultFrom), !this.enabled)
|
|
14
|
-
return s.info(`[dry-run] → ${c(r.to)} | ${r.subject}`), {
|
|
15
|
-
messageId: "dry-run",
|
|
16
|
-
accepted: !0
|
|
17
|
-
};
|
|
18
|
-
try {
|
|
19
|
-
const n = await this.provider.send(r);
|
|
20
|
-
return s.info(`Sent → ${c(r.to)} | ${r.subject} [${n.messageId}]`), n;
|
|
21
|
-
} catch (n) {
|
|
22
|
-
throw s.error({ err: n }, `Failed → ${c(r.to)} | ${r.subject}`), n;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
async sendTemplate(t, r, n) {
|
|
26
|
-
if (!this.templateEngine) throw new Error("MailerService: templateEngine is required for sendTemplate(). Pass one in MailerOptions or use send() with raw HTML.");
|
|
27
|
-
const o = await this.templateEngine.render(t, n);
|
|
28
|
-
return this.send({
|
|
29
|
-
...r,
|
|
30
|
-
html: o
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
getProvider() {
|
|
34
|
-
return this.provider;
|
|
35
|
-
}
|
|
36
|
-
async shutdown() {
|
|
37
|
-
this.provider.shutdown && await this.provider.shutdown();
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
function c(t) {
|
|
41
|
-
return Array.isArray(t) ? t.map((r) => typeof r == "string" ? r : r.address).join(", ") : typeof t == "string" ? t : t.address;
|
|
42
|
-
}
|
|
43
|
-
var h = f.for("MailerAdapter"), v = class {
|
|
44
|
-
name = "MailerAdapter";
|
|
45
|
-
mailer;
|
|
46
|
-
constructor(t) {
|
|
47
|
-
this.options = t, this.mailer = new u(t);
|
|
48
|
-
}
|
|
49
|
-
afterStart({ container: t }) {
|
|
50
|
-
t.registerInstance(p, this.mailer), h.info(`Mail provider: ${this.options.provider.name}${this.options.enabled === !1 ? " (disabled)" : ""}`);
|
|
51
|
-
}
|
|
52
|
-
async shutdown() {
|
|
53
|
-
await this.mailer.shutdown(), h.info("Mailer shut down");
|
|
54
|
-
}
|
|
55
|
-
}, $ = class {
|
|
56
|
-
name = "smtp";
|
|
57
|
-
transporter;
|
|
58
|
-
constructor(t) {
|
|
59
|
-
this.options = t;
|
|
60
|
-
}
|
|
61
|
-
async ensureTransporter() {
|
|
62
|
-
if (!this.transporter)
|
|
63
|
-
try {
|
|
64
|
-
const t = await import("nodemailer");
|
|
65
|
-
this.transporter = (t.createTransport ?? t.default?.createTransport)({
|
|
66
|
-
host: this.options.host,
|
|
67
|
-
port: this.options.port ?? 587,
|
|
68
|
-
secure: this.options.secure ?? this.options.port === 465,
|
|
69
|
-
auth: this.options.auth,
|
|
70
|
-
connectionTimeout: this.options.connectionTimeout ?? 1e4
|
|
71
|
-
});
|
|
72
|
-
} catch {
|
|
73
|
-
throw new Error('SmtpProvider requires "nodemailer" package. Install: pnpm add nodemailer');
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
async send(t) {
|
|
77
|
-
await this.ensureTransporter();
|
|
78
|
-
const r = await this.transporter.sendMail({
|
|
79
|
-
from: a(t.from),
|
|
80
|
-
to: l(t.to),
|
|
81
|
-
cc: t.cc ? l(t.cc) : void 0,
|
|
82
|
-
bcc: t.bcc ? l(t.bcc) : void 0,
|
|
83
|
-
replyTo: t.replyTo ? a(t.replyTo) : void 0,
|
|
84
|
-
subject: t.subject,
|
|
85
|
-
text: t.text,
|
|
86
|
-
html: t.html,
|
|
87
|
-
attachments: t.attachments,
|
|
88
|
-
headers: t.headers
|
|
89
|
-
});
|
|
90
|
-
return {
|
|
91
|
-
messageId: r.messageId,
|
|
92
|
-
accepted: (r.accepted?.length ?? 0) > 0,
|
|
93
|
-
raw: r
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
async shutdown() {
|
|
97
|
-
this.transporter && this.transporter.close();
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
function a(t) {
|
|
101
|
-
if (t)
|
|
102
|
-
return typeof t == "string" ? t : t.name ? `"${t.name}" <${t.address}>` : t.address;
|
|
103
|
-
}
|
|
104
|
-
function l(t) {
|
|
105
|
-
return t ? typeof t == "string" ? t : Array.isArray(t) ? t.map((r) => a(r)).join(", ") : a(t) ?? "" : "";
|
|
106
|
-
}
|
|
107
|
-
var e = f.for("ConsoleMail"), d = 0, w = class {
|
|
108
|
-
name = "console";
|
|
109
|
-
async send(t) {
|
|
110
|
-
const r = `console-${++d}`, n = Array.isArray(t.to) ? t.to.map((o) => typeof o == "string" ? o : o.address).join(", ") : typeof t.to == "string" ? t.to : t.to.address;
|
|
111
|
-
return e.info("────────────────────────────────────────"), e.info(`From: ${i(t.from)}`), e.info(`To: ${n}`), t.cc && e.info(`CC: ${i(t.cc)}`), t.bcc && e.info(`BCC: ${i(t.bcc)}`), e.info(`Subject: ${t.subject}`), t.text && e.info(`Text: ${t.text.slice(0, 200)}${t.text.length > 200 ? "..." : ""}`), t.html && e.info(`HTML: ${t.html.slice(0, 200)}${t.html.length > 200 ? "..." : ""}`), t.attachments?.length && e.info(`Attach: ${t.attachments.map((o) => o.filename).join(", ")}`), e.info(`ID: ${r}`), e.info("────────────────────────────────────────"), {
|
|
112
|
-
messageId: r,
|
|
113
|
-
accepted: !0
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
function i(t) {
|
|
118
|
-
return t ? typeof t == "string" ? t : Array.isArray(t) ? t.map(i).join(", ") : t.name ? `"${t.name}" <${t.address}>` : t.address : "(none)";
|
|
119
|
-
}
|
|
120
|
-
export {
|
|
121
|
-
w as ConsoleProvider,
|
|
122
|
-
p as MAILER,
|
|
123
|
-
v as MailerAdapter,
|
|
124
|
-
u as MailerService,
|
|
125
|
-
$ as SmtpProvider
|
|
126
|
-
};
|
package/dist/mailer.service.d.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type { MailProvider, MailMessage, MailResult, MailerOptions } from './types';
|
|
2
|
-
/** DI token for resolving MailerService from the container */
|
|
3
|
-
export declare const MAILER: unique symbol;
|
|
4
|
-
/**
|
|
5
|
-
* Central mail service — send emails through any provider.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* @Service()
|
|
10
|
-
* class UserService {
|
|
11
|
-
* constructor(@Inject(MAILER) private mailer: MailerService) {}
|
|
12
|
-
*
|
|
13
|
-
* async sendWelcome(user: User) {
|
|
14
|
-
* await this.mailer.send({
|
|
15
|
-
* to: user.email,
|
|
16
|
-
* subject: 'Welcome!',
|
|
17
|
-
* html: '<h1>Welcome to our app</h1>',
|
|
18
|
-
* })
|
|
19
|
-
* }
|
|
20
|
-
*
|
|
21
|
-
* // Or with templates:
|
|
22
|
-
* async sendInvoice(user: User, invoice: Invoice) {
|
|
23
|
-
* await this.mailer.sendTemplate('invoice', {
|
|
24
|
-
* to: user.email,
|
|
25
|
-
* subject: `Invoice #${invoice.number}`,
|
|
26
|
-
* }, { user, invoice })
|
|
27
|
-
* }
|
|
28
|
-
* }
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export declare class MailerService {
|
|
32
|
-
private provider;
|
|
33
|
-
private defaultFrom?;
|
|
34
|
-
private templateEngine?;
|
|
35
|
-
private enabled;
|
|
36
|
-
constructor(options: MailerOptions);
|
|
37
|
-
/**
|
|
38
|
-
* Send an email message.
|
|
39
|
-
* Applies defaultFrom if no from address is set.
|
|
40
|
-
*/
|
|
41
|
-
send(message: MailMessage): Promise<MailResult>;
|
|
42
|
-
/**
|
|
43
|
-
* Render a template and send the resulting HTML as an email.
|
|
44
|
-
* Requires a templateEngine to be configured.
|
|
45
|
-
*
|
|
46
|
-
* @param template - Template name (resolved by the engine)
|
|
47
|
-
* @param message - Mail message (html will be overwritten by the rendered template)
|
|
48
|
-
* @param data - Template variables
|
|
49
|
-
*/
|
|
50
|
-
sendTemplate(template: string, message: Omit<MailMessage, 'html'>, data: Record<string, any>): Promise<MailResult>;
|
|
51
|
-
/** Get the underlying provider (for advanced use) */
|
|
52
|
-
getProvider(): MailProvider;
|
|
53
|
-
/** Shutdown the provider */
|
|
54
|
-
shutdown(): Promise<void>;
|
|
55
|
-
}
|
|
56
|
-
//# sourceMappingURL=mailer.service.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mailer.service.d.ts","sourceRoot":"","sources":["../src/mailer.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EAGV,aAAa,EACd,MAAM,SAAS,CAAA;AAIhB,8DAA8D;AAC9D,eAAO,MAAM,MAAM,eAA0B,CAAA;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,WAAW,CAAC,CAAe;IACnC,OAAO,CAAC,cAAc,CAAC,CAAoB;IAC3C,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,aAAa;IAOlC;;;OAGG;IACG,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAuBrD;;;;;;;OAOG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACxB,OAAO,CAAC,UAAU,CAAC;IAYtB,qDAAqD;IACrD,WAAW,IAAI,YAAY;IAI3B,4BAA4B;IACtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAKhC"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { MailProvider, MailMessage, MailResult } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
* Console mail provider — logs emails instead of sending them.
|
|
4
|
-
* Perfect for development and testing.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* new MailerAdapter({
|
|
9
|
-
* provider: new ConsoleProvider(),
|
|
10
|
-
* defaultFrom: 'dev@localhost',
|
|
11
|
-
* })
|
|
12
|
-
* ```
|
|
13
|
-
*/
|
|
14
|
-
export declare class ConsoleProvider implements MailProvider {
|
|
15
|
-
name: string;
|
|
16
|
-
send(message: MailMessage): Promise<MailResult>;
|
|
17
|
-
}
|
|
18
|
-
//# sourceMappingURL=console.provider.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"console.provider.d.ts","sourceRoot":"","sources":["../../src/providers/console.provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAMrE;;;;;;;;;;;GAWG;AACH,qBAAa,eAAgB,YAAW,YAAY;IAClD,IAAI,SAAY;IAEV,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;CA0BtD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA"}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { MailProvider, MailMessage, MailResult } from '../types';
|
|
2
|
-
export interface SmtpOptions {
|
|
3
|
-
/** SMTP host (e.g. 'smtp.gmail.com', 'smtp.resend.com') */
|
|
4
|
-
host: string;
|
|
5
|
-
/** SMTP port (default: 587) */
|
|
6
|
-
port?: number;
|
|
7
|
-
/** Use TLS (default: true for port 465, false otherwise) */
|
|
8
|
-
secure?: boolean;
|
|
9
|
-
/** Authentication credentials */
|
|
10
|
-
auth?: {
|
|
11
|
-
user: string;
|
|
12
|
-
pass: string;
|
|
13
|
-
};
|
|
14
|
-
/** Connection timeout in ms (default: 10000) */
|
|
15
|
-
connectionTimeout?: number;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* SMTP mail provider using nodemailer.
|
|
19
|
-
*
|
|
20
|
-
* Requires `nodemailer` as a peer dependency:
|
|
21
|
-
* ```bash
|
|
22
|
-
* pnpm add nodemailer @types/nodemailer
|
|
23
|
-
* ```
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```ts
|
|
27
|
-
* // Gmail
|
|
28
|
-
* new SmtpProvider({
|
|
29
|
-
* host: 'smtp.gmail.com',
|
|
30
|
-
* port: 587,
|
|
31
|
-
* auth: { user: 'you@gmail.com', pass: 'app-password' },
|
|
32
|
-
* })
|
|
33
|
-
*
|
|
34
|
-
* // Resend via SMTP
|
|
35
|
-
* new SmtpProvider({
|
|
36
|
-
* host: 'smtp.resend.com',
|
|
37
|
-
* port: 465,
|
|
38
|
-
* secure: true,
|
|
39
|
-
* auth: { user: 'resend', pass: process.env.RESEND_API_KEY! },
|
|
40
|
-
* })
|
|
41
|
-
*
|
|
42
|
-
* // Mailpit (local dev)
|
|
43
|
-
* new SmtpProvider({ host: 'localhost', port: 1025 })
|
|
44
|
-
* ```
|
|
45
|
-
*/
|
|
46
|
-
export declare class SmtpProvider implements MailProvider {
|
|
47
|
-
private options;
|
|
48
|
-
name: string;
|
|
49
|
-
private transporter;
|
|
50
|
-
constructor(options: SmtpOptions);
|
|
51
|
-
private ensureTransporter;
|
|
52
|
-
send(message: MailMessage): Promise<MailResult>;
|
|
53
|
-
shutdown(): Promise<void>;
|
|
54
|
-
}
|
|
55
|
-
//# sourceMappingURL=smtp.provider.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"smtp.provider.d.ts","sourceRoot":"","sources":["../../src/providers/smtp.provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAErE,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAA;IACZ,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,iCAAiC;IACjC,IAAI,CAAC,EAAE;QACL,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;IACD,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,YAAa,YAAW,YAAY;IAInC,OAAO,CAAC,OAAO;IAH3B,IAAI,SAAS;IACb,OAAO,CAAC,WAAW,CAAK;gBAEJ,OAAO,EAAE,WAAW;YAE1B,iBAAiB;IAiBzB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAuB/C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAKhC"}
|
package/dist/types.d.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
export interface MailAddress {
|
|
2
|
-
name?: string;
|
|
3
|
-
address: string;
|
|
4
|
-
}
|
|
5
|
-
export type MailRecipient = string | MailAddress;
|
|
6
|
-
export interface MailAttachment {
|
|
7
|
-
filename: string;
|
|
8
|
-
content?: string | Buffer;
|
|
9
|
-
path?: string;
|
|
10
|
-
contentType?: string;
|
|
11
|
-
encoding?: string;
|
|
12
|
-
}
|
|
13
|
-
export interface MailMessage {
|
|
14
|
-
/** Sender address */
|
|
15
|
-
from?: MailRecipient;
|
|
16
|
-
/** Recipient(s) */
|
|
17
|
-
to: MailRecipient | MailRecipient[];
|
|
18
|
-
/** CC recipient(s) */
|
|
19
|
-
cc?: MailRecipient | MailRecipient[];
|
|
20
|
-
/** BCC recipient(s) */
|
|
21
|
-
bcc?: MailRecipient | MailRecipient[];
|
|
22
|
-
/** Reply-to address */
|
|
23
|
-
replyTo?: MailRecipient;
|
|
24
|
-
/** Email subject */
|
|
25
|
-
subject: string;
|
|
26
|
-
/** Plain text body */
|
|
27
|
-
text?: string;
|
|
28
|
-
/** HTML body */
|
|
29
|
-
html?: string;
|
|
30
|
-
/** File attachments */
|
|
31
|
-
attachments?: MailAttachment[];
|
|
32
|
-
/** Custom headers */
|
|
33
|
-
headers?: Record<string, string>;
|
|
34
|
-
/** Provider-specific options (e.g. Resend tags, SES configuration set) */
|
|
35
|
-
metadata?: Record<string, any>;
|
|
36
|
-
}
|
|
37
|
-
export interface MailResult {
|
|
38
|
-
/** Provider-assigned message ID */
|
|
39
|
-
messageId: string;
|
|
40
|
-
/** Whether the send was accepted (does not guarantee delivery) */
|
|
41
|
-
accepted: boolean;
|
|
42
|
-
/** Raw response from the provider */
|
|
43
|
-
raw?: any;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Template engine for rendering email bodies.
|
|
47
|
-
* Implement this to use EJS, Handlebars, Pug, or any template system.
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* ```ts
|
|
51
|
-
* import Handlebars from 'handlebars'
|
|
52
|
-
*
|
|
53
|
-
* class HandlebarsEngine implements MailTemplateEngine {
|
|
54
|
-
* private templates = new Map<string, HandlebarsTemplateDelegate>()
|
|
55
|
-
*
|
|
56
|
-
* register(name: string, source: string) {
|
|
57
|
-
* this.templates.set(name, Handlebars.compile(source))
|
|
58
|
-
* }
|
|
59
|
-
*
|
|
60
|
-
* async render(template: string, data: any) {
|
|
61
|
-
* const fn = this.templates.get(template)
|
|
62
|
-
* if (!fn) throw new Error(`Template "${template}" not found`)
|
|
63
|
-
* return fn(data)
|
|
64
|
-
* }
|
|
65
|
-
* }
|
|
66
|
-
* ```
|
|
67
|
-
*/
|
|
68
|
-
export interface MailTemplateEngine {
|
|
69
|
-
/** Render a named template with data. Returns HTML string. */
|
|
70
|
-
render(template: string, data: Record<string, any>): Promise<string> | string;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Abstract mail provider. Implement this to use any email service:
|
|
74
|
-
* SMTP (nodemailer), Resend, AWS SES, SendGrid, Postmark, Mailgun, etc.
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* ```ts
|
|
78
|
-
* class ResendProvider implements MailProvider {
|
|
79
|
-
* name = 'resend'
|
|
80
|
-
* private client: Resend
|
|
81
|
-
*
|
|
82
|
-
* constructor(apiKey: string) {
|
|
83
|
-
* this.client = new Resend(apiKey)
|
|
84
|
-
* }
|
|
85
|
-
*
|
|
86
|
-
* async send(message: MailMessage): Promise<MailResult> {
|
|
87
|
-
* const { data, error } = await this.client.emails.send({
|
|
88
|
-
* from: formatAddress(message.from),
|
|
89
|
-
* to: formatRecipients(message.to),
|
|
90
|
-
* subject: message.subject,
|
|
91
|
-
* html: message.html,
|
|
92
|
-
* text: message.text,
|
|
93
|
-
* })
|
|
94
|
-
* if (error) throw error
|
|
95
|
-
* return { messageId: data.id, accepted: true, raw: data }
|
|
96
|
-
* }
|
|
97
|
-
* }
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
export interface MailProvider {
|
|
101
|
-
/** Provider name for logging */
|
|
102
|
-
name: string;
|
|
103
|
-
/** Send an email message */
|
|
104
|
-
send(message: MailMessage): Promise<MailResult>;
|
|
105
|
-
/** Optional cleanup (close connections, etc.) */
|
|
106
|
-
shutdown?(): Promise<void>;
|
|
107
|
-
}
|
|
108
|
-
export interface MailerOptions {
|
|
109
|
-
/** Mail provider to use */
|
|
110
|
-
provider: MailProvider;
|
|
111
|
-
/** Default "from" address for all emails */
|
|
112
|
-
defaultFrom?: MailRecipient;
|
|
113
|
-
/** Optional template engine for rendering HTML from templates */
|
|
114
|
-
templateEngine?: MailTemplateEngine;
|
|
115
|
-
/** Enable/disable sending (useful for testing — logs instead of sending) */
|
|
116
|
-
enabled?: boolean;
|
|
117
|
-
}
|
|
118
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,CAAA;AAEhD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,qBAAqB;IACrB,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,mBAAmB;IACnB,EAAE,EAAE,aAAa,GAAG,aAAa,EAAE,CAAA;IACnC,sBAAsB;IACtB,EAAE,CAAC,EAAE,aAAa,GAAG,aAAa,EAAE,CAAA;IACpC,uBAAuB;IACvB,GAAG,CAAC,EAAE,aAAa,GAAG,aAAa,EAAE,CAAA;IACrC,uBAAuB;IACvB,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,gBAAgB;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uBAAuB;IACvB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAA;IAC9B,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,kEAAkE;IAClE,QAAQ,EAAE,OAAO,CAAA;IACjB,qCAAqC;IACrC,GAAG,CAAC,EAAE,GAAG,CAAA;CACV;AAID;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAA;CAC9E;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAA;IAEZ,4BAA4B;IAC5B,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IAE/C,iDAAiD;IACjD,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B;AAID,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,QAAQ,EAAE,YAAY,CAAA;IAEtB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,aAAa,CAAA;IAE3B,iEAAiE;IACjE,cAAc,CAAC,EAAE,kBAAkB,CAAA;IAEnC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB"}
|