@gravito/signal 3.0.3 → 3.0.4
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/CHANGELOG.md +16 -0
- package/README.md +89 -60
- package/README.zh-TW.md +140 -9
- package/dist/MjmlRenderer-IUH663FT.mjs +8 -0
- package/dist/ReactMjmlRenderer-C3P5YO5L.mjs +8 -0
- package/dist/ReactRenderer-2JFLRVST.mjs +45 -0
- package/dist/{ReactRenderer-L5INVYKT.mjs → ReactRenderer-LYEOSYFS.mjs} +9 -8
- package/dist/ReactRenderer-V54CUUEI.mjs +45 -0
- package/dist/VueMjmlRenderer-4F4CXHDB.mjs +8 -0
- package/dist/VueMjmlRenderer-5WZR4CQG.mjs +8 -0
- package/dist/VueMjmlRenderer-U5YMWI44.mjs +8 -0
- package/dist/VueRenderer-3YBRQXME.mjs +48 -0
- package/dist/VueRenderer-46JGXTJ2.mjs +48 -0
- package/dist/VueRenderer-5KWD4R3C.mjs +48 -0
- package/dist/VueRenderer-C23U4O5E.mjs +48 -0
- package/dist/VueRenderer-LEVDFLHP.mjs +31 -0
- package/dist/VueRenderer-RNHSCCRI.mjs +48 -0
- package/dist/chunk-3WOR3XSL.mjs +82 -0
- package/dist/chunk-DBFIVHHG.mjs +79 -0
- package/dist/{chunk-6DZX6EAA.mjs → chunk-HEBXNMVQ.mjs} +12 -1
- package/dist/chunk-KB7IDDBT.mjs +82 -0
- package/dist/chunk-LZL5UUPC.mjs +82 -0
- package/dist/chunk-W6LXIJKK.mjs +57 -0
- package/dist/chunk-XBIVBJS2.mjs +8 -0
- package/dist/index.d.mts +1680 -209
- package/dist/index.d.ts +1680 -209
- package/dist/index.js +69405 -542
- package/dist/index.mjs +993 -110
- package/dist/lib-HJTRWKU5.mjs +67788 -0
- package/dist/{VueRenderer-Z5PRVBNH.mjs → server-renderer-4IM3P5XZ.mjs} +308 -423
- package/dist/server-renderer-7KWFSTPV.mjs +37193 -0
- package/dist/{VueRenderer-S65ZARRI.mjs → server-renderer-S5FPSTJ2.mjs} +931 -877
- package/dist/server-renderer-X5LUFVWT.mjs +37193 -0
- package/doc/OPTIMIZATION_PLAN.md +496 -0
- package/package.json +14 -12
- package/scripts/check-coverage.ts +64 -0
- package/src/Mailable.ts +340 -44
- package/src/OrbitSignal.ts +350 -50
- package/src/TypedMailable.ts +96 -0
- package/src/dev/DevMailbox.ts +89 -33
- package/src/dev/DevServer.ts +14 -14
- package/src/dev/storage/FileMailboxStorage.ts +66 -0
- package/src/dev/storage/MailboxStorage.ts +15 -0
- package/src/dev/storage/MemoryMailboxStorage.ts +36 -0
- package/src/dev/ui/mailbox.ts +1 -1
- package/src/dev/ui/preview.ts +4 -4
- package/src/errors.ts +69 -0
- package/src/events.ts +72 -0
- package/src/index.ts +20 -1
- package/src/renderers/HtmlRenderer.ts +20 -18
- package/src/renderers/MjmlRenderer.ts +73 -0
- package/src/renderers/ReactMjmlRenderer.ts +94 -0
- package/src/renderers/ReactRenderer.ts +26 -21
- package/src/renderers/Renderer.ts +43 -3
- package/src/renderers/TemplateRenderer.ts +48 -15
- package/src/renderers/VueMjmlRenderer.ts +99 -0
- package/src/renderers/VueRenderer.ts +26 -21
- package/src/renderers/mjml-templates.ts +50 -0
- package/src/transports/BaseTransport.ts +148 -0
- package/src/transports/LogTransport.ts +28 -6
- package/src/transports/MemoryTransport.ts +34 -6
- package/src/transports/SesTransport.ts +62 -17
- package/src/transports/SmtpTransport.ts +123 -27
- package/src/transports/Transport.ts +33 -4
- package/src/types.ts +172 -3
- package/src/utils/html.ts +43 -0
- package/src/webhooks/SendGridWebhookDriver.ts +80 -0
- package/src/webhooks/SesWebhookDriver.ts +44 -0
- package/tests/DevMailbox.test.ts +54 -0
- package/tests/FileMailboxStorage.test.ts +56 -0
- package/tests/MjmlLayout.test.ts +28 -0
- package/tests/MjmlRenderer.test.ts +53 -0
- package/tests/OrbitSignalWebhook.test.ts +56 -0
- package/tests/ReactMjmlRenderer.test.ts +33 -0
- package/tests/SendGridWebhookDriver.test.ts +69 -0
- package/tests/SesWebhookDriver.test.ts +46 -0
- package/tests/VueMjmlRenderer.test.ts +35 -0
- package/tests/dev-server.test.ts +1 -1
- package/tests/transports.test.ts +3 -3
- package/tsconfig.json +12 -24
- package/dist/OrbitMail-2Z7ZTKYA.mjs +0 -7
- package/dist/OrbitMail-BGV32HWN.mjs +0 -7
- package/dist/OrbitMail-FUYZQSAV.mjs +0 -7
- package/dist/OrbitMail-NAPCRK7B.mjs +0 -7
- package/dist/OrbitMail-REGJ276B.mjs +0 -7
- package/dist/OrbitMail-TCFBJWDT.mjs +0 -7
- package/dist/OrbitMail-XZZW6U4N.mjs +0 -7
- package/dist/OrbitSignal-IPSA2CDO.mjs +0 -7
- package/dist/OrbitSignal-MABW4DDW.mjs +0 -7
- package/dist/OrbitSignal-QSW5VQ5M.mjs +0 -7
- package/dist/OrbitSignal-R22QHWAA.mjs +0 -7
- package/dist/OrbitSignal-ZKKMEC27.mjs +0 -7
- package/dist/chunk-3U2CYJO5.mjs +0 -367
- package/dist/chunk-3XFC4T6M.mjs +0 -392
- package/dist/chunk-456QRYFW.mjs +0 -401
- package/dist/chunk-DT3R2TNV.mjs +0 -367
- package/dist/chunk-F6MVTUCT.mjs +0 -421
- package/dist/chunk-GADWIVC4.mjs +0 -400
- package/dist/chunk-HHKFAMSE.mjs +0 -380
- package/dist/chunk-NEQCQSZI.mjs +0 -406
- package/dist/chunk-OKRNL6PN.mjs +0 -400
- package/dist/chunk-ULN3GMY2.mjs +0 -367
- package/dist/chunk-XAWO7RSP.mjs +0 -398
- package/dist/chunk-YLVDJSED.mjs +0 -431
package/dist/index.d.ts
CHANGED
|
@@ -3,23 +3,80 @@ export { Queueable } from '@gravito/stream';
|
|
|
3
3
|
import { GravitoContext, GravitoOrbit, PlanetCore } from '@gravito/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Interface for email transport mechanisms
|
|
6
|
+
* Interface for email transport mechanisms.
|
|
7
|
+
*
|
|
8
|
+
* Transports are responsible for the final delivery of an email message to its destination,
|
|
9
|
+
* whether it's a real SMTP server, a cloud service like AWS SES, or a local log for development.
|
|
10
|
+
* This abstraction allows the core mail service to remain agnostic of the delivery method.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* class CustomTransport implements Transport {
|
|
15
|
+
* async send(message: Message): Promise<void> {
|
|
16
|
+
* // Implementation logic to deliver the message
|
|
17
|
+
* console.log(`Sending email to ${message.to[0].address}`);
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
7
21
|
*
|
|
8
22
|
* @public
|
|
9
|
-
* @since 3.0.0
|
|
10
23
|
*/
|
|
11
24
|
interface Transport {
|
|
12
25
|
/**
|
|
13
|
-
* Send the given message using the underlying transport.
|
|
26
|
+
* Send the given message using the underlying transport mechanism.
|
|
14
27
|
*
|
|
15
|
-
*
|
|
28
|
+
* This method handles the actual communication with the delivery service.
|
|
29
|
+
* Implementations should handle connection management and protocol-specific logic.
|
|
30
|
+
*
|
|
31
|
+
* @param message - The finalized message object containing recipients, subject, and content.
|
|
32
|
+
* @returns A promise that resolves when the message has been successfully handed off to the transport.
|
|
33
|
+
* @throws {MailTransportError} If the delivery fails after all internal retry attempts.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const transport: Transport = new LogTransport();
|
|
38
|
+
* await transport.send({
|
|
39
|
+
* from: { address: 'sender@example.com' },
|
|
40
|
+
* to: [{ address: 'receiver@example.com' }],
|
|
41
|
+
* subject: 'Hello',
|
|
42
|
+
* html: '<p>World</p>'
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
16
45
|
*/
|
|
17
46
|
send(message: Message): Promise<void>;
|
|
18
47
|
}
|
|
19
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Interface for Webhook Drivers.
|
|
51
|
+
*/
|
|
52
|
+
interface WebhookDriver {
|
|
53
|
+
handle(c: GravitoContext): Promise<{
|
|
54
|
+
event: string;
|
|
55
|
+
payload: any;
|
|
56
|
+
}[] | null>;
|
|
57
|
+
}
|
|
58
|
+
|
|
20
59
|
/**
|
|
21
60
|
* Representation of an email address with optional display name.
|
|
22
61
|
*
|
|
62
|
+
* Defines the structure for email addresses used throughout the mail system.
|
|
63
|
+
* Supports both simple string addresses and formatted addresses with display names.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* // Simple address
|
|
68
|
+
* const addr1: Address = { address: 'user@example.com' }
|
|
69
|
+
*
|
|
70
|
+
* // Address with display name
|
|
71
|
+
* const addr2: Address = {
|
|
72
|
+
* name: 'John Doe',
|
|
73
|
+
* address: 'john@example.com'
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @see {@link Envelope} For the email envelope structure
|
|
78
|
+
* @see {@link Message} For the complete message structure
|
|
79
|
+
*
|
|
23
80
|
* @public
|
|
24
81
|
* @since 3.0.0
|
|
25
82
|
*/
|
|
@@ -32,6 +89,29 @@ interface Address {
|
|
|
32
89
|
/**
|
|
33
90
|
* Configuration for an email attachment.
|
|
34
91
|
*
|
|
92
|
+
* Defines file attachments for email messages. Supports both regular attachments
|
|
93
|
+
* and inline attachments (e.g., embedded images referenced via Content-ID).
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* // Regular file attachment
|
|
98
|
+
* const attachment: Attachment = {
|
|
99
|
+
* filename: 'document.pdf',
|
|
100
|
+
* content: Buffer.from('...'),
|
|
101
|
+
* contentType: 'application/pdf'
|
|
102
|
+
* }
|
|
103
|
+
*
|
|
104
|
+
* // Inline image attachment
|
|
105
|
+
* const inlineImage: Attachment = {
|
|
106
|
+
* filename: 'logo.png',
|
|
107
|
+
* content: Buffer.from('...'),
|
|
108
|
+
* contentType: 'image/png',
|
|
109
|
+
* cid: 'logo@example.com' // Reference in HTML: <img src="cid:logo@example.com">
|
|
110
|
+
* }
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* @see {@link Envelope} For adding attachments to emails
|
|
114
|
+
*
|
|
35
115
|
* @public
|
|
36
116
|
* @since 3.0.0
|
|
37
117
|
*/
|
|
@@ -50,7 +130,22 @@ interface Attachment {
|
|
|
50
130
|
/**
|
|
51
131
|
* The envelope containing metadata for an email message.
|
|
52
132
|
*
|
|
53
|
-
* Used during the construction phase of a Mailable.
|
|
133
|
+
* Used during the construction phase of a Mailable. All fields are optional
|
|
134
|
+
* at this stage and will be validated when converting to a Message.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const envelope: Envelope = {
|
|
139
|
+
* from: { name: 'App', address: 'noreply@app.com' },
|
|
140
|
+
* to: [{ address: 'user@example.com' }],
|
|
141
|
+
* subject: 'Welcome!',
|
|
142
|
+
* priority: 'high',
|
|
143
|
+
* replyTo: { address: 'support@app.com' }
|
|
144
|
+
* }
|
|
145
|
+
* ```
|
|
146
|
+
*
|
|
147
|
+
* @see {@link Mailable} For building envelopes fluently
|
|
148
|
+
* @see {@link Message} For the validated, finalized message structure
|
|
54
149
|
*
|
|
55
150
|
* @public
|
|
56
151
|
* @since 3.0.0
|
|
@@ -77,6 +172,23 @@ interface Envelope {
|
|
|
77
172
|
* A fully finalized email message ready to be sent by a transport.
|
|
78
173
|
*
|
|
79
174
|
* Requires mandatory fields that were optional in the Envelope.
|
|
175
|
+
* This structure is passed to Transport implementations for actual delivery.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* const message: Message = {
|
|
180
|
+
* from: { name: 'App', address: 'noreply@app.com' },
|
|
181
|
+
* to: [{ address: 'user@example.com' }],
|
|
182
|
+
* subject: 'Welcome to our service',
|
|
183
|
+
* html: '<h1>Welcome!</h1><p>Thanks for joining.</p>',
|
|
184
|
+
* text: 'Welcome! Thanks for joining.',
|
|
185
|
+
* priority: 'normal',
|
|
186
|
+
* headers: { 'X-Custom-Header': 'value' }
|
|
187
|
+
* }
|
|
188
|
+
* ```
|
|
189
|
+
*
|
|
190
|
+
* @see {@link Envelope} For the construction phase structure
|
|
191
|
+
* @see {@link Transport} For implementations that send messages
|
|
80
192
|
*
|
|
81
193
|
* @public
|
|
82
194
|
* @since 3.0.0
|
|
@@ -98,106 +210,330 @@ interface Message extends Envelope {
|
|
|
98
210
|
/**
|
|
99
211
|
* Global configuration options for OrbitSignal and Mailable instances.
|
|
100
212
|
*
|
|
213
|
+
* Configures the mail service behavior, transport mechanism, development tools,
|
|
214
|
+
* and internationalization support.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* import { OrbitSignal, SmtpTransport } from '@gravito/signal'
|
|
219
|
+
*
|
|
220
|
+
* const config: MailConfig = {
|
|
221
|
+
* from: { name: 'My App', address: 'noreply@myapp.com' },
|
|
222
|
+
* transport: new SmtpTransport({
|
|
223
|
+
* host: 'smtp.mailtrap.io',
|
|
224
|
+
* port: 2525,
|
|
225
|
+
* auth: { user: 'user', pass: 'pass' }
|
|
226
|
+
* }),
|
|
227
|
+
* devMode: process.env.NODE_ENV === 'development',
|
|
228
|
+
* viewsDir: './src/emails',
|
|
229
|
+
* devUiPrefix: '/__mail',
|
|
230
|
+
* translator: (key, replace, locale) => i18n.t(key, { ...replace, locale })
|
|
231
|
+
* }
|
|
232
|
+
*
|
|
233
|
+
* const mail = new OrbitSignal(config)
|
|
234
|
+
* ```
|
|
235
|
+
*
|
|
236
|
+
* @see {@link OrbitSignal} For the mail service implementation
|
|
237
|
+
* @see {@link Transport} For available transport options
|
|
238
|
+
*
|
|
101
239
|
* @public
|
|
102
240
|
* @since 3.0.0
|
|
103
241
|
*/
|
|
104
242
|
interface MailConfig {
|
|
105
243
|
/**
|
|
106
244
|
* Default sender address used if not specified in the Mailable.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* from: { name: 'My App', address: 'noreply@myapp.com' }
|
|
249
|
+
* ```
|
|
107
250
|
*/
|
|
108
251
|
from?: Address;
|
|
109
252
|
/**
|
|
110
253
|
* The transport mechanism used to send emails (e.g., SMTP, SES, Log).
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* import { SmtpTransport } from '@gravito/signal'
|
|
258
|
+
* transport: new SmtpTransport({ host: 'smtp.example.com', port: 587 })
|
|
259
|
+
* ```
|
|
111
260
|
*/
|
|
112
261
|
transport?: Transport;
|
|
113
262
|
/**
|
|
114
263
|
* Enable development mode.
|
|
115
264
|
* When true, emails are intercepted by the DevMailbox instead of being sent.
|
|
265
|
+
*
|
|
266
|
+
* @default false
|
|
267
|
+
* @example
|
|
268
|
+
* ```typescript
|
|
269
|
+
* devMode: process.env.NODE_ENV === 'development'
|
|
270
|
+
* ```
|
|
116
271
|
*/
|
|
117
272
|
devMode?: boolean | undefined;
|
|
118
273
|
/**
|
|
119
274
|
* Directory where email templates are located for use with OrbitPrism.
|
|
120
|
-
*
|
|
275
|
+
*
|
|
276
|
+
* @default "src/emails"
|
|
277
|
+
* @example
|
|
278
|
+
* ```typescript
|
|
279
|
+
* viewsDir: './resources/views/emails'
|
|
280
|
+
* ```
|
|
121
281
|
*/
|
|
122
282
|
viewsDir?: string | undefined;
|
|
123
283
|
/**
|
|
124
284
|
* URL prefix for the Mail Dev UI.
|
|
125
|
-
*
|
|
285
|
+
*
|
|
286
|
+
* @default "/__mail"
|
|
287
|
+
* @example
|
|
288
|
+
* ```typescript
|
|
289
|
+
* devUiPrefix: '/dev/mailbox'
|
|
290
|
+
* ```
|
|
126
291
|
*/
|
|
127
292
|
devUiPrefix?: string | undefined;
|
|
128
293
|
/**
|
|
129
294
|
* Whether to allow access to the Mail Dev UI in production environments.
|
|
295
|
+
*
|
|
130
296
|
* @default false
|
|
297
|
+
* @example
|
|
298
|
+
* ```typescript
|
|
299
|
+
* devUiAllowInProduction: process.env.ALLOW_MAIL_UI === 'true'
|
|
300
|
+
* ```
|
|
131
301
|
*/
|
|
132
302
|
devUiAllowInProduction?: boolean | undefined;
|
|
133
303
|
/**
|
|
134
304
|
* Authorization gate for the Mail Dev UI.
|
|
135
305
|
* Should return true to allow access to the UI.
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```typescript
|
|
309
|
+
* devUiGate: async (ctx) => {
|
|
310
|
+
* const user = await ctx.get('auth').user()
|
|
311
|
+
* return user?.role === 'admin'
|
|
312
|
+
* }
|
|
313
|
+
* ```
|
|
136
314
|
*/
|
|
137
315
|
devUiGate?: ((ctx: GravitoContext) => boolean | Promise<boolean>) | undefined;
|
|
138
316
|
/**
|
|
139
317
|
* Translation function for internationalization within emails.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```typescript
|
|
321
|
+
* translator: (key, replace, locale) => {
|
|
322
|
+
* return i18n.t(key, { ...replace, locale: locale || 'en' })
|
|
323
|
+
* }
|
|
324
|
+
* ```
|
|
140
325
|
*/
|
|
141
326
|
translator?: ((key: string, replace?: Record<string, unknown>, locale?: string) => string) | undefined;
|
|
327
|
+
/**
|
|
328
|
+
* URL prefix for Webhook endpoints.
|
|
329
|
+
*/
|
|
330
|
+
webhookPrefix?: string | undefined;
|
|
331
|
+
/**
|
|
332
|
+
* Dictionary of registered webhook drivers.
|
|
333
|
+
*/
|
|
334
|
+
webhookDrivers?: Record<string, WebhookDriver> | undefined;
|
|
142
335
|
}
|
|
143
336
|
|
|
144
337
|
/**
|
|
145
|
-
*
|
|
146
|
-
|
|
147
|
-
|
|
338
|
+
* Interface for DevMailbox storage engines.
|
|
339
|
+
*/
|
|
340
|
+
interface MailboxStorage {
|
|
341
|
+
/** Retrieve all entries. */
|
|
342
|
+
all(): Promise<MailboxEntry[]>;
|
|
343
|
+
/** Add a single entry. */
|
|
344
|
+
push(entry: MailboxEntry): Promise<void>;
|
|
345
|
+
/** Trim entries to a specific count. */
|
|
346
|
+
trim(max: number): Promise<void>;
|
|
347
|
+
clear(): Promise<void>;
|
|
348
|
+
delete?(id: string): Promise<boolean>;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Entry structure for messages stored in DevMailbox.
|
|
148
353
|
*/
|
|
149
354
|
interface MailboxEntry {
|
|
150
|
-
/** Unique identifier for the
|
|
355
|
+
/** Unique identifier for the entry. */
|
|
151
356
|
id: string;
|
|
152
|
-
/**
|
|
153
|
-
envelope:
|
|
154
|
-
/** HTML content
|
|
357
|
+
/** The email envelope metadata. */
|
|
358
|
+
envelope: any;
|
|
359
|
+
/** The rendered HTML content. */
|
|
155
360
|
html: string;
|
|
156
|
-
/**
|
|
361
|
+
/** Optional plain text content. */
|
|
157
362
|
text?: string;
|
|
158
|
-
/** Timestamp when the
|
|
363
|
+
/** Timestamp when the message was captured. */
|
|
159
364
|
sentAt: Date;
|
|
160
365
|
}
|
|
161
366
|
/**
|
|
162
|
-
*
|
|
367
|
+
* Capture and store emails during development for preview and testing.
|
|
163
368
|
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
369
|
+
* Supports different storage engines (Memory, FileSystem) and implements
|
|
370
|
+
* capacity limits via a Ring Buffer strategy.
|
|
166
371
|
*
|
|
167
372
|
* @example
|
|
168
373
|
* ```typescript
|
|
374
|
+
* // Default memory mailbox
|
|
169
375
|
* const mailbox = new DevMailbox()
|
|
170
|
-
*
|
|
171
|
-
*
|
|
376
|
+
*
|
|
377
|
+
* // Persistent file mailbox
|
|
378
|
+
* const storage = new FileMailboxStorage('./storage/mail')
|
|
379
|
+
* const persistentMailbox = new DevMailbox(100, storage)
|
|
172
380
|
* ```
|
|
173
381
|
*
|
|
174
382
|
* @since 3.0.0
|
|
175
383
|
* @public
|
|
176
384
|
*/
|
|
177
385
|
declare class DevMailbox {
|
|
178
|
-
private
|
|
179
|
-
private
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
386
|
+
private storage;
|
|
387
|
+
private _maxEntries;
|
|
388
|
+
/**
|
|
389
|
+
* Creates an instance of DevMailbox.
|
|
390
|
+
*
|
|
391
|
+
* @param maxEntries - Maximum number of emails to store (default: 50)
|
|
392
|
+
* @param storage - Optional custom storage engine (defaults to Memory)
|
|
393
|
+
*/
|
|
394
|
+
constructor(maxEntries?: number, storage?: MailboxStorage);
|
|
395
|
+
/**
|
|
396
|
+
* Adds a new message to the mailbox.
|
|
397
|
+
*
|
|
398
|
+
* If the mailbox exceeds the maximum capacity, the oldest messages are removed.
|
|
399
|
+
*/
|
|
400
|
+
add(message: Message): Promise<MailboxEntry>;
|
|
401
|
+
/**
|
|
402
|
+
* Sets the maximum number of emails to store.
|
|
403
|
+
*
|
|
404
|
+
* If the current mailbox exceeds the new capacity, the oldest messages are removed.
|
|
405
|
+
*/
|
|
406
|
+
setMaxEntries(count: number): Promise<void>;
|
|
407
|
+
/**
|
|
408
|
+
* Returns the maximum capacity of the mailbox.
|
|
409
|
+
*/
|
|
410
|
+
get maxEntries(): number;
|
|
411
|
+
/**
|
|
412
|
+
* Lists all messages in the mailbox.
|
|
413
|
+
*/
|
|
414
|
+
list(): Promise<MailboxEntry[]>;
|
|
415
|
+
/**
|
|
416
|
+
* Retrieves a specific message by ID.
|
|
417
|
+
*/
|
|
418
|
+
get(id: string): Promise<MailboxEntry | undefined>;
|
|
419
|
+
/**
|
|
420
|
+
* Deletes a specific message by ID.
|
|
421
|
+
*/
|
|
422
|
+
delete(id: string): Promise<boolean>;
|
|
423
|
+
/**
|
|
424
|
+
* Clears all messages from the mailbox.
|
|
425
|
+
*/
|
|
426
|
+
clear(): Promise<void>;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Mail transport error codes.
|
|
431
|
+
*
|
|
432
|
+
* Categorizes common failure modes in the mail delivery process to allow
|
|
433
|
+
* for programmatic handling (e.g., retries on rate limits).
|
|
434
|
+
*
|
|
435
|
+
* @public
|
|
436
|
+
* @since 3.1.0
|
|
437
|
+
*/
|
|
438
|
+
declare enum MailErrorCode {
|
|
439
|
+
/** Connection to mail server failed */
|
|
440
|
+
CONNECTION_FAILED = "CONNECTION_FAILED",
|
|
441
|
+
/** Authentication with mail server failed */
|
|
442
|
+
AUTH_FAILED = "AUTH_FAILED",
|
|
443
|
+
/** One or more recipients were rejected by the server */
|
|
444
|
+
RECIPIENT_REJECTED = "RECIPIENT_REJECTED",
|
|
445
|
+
/** The message was rejected by the server */
|
|
446
|
+
MESSAGE_REJECTED = "MESSAGE_REJECTED",
|
|
447
|
+
/** Rate limit exceeded */
|
|
448
|
+
RATE_LIMIT = "RATE_LIMIT",
|
|
449
|
+
/** Unknown or unclassified error */
|
|
450
|
+
UNKNOWN = "UNKNOWN"
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Error class for mail transport failures.
|
|
454
|
+
*
|
|
455
|
+
* Provides structured error information for mail sending failures,
|
|
456
|
+
* including error codes and original cause tracking for debugging.
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```typescript
|
|
460
|
+
* throw new MailTransportError(
|
|
461
|
+
* 'Failed to connect to SMTP server',
|
|
462
|
+
* MailErrorCode.CONNECTION_FAILED,
|
|
463
|
+
* originalError
|
|
464
|
+
* )
|
|
465
|
+
* ```
|
|
466
|
+
*
|
|
467
|
+
* @public
|
|
468
|
+
* @since 3.1.0
|
|
469
|
+
*/
|
|
470
|
+
declare class MailTransportError extends Error {
|
|
471
|
+
readonly code: MailErrorCode;
|
|
472
|
+
readonly cause?: Error | undefined;
|
|
473
|
+
/**
|
|
474
|
+
* Create a new mail transport error.
|
|
475
|
+
*
|
|
476
|
+
* @param message - Human-readable error message
|
|
477
|
+
* @param code - Categorized error code
|
|
478
|
+
* @param cause - Original error that caused this failure
|
|
479
|
+
*
|
|
480
|
+
* @example
|
|
481
|
+
* ```typescript
|
|
482
|
+
* const error = new MailTransportError('Auth failed', MailErrorCode.AUTH_FAILED);
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
constructor(message: string, code: MailErrorCode, cause?: Error | undefined);
|
|
185
486
|
}
|
|
186
487
|
|
|
187
488
|
/**
|
|
188
489
|
* Result of a content rendering operation.
|
|
189
490
|
*
|
|
491
|
+
* This interface defines the structure of the output produced by any renderer.
|
|
492
|
+
* It ensures consistency across different rendering strategies (HTML, React, Vue, etc.),
|
|
493
|
+
* providing both the final HTML for the email body and an optional plain text version
|
|
494
|
+
* for clients that do not support HTML.
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```typescript
|
|
498
|
+
* const result: RenderResult = {
|
|
499
|
+
* html: '<html><body><h1>Hello</h1></body></html>',
|
|
500
|
+
* text: 'Hello'
|
|
501
|
+
* };
|
|
502
|
+
* ```
|
|
503
|
+
*
|
|
190
504
|
* @public
|
|
191
505
|
* @since 3.0.0
|
|
192
506
|
*/
|
|
193
507
|
interface RenderResult {
|
|
194
|
-
/**
|
|
508
|
+
/**
|
|
509
|
+
* The rendered HTML string.
|
|
510
|
+
*
|
|
511
|
+
* This is the primary content used for the email body.
|
|
512
|
+
*/
|
|
195
513
|
html: string;
|
|
196
|
-
/**
|
|
514
|
+
/**
|
|
515
|
+
* Optional rendered plain text string.
|
|
516
|
+
*
|
|
517
|
+
* Used as a fallback for email clients that cannot display HTML or for accessibility.
|
|
518
|
+
*/
|
|
197
519
|
text?: string;
|
|
198
520
|
}
|
|
199
521
|
/**
|
|
200
|
-
* Interface for email content renderers
|
|
522
|
+
* Interface for email content renderers.
|
|
523
|
+
*
|
|
524
|
+
* Renderers are responsible for transforming various input formats (raw HTML,
|
|
525
|
+
* templates, or UI components) into a standardized {@link RenderResult}.
|
|
526
|
+
* This abstraction allows the mail system to support multiple view engines
|
|
527
|
+
* and frameworks interchangeably.
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```typescript
|
|
531
|
+
* class MyRenderer implements Renderer {
|
|
532
|
+
* async render(data: Record<string, unknown>): Promise<RenderResult> {
|
|
533
|
+
* return { html: `<div>${data.name}</div>`, text: String(data.name) };
|
|
534
|
+
* }
|
|
535
|
+
* }
|
|
536
|
+
* ```
|
|
201
537
|
*
|
|
202
538
|
* @public
|
|
203
539
|
* @since 3.0.0
|
|
@@ -206,7 +542,12 @@ interface Renderer {
|
|
|
206
542
|
/**
|
|
207
543
|
* Render the content into HTML and optionally plain text.
|
|
208
544
|
*
|
|
545
|
+
* This method performs the actual transformation of the source content
|
|
546
|
+
* using the provided data context.
|
|
547
|
+
*
|
|
209
548
|
* @param data - The data context for rendering.
|
|
549
|
+
* @returns A promise resolving to the rendered content.
|
|
550
|
+
* @throws {Error} If rendering fails due to syntax errors or missing dependencies.
|
|
210
551
|
*/
|
|
211
552
|
render(data: Record<string, unknown>): Promise<RenderResult>;
|
|
212
553
|
}
|
|
@@ -215,21 +556,53 @@ type ComponentType = any;
|
|
|
215
556
|
/**
|
|
216
557
|
* Base class for all mailable messages.
|
|
217
558
|
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
559
|
+
* @description
|
|
560
|
+
* Mailable provides a fluent API to build email envelopes and render content
|
|
561
|
+
* using multiple engines: HTML, Prism templates, React, and Vue components.
|
|
562
|
+
*
|
|
563
|
+
* @architecture
|
|
564
|
+
* ```
|
|
565
|
+
* Mailable
|
|
566
|
+
* ├── Envelope (from, to, subject, cc, bcc, attachments)
|
|
567
|
+
* ├── Renderer (HtmlRenderer | TemplateRenderer | ReactRenderer | VueRenderer)
|
|
568
|
+
* └── Queueable (queue support interface)
|
|
569
|
+
* ```
|
|
570
|
+
*
|
|
571
|
+
* @lifecycle
|
|
572
|
+
* 1. Create Mailable subclass
|
|
573
|
+
* 2. Implement build() method to configure envelope and content
|
|
574
|
+
* 3. Call send() to send immediately, or queue() for background processing
|
|
575
|
+
* 4. OrbitSignal calls buildEnvelope() → renderContent() → transport.send()
|
|
220
576
|
*
|
|
221
577
|
* @example
|
|
222
578
|
* ```typescript
|
|
223
|
-
*
|
|
579
|
+
* import { Mailable } from '@gravito/signal'
|
|
580
|
+
*
|
|
581
|
+
* class WelcomeEmail extends Mailable {
|
|
582
|
+
* constructor(private user: User) {
|
|
583
|
+
* super()
|
|
584
|
+
* }
|
|
585
|
+
*
|
|
224
586
|
* build() {
|
|
225
|
-
* return this
|
|
226
|
-
*
|
|
587
|
+
* return this
|
|
588
|
+
* .to(this.user.email)
|
|
589
|
+
* .subject('Welcome!')
|
|
590
|
+
* .view('emails/welcome', { name: this.user.name })
|
|
227
591
|
* }
|
|
228
592
|
* }
|
|
229
593
|
*
|
|
230
|
-
*
|
|
594
|
+
* // Send immediately
|
|
595
|
+
* await mail.send(new WelcomeEmail(user))
|
|
596
|
+
*
|
|
597
|
+
* // Queue for background processing
|
|
598
|
+
* await new WelcomeEmail(user).onQueue('emails').queue()
|
|
231
599
|
* ```
|
|
232
600
|
*
|
|
601
|
+
* @see {@link OrbitSignal} Mail service orchestrator
|
|
602
|
+
* @see {@link Renderer} Content rendering interface
|
|
603
|
+
* @see {@link Envelope} Email metadata structure
|
|
604
|
+
* @see {@link Queueable} Queue integration interface
|
|
605
|
+
*
|
|
233
606
|
* @public
|
|
234
607
|
* @since 3.0.0
|
|
235
608
|
*/
|
|
@@ -242,71 +615,167 @@ declare abstract class Mailable implements Queueable {
|
|
|
242
615
|
/**
|
|
243
616
|
* Set the sender address for the email.
|
|
244
617
|
*
|
|
245
|
-
*
|
|
618
|
+
* This defines the "From" field in the email envelope. If not called, the default
|
|
619
|
+
* sender from the mail configuration will be used.
|
|
620
|
+
*
|
|
621
|
+
* @param address - The email address or address object containing name and address.
|
|
622
|
+
* @returns The current mailable instance for chaining.
|
|
623
|
+
*
|
|
624
|
+
* @example
|
|
625
|
+
* ```typescript
|
|
626
|
+
* mailable.from('admin@example.com')
|
|
627
|
+
* mailable.from({ name: 'Support', address: 'support@example.com' })
|
|
628
|
+
* ```
|
|
246
629
|
*/
|
|
247
630
|
from(address: string | Address): this;
|
|
248
631
|
/**
|
|
249
632
|
* Set the primary recipient(s) for the email.
|
|
250
633
|
*
|
|
251
|
-
*
|
|
634
|
+
* Configures the "To" field. Supports single or multiple recipients in various formats.
|
|
635
|
+
*
|
|
636
|
+
* @param address - A single email string, an address object, or an array of either.
|
|
637
|
+
* @returns The current mailable instance for chaining.
|
|
638
|
+
*
|
|
639
|
+
* @example
|
|
640
|
+
* ```typescript
|
|
641
|
+
* mailable.to('user@example.com')
|
|
642
|
+
* mailable.to(['a@example.com', 'b@example.com'])
|
|
643
|
+
* mailable.to({ name: 'John', address: 'john@example.com' })
|
|
644
|
+
* ```
|
|
252
645
|
*/
|
|
253
646
|
to(address: string | Address | (string | Address)[]): this;
|
|
254
647
|
/**
|
|
255
648
|
* Set the carbon copy (CC) recipient(s).
|
|
256
649
|
*
|
|
257
|
-
*
|
|
650
|
+
* Adds recipients to the "Cc" field of the email.
|
|
651
|
+
*
|
|
652
|
+
* @param address - A single email string, an address object, or an array of either.
|
|
653
|
+
* @returns The current mailable instance for chaining.
|
|
654
|
+
*
|
|
655
|
+
* @example
|
|
656
|
+
* ```typescript
|
|
657
|
+
* mailable.cc('manager@example.com')
|
|
658
|
+
* ```
|
|
258
659
|
*/
|
|
259
660
|
cc(address: string | Address | (string | Address)[]): this;
|
|
260
661
|
/**
|
|
261
662
|
* Set the blind carbon copy (BCC) recipient(s).
|
|
262
663
|
*
|
|
263
|
-
*
|
|
664
|
+
* Adds recipients to the "Bcc" field. These recipients are hidden from others.
|
|
665
|
+
*
|
|
666
|
+
* @param address - A single email string, an address object, or an array of either.
|
|
667
|
+
* @returns The current mailable instance for chaining.
|
|
668
|
+
*
|
|
669
|
+
* @example
|
|
670
|
+
* ```typescript
|
|
671
|
+
* mailable.bcc('audit@example.com')
|
|
672
|
+
* ```
|
|
264
673
|
*/
|
|
265
674
|
bcc(address: string | Address | (string | Address)[]): this;
|
|
266
675
|
/**
|
|
267
676
|
* Set the reply-to address.
|
|
268
677
|
*
|
|
269
|
-
*
|
|
678
|
+
* Specifies where replies to this email should be directed.
|
|
679
|
+
*
|
|
680
|
+
* @param address - The email address or address object for replies.
|
|
681
|
+
* @returns The current mailable instance for chaining.
|
|
682
|
+
*
|
|
683
|
+
* @example
|
|
684
|
+
* ```typescript
|
|
685
|
+
* mailable.replyTo('no-reply@example.com')
|
|
686
|
+
* ```
|
|
270
687
|
*/
|
|
271
688
|
replyTo(address: string | Address): this;
|
|
272
689
|
/**
|
|
273
690
|
* Set the subject line for the email.
|
|
274
691
|
*
|
|
275
|
-
*
|
|
692
|
+
* Defines the text that appears in the recipient's inbox subject field.
|
|
693
|
+
*
|
|
694
|
+
* @param subject - The subject text.
|
|
695
|
+
* @returns The current mailable instance for chaining.
|
|
696
|
+
*
|
|
697
|
+
* @example
|
|
698
|
+
* ```typescript
|
|
699
|
+
* mailable.subject('Your Order Confirmation')
|
|
700
|
+
* ```
|
|
276
701
|
*/
|
|
277
702
|
subject(subject: string): this;
|
|
278
703
|
/**
|
|
279
704
|
* Set the email priority.
|
|
280
705
|
*
|
|
281
|
-
*
|
|
706
|
+
* Hints to the email client how urgent this message is.
|
|
707
|
+
*
|
|
708
|
+
* @param level - The priority level: 'high', 'normal', or 'low'.
|
|
709
|
+
* @returns The current mailable instance for chaining.
|
|
710
|
+
*
|
|
711
|
+
* @example
|
|
712
|
+
* ```typescript
|
|
713
|
+
* mailable.emailPriority('high')
|
|
714
|
+
* ```
|
|
282
715
|
*/
|
|
283
716
|
emailPriority(level: 'high' | 'normal' | 'low'): this;
|
|
284
717
|
/**
|
|
285
718
|
* Attach a file to the email.
|
|
286
719
|
*
|
|
287
|
-
*
|
|
720
|
+
* Adds a file attachment to the message. Can be called multiple times for multiple files.
|
|
721
|
+
*
|
|
722
|
+
* @param attachment - The attachment configuration including path, content, or filename.
|
|
723
|
+
* @returns The current mailable instance for chaining.
|
|
724
|
+
*
|
|
725
|
+
* @example
|
|
726
|
+
* ```typescript
|
|
727
|
+
* mailable.attach({
|
|
728
|
+
* filename: 'invoice.pdf',
|
|
729
|
+
* path: './storage/invoices/123.pdf'
|
|
730
|
+
* })
|
|
731
|
+
* ```
|
|
288
732
|
*/
|
|
289
733
|
attach(attachment: Attachment): this;
|
|
290
734
|
/**
|
|
291
|
-
* Set the content using raw HTML string.
|
|
735
|
+
* Set the content using a raw HTML string.
|
|
292
736
|
*
|
|
293
|
-
*
|
|
737
|
+
* Use this for simple emails where a full template engine is not required.
|
|
738
|
+
*
|
|
739
|
+
* @param content - The raw HTML content.
|
|
740
|
+
* @returns The current mailable instance for chaining.
|
|
741
|
+
*
|
|
742
|
+
* @example
|
|
743
|
+
* ```typescript
|
|
744
|
+
* mailable.html('<h1>Hello</h1><p>Welcome to our platform.</p>')
|
|
745
|
+
* ```
|
|
294
746
|
*/
|
|
295
747
|
html(content: string): this;
|
|
296
748
|
/**
|
|
297
749
|
* Set the content using an OrbitPrism template.
|
|
298
750
|
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
751
|
+
* Renders a template file with the provided data. This is the recommended way
|
|
752
|
+
* to build complex, data-driven emails.
|
|
753
|
+
*
|
|
754
|
+
* @param template - The template name or path relative to the configured views directory.
|
|
755
|
+
* @param data - The data object to be injected into the template.
|
|
756
|
+
* @returns The current mailable instance for chaining.
|
|
757
|
+
*
|
|
758
|
+
* @example
|
|
759
|
+
* ```typescript
|
|
760
|
+
* mailable.view('emails.welcome', { name: 'Alice' })
|
|
761
|
+
* ```
|
|
301
762
|
*/
|
|
302
763
|
view(template: string, data?: Record<string, unknown>): this;
|
|
303
764
|
/**
|
|
304
765
|
* Set the content using a React component.
|
|
305
|
-
* Dynamically imports ReactRenderer to avoid hard dependency errors if React is not installed.
|
|
306
766
|
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
767
|
+
* Leverages React's component model for email design. The renderer is loaded
|
|
768
|
+
* dynamically to keep the core package lightweight.
|
|
769
|
+
*
|
|
770
|
+
* @param component - The React component class or function.
|
|
771
|
+
* @param props - The properties to pass to the component.
|
|
772
|
+
* @param deps - Optional React/ReactDOMServer overrides for custom environments.
|
|
773
|
+
* @returns The current mailable instance for chaining.
|
|
774
|
+
*
|
|
775
|
+
* @example
|
|
776
|
+
* ```typescript
|
|
777
|
+
* mailable.react(WelcomeEmailComponent, { name: 'Alice' })
|
|
778
|
+
* ```
|
|
310
779
|
*/
|
|
311
780
|
react<P extends object>(component: ComponentType, props?: P, deps?: {
|
|
312
781
|
createElement?: (...args: any[]) => any;
|
|
@@ -314,11 +783,19 @@ declare abstract class Mailable implements Queueable {
|
|
|
314
783
|
}): this;
|
|
315
784
|
/**
|
|
316
785
|
* Set the content using a Vue component.
|
|
317
|
-
* Dynamically imports VueRenderer to avoid hard dependency errors if Vue is not installed.
|
|
318
786
|
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
787
|
+
* Leverages Vue's component model for email design. The renderer is loaded
|
|
788
|
+
* dynamically to keep the core package lightweight.
|
|
789
|
+
*
|
|
790
|
+
* @param component - The Vue component object.
|
|
791
|
+
* @param props - The properties to pass to the component.
|
|
792
|
+
* @param deps - Optional Vue/VueServerRenderer overrides for custom environments.
|
|
793
|
+
* @returns The current mailable instance for chaining.
|
|
794
|
+
*
|
|
795
|
+
* @example
|
|
796
|
+
* ```typescript
|
|
797
|
+
* mailable.vue(WelcomeEmailComponent, { name: 'Alice' })
|
|
798
|
+
* ```
|
|
322
799
|
*/
|
|
323
800
|
vue<P extends object>(component: ComponentType, props?: P, deps?: {
|
|
324
801
|
createSSRApp?: (...args: any[]) => any;
|
|
@@ -326,8 +803,57 @@ declare abstract class Mailable implements Queueable {
|
|
|
326
803
|
renderToString?: (app: any) => Promise<string>;
|
|
327
804
|
}): this;
|
|
328
805
|
/**
|
|
329
|
-
*
|
|
330
|
-
*
|
|
806
|
+
* Set the content using an MJML markup string.
|
|
807
|
+
*
|
|
808
|
+
* MJML ensures responsive email compatibility across various clients.
|
|
809
|
+
*
|
|
810
|
+
* @param content - The MJML markup string or inner content.
|
|
811
|
+
* @param options - MJML transformation options.
|
|
812
|
+
* @param options.layout - Optional full MJML layout string. Use '{{content}}' as placeholder.
|
|
813
|
+
* @returns The current mailable instance for chaining.
|
|
814
|
+
*
|
|
815
|
+
* @example
|
|
816
|
+
* ```typescript
|
|
817
|
+
* mailable.mjml('<mj-text>Hello</mj-text>', {
|
|
818
|
+
* layout: '<mjml><mj-body>{{content}}</mj-body></mjml>'
|
|
819
|
+
* })
|
|
820
|
+
* ```
|
|
821
|
+
*/
|
|
822
|
+
mjml(content: string, options?: Record<string, any> & {
|
|
823
|
+
layout?: string;
|
|
824
|
+
}): this;
|
|
825
|
+
/**
|
|
826
|
+
* Set the content using a React component that outputs MJML.
|
|
827
|
+
*
|
|
828
|
+
* @param component - The React component.
|
|
829
|
+
* @param props - Component properties.
|
|
830
|
+
* @param options - MJML options.
|
|
831
|
+
* @returns The current mailable instance for chaining.
|
|
832
|
+
*/
|
|
833
|
+
mjmlReact<P extends object>(component: ComponentType, props?: P, options?: Record<string, any>): this;
|
|
834
|
+
/**
|
|
835
|
+
* Set the content using a Vue component that outputs MJML.
|
|
836
|
+
*
|
|
837
|
+
* @param component - The Vue component.
|
|
838
|
+
* @param props - Component properties.
|
|
839
|
+
* @param options - MJML options.
|
|
840
|
+
* @returns The current mailable instance for chaining.
|
|
841
|
+
*/
|
|
842
|
+
mjmlVue<P extends object>(component: ComponentType, props?: P, options?: Record<string, any>): this;
|
|
843
|
+
/**
|
|
844
|
+
* Configure the mailable's envelope and content.
|
|
845
|
+
*
|
|
846
|
+
* This abstract method must be implemented by subclasses to define the email's
|
|
847
|
+
* recipients, subject, and body using the fluent API.
|
|
848
|
+
*
|
|
849
|
+
* @returns The current mailable instance.
|
|
850
|
+
*
|
|
851
|
+
* @example
|
|
852
|
+
* ```typescript
|
|
853
|
+
* build() {
|
|
854
|
+
* return this.to('user@example.com').subject('Hi').html('...')
|
|
855
|
+
* }
|
|
856
|
+
* ```
|
|
331
857
|
*/
|
|
332
858
|
abstract build(): this;
|
|
333
859
|
/** The name of the queue to push this mailable to. */
|
|
@@ -339,32 +865,85 @@ declare abstract class Mailable implements Queueable {
|
|
|
339
865
|
/** Priority of the message in the queue. */
|
|
340
866
|
priority?: number | string;
|
|
341
867
|
/**
|
|
342
|
-
* Set the queue
|
|
868
|
+
* Set the target queue for background processing.
|
|
869
|
+
*
|
|
870
|
+
* @param queue - The name of the queue.
|
|
871
|
+
* @returns The current mailable instance for chaining.
|
|
872
|
+
*
|
|
873
|
+
* @example
|
|
874
|
+
* ```typescript
|
|
875
|
+
* mailable.onQueue('notifications')
|
|
876
|
+
* ```
|
|
343
877
|
*/
|
|
344
878
|
onQueue(queue: string): this;
|
|
345
879
|
/**
|
|
346
|
-
* Set the connection
|
|
880
|
+
* Set the queue connection to be used.
|
|
881
|
+
*
|
|
882
|
+
* @param connection - The name of the connection (e.g., 'redis', 'sqs').
|
|
883
|
+
* @returns The current mailable instance for chaining.
|
|
884
|
+
*
|
|
885
|
+
* @example
|
|
886
|
+
* ```typescript
|
|
887
|
+
* mailable.onConnection('redis')
|
|
888
|
+
* ```
|
|
347
889
|
*/
|
|
348
890
|
onConnection(connection: string): this;
|
|
349
891
|
/**
|
|
350
|
-
* Set a delay for the queued
|
|
892
|
+
* Set a delay for the queued message.
|
|
893
|
+
*
|
|
894
|
+
* The message will remain in the queue and only be processed after the delay.
|
|
895
|
+
*
|
|
896
|
+
* @param seconds - The delay in seconds.
|
|
897
|
+
* @returns The current mailable instance for chaining.
|
|
898
|
+
*
|
|
899
|
+
* @example
|
|
900
|
+
* ```typescript
|
|
901
|
+
* mailable.delay(3600) // Delay for 1 hour
|
|
902
|
+
* ```
|
|
351
903
|
*/
|
|
352
904
|
delay(seconds: number): this;
|
|
353
905
|
/**
|
|
354
|
-
* Set the priority for the queued
|
|
906
|
+
* Set the priority for the queued message.
|
|
907
|
+
*
|
|
908
|
+
* Higher priority messages are typically processed before lower priority ones.
|
|
909
|
+
*
|
|
910
|
+
* @param priority - The priority value (numeric or string).
|
|
911
|
+
* @returns The current mailable instance for chaining.
|
|
912
|
+
*
|
|
913
|
+
* @example
|
|
914
|
+
* ```typescript
|
|
915
|
+
* mailable.withPriority(10)
|
|
916
|
+
* ```
|
|
355
917
|
*/
|
|
356
918
|
withPriority(priority: string | number): this;
|
|
357
919
|
/**
|
|
358
|
-
*
|
|
359
|
-
*
|
|
920
|
+
* Push the mailable onto the configured queue.
|
|
921
|
+
*
|
|
922
|
+
* Automatically resolves the mail service from the Gravito container and
|
|
923
|
+
* dispatches this mailable for background processing.
|
|
924
|
+
*
|
|
925
|
+
* @returns A promise that resolves when the mailable is queued.
|
|
926
|
+
*
|
|
927
|
+
* @example
|
|
928
|
+
* ```typescript
|
|
929
|
+
* await new WelcomeEmail(user).queue()
|
|
930
|
+
* ```
|
|
360
931
|
*/
|
|
361
932
|
queue(): Promise<void>;
|
|
362
933
|
protected currentLocale?: string;
|
|
363
934
|
protected translator?: (key: string, replace?: Record<string, unknown>, locale?: string) => string;
|
|
364
935
|
/**
|
|
365
|
-
* Set the locale for the
|
|
936
|
+
* Set the locale for the email content.
|
|
937
|
+
*
|
|
938
|
+
* Used by the translator to resolve localized strings in templates or components.
|
|
939
|
+
*
|
|
940
|
+
* @param locale - The locale identifier (e.g., 'en-US', 'fr').
|
|
941
|
+
* @returns The current mailable instance for chaining.
|
|
366
942
|
*
|
|
367
|
-
* @
|
|
943
|
+
* @example
|
|
944
|
+
* ```typescript
|
|
945
|
+
* mailable.locale('es')
|
|
946
|
+
* ```
|
|
368
947
|
*/
|
|
369
948
|
locale(locale: string): this;
|
|
370
949
|
/**
|
|
@@ -373,21 +952,48 @@ declare abstract class Mailable implements Queueable {
|
|
|
373
952
|
*/
|
|
374
953
|
setTranslator(translator: (key: string, replace?: Record<string, unknown>, locale?: string) => string): void;
|
|
375
954
|
/**
|
|
376
|
-
* Translate a
|
|
955
|
+
* Translate a key into a localized string.
|
|
377
956
|
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
* @
|
|
957
|
+
* Uses the configured translator and current locale to resolve the key.
|
|
958
|
+
*
|
|
959
|
+
* @param key - The translation key.
|
|
960
|
+
* @param replace - Key-value pairs for string interpolation.
|
|
961
|
+
* @returns The translated string, or the key itself if no translator is available.
|
|
962
|
+
*
|
|
963
|
+
* @example
|
|
964
|
+
* ```typescript
|
|
965
|
+
* const text = mailable.t('messages.welcome', { name: 'Alice' })
|
|
966
|
+
* ```
|
|
381
967
|
*/
|
|
382
968
|
t(key: string, replace?: Record<string, unknown>): string;
|
|
383
969
|
/**
|
|
384
|
-
* Compile the
|
|
385
|
-
*
|
|
970
|
+
* Compile the final email envelope.
|
|
971
|
+
*
|
|
972
|
+
* Merges mailable-specific settings with global configuration defaults.
|
|
973
|
+
* This is called internally by the mail service before sending.
|
|
974
|
+
*
|
|
975
|
+
* @param configPromise - The mail configuration or a promise resolving to it.
|
|
976
|
+
* @returns The fully constructed envelope.
|
|
977
|
+
*
|
|
978
|
+
* @example
|
|
979
|
+
* ```typescript
|
|
980
|
+
* const envelope = await mailable.buildEnvelope(config)
|
|
981
|
+
* ```
|
|
386
982
|
*/
|
|
387
983
|
buildEnvelope(configPromise: MailConfig | Promise<MailConfig>): Promise<Envelope>;
|
|
388
984
|
/**
|
|
389
|
-
*
|
|
390
|
-
*
|
|
985
|
+
* Render the email content to HTML and plain text.
|
|
986
|
+
*
|
|
987
|
+
* Executes the chosen renderer (HTML, Template, React, or Vue) with the
|
|
988
|
+
* provided data and i18n helpers.
|
|
989
|
+
*
|
|
990
|
+
* @returns The rendered HTML and optional plain text content.
|
|
991
|
+
* @throws {Error} If no renderer has been specified.
|
|
992
|
+
*
|
|
993
|
+
* @example
|
|
994
|
+
* ```typescript
|
|
995
|
+
* const { html } = await mailable.renderContent()
|
|
996
|
+
* ```
|
|
391
997
|
*/
|
|
392
998
|
renderContent(): Promise<{
|
|
393
999
|
html: string;
|
|
@@ -397,248 +1003,1113 @@ declare abstract class Mailable implements Queueable {
|
|
|
397
1003
|
}
|
|
398
1004
|
|
|
399
1005
|
/**
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
* Provides email sending capabilities with support for multiple transports,
|
|
403
|
-
* development mode with email preview UI, and queue integration.
|
|
404
|
-
*
|
|
405
|
-
* @example
|
|
406
|
-
* ```typescript
|
|
407
|
-
* import { OrbitSignal } from '@gravito/signal'
|
|
408
|
-
* import { SmtpTransport } from '@gravito/signal'
|
|
409
|
-
*
|
|
410
|
-
* const app = new Application({
|
|
411
|
-
* orbits: [
|
|
412
|
-
* new OrbitSignal({
|
|
413
|
-
* transport: new SmtpTransport({
|
|
414
|
-
* host: 'smtp.example.com',
|
|
415
|
-
* port: 587,
|
|
416
|
-
* auth: { user: 'user', pass: 'pass' }
|
|
417
|
-
* }),
|
|
418
|
-
* from: { name: 'App', email: 'noreply@example.com' }
|
|
419
|
-
* })
|
|
420
|
-
* ]
|
|
421
|
-
* })
|
|
1006
|
+
* Mail event types.
|
|
422
1007
|
*
|
|
423
|
-
*
|
|
424
|
-
* await c.get('mail').send(new WelcomeEmail(user))
|
|
425
|
-
* ```
|
|
1008
|
+
* Defines the available lifecycle hooks for the mail service.
|
|
426
1009
|
*
|
|
427
|
-
* @since 3.0.0
|
|
428
1010
|
* @public
|
|
1011
|
+
* @since 3.1.0
|
|
429
1012
|
*/
|
|
430
|
-
|
|
431
|
-
private config;
|
|
432
|
-
private devMailbox?;
|
|
433
|
-
private core?;
|
|
434
|
-
constructor(config?: MailConfig);
|
|
435
|
-
/**
|
|
436
|
-
* Install the orbit into PlanetCore
|
|
437
|
-
*
|
|
438
|
-
* @param core - The PlanetCore instance.
|
|
439
|
-
*/
|
|
440
|
-
install(core: PlanetCore): void;
|
|
441
|
-
/**
|
|
442
|
-
* Send a mailable instance
|
|
443
|
-
*/
|
|
444
|
-
send(mailable: Mailable): Promise<void>;
|
|
445
|
-
/**
|
|
446
|
-
* Queue a mailable instance
|
|
447
|
-
*/
|
|
448
|
-
queue(mailable: Mailable): Promise<void>;
|
|
449
|
-
}
|
|
450
|
-
declare module '@gravito/core' {
|
|
451
|
-
interface GravitoVariables {
|
|
452
|
-
/** Mail service for sending emails */
|
|
453
|
-
mail?: OrbitSignal;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
1013
|
+
type MailEventType = 'beforeSend' | 'afterSend' | 'sendFailed' | 'beforeRender' | 'afterRender' | 'webhookReceived';
|
|
457
1014
|
/**
|
|
458
|
-
*
|
|
1015
|
+
* Mail lifecycle event.
|
|
459
1016
|
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
1017
|
+
* Emitted at key points during email sending lifecycle.
|
|
1018
|
+
* Used for logging, analytics, or custom processing.
|
|
462
1019
|
*
|
|
463
1020
|
* @example
|
|
464
1021
|
* ```typescript
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
* // text: 'Hello World'
|
|
1022
|
+
* mail.on('afterSend', async (event) => {
|
|
1023
|
+
* console.log('Email sent to:', event.message?.to)
|
|
1024
|
+
* })
|
|
469
1025
|
* ```
|
|
470
1026
|
*
|
|
471
|
-
* @since 3.0.0
|
|
472
1027
|
* @public
|
|
1028
|
+
* @since 3.1.0
|
|
473
1029
|
*/
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
1030
|
+
interface MailEvent {
|
|
1031
|
+
/** Type of event */
|
|
1032
|
+
type: MailEventType;
|
|
1033
|
+
/** Mailable instance that triggered the event */
|
|
1034
|
+
mailable: Mailable;
|
|
1035
|
+
/** Finalized message (available for send events) */
|
|
1036
|
+
message?: Message;
|
|
1037
|
+
/** Error that occurred (available for sendFailed event) */
|
|
1038
|
+
error?: Error;
|
|
1039
|
+
/** Timestamp when event occurred */
|
|
1040
|
+
timestamp: Date;
|
|
1041
|
+
/** Webhook payload (available for webhookReceived event) */
|
|
1042
|
+
webhook?: {
|
|
1043
|
+
driver: string;
|
|
1044
|
+
event: string;
|
|
1045
|
+
payload: any;
|
|
1046
|
+
};
|
|
479
1047
|
}
|
|
480
|
-
|
|
481
1048
|
/**
|
|
482
|
-
*
|
|
1049
|
+
* Mail event handler function.
|
|
483
1050
|
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
1051
|
+
* Defines the signature for functions that subscribe to mail events.
|
|
1052
|
+
*
|
|
1053
|
+
* @param event - The mail event object
|
|
486
1054
|
*
|
|
487
1055
|
* @example
|
|
488
1056
|
* ```typescript
|
|
489
|
-
* const
|
|
490
|
-
*
|
|
1057
|
+
* const handler: MailEventHandler = (event) => {
|
|
1058
|
+
* console.log(`Event ${event.type} triggered`);
|
|
1059
|
+
* };
|
|
491
1060
|
* ```
|
|
492
1061
|
*
|
|
493
|
-
* @since 3.0.0
|
|
494
1062
|
* @public
|
|
1063
|
+
* @since 3.1.0
|
|
495
1064
|
*/
|
|
496
|
-
|
|
497
|
-
private template;
|
|
498
|
-
private viewsDir;
|
|
499
|
-
constructor(templateName: string, viewsDir?: string);
|
|
500
|
-
render(data: Record<string, unknown>): Promise<RenderResult>;
|
|
501
|
-
private stripHtml;
|
|
502
|
-
}
|
|
1065
|
+
type MailEventHandler = (event: MailEvent) => void | Promise<void>;
|
|
503
1066
|
|
|
504
1067
|
/**
|
|
505
|
-
*
|
|
1068
|
+
* OrbitSignal - Mail service orbit for Gravito framework.
|
|
506
1069
|
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
1070
|
+
* @description
|
|
1071
|
+
* A production-ready email service providing multi-transport support, automatic retry,
|
|
1072
|
+
* event-driven lifecycle hooks, and development tooling. Integrates seamlessly with
|
|
1073
|
+
* Gravito's orbit system and queue infrastructure.
|
|
1074
|
+
*
|
|
1075
|
+
* @architecture
|
|
1076
|
+
* ```
|
|
1077
|
+
* OrbitSignal
|
|
1078
|
+
* ├── Configuration (MailConfig)
|
|
1079
|
+
* │ ├── transport: Transport (SMTP, SES, Log, Memory)
|
|
1080
|
+
* │ ├── from: Default sender
|
|
1081
|
+
* │ ├── devMode: Development interception
|
|
1082
|
+
* │ └── translator: i18n support
|
|
1083
|
+
* ├── Lifecycle Events
|
|
1084
|
+
* │ ├── beforeRender → afterRender
|
|
1085
|
+
* │ ├── beforeSend → afterSend
|
|
1086
|
+
* │ └── sendFailed (on error)
|
|
1087
|
+
* ├── Transport Layer
|
|
1088
|
+
* │ ├── BaseTransport (retry, backoff)
|
|
1089
|
+
* │ ├── SmtpTransport (connection pooling)
|
|
1090
|
+
* │ ├── SesTransport (AWS SES)
|
|
1091
|
+
* │ ├── LogTransport (console output)
|
|
1092
|
+
* │ └── MemoryTransport (dev mode)
|
|
1093
|
+
* └── Dev Tools
|
|
1094
|
+
* ├── DevMailbox (in-memory storage)
|
|
1095
|
+
* └── DevServer (preview UI at /__mail)
|
|
1096
|
+
* ```
|
|
1097
|
+
*
|
|
1098
|
+
* @example
|
|
1099
|
+
* **Basic SMTP Configuration**
|
|
1100
|
+
* ```typescript
|
|
1101
|
+
* import { OrbitSignal, SmtpTransport } from '@gravito/signal'
|
|
1102
|
+
*
|
|
1103
|
+
* const mail = new OrbitSignal({
|
|
1104
|
+
* transport: new SmtpTransport({
|
|
1105
|
+
* host: 'smtp.mailtrap.io',
|
|
1106
|
+
* port: 2525,
|
|
1107
|
+
* auth: { user: 'username', pass: 'password' }
|
|
1108
|
+
* }),
|
|
1109
|
+
* from: { name: 'My App', address: 'noreply@myapp.com' }
|
|
1110
|
+
* })
|
|
1111
|
+
*
|
|
1112
|
+
* mail.install(core)
|
|
1113
|
+
* ```
|
|
1114
|
+
*
|
|
1115
|
+
* @example
|
|
1116
|
+
* **AWS SES with Retry Configuration**
|
|
1117
|
+
* ```typescript
|
|
1118
|
+
* import { OrbitSignal, SesTransport } from '@gravito/signal'
|
|
1119
|
+
*
|
|
1120
|
+
* const mail = new OrbitSignal({
|
|
1121
|
+
* transport: new SesTransport({
|
|
1122
|
+
* region: 'us-east-1',
|
|
1123
|
+
* retries: 3,
|
|
1124
|
+
* retryDelay: 1000,
|
|
1125
|
+
* retryMultiplier: 2
|
|
1126
|
+
* }),
|
|
1127
|
+
* from: { name: 'Production App', address: 'noreply@example.com' }
|
|
1128
|
+
* })
|
|
1129
|
+
* ```
|
|
1130
|
+
*
|
|
1131
|
+
* @example
|
|
1132
|
+
* **Development Mode with Preview UI**
|
|
1133
|
+
* ```typescript
|
|
1134
|
+
* const mail = new OrbitSignal({
|
|
1135
|
+
* devMode: process.env.NODE_ENV === 'development',
|
|
1136
|
+
* devUiPrefix: '/__mail',
|
|
1137
|
+
* from: { name: 'Dev App', address: 'dev@localhost' }
|
|
1138
|
+
* })
|
|
1139
|
+
*
|
|
1140
|
+
* // All emails intercepted to memory, view at http://localhost:3000/__mail
|
|
1141
|
+
* ```
|
|
1142
|
+
*
|
|
1143
|
+
* @example
|
|
1144
|
+
* **Event-Driven Analytics & Error Handling**
|
|
1145
|
+
* ```typescript
|
|
1146
|
+
* const mail = new OrbitSignal({ ... })
|
|
1147
|
+
*
|
|
1148
|
+
* // Track successful sends
|
|
1149
|
+
* mail.on('afterSend', async (event) => {
|
|
1150
|
+
* await analytics.track('email_sent', {
|
|
1151
|
+
* to: event.message?.to,
|
|
1152
|
+
* subject: event.message?.subject,
|
|
1153
|
+
* timestamp: event.timestamp
|
|
1154
|
+
* })
|
|
1155
|
+
* })
|
|
1156
|
+
*
|
|
1157
|
+
* // Log failures for monitoring
|
|
1158
|
+
* mail.on('sendFailed', async (event) => {
|
|
1159
|
+
* logger.error('Email send failed', {
|
|
1160
|
+
* error: event.error?.message,
|
|
1161
|
+
* mailable: event.mailable.constructor.name
|
|
1162
|
+
* })
|
|
1163
|
+
* await sentry.captureException(event.error)
|
|
1164
|
+
* })
|
|
1165
|
+
* ```
|
|
1166
|
+
*
|
|
1167
|
+
* @example
|
|
1168
|
+
* **SMTP with Connection Pooling**
|
|
1169
|
+
* ```typescript
|
|
1170
|
+
* const mail = new OrbitSignal({
|
|
1171
|
+
* transport: new SmtpTransport({
|
|
1172
|
+
* host: 'smtp.gmail.com',
|
|
1173
|
+
* port: 465,
|
|
1174
|
+
* secure: true,
|
|
1175
|
+
* auth: { user: 'user@gmail.com', pass: 'app-password' },
|
|
1176
|
+
* poolSize: 5,
|
|
1177
|
+
* maxIdleTime: 30000
|
|
1178
|
+
* })
|
|
1179
|
+
* })
|
|
1180
|
+
*
|
|
1181
|
+
* // Graceful shutdown
|
|
1182
|
+
* process.on('SIGTERM', async () => {
|
|
1183
|
+
* await mail.config.transport?.close?.()
|
|
1184
|
+
* })
|
|
1185
|
+
* ```
|
|
1186
|
+
*
|
|
1187
|
+
* @example
|
|
1188
|
+
* **Usage in Route Handlers**
|
|
1189
|
+
* ```typescript
|
|
1190
|
+
* // Injected automatically into GravitoContext
|
|
1191
|
+
* app.post('/register', async (c) => {
|
|
1192
|
+
* const user = await createUser(c.req.json())
|
|
1193
|
+
*
|
|
1194
|
+
* await c.get('mail').send(new WelcomeEmail(user))
|
|
1195
|
+
*
|
|
1196
|
+
* return c.json({ success: true })
|
|
1197
|
+
* })
|
|
1198
|
+
* ```
|
|
509
1199
|
*
|
|
510
1200
|
* @example
|
|
1201
|
+
* **Queue Integration for Background Processing**
|
|
511
1202
|
* ```typescript
|
|
512
|
-
*
|
|
513
|
-
*
|
|
514
|
-
*
|
|
1203
|
+
* // Requires @gravito/stream
|
|
1204
|
+
* const email = new WelcomeEmail(user)
|
|
1205
|
+
* .onQueue('emails')
|
|
1206
|
+
* .delay(60)
|
|
1207
|
+
*
|
|
1208
|
+
* await email.queue()
|
|
515
1209
|
* ```
|
|
516
1210
|
*
|
|
1211
|
+
* @example
|
|
1212
|
+
* **Error Handling Best Practices**
|
|
1213
|
+
* ```typescript
|
|
1214
|
+
* try {
|
|
1215
|
+
* await mail.send(new InvoiceEmail(order))
|
|
1216
|
+
* } catch (error) {
|
|
1217
|
+
* if (error instanceof MailTransportError) {
|
|
1218
|
+
* switch (error.code) {
|
|
1219
|
+
* case MailErrorCode.RATE_LIMIT:
|
|
1220
|
+
* await queue.pushDelayed(email, 300)
|
|
1221
|
+
* break
|
|
1222
|
+
* case MailErrorCode.RECIPIENT_REJECTED:
|
|
1223
|
+
* await markUserEmailInvalid(user.id)
|
|
1224
|
+
* break
|
|
1225
|
+
* default:
|
|
1226
|
+
* throw error
|
|
1227
|
+
* }
|
|
1228
|
+
* }
|
|
1229
|
+
* }
|
|
1230
|
+
* ```
|
|
1231
|
+
*
|
|
1232
|
+
* @see {@link Mailable} Base class for email definitions
|
|
1233
|
+
* @see {@link TypedMailable} Strongly-typed mailable with generic data
|
|
1234
|
+
* @see {@link Transport} Transport interface
|
|
1235
|
+
* @see {@link BaseTransport} Retry-enabled base transport
|
|
1236
|
+
* @see {@link MailConfig} Configuration interface
|
|
1237
|
+
* @see {@link MailEvent} Event types
|
|
1238
|
+
* @see {@link MailTransportError} Error handling
|
|
1239
|
+
*
|
|
517
1240
|
* @since 3.0.0
|
|
518
1241
|
* @public
|
|
519
1242
|
*/
|
|
1243
|
+
declare class OrbitSignal implements GravitoOrbit {
|
|
1244
|
+
private config;
|
|
1245
|
+
private devMailbox?;
|
|
1246
|
+
private core?;
|
|
1247
|
+
private eventHandlers;
|
|
1248
|
+
constructor(config?: MailConfig);
|
|
1249
|
+
/**
|
|
1250
|
+
* Install the orbit into PlanetCore.
|
|
1251
|
+
*
|
|
1252
|
+
* Registers the mail service in the IoC container and sets up development
|
|
1253
|
+
* tools if enabled. It also injects the service into the GravitoContext
|
|
1254
|
+
* for easy access in route handlers.
|
|
1255
|
+
*
|
|
1256
|
+
* @param core - The PlanetCore instance to install into
|
|
1257
|
+
*
|
|
1258
|
+
* @example
|
|
1259
|
+
* ```typescript
|
|
1260
|
+
* const mail = new OrbitSignal(config);
|
|
1261
|
+
* mail.install(core);
|
|
1262
|
+
* ```
|
|
1263
|
+
*/
|
|
1264
|
+
install(core: PlanetCore): void;
|
|
1265
|
+
/**
|
|
1266
|
+
* Internal: Handle processed webhook.
|
|
1267
|
+
*/
|
|
1268
|
+
private handleWebhook;
|
|
1269
|
+
/**
|
|
1270
|
+
* Send a mailable instance immediately.
|
|
1271
|
+
*
|
|
1272
|
+
* Orchestrates the full email sending lifecycle: building the envelope,
|
|
1273
|
+
* rendering content, emitting events, and delivering via the configured transport.
|
|
1274
|
+
*
|
|
1275
|
+
* @param mailable - The email definition to send
|
|
1276
|
+
* @throws {Error} If mandatory fields (from, to) are missing or transport fails
|
|
1277
|
+
*
|
|
1278
|
+
* @example
|
|
1279
|
+
* ```typescript
|
|
1280
|
+
* await mail.send(new WelcomeEmail(user));
|
|
1281
|
+
* ```
|
|
1282
|
+
*/
|
|
1283
|
+
send(mailable: Mailable): Promise<void>;
|
|
1284
|
+
/**
|
|
1285
|
+
* Queue a mailable instance for background processing.
|
|
1286
|
+
*
|
|
1287
|
+
* Attempts to use the 'queue' service (OrbitStream) if available in the
|
|
1288
|
+
* container. Falls back to immediate sending if no queue service is found.
|
|
1289
|
+
*
|
|
1290
|
+
* @param mailable - The email definition to queue
|
|
1291
|
+
*
|
|
1292
|
+
* @example
|
|
1293
|
+
* ```typescript
|
|
1294
|
+
* await mail.queue(new WelcomeEmail(user));
|
|
1295
|
+
* ```
|
|
1296
|
+
*/
|
|
1297
|
+
queue(mailable: Mailable): Promise<void>;
|
|
1298
|
+
/**
|
|
1299
|
+
* Register an event handler.
|
|
1300
|
+
*
|
|
1301
|
+
* @description
|
|
1302
|
+
* Subscribe to mail lifecycle events for logging, analytics, or custom processing.
|
|
1303
|
+
*
|
|
1304
|
+
* @param event - The event type to listen for
|
|
1305
|
+
* @param handler - The handler function to execute
|
|
1306
|
+
* @returns This instance for method chaining
|
|
1307
|
+
*
|
|
1308
|
+
* @example
|
|
1309
|
+
* ```typescript
|
|
1310
|
+
* mail.on('afterSend', async (event) => {
|
|
1311
|
+
* await analytics.track('email_sent', {
|
|
1312
|
+
* to: event.message?.to,
|
|
1313
|
+
* subject: event.message?.subject
|
|
1314
|
+
* })
|
|
1315
|
+
* })
|
|
1316
|
+
* ```
|
|
1317
|
+
*
|
|
1318
|
+
* @public
|
|
1319
|
+
* @since 3.1.0
|
|
1320
|
+
*/
|
|
1321
|
+
on(event: MailEventType, handler: MailEventHandler): this;
|
|
1322
|
+
private emit;
|
|
1323
|
+
}
|
|
1324
|
+
declare module '@gravito/core' {
|
|
1325
|
+
interface GravitoVariables {
|
|
1326
|
+
/** Mail service for sending emails */
|
|
1327
|
+
mail?: OrbitSignal;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/**
|
|
1332
|
+
* Renderer for plain HTML content.
|
|
1333
|
+
*
|
|
1334
|
+
* The simplest renderer - accepts raw HTML strings and automatically generates
|
|
1335
|
+
* a plain text version by stripping HTML tags. Ideal for pre-rendered HTML or
|
|
1336
|
+
* when using external HTML generation libraries.
|
|
1337
|
+
*
|
|
1338
|
+
* @example
|
|
1339
|
+
* ```typescript
|
|
1340
|
+
* const renderer = new HtmlRenderer('<h1>Hello</h1>');
|
|
1341
|
+
* const result = await renderer.render();
|
|
1342
|
+
* // result.html: '<h1>Hello</h1>'
|
|
1343
|
+
* // result.text: 'Hello'
|
|
1344
|
+
* ```
|
|
1345
|
+
*
|
|
1346
|
+
* @public
|
|
1347
|
+
* @since 3.0.0
|
|
1348
|
+
*/
|
|
1349
|
+
declare class HtmlRenderer implements Renderer {
|
|
1350
|
+
private content;
|
|
1351
|
+
/**
|
|
1352
|
+
* Creates an instance of HtmlRenderer.
|
|
1353
|
+
*
|
|
1354
|
+
* @param content - The raw HTML string to be rendered.
|
|
1355
|
+
*/
|
|
1356
|
+
constructor(content: string);
|
|
1357
|
+
/**
|
|
1358
|
+
* Returns the original HTML and a stripped plain text version.
|
|
1359
|
+
*
|
|
1360
|
+
* @returns A promise resolving to the rendered content.
|
|
1361
|
+
*/
|
|
1362
|
+
render(): Promise<RenderResult>;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
/**
|
|
1366
|
+
* Renderer for MJML-based emails.
|
|
1367
|
+
*
|
|
1368
|
+
* MJML is a markup language designed to reduce the pain of coding a responsive email.
|
|
1369
|
+
* This renderer lazily loads the `mjml` package to keep the core lightweight.
|
|
1370
|
+
*
|
|
1371
|
+
* @example
|
|
1372
|
+
* ```typescript
|
|
1373
|
+
* const renderer = new MjmlRenderer('<mjml><mj-body>...</mj-body></mjml>');
|
|
1374
|
+
* const result = await renderer.render();
|
|
1375
|
+
* ```
|
|
1376
|
+
*
|
|
1377
|
+
* @public
|
|
1378
|
+
* @since 1.1.0
|
|
1379
|
+
*/
|
|
1380
|
+
declare class MjmlRenderer implements Renderer {
|
|
1381
|
+
private content;
|
|
1382
|
+
private options;
|
|
1383
|
+
private deps;
|
|
1384
|
+
/**
|
|
1385
|
+
* Creates an instance of MjmlRenderer.
|
|
1386
|
+
*
|
|
1387
|
+
* @param content - The MJML markup string to be rendered.
|
|
1388
|
+
* @param options - Optional MJML transformation options.
|
|
1389
|
+
* @param deps - Optional dependency injection for testing.
|
|
1390
|
+
*/
|
|
1391
|
+
constructor(content: string, options?: Record<string, any>, deps?: {
|
|
1392
|
+
mjml2html?: (mjml: string, options?: any) => any;
|
|
1393
|
+
});
|
|
1394
|
+
/**
|
|
1395
|
+
* Renders the MJML content to static HTML.
|
|
1396
|
+
*
|
|
1397
|
+
* This method performs a dynamic import of `mjml` to ensure it's only
|
|
1398
|
+
* loaded if this renderer is actually used.
|
|
1399
|
+
*
|
|
1400
|
+
* @returns A promise resolving to the rendered content.
|
|
1401
|
+
* @throws {Error} If MJML dependencies cannot be loaded or rendering fails.
|
|
1402
|
+
*/
|
|
1403
|
+
render(): Promise<RenderResult>;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/**
|
|
1407
|
+
* Base layout for MJML emails.
|
|
1408
|
+
* Includes common head styles and responsive settings.
|
|
1409
|
+
*
|
|
1410
|
+
* Placeholder: {{content}}
|
|
1411
|
+
*/
|
|
1412
|
+
declare const baseLayout: string;
|
|
1413
|
+
/**
|
|
1414
|
+
* A simple transactional component layout.
|
|
1415
|
+
*/
|
|
1416
|
+
declare const transactionLayout = "\n<mj-section background-color=\"#ffffff\" padding-top=\"0px\">\n <mj-column>\n {{content}}\n </mj-column>\n</mj-section>\n";
|
|
1417
|
+
|
|
1418
|
+
/**
|
|
1419
|
+
* Renderer for React component-based MJML emails.
|
|
1420
|
+
*
|
|
1421
|
+
* Renders React components to MJML string using SSR, then converts
|
|
1422
|
+
* the MJML to responsive HTML.
|
|
1423
|
+
*
|
|
1424
|
+
* @typeParam P - Props type for the React component.
|
|
1425
|
+
* @public
|
|
1426
|
+
* @since 1.1.0
|
|
1427
|
+
*/
|
|
1428
|
+
declare class ReactMjmlRenderer<P extends object = object> implements Renderer {
|
|
1429
|
+
private component;
|
|
1430
|
+
private props?;
|
|
1431
|
+
private options;
|
|
1432
|
+
private deps;
|
|
1433
|
+
/**
|
|
1434
|
+
* Creates an instance of ReactMjmlRenderer.
|
|
1435
|
+
*
|
|
1436
|
+
* @param component - The React component to render.
|
|
1437
|
+
* @param props - Initial props for the component.
|
|
1438
|
+
* @param options - Optional MJML transformation options.
|
|
1439
|
+
* @param deps - Optional dependency injection for testing.
|
|
1440
|
+
*/
|
|
1441
|
+
constructor(component: any, props?: P | undefined, options?: Record<string, any>, deps?: {
|
|
1442
|
+
createElement?: (...args: any[]) => any;
|
|
1443
|
+
renderToStaticMarkup?: (element: any) => string;
|
|
1444
|
+
mjml2html?: (mjml: string, options?: any) => any;
|
|
1445
|
+
});
|
|
1446
|
+
/**
|
|
1447
|
+
* Renders the React component to a static HTML string via MJML.
|
|
1448
|
+
*
|
|
1449
|
+
* @param data - Runtime data to be merged with initial props.
|
|
1450
|
+
* @returns A promise resolving to the rendered content.
|
|
1451
|
+
* @throws {Error} If MJML rendering fails.
|
|
1452
|
+
*/
|
|
1453
|
+
render(data: Record<string, unknown>): Promise<RenderResult>;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
/**
|
|
1457
|
+
* Renderer for template-based emails using Gravito Prism.
|
|
1458
|
+
*
|
|
1459
|
+
* Renders email templates from the filesystem using the Prism template engine.
|
|
1460
|
+
* It uses a static cache for the template engine to avoid redundant initialization
|
|
1461
|
+
* costs when rendering multiple emails from the same directory.
|
|
1462
|
+
*
|
|
1463
|
+
* @example
|
|
1464
|
+
* ```typescript
|
|
1465
|
+
* const renderer = new TemplateRenderer('welcome', './src/emails');
|
|
1466
|
+
* const result = await renderer.render({ name: 'John' });
|
|
1467
|
+
* ```
|
|
1468
|
+
*
|
|
1469
|
+
* @public
|
|
1470
|
+
* @since 3.0.0
|
|
1471
|
+
*/
|
|
1472
|
+
declare class TemplateRenderer implements Renderer {
|
|
1473
|
+
private template;
|
|
1474
|
+
private viewsDir;
|
|
1475
|
+
private static engineCache;
|
|
1476
|
+
/**
|
|
1477
|
+
* Creates an instance of TemplateRenderer.
|
|
1478
|
+
*
|
|
1479
|
+
* @param templateName - The name of the template file (without extension).
|
|
1480
|
+
* @param viewsDir - The directory containing template files. Defaults to `src/emails`.
|
|
1481
|
+
*/
|
|
1482
|
+
constructor(templateName: string, viewsDir?: string);
|
|
1483
|
+
/**
|
|
1484
|
+
* Renders the template with the provided data.
|
|
1485
|
+
*
|
|
1486
|
+
* This method lazily loads `@gravito/prism` to ensure the core package
|
|
1487
|
+
* remains lightweight for users who don't need template rendering.
|
|
1488
|
+
*
|
|
1489
|
+
* @param data - The data context for template interpolation.
|
|
1490
|
+
* @returns A promise resolving to the rendered content.
|
|
1491
|
+
* @throws {Error} If the template engine fails to load or rendering fails.
|
|
1492
|
+
*/
|
|
1493
|
+
render(data: Record<string, unknown>): Promise<RenderResult>;
|
|
1494
|
+
/**
|
|
1495
|
+
* Clear template engine cache.
|
|
1496
|
+
*
|
|
1497
|
+
* Useful in development environments to force recompilation of templates
|
|
1498
|
+
* after they have been modified on disk.
|
|
1499
|
+
*
|
|
1500
|
+
* @example
|
|
1501
|
+
* ```typescript
|
|
1502
|
+
* TemplateRenderer.clearCache();
|
|
1503
|
+
* ```
|
|
1504
|
+
*
|
|
1505
|
+
* @public
|
|
1506
|
+
* @since 3.1.0
|
|
1507
|
+
*/
|
|
1508
|
+
static clearCache(): void;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
* Renderer for Vue component-based MJML emails.
|
|
1513
|
+
*
|
|
1514
|
+
* Renders Vue 3 components to MJML string using SSR, then converts
|
|
1515
|
+
* the MJML to responsive HTML.
|
|
1516
|
+
*
|
|
1517
|
+
* @typeParam P - Props type for the Vue component.
|
|
1518
|
+
* @public
|
|
1519
|
+
* @since 1.1.0
|
|
1520
|
+
*/
|
|
1521
|
+
declare class VueMjmlRenderer<P extends object = object> implements Renderer {
|
|
1522
|
+
private component;
|
|
1523
|
+
private props?;
|
|
1524
|
+
private options;
|
|
1525
|
+
private deps;
|
|
1526
|
+
/**
|
|
1527
|
+
* Creates an instance of VueMjmlRenderer.
|
|
1528
|
+
*
|
|
1529
|
+
* @param component - The Vue component to render.
|
|
1530
|
+
* @param props - Initial props for the component.
|
|
1531
|
+
* @param options - Optional MJML transformation options.
|
|
1532
|
+
* @param deps - Optional dependency injection for testing.
|
|
1533
|
+
*/
|
|
1534
|
+
constructor(component: any, props?: P | undefined, options?: Record<string, any>, deps?: {
|
|
1535
|
+
createSSRApp?: (...args: any[]) => any;
|
|
1536
|
+
h?: (...args: any[]) => any;
|
|
1537
|
+
renderToString?: (app: any) => Promise<string>;
|
|
1538
|
+
mjml2html?: (mjml: string, options?: any) => any;
|
|
1539
|
+
});
|
|
1540
|
+
/**
|
|
1541
|
+
* Renders the Vue component to a static HTML string via MJML.
|
|
1542
|
+
*
|
|
1543
|
+
* @param data - Runtime data to be merged with initial props.
|
|
1544
|
+
* @returns A promise resolving to the rendered content.
|
|
1545
|
+
* @throws {Error} If MJML rendering fails.
|
|
1546
|
+
*/
|
|
1547
|
+
render(data: Record<string, unknown>): Promise<RenderResult>;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
/**
|
|
1551
|
+
* Abstract base class for strongly-typed Mailable messages.
|
|
1552
|
+
*
|
|
1553
|
+
* @description
|
|
1554
|
+
* TypedMailable extends the base Mailable class to provide compile-time type safety
|
|
1555
|
+
* for email data props. This ensures that the data passed to templates, React, or Vue
|
|
1556
|
+
* components is correctly typed and validated at build time.
|
|
1557
|
+
*
|
|
1558
|
+
* @typeParam TData - The shape of data required by this mailable's template/component.
|
|
1559
|
+
* Must extend Record<string, unknown>.
|
|
1560
|
+
*
|
|
1561
|
+
* @example
|
|
1562
|
+
* ```typescript
|
|
1563
|
+
* import { TypedMailable } from '@gravito/signal'
|
|
1564
|
+
*
|
|
1565
|
+
* // Define the data interface
|
|
1566
|
+
* interface WelcomeData {
|
|
1567
|
+
* name: string
|
|
1568
|
+
* email: string
|
|
1569
|
+
* activationUrl: string
|
|
1570
|
+
* }
|
|
1571
|
+
*
|
|
1572
|
+
* // Create strongly-typed mailable
|
|
1573
|
+
* class WelcomeEmail extends TypedMailable<WelcomeData> {
|
|
1574
|
+
* protected data: WelcomeData
|
|
1575
|
+
*
|
|
1576
|
+
* constructor(data: WelcomeData) {
|
|
1577
|
+
* super()
|
|
1578
|
+
* this.data = data
|
|
1579
|
+
* }
|
|
1580
|
+
*
|
|
1581
|
+
* build() {
|
|
1582
|
+
* return this
|
|
1583
|
+
* .to(this.data.email)
|
|
1584
|
+
* .subject('Welcome to Gravito!')
|
|
1585
|
+
* .view('emails/welcome', this.data) // Type-safe: compiler ensures WelcomeData matches template
|
|
1586
|
+
* }
|
|
1587
|
+
* }
|
|
1588
|
+
*
|
|
1589
|
+
* // Usage - compiler enforces correct data shape
|
|
1590
|
+
* const email = new WelcomeEmail({
|
|
1591
|
+
* name: 'Alice',
|
|
1592
|
+
* email: 'alice@example.com',
|
|
1593
|
+
* activationUrl: 'https://app.com/activate?token=abc123'
|
|
1594
|
+
* })
|
|
1595
|
+
*
|
|
1596
|
+
* await mail.send(email)
|
|
1597
|
+
* ```
|
|
1598
|
+
*
|
|
1599
|
+
* @example
|
|
1600
|
+
* ```typescript
|
|
1601
|
+
* // With React components
|
|
1602
|
+
* interface InvoiceData {
|
|
1603
|
+
* invoiceNumber: string
|
|
1604
|
+
* amount: number
|
|
1605
|
+
* dueDate: Date
|
|
1606
|
+
* items: Array<{ name: string; price: number }>
|
|
1607
|
+
* }
|
|
1608
|
+
*
|
|
1609
|
+
* class InvoiceEmail extends TypedMailable<InvoiceData> {
|
|
1610
|
+
* protected data: InvoiceData
|
|
1611
|
+
*
|
|
1612
|
+
* constructor(data: InvoiceData) {
|
|
1613
|
+
* super()
|
|
1614
|
+
* this.data = data
|
|
1615
|
+
* }
|
|
1616
|
+
*
|
|
1617
|
+
* build() {
|
|
1618
|
+
* return this
|
|
1619
|
+
* .to('billing@example.com')
|
|
1620
|
+
* .subject(`Invoice ${this.data.invoiceNumber}`)
|
|
1621
|
+
* .react(InvoiceComponent, this.data) // Type-safe props
|
|
1622
|
+
* }
|
|
1623
|
+
* }
|
|
1624
|
+
* ```
|
|
1625
|
+
*
|
|
1626
|
+
* @see {@link Mailable} Base mailable class
|
|
1627
|
+
* @see {@link OrbitSignal} Mail service orchestrator
|
|
1628
|
+
*
|
|
1629
|
+
* @public
|
|
1630
|
+
* @since 3.0.0
|
|
1631
|
+
*/
|
|
1632
|
+
declare abstract class TypedMailable<TData extends Record<string, unknown>> extends Mailable {
|
|
1633
|
+
/**
|
|
1634
|
+
* The strongly-typed data for this mailable.
|
|
1635
|
+
*
|
|
1636
|
+
* This property holds the data that will be passed to the template or component
|
|
1637
|
+
* during rendering. By defining it as an abstract property with the generic
|
|
1638
|
+
* type TData, we force subclasses to provide a concrete, type-safe implementation.
|
|
1639
|
+
*
|
|
1640
|
+
* @protected
|
|
1641
|
+
*/
|
|
1642
|
+
protected abstract data: TData;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
/**
|
|
1646
|
+
* Transport retry configuration options.
|
|
1647
|
+
*
|
|
1648
|
+
* Defines the behavior of the automatic retry mechanism, including the number of attempts
|
|
1649
|
+
* and the timing between them using exponential backoff.
|
|
1650
|
+
*
|
|
1651
|
+
* @example
|
|
1652
|
+
* ```typescript
|
|
1653
|
+
* const options: TransportOptions = {
|
|
1654
|
+
* maxRetries: 5,
|
|
1655
|
+
* retryDelay: 500,
|
|
1656
|
+
* backoffMultiplier: 3
|
|
1657
|
+
* };
|
|
1658
|
+
* ```
|
|
1659
|
+
*
|
|
1660
|
+
* @public
|
|
1661
|
+
*/
|
|
1662
|
+
interface TransportOptions {
|
|
1663
|
+
/**
|
|
1664
|
+
* Maximum number of retry attempts before giving up.
|
|
1665
|
+
* Set to 0 to disable retries.
|
|
1666
|
+
*/
|
|
1667
|
+
maxRetries?: number;
|
|
1668
|
+
/**
|
|
1669
|
+
* Initial delay in milliseconds before the first retry attempt.
|
|
1670
|
+
*/
|
|
1671
|
+
retryDelay?: number;
|
|
1672
|
+
/**
|
|
1673
|
+
* Multiplier applied to the delay after each failed attempt.
|
|
1674
|
+
* Used to implement exponential backoff to avoid overwhelming the service.
|
|
1675
|
+
*/
|
|
1676
|
+
backoffMultiplier?: number;
|
|
1677
|
+
}
|
|
1678
|
+
/**
|
|
1679
|
+
* Base transport class with automatic retry mechanism.
|
|
1680
|
+
*
|
|
1681
|
+
* This abstract class provides a robust foundation for all transport implementations by
|
|
1682
|
+
* handling transient failures through an exponential backoff retry strategy. It ensures
|
|
1683
|
+
* that temporary network issues or service rate limits do not immediately fail the email delivery.
|
|
1684
|
+
*
|
|
1685
|
+
* The retry mechanism works as follows:
|
|
1686
|
+
* 1. Attempt to send the message via `doSend()`.
|
|
1687
|
+
* 2. If it fails, wait for `retryDelay` milliseconds.
|
|
1688
|
+
* 3. Increment the delay by `backoffMultiplier` for the next attempt.
|
|
1689
|
+
* 4. Repeat until success or `maxRetries` is reached.
|
|
1690
|
+
*
|
|
1691
|
+
* @example
|
|
1692
|
+
* ```typescript
|
|
1693
|
+
* class MyTransport extends BaseTransport {
|
|
1694
|
+
* constructor() {
|
|
1695
|
+
* super({ maxRetries: 3, retryDelay: 1000 })
|
|
1696
|
+
* }
|
|
1697
|
+
*
|
|
1698
|
+
* protected async doSend(message: Message): Promise<void> {
|
|
1699
|
+
* // Actual implementation of the sending logic
|
|
1700
|
+
* // If this throws, BaseTransport will catch and retry
|
|
1701
|
+
* }
|
|
1702
|
+
* }
|
|
1703
|
+
* ```
|
|
1704
|
+
*
|
|
1705
|
+
* @public
|
|
1706
|
+
*/
|
|
1707
|
+
declare abstract class BaseTransport implements Transport {
|
|
1708
|
+
protected options: Required<TransportOptions>;
|
|
1709
|
+
/**
|
|
1710
|
+
* Initializes the transport with retry options.
|
|
1711
|
+
*
|
|
1712
|
+
* @param options - Configuration for the retry mechanism.
|
|
1713
|
+
*/
|
|
1714
|
+
constructor(options?: TransportOptions);
|
|
1715
|
+
/**
|
|
1716
|
+
* Orchestrates the message delivery with retry logic.
|
|
1717
|
+
*
|
|
1718
|
+
* This method wraps the concrete `doSend` implementation in a retry loop.
|
|
1719
|
+
* It tracks the last error encountered to provide context if all retries fail.
|
|
1720
|
+
*
|
|
1721
|
+
* @param message - The message to be delivered.
|
|
1722
|
+
* @returns A promise that resolves when the message is successfully sent.
|
|
1723
|
+
* @throws {MailTransportError} If the message cannot be sent after the maximum number of retries.
|
|
1724
|
+
*
|
|
1725
|
+
* @example
|
|
1726
|
+
* ```typescript
|
|
1727
|
+
* const transport = new SmtpTransport(config);
|
|
1728
|
+
* try {
|
|
1729
|
+
* await transport.send(message);
|
|
1730
|
+
* } catch (error) {
|
|
1731
|
+
* console.error('Failed to send email after retries', error);
|
|
1732
|
+
* }
|
|
1733
|
+
* ```
|
|
1734
|
+
*/
|
|
1735
|
+
send(message: Message): Promise<void>;
|
|
1736
|
+
/**
|
|
1737
|
+
* Actual transport implementation to be provided by subclasses.
|
|
1738
|
+
*
|
|
1739
|
+
* This method should contain the protocol-specific logic for delivering the message.
|
|
1740
|
+
* It will be automatically retried by the `send` method if it throws an error.
|
|
1741
|
+
*
|
|
1742
|
+
* @param message - The message to send.
|
|
1743
|
+
* @returns A promise that resolves when the delivery is successful.
|
|
1744
|
+
* @throws {Error} Any error encountered during delivery, which will trigger a retry.
|
|
1745
|
+
*/
|
|
1746
|
+
protected abstract doSend(message: Message): Promise<void>;
|
|
1747
|
+
/**
|
|
1748
|
+
* Utility method to pause execution for a given duration.
|
|
1749
|
+
*
|
|
1750
|
+
* @param ms - Milliseconds to sleep.
|
|
1751
|
+
* @returns A promise that resolves after the delay.
|
|
1752
|
+
*/
|
|
1753
|
+
private sleep;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
/**
|
|
1757
|
+
* Log transport for development and testing.
|
|
1758
|
+
*
|
|
1759
|
+
* This transport outputs email details directly to the console instead of performing
|
|
1760
|
+
* actual delivery. It is essential for local development to avoid sending real emails
|
|
1761
|
+
* while still being able to verify the content, recipients, and subject of outgoing mail.
|
|
1762
|
+
*
|
|
1763
|
+
* @example
|
|
1764
|
+
* ```typescript
|
|
1765
|
+
* import { LogTransport } from '@gravito/signal';
|
|
1766
|
+
*
|
|
1767
|
+
* const transport = new LogTransport();
|
|
1768
|
+
* await transport.send({
|
|
1769
|
+
* from: { address: 'dev@localhost' },
|
|
1770
|
+
* to: [{ address: 'user@example.com' }],
|
|
1771
|
+
* subject: 'Test Email',
|
|
1772
|
+
* html: '<h1>Hello</h1>'
|
|
1773
|
+
* });
|
|
1774
|
+
* ```
|
|
1775
|
+
*
|
|
1776
|
+
* @public
|
|
1777
|
+
*/
|
|
520
1778
|
declare class LogTransport implements Transport {
|
|
1779
|
+
/**
|
|
1780
|
+
* Outputs the message details to the system console.
|
|
1781
|
+
*
|
|
1782
|
+
* Formats the email metadata (From, To, Subject) and content size into a readable
|
|
1783
|
+
* block in the console output.
|
|
1784
|
+
*
|
|
1785
|
+
* @param message - The message to log.
|
|
1786
|
+
* @returns A promise that resolves immediately after logging.
|
|
1787
|
+
*
|
|
1788
|
+
* @example
|
|
1789
|
+
* ```typescript
|
|
1790
|
+
* const transport = new LogTransport();
|
|
1791
|
+
* await transport.send(message);
|
|
1792
|
+
* // Console: 📧 [OrbitSignal] Email Sent (Simulated)...
|
|
1793
|
+
* ```
|
|
1794
|
+
*/
|
|
521
1795
|
send(message: Message): Promise<void>;
|
|
522
1796
|
}
|
|
523
1797
|
|
|
524
1798
|
/**
|
|
525
1799
|
* Memory transport for development mode.
|
|
526
1800
|
*
|
|
527
|
-
*
|
|
528
|
-
*
|
|
1801
|
+
* This transport captures outgoing emails and stores them in an in-memory mailbox.
|
|
1802
|
+
* It is primarily used by the Gravito Dev UI to provide a live preview of emails
|
|
1803
|
+
* during development without requiring an external mail server.
|
|
529
1804
|
*
|
|
530
1805
|
* @example
|
|
531
1806
|
* ```typescript
|
|
532
|
-
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
1807
|
+
* import { DevMailbox, MemoryTransport } from '@gravito/signal';
|
|
1808
|
+
*
|
|
1809
|
+
* const mailbox = new DevMailbox();
|
|
1810
|
+
* const transport = new MemoryTransport(mailbox);
|
|
1811
|
+
* await transport.send(message);
|
|
1812
|
+
*
|
|
1813
|
+
* console.log(mailbox.getAll().length); // 1
|
|
535
1814
|
* ```
|
|
536
1815
|
*
|
|
537
|
-
* @since 3.0.0
|
|
538
1816
|
* @public
|
|
539
1817
|
*/
|
|
540
1818
|
declare class MemoryTransport implements Transport {
|
|
541
1819
|
private mailbox;
|
|
1820
|
+
/**
|
|
1821
|
+
* Creates a new MemoryTransport instance.
|
|
1822
|
+
*
|
|
1823
|
+
* @param mailbox - The in-memory storage where messages will be collected.
|
|
1824
|
+
*/
|
|
542
1825
|
constructor(mailbox: DevMailbox);
|
|
1826
|
+
/**
|
|
1827
|
+
* Stores the message in the associated mailbox.
|
|
1828
|
+
*
|
|
1829
|
+
* The message is added to the internal list of the `DevMailbox` instance,
|
|
1830
|
+
* making it available for retrieval by the Dev UI or test assertions.
|
|
1831
|
+
*
|
|
1832
|
+
* @param message - The message to store.
|
|
1833
|
+
* @returns A promise that resolves once the message is added to the mailbox.
|
|
1834
|
+
*
|
|
1835
|
+
* @example
|
|
1836
|
+
* ```typescript
|
|
1837
|
+
* await transport.send({
|
|
1838
|
+
* from: { address: 'dev@localhost' },
|
|
1839
|
+
* to: [{ address: 'test@example.com' }],
|
|
1840
|
+
* subject: 'Memory Test',
|
|
1841
|
+
* html: '<p>Stored in memory</p>'
|
|
1842
|
+
* });
|
|
1843
|
+
* ```
|
|
1844
|
+
*/
|
|
543
1845
|
send(message: Message): Promise<void>;
|
|
544
1846
|
}
|
|
545
1847
|
|
|
546
1848
|
/**
|
|
547
1849
|
* Configuration for AWS SES email transport.
|
|
548
1850
|
*
|
|
1851
|
+
* Defines the AWS region and credentials required to communicate with the
|
|
1852
|
+
* Amazon Simple Email Service API.
|
|
1853
|
+
*
|
|
1854
|
+
* @example
|
|
1855
|
+
* ```typescript
|
|
1856
|
+
* const config: SesConfig = {
|
|
1857
|
+
* region: 'us-east-1',
|
|
1858
|
+
* accessKeyId: 'AKIA...',
|
|
1859
|
+
* secretAccessKey: 'wJalr...',
|
|
1860
|
+
* maxRetries: 5
|
|
1861
|
+
* };
|
|
1862
|
+
* ```
|
|
1863
|
+
*
|
|
549
1864
|
* @public
|
|
550
|
-
* @since 3.0.0
|
|
551
1865
|
*/
|
|
552
|
-
interface SesConfig {
|
|
553
|
-
/** AWS region (e.g., 'us-east-1') */
|
|
1866
|
+
interface SesConfig extends TransportOptions {
|
|
1867
|
+
/** AWS region where the SES service is hosted (e.g., 'us-east-1'). */
|
|
554
1868
|
region: string;
|
|
555
|
-
/** AWS access key ID
|
|
1869
|
+
/** AWS access key ID. If omitted, the SDK will attempt to use default credential providers. */
|
|
556
1870
|
accessKeyId?: string;
|
|
557
|
-
/** AWS secret access key
|
|
1871
|
+
/** AWS secret access key. Required if accessKeyId is provided. */
|
|
558
1872
|
secretAccessKey?: string;
|
|
559
1873
|
}
|
|
560
1874
|
/**
|
|
561
|
-
* AWS SES (Simple Email Service) transport.
|
|
1875
|
+
* AWS SES (Simple Email Service) transport with automatic retry.
|
|
562
1876
|
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
1877
|
+
* This transport delivers emails via the Amazon SES API. It requires the
|
|
1878
|
+
* `@aws-sdk/client-ses` package to be installed as a dependency. It provides
|
|
1879
|
+
* a reliable way to send high volumes of email using AWS infrastructure and
|
|
1880
|
+
* includes automatic retry logic for transient API errors.
|
|
565
1881
|
*
|
|
566
1882
|
* @example
|
|
567
1883
|
* ```typescript
|
|
1884
|
+
* import { SesTransport } from '@gravito/signal';
|
|
1885
|
+
*
|
|
568
1886
|
* const transport = new SesTransport({
|
|
569
|
-
* region: 'us-
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
* await transport.send(message)
|
|
1887
|
+
* region: 'us-west-2'
|
|
1888
|
+
* });
|
|
1889
|
+
*
|
|
1890
|
+
* await transport.send(message);
|
|
574
1891
|
* ```
|
|
575
1892
|
*
|
|
576
|
-
* @since 3.0.0
|
|
577
1893
|
* @public
|
|
578
1894
|
*/
|
|
579
|
-
declare class SesTransport
|
|
1895
|
+
declare class SesTransport extends BaseTransport {
|
|
580
1896
|
private transporter;
|
|
1897
|
+
/**
|
|
1898
|
+
* Initializes the SES transport with the provided configuration.
|
|
1899
|
+
*
|
|
1900
|
+
* Configures the AWS SES client and wraps it in a nodemailer transporter
|
|
1901
|
+
* for consistent message handling.
|
|
1902
|
+
*
|
|
1903
|
+
* @param config - AWS SES connection and retry configuration.
|
|
1904
|
+
*/
|
|
581
1905
|
constructor(config: SesConfig);
|
|
582
|
-
|
|
1906
|
+
/**
|
|
1907
|
+
* Internal method to perform the actual SES delivery.
|
|
1908
|
+
*
|
|
1909
|
+
* Converts the generic `Message` object into a raw email format and sends it
|
|
1910
|
+
* via the SES `SendRawEmail` API.
|
|
1911
|
+
*
|
|
1912
|
+
* @param message - The message to deliver.
|
|
1913
|
+
* @returns A promise that resolves when SES accepts the message for delivery.
|
|
1914
|
+
* @throws {Error} If the SES API returns an error or connection fails.
|
|
1915
|
+
*/
|
|
1916
|
+
protected doSend(message: Message): Promise<void>;
|
|
1917
|
+
/**
|
|
1918
|
+
* Formats an Address object into a standard RFC 822 string.
|
|
1919
|
+
*
|
|
1920
|
+
* @param addr - The address object to format.
|
|
1921
|
+
* @returns A string in the format "Name <email@example.com>" or just "email@example.com".
|
|
1922
|
+
*/
|
|
583
1923
|
private formatAddress;
|
|
584
1924
|
}
|
|
585
1925
|
|
|
586
1926
|
/**
|
|
587
1927
|
* Configuration for SMTP email transport.
|
|
588
1928
|
*
|
|
1929
|
+
* Defines the connection parameters, authentication, and pooling settings for
|
|
1930
|
+
* communicating with an SMTP server.
|
|
1931
|
+
*
|
|
1932
|
+
* @example
|
|
1933
|
+
* ```typescript
|
|
1934
|
+
* const config: SmtpConfig = {
|
|
1935
|
+
* host: 'smtp.mailtrap.io',
|
|
1936
|
+
* port: 2525,
|
|
1937
|
+
* auth: { user: 'username', pass: 'password' },
|
|
1938
|
+
* poolSize: 10
|
|
1939
|
+
* };
|
|
1940
|
+
* ```
|
|
1941
|
+
*
|
|
589
1942
|
* @public
|
|
590
|
-
* @since 3.0.0
|
|
591
1943
|
*/
|
|
592
|
-
interface SmtpConfig {
|
|
593
|
-
/** SMTP server hostname */
|
|
1944
|
+
interface SmtpConfig extends TransportOptions {
|
|
1945
|
+
/** SMTP server hostname or IP address. */
|
|
594
1946
|
host: string;
|
|
595
|
-
/** SMTP server port (typically 25, 465, or 587) */
|
|
1947
|
+
/** SMTP server port (typically 25, 465, or 587). */
|
|
596
1948
|
port: number;
|
|
597
|
-
/**
|
|
1949
|
+
/** Whether to use a secure TLS/SSL connection. Should be true for port 465. */
|
|
598
1950
|
secure?: boolean;
|
|
599
|
-
/** Authentication credentials */
|
|
1951
|
+
/** Authentication credentials for the SMTP server. */
|
|
600
1952
|
auth?: {
|
|
601
|
-
/** SMTP username */
|
|
1953
|
+
/** SMTP username. */
|
|
602
1954
|
user: string;
|
|
603
|
-
/** SMTP password */
|
|
1955
|
+
/** SMTP password. */
|
|
604
1956
|
pass: string;
|
|
605
1957
|
};
|
|
606
|
-
/** TLS options */
|
|
1958
|
+
/** TLS specific options for the connection. */
|
|
607
1959
|
tls?: {
|
|
608
|
-
/**
|
|
1960
|
+
/** Whether to reject unauthorized certificates (useful for self-signed certs). */
|
|
609
1961
|
rejectUnauthorized?: boolean;
|
|
610
|
-
/**
|
|
1962
|
+
/** Specific cipher suite to use for the connection. */
|
|
611
1963
|
ciphers?: string;
|
|
612
1964
|
};
|
|
1965
|
+
/** Number of concurrent connections to maintain in the pool. */
|
|
1966
|
+
poolSize?: number;
|
|
1967
|
+
/** Maximum time in milliseconds a connection can remain idle before being closed. */
|
|
1968
|
+
maxIdleTime?: number;
|
|
613
1969
|
}
|
|
614
1970
|
/**
|
|
615
|
-
* SMTP email transport.
|
|
1971
|
+
* SMTP email transport with connection pooling and automatic retry.
|
|
616
1972
|
*
|
|
617
|
-
*
|
|
618
|
-
*
|
|
1973
|
+
* This transport uses the standard SMTP protocol to deliver emails. It leverages
|
|
1974
|
+
* `nodemailer` for robust protocol implementation and includes built-in support
|
|
1975
|
+
* for connection pooling to improve performance when sending multiple emails.
|
|
1976
|
+
* It inherits automatic retry logic from `BaseTransport`.
|
|
619
1977
|
*
|
|
620
1978
|
* @example
|
|
621
1979
|
* ```typescript
|
|
1980
|
+
* import { SmtpTransport } from '@gravito/signal';
|
|
1981
|
+
*
|
|
622
1982
|
* const transport = new SmtpTransport({
|
|
623
|
-
* host: 'smtp.
|
|
1983
|
+
* host: 'smtp.example.com',
|
|
624
1984
|
* port: 587,
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
* }
|
|
630
|
-
* })
|
|
631
|
-
* await transport.send(message)
|
|
1985
|
+
* auth: { user: 'user', pass: 'pass' }
|
|
1986
|
+
* });
|
|
1987
|
+
*
|
|
1988
|
+
* await transport.send(message);
|
|
632
1989
|
* ```
|
|
633
1990
|
*
|
|
634
|
-
* @since 3.0.0
|
|
635
1991
|
* @public
|
|
636
1992
|
*/
|
|
637
|
-
declare class SmtpTransport
|
|
1993
|
+
declare class SmtpTransport extends BaseTransport {
|
|
638
1994
|
private transporter;
|
|
1995
|
+
/**
|
|
1996
|
+
* Initializes the SMTP transport with the provided configuration.
|
|
1997
|
+
*
|
|
1998
|
+
* Sets up the underlying nodemailer transporter with connection pooling enabled.
|
|
1999
|
+
*
|
|
2000
|
+
* @param config - SMTP connection and retry configuration.
|
|
2001
|
+
*/
|
|
639
2002
|
constructor(config: SmtpConfig);
|
|
640
|
-
|
|
2003
|
+
/**
|
|
2004
|
+
* Internal method to perform the actual SMTP delivery.
|
|
2005
|
+
*
|
|
2006
|
+
* Maps the generic `Message` object to the format expected by nodemailer.
|
|
2007
|
+
*
|
|
2008
|
+
* @param message - The message to deliver.
|
|
2009
|
+
* @returns A promise that resolves when the SMTP server accepts the message.
|
|
2010
|
+
* @throws {Error} If the SMTP server rejects the message or connection fails.
|
|
2011
|
+
*/
|
|
2012
|
+
protected doSend(message: Message): Promise<void>;
|
|
2013
|
+
/**
|
|
2014
|
+
* Gracefully shuts down the transport and closes all pooled connections.
|
|
2015
|
+
*
|
|
2016
|
+
* This should be called during application shutdown to ensure no resources are leaked.
|
|
2017
|
+
*
|
|
2018
|
+
* @returns A promise that resolves when all connections are closed.
|
|
2019
|
+
*
|
|
2020
|
+
* @example
|
|
2021
|
+
* ```typescript
|
|
2022
|
+
* await transport.close();
|
|
2023
|
+
* ```
|
|
2024
|
+
*/
|
|
2025
|
+
close(): Promise<void>;
|
|
2026
|
+
/**
|
|
2027
|
+
* Verifies the SMTP connection and authentication.
|
|
2028
|
+
*
|
|
2029
|
+
* Useful for health checks or validating configuration during startup.
|
|
2030
|
+
*
|
|
2031
|
+
* @returns A promise that resolves to true if the connection is valid, false otherwise.
|
|
2032
|
+
*
|
|
2033
|
+
* @example
|
|
2034
|
+
* ```typescript
|
|
2035
|
+
* const isValid = await transport.verify();
|
|
2036
|
+
* if (!isValid) throw new Error('SMTP configuration is invalid');
|
|
2037
|
+
* ```
|
|
2038
|
+
*/
|
|
2039
|
+
verify(): Promise<boolean>;
|
|
2040
|
+
/**
|
|
2041
|
+
* Formats an Address object into a standard RFC 822 string.
|
|
2042
|
+
*
|
|
2043
|
+
* @param addr - The address object to format.
|
|
2044
|
+
* @returns A string in the format "Name <email@example.com>" or just "email@example.com".
|
|
2045
|
+
*/
|
|
641
2046
|
private formatAddress;
|
|
642
2047
|
}
|
|
643
2048
|
|
|
644
|
-
|
|
2049
|
+
/**
|
|
2050
|
+
* Configuration for SendGrid Webhook Driver.
|
|
2051
|
+
*/
|
|
2052
|
+
interface SendGridWebhookConfig {
|
|
2053
|
+
/**
|
|
2054
|
+
* Public key or Verification Secret for signature validation.
|
|
2055
|
+
* If provided, all requests will be validated.
|
|
2056
|
+
*/
|
|
2057
|
+
publicKey?: string;
|
|
2058
|
+
}
|
|
2059
|
+
/**
|
|
2060
|
+
* SendGrid Webhook Driver.
|
|
2061
|
+
*
|
|
2062
|
+
* Handles Event Webhooks from SendGrid (delivered, bounced, opened, clicked, etc.).
|
|
2063
|
+
*
|
|
2064
|
+
* @see https://docs.sendgrid.com/for-developers/tracking-events/event-webhook
|
|
2065
|
+
* @public
|
|
2066
|
+
* @since 1.1.0
|
|
2067
|
+
*/
|
|
2068
|
+
declare class SendGridWebhookDriver implements WebhookDriver {
|
|
2069
|
+
private config;
|
|
2070
|
+
constructor(config?: SendGridWebhookConfig);
|
|
2071
|
+
/**
|
|
2072
|
+
* Handles the SendGrid webhook request.
|
|
2073
|
+
*/
|
|
2074
|
+
handle(c: GravitoContext): Promise<{
|
|
2075
|
+
event: string;
|
|
2076
|
+
payload: any;
|
|
2077
|
+
}[] | null>;
|
|
2078
|
+
/**
|
|
2079
|
+
* Verifies the SendGrid webhook signature.
|
|
2080
|
+
*
|
|
2081
|
+
* @param payload - Raw request body string.
|
|
2082
|
+
* @param signature - Signature from X-Twilio-Email-Event-Webhook-Signature header.
|
|
2083
|
+
* @param timestamp - Timestamp from X-Twilio-Email-Event-Webhook-Timestamp header.
|
|
2084
|
+
* @returns True if signature is valid.
|
|
2085
|
+
*
|
|
2086
|
+
* @remarks
|
|
2087
|
+
* Real SendGrid validation uses Elliptic Curve (ECDSA).
|
|
2088
|
+
* This is a placeholder for the logic structure.
|
|
2089
|
+
*/
|
|
2090
|
+
private verifySignature;
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
/**
|
|
2094
|
+
* AWS SES Webhook Driver.
|
|
2095
|
+
*
|
|
2096
|
+
* Handles SES Notifications via Amazon SNS (Complaints, Bounces, Deliveries).
|
|
2097
|
+
*
|
|
2098
|
+
* @see https://docs.aws.amazon.com/ses/latest/dg/monitor-sending-activity-using-notifications.html
|
|
2099
|
+
* @public
|
|
2100
|
+
* @since 1.1.0
|
|
2101
|
+
*/
|
|
2102
|
+
declare class SesWebhookDriver implements WebhookDriver {
|
|
2103
|
+
/**
|
|
2104
|
+
* Handles the AWS SES/SNS webhook request.
|
|
2105
|
+
*
|
|
2106
|
+
* @param c - The Gravito request context.
|
|
2107
|
+
* @returns Array of processed events or null if ignored.
|
|
2108
|
+
*/
|
|
2109
|
+
handle(c: GravitoContext): Promise<{
|
|
2110
|
+
event: string;
|
|
2111
|
+
payload: any;
|
|
2112
|
+
}[] | null>;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
export { type Address, type Attachment, BaseTransport, DevMailbox, type Envelope, HtmlRenderer, LogTransport, type MailConfig, MailErrorCode, type MailEvent, type MailEventHandler, type MailEventType, MailTransportError, Mailable, type MailboxEntry, MemoryTransport, type Message, MjmlRenderer, OrbitSignal, ReactMjmlRenderer, type RenderResult, type Renderer, type SendGridWebhookConfig, SendGridWebhookDriver, SesTransport, SesWebhookDriver, SmtpTransport, TemplateRenderer, type Transport, type TransportOptions, TypedMailable, VueMjmlRenderer, baseLayout, transactionLayout };
|