@rudderjs/mail 0.0.5

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Suleiman Shahbari
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # @rudderjs/mail
2
+
3
+ Mail facade, mailable abstraction, and provider factory with built-in `log` and `smtp` drivers.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @rudderjs/mail
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ```ts
14
+ // config/mail.ts
15
+ import type { MailConfig } from '@rudderjs/mail'
16
+
17
+ export default {
18
+ default: Env.get('MAIL_MAILER', 'log'),
19
+ from: {
20
+ address: Env.get('MAIL_FROM_ADDRESS', 'noreply@example.com'),
21
+ name: Env.get('MAIL_FROM_NAME', 'My App'),
22
+ },
23
+ mailers: {
24
+ log: { driver: 'log' },
25
+ smtp: {
26
+ driver: 'smtp',
27
+ host: Env.get('MAIL_HOST', 'smtp.mailtrap.io'),
28
+ port: Number(Env.get('MAIL_PORT', '587')),
29
+ username: Env.get('MAIL_USERNAME', ''),
30
+ password: Env.get('MAIL_PASSWORD', ''),
31
+ encryption: 'tls',
32
+ },
33
+ },
34
+ } satisfies MailConfig
35
+ ```
36
+
37
+ ```ts
38
+ // bootstrap/providers.ts
39
+ import { mail } from '@rudderjs/mail'
40
+ import configs from '../config/index.js'
41
+
42
+ export default [mail(configs.mail)]
43
+ ```
44
+
45
+ ## Defining Mailables
46
+
47
+ Mailables describe an email. Extend `Mailable` and implement `build()` to set the subject, HTML, and plain-text body using the fluent protected methods.
48
+
49
+ ```ts
50
+ import { Mailable } from '@rudderjs/mail'
51
+
52
+ export class WelcomeEmail extends Mailable {
53
+ constructor(private readonly name: string) { super() }
54
+
55
+ build() {
56
+ return this
57
+ .subject(`Welcome, ${this.name}!`)
58
+ .html(`<h1>Hi ${this.name}, welcome aboard.</h1>`)
59
+ .text(`Hi ${this.name}, welcome aboard.`)
60
+ }
61
+ }
62
+ ```
63
+
64
+ ## Sending Mail
65
+
66
+ ```ts
67
+ import { Mail } from '@rudderjs/mail'
68
+ import { WelcomeEmail } from './WelcomeEmail.js'
69
+
70
+ // Single recipient
71
+ await Mail.to('user@example.com').send(new WelcomeEmail('Alice'))
72
+
73
+ // Multiple recipients with CC and BCC
74
+ await Mail.to('a@example.com', 'b@example.com')
75
+ .cc('manager@example.com')
76
+ .bcc('audit@example.com')
77
+ .send(new WelcomeEmail('Alice'))
78
+ ```
79
+
80
+ ## `Mail` / `MailPendingSend` Methods
81
+
82
+ | Method | Returns | Description |
83
+ |--------|---------|-------------|
84
+ | `Mail.to(...addresses)` | `MailPendingSend` | Start a fluent send chain. |
85
+ | `.cc(...addresses)` | `this` | Add CC recipients. |
86
+ | `.bcc(...addresses)` | `this` | Add BCC recipients. |
87
+ | `.send(mailable)` | `Promise<void>` | Compile and send via the registered adapter. |
88
+
89
+ ## `Mailable` Protected Methods
90
+
91
+ | Method | Description |
92
+ |--------|-------------|
93
+ | `subject(text)` | Set the email subject. |
94
+ | `html(html)` | Set the HTML body. |
95
+ | `text(text)` | Set the plain-text body. |
96
+
97
+ ## Configuration
98
+
99
+ ### `MailConfig`
100
+
101
+ ```ts
102
+ interface MailConfig {
103
+ default: string
104
+ from: { address: string; name?: string }
105
+ mailers: Record<string, MailConnectionConfig>
106
+ }
107
+ ```
108
+
109
+ ### `NodemailerConfig` (smtp driver)
110
+
111
+ ```ts
112
+ {
113
+ driver: 'smtp',
114
+ host: string,
115
+ port: number,
116
+ username?: string,
117
+ password?: string,
118
+ encryption?: 'tls' | 'ssl' | 'none',
119
+ }
120
+ ```
121
+
122
+ ## Built-in Drivers
123
+
124
+ ### `log`
125
+
126
+ Prints outgoing emails to the console. No external dependencies. Ideal for local development.
127
+
128
+ ```ts
129
+ { driver: 'log' }
130
+ ```
131
+
132
+ ### `smtp`
133
+
134
+ Sends emails via SMTP using Nodemailer. Requires `pnpm add nodemailer`.
135
+
136
+ ```ts
137
+ { driver: 'smtp', host: 'smtp.mailtrap.io', port: 587 }
138
+ ```
139
+
140
+ ## `LogAdapter`
141
+
142
+ Exported for standalone use and testing:
143
+
144
+ ```ts
145
+ import { LogAdapter } from '@rudderjs/mail'
146
+
147
+ const adapter = new LogAdapter()
148
+ await adapter.send(mailable, { to: ['user@example.com'], from: { address: 'noreply@app.com' } })
149
+ ```
150
+
151
+ ## Notes
152
+
153
+ - `build()` can be `async` — useful for loading dynamic content before sending.
154
+ - Both `html` and `text` are optional — set at least one for deliverability.
155
+ - The global `from` address in config is used for all outgoing mail unless overridden per-mailable.
156
+ - `smtp` driver requires `pnpm add nodemailer` — it is an optional dependency.
@@ -0,0 +1,26 @@
1
+ import type { Mailable } from './mailable.js';
2
+ import type { MailAdapter, SendOptions } from './index.js';
3
+ /**
4
+ * Tries mailers in order — if the first fails, falls back to the next.
5
+ * All configured mailers must fail before the send is considered a failure.
6
+ *
7
+ * @example
8
+ * // In config/mail.ts:
9
+ * mailers: {
10
+ * failover: {
11
+ * driver: 'failover',
12
+ * mailers: ['smtp', 'ses', 'log'],
13
+ * retryAfter: 60, // seconds before retrying a failed mailer
14
+ * },
15
+ * smtp: { driver: 'smtp', host: '...', port: 587 },
16
+ * ses: { driver: 'smtp', host: 'email-smtp.us-east-1.amazonaws.com', ... },
17
+ * }
18
+ */
19
+ export declare class FailoverAdapter implements MailAdapter {
20
+ private readonly _adapters;
21
+ private readonly _retryAfter;
22
+ private _lastFailures;
23
+ constructor(_adapters: MailAdapter[], _retryAfter?: number);
24
+ send(mailable: Mailable, options: SendOptions): Promise<void>;
25
+ }
26
+ //# sourceMappingURL=failover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failover.d.ts","sourceRoot":"","sources":["../src/failover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAI1D;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,eAAgB,YAAW,WAAW;IAI/C,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAJ9B,OAAO,CAAC,aAAa,CAA4B;gBAG9B,SAAS,EAAE,WAAW,EAAE,EACxB,WAAW,SAAK;IAG7B,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAyBpE"}
@@ -0,0 +1,48 @@
1
+ // ─── FailoverAdapter ────────────────────────────────────────
2
+ /**
3
+ * Tries mailers in order — if the first fails, falls back to the next.
4
+ * All configured mailers must fail before the send is considered a failure.
5
+ *
6
+ * @example
7
+ * // In config/mail.ts:
8
+ * mailers: {
9
+ * failover: {
10
+ * driver: 'failover',
11
+ * mailers: ['smtp', 'ses', 'log'],
12
+ * retryAfter: 60, // seconds before retrying a failed mailer
13
+ * },
14
+ * smtp: { driver: 'smtp', host: '...', port: 587 },
15
+ * ses: { driver: 'smtp', host: 'email-smtp.us-east-1.amazonaws.com', ... },
16
+ * }
17
+ */
18
+ export class FailoverAdapter {
19
+ _adapters;
20
+ _retryAfter;
21
+ _lastFailures = new Map();
22
+ constructor(_adapters, _retryAfter = 60) {
23
+ this._adapters = _adapters;
24
+ this._retryAfter = _retryAfter;
25
+ }
26
+ async send(mailable, options) {
27
+ const errors = [];
28
+ const now = Date.now();
29
+ for (let i = 0; i < this._adapters.length; i++) {
30
+ // Skip adapters that recently failed (within retryAfter window)
31
+ const lastFailure = this._lastFailures.get(i);
32
+ if (lastFailure !== undefined && now - lastFailure < this._retryAfter * 1000) {
33
+ continue;
34
+ }
35
+ try {
36
+ await this._adapters[i].send(mailable, options);
37
+ return; // success
38
+ }
39
+ catch (err) {
40
+ this._lastFailures.set(i, now);
41
+ errors.push(err instanceof Error ? err : new Error(String(err)));
42
+ }
43
+ }
44
+ throw new Error(`[RudderJS Mail] All mailers failed.\n` +
45
+ errors.map((e, i) => ` Mailer ${i}: ${e.message}`).join('\n'));
46
+ }
47
+ }
48
+ //# sourceMappingURL=failover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failover.js","sourceRoot":"","sources":["../src/failover.ts"],"names":[],"mappings":"AAGA,+DAA+D;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,eAAe;IAIP;IACA;IAJX,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAA;IAEjD,YACmB,SAAwB,EACxB,cAAc,EAAE;QADhB,cAAS,GAAT,SAAS,CAAe;QACxB,gBAAW,GAAX,WAAW,CAAK;IAChC,CAAC;IAEJ,KAAK,CAAC,IAAI,CAAC,QAAkB,EAAE,OAAoB;QACjD,MAAM,MAAM,GAAY,EAAE,CAAA;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAEtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/C,gEAAgE;YAChE,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;YAC7C,IAAI,WAAW,KAAK,SAAS,IAAI,GAAG,GAAG,WAAW,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,EAAE,CAAC;gBAC7E,SAAQ;YACV,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;gBAChD,OAAM,CAAC,UAAU;YACnB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;gBAC9B,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;YAClE,CAAC;QACH,CAAC;QAED,MAAM,IAAI,KAAK,CACb,uCAAuC;YACvC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC/D,CAAA;IACH,CAAC;CACF"}
package/dist/fake.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ import type { MailAdapter, SendOptions } from './index.js';
2
+ import type { Mailable } from './mailable.js';
3
+ /**
4
+ * Testing fake for @rudderjs/mail.
5
+ *
6
+ * Records all sent and queued mailables instead of delivering them,
7
+ * and provides assertion methods for verifying mail behavior in tests.
8
+ *
9
+ * @example
10
+ * const fake = FakeMailAdapter.fake()
11
+ *
12
+ * await Mail.to('user@example.com').send(new WelcomeMail())
13
+ *
14
+ * fake.assertSent(WelcomeMail)
15
+ * fake.assertSentCount(1)
16
+ * fake.restore()
17
+ */
18
+ export declare class FakeMailAdapter implements MailAdapter {
19
+ private readonly _sent;
20
+ private readonly _queued;
21
+ send(mailable: Mailable, options: SendOptions): Promise<void>;
22
+ /** Record a mailable as queued (called internally by the queue integration). */
23
+ recordQueued(mailable: Mailable, options: SendOptions): void;
24
+ /** Assert that a mailable of the given class was sent, optionally matching a predicate. */
25
+ assertSent(mailableClass: new (...args: unknown[]) => Mailable, predicate?: (entry: {
26
+ mailable: Mailable;
27
+ options: SendOptions;
28
+ }) => boolean): void;
29
+ /** Assert that exactly N mailables were sent (across all classes). */
30
+ assertSentCount(count: number): void;
31
+ /** Assert that a mailable of the given class was NOT sent. */
32
+ assertNotSent(mailableClass: new (...args: unknown[]) => Mailable): void;
33
+ /** Assert that no mailables were sent at all. */
34
+ assertNothingSent(): void;
35
+ /** Assert that a mailable of the given class was queued. */
36
+ assertQueued(mailableClass: new (...args: unknown[]) => Mailable, predicate?: (entry: {
37
+ mailable: Mailable;
38
+ options: SendOptions;
39
+ }) => boolean): void;
40
+ /** Assert that a mailable of the given class was NOT queued. */
41
+ assertNotQueued(mailableClass: new (...args: unknown[]) => Mailable): void;
42
+ /** Assert that no mailables were queued at all. */
43
+ assertNothingQueued(): void;
44
+ /** Get all sent mailables, optionally filtered by class. */
45
+ sent(mailableClass?: new (...args: unknown[]) => Mailable): Array<{
46
+ mailable: Mailable;
47
+ options: SendOptions;
48
+ }>;
49
+ /** Get all queued mailables, optionally filtered by class. */
50
+ queued(mailableClass?: new (...args: unknown[]) => Mailable): Array<{
51
+ mailable: Mailable;
52
+ options: SendOptions;
53
+ }>;
54
+ /** Restore the mail registry — clears the fake adapter. */
55
+ restore(): void;
56
+ /** Install the fake — replaces the registered mail adapter with this fake. */
57
+ static fake(): FakeMailAdapter;
58
+ private _isInstance;
59
+ private _matchingSent;
60
+ private _matchingQueued;
61
+ }
62
+ //# sourceMappingURL=fake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake.d.ts","sourceRoot":"","sources":["../src/fake.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C;;;;;;;;;;;;;;GAcG;AACH,qBAAa,eAAgB,YAAW,WAAW;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4D;IAClF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0D;IAI5E,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAMnE,gFAAgF;IAChF,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAM5D,2FAA2F;IAC3F,UAAU,CACR,aAAa,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,EACnD,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,GAC3E,IAAI;IAQP,sEAAsE;IACtE,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQpC,8DAA8D;IAC9D,aAAa,CAAC,aAAa,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,GAAG,IAAI;IASxE,iDAAiD;IACjD,iBAAiB,IAAI,IAAI;IAUzB,4DAA4D;IAC5D,YAAY,CACV,aAAa,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,EACnD,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,GAC3E,IAAI;IAQP,gEAAgE;IAChE,eAAe,CAAC,aAAa,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,GAAG,IAAI;IAS1E,mDAAmD;IACnD,mBAAmB,IAAI,IAAI;IAU3B,4DAA4D;IAC5D,IAAI,CACF,aAAa,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,GACnD,KAAK,CAAC;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC;IAKtD,8DAA8D;IAC9D,MAAM,CACJ,aAAa,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,QAAQ,GACnD,KAAK,CAAC;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC;IAOtD,2DAA2D;IAC3D,OAAO,IAAI,IAAI;IAMf,8EAA8E;IAC9E,MAAM,CAAC,IAAI,IAAI,eAAe;IAQ9B,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,eAAe;CAUxB"}
package/dist/fake.js ADDED
@@ -0,0 +1,113 @@
1
+ import assert from 'node:assert/strict';
2
+ import { MailRegistry } from './index.js';
3
+ /**
4
+ * Testing fake for @rudderjs/mail.
5
+ *
6
+ * Records all sent and queued mailables instead of delivering them,
7
+ * and provides assertion methods for verifying mail behavior in tests.
8
+ *
9
+ * @example
10
+ * const fake = FakeMailAdapter.fake()
11
+ *
12
+ * await Mail.to('user@example.com').send(new WelcomeMail())
13
+ *
14
+ * fake.assertSent(WelcomeMail)
15
+ * fake.assertSentCount(1)
16
+ * fake.restore()
17
+ */
18
+ export class FakeMailAdapter {
19
+ _sent = [];
20
+ _queued = [];
21
+ // ─── MailAdapter interface ───────────────────────────────
22
+ async send(mailable, options) {
23
+ this._sent.push({ mailable, options });
24
+ }
25
+ // ─── Queued mail tracking ────────────────────────────────
26
+ /** Record a mailable as queued (called internally by the queue integration). */
27
+ recordQueued(mailable, options) {
28
+ this._queued.push({ mailable, options });
29
+ }
30
+ // ─── Assertions: sent ────────────────────────────────────
31
+ /** Assert that a mailable of the given class was sent, optionally matching a predicate. */
32
+ assertSent(mailableClass, predicate) {
33
+ const matching = this._matchingSent(mailableClass, predicate);
34
+ assert.ok(matching.length > 0, `[RudderJS Mail] Expected "${mailableClass.name}" to be sent, but it was not.`);
35
+ }
36
+ /** Assert that exactly N mailables were sent (across all classes). */
37
+ assertSentCount(count) {
38
+ assert.strictEqual(this._sent.length, count, `[RudderJS Mail] Expected ${count} mail(s) to be sent, but ${this._sent.length} were sent.`);
39
+ }
40
+ /** Assert that a mailable of the given class was NOT sent. */
41
+ assertNotSent(mailableClass) {
42
+ const matching = this._matchingSent(mailableClass);
43
+ assert.strictEqual(matching.length, 0, `[RudderJS Mail] Expected "${mailableClass.name}" not to be sent, but it was sent ${matching.length} time(s).`);
44
+ }
45
+ /** Assert that no mailables were sent at all. */
46
+ assertNothingSent() {
47
+ assert.strictEqual(this._sent.length, 0, `[RudderJS Mail] Expected no mail to be sent, but ${this._sent.length} were sent.`);
48
+ }
49
+ // ─── Assertions: queued ──────────────────────────────────
50
+ /** Assert that a mailable of the given class was queued. */
51
+ assertQueued(mailableClass, predicate) {
52
+ const matching = this._matchingQueued(mailableClass, predicate);
53
+ assert.ok(matching.length > 0, `[RudderJS Mail] Expected "${mailableClass.name}" to be queued, but it was not.`);
54
+ }
55
+ /** Assert that a mailable of the given class was NOT queued. */
56
+ assertNotQueued(mailableClass) {
57
+ const matching = this._matchingQueued(mailableClass);
58
+ assert.strictEqual(matching.length, 0, `[RudderJS Mail] Expected "${mailableClass.name}" not to be queued, but it was queued ${matching.length} time(s).`);
59
+ }
60
+ /** Assert that no mailables were queued at all. */
61
+ assertNothingQueued() {
62
+ assert.strictEqual(this._queued.length, 0, `[RudderJS Mail] Expected no mail to be queued, but ${this._queued.length} were queued.`);
63
+ }
64
+ // ─── Access ──────────────────────────────────────────────
65
+ /** Get all sent mailables, optionally filtered by class. */
66
+ sent(mailableClass) {
67
+ if (!mailableClass)
68
+ return [...this._sent];
69
+ return this._matchingSent(mailableClass);
70
+ }
71
+ /** Get all queued mailables, optionally filtered by class. */
72
+ queued(mailableClass) {
73
+ if (!mailableClass)
74
+ return [...this._queued];
75
+ return this._matchingQueued(mailableClass);
76
+ }
77
+ // ─── Cleanup ─────────────────────────────────────────────
78
+ /** Restore the mail registry — clears the fake adapter. */
79
+ restore() {
80
+ MailRegistry.reset();
81
+ }
82
+ // ─── Install ─────────────────────────────────────────────
83
+ /** Install the fake — replaces the registered mail adapter with this fake. */
84
+ static fake() {
85
+ const fake = new FakeMailAdapter();
86
+ MailRegistry.set(fake);
87
+ return fake;
88
+ }
89
+ // ─── Internal ────────────────────────────────────────────
90
+ _isInstance(mailable, mailableClass) {
91
+ return (mailable instanceof mailableClass ||
92
+ mailable.constructor.name === mailableClass.name);
93
+ }
94
+ _matchingSent(mailableClass, predicate) {
95
+ return this._sent.filter((entry) => {
96
+ if (!this._isInstance(entry.mailable, mailableClass))
97
+ return false;
98
+ if (predicate && !predicate(entry))
99
+ return false;
100
+ return true;
101
+ });
102
+ }
103
+ _matchingQueued(mailableClass, predicate) {
104
+ return this._queued.filter((entry) => {
105
+ if (!this._isInstance(entry.mailable, mailableClass))
106
+ return false;
107
+ if (predicate && !predicate(entry))
108
+ return false;
109
+ return true;
110
+ });
111
+ }
112
+ }
113
+ //# sourceMappingURL=fake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake.js","sourceRoot":"","sources":["../src/fake.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import { ServiceProvider, type Application } from '@rudderjs/core';
2
+ import { Mailable } from './mailable.js';
3
+ export { Mailable } from './mailable.js';
4
+ export type { MailMessage } from './mailable.js';
5
+ export interface SendOptions {
6
+ to: string[];
7
+ from: {
8
+ address: string;
9
+ name?: string;
10
+ };
11
+ cc?: string[];
12
+ bcc?: string[];
13
+ }
14
+ export interface MailAdapter {
15
+ send(mailable: Mailable, options: SendOptions): Promise<void>;
16
+ }
17
+ export interface MailAdapterProvider {
18
+ create(): MailAdapter;
19
+ }
20
+ export declare class MailRegistry {
21
+ private static adapter;
22
+ private static _from;
23
+ static set(adapter: MailAdapter): void;
24
+ static get(): MailAdapter | null;
25
+ static setFrom(from: {
26
+ address: string;
27
+ name?: string;
28
+ }): void;
29
+ static getFrom(): {
30
+ address: string;
31
+ name?: string;
32
+ };
33
+ /** @internal — clears the registered adapter and resets from. Used for testing. */
34
+ static reset(): void;
35
+ }
36
+ export declare class MailPendingSend {
37
+ private readonly _to;
38
+ private _cc;
39
+ private _bcc;
40
+ private _queue?;
41
+ constructor(_to: string[]);
42
+ cc(...addresses: string[]): this;
43
+ bcc(...addresses: string[]): this;
44
+ /** Specify which queue to use for queued mail. */
45
+ onQueue(name: string): this;
46
+ send(mailable: Mailable): Promise<void>;
47
+ /** Queue the mailable for background sending. Requires `@rudderjs/queue`. */
48
+ queue(mailable: Mailable): Promise<void>;
49
+ /** Queue the mailable to be sent after a delay (ms). Requires `@rudderjs/queue`. */
50
+ later(delay: number, mailable: Mailable): Promise<void>;
51
+ }
52
+ export declare class Mail {
53
+ static to(...addresses: string[]): MailPendingSend;
54
+ /** Replace the mail adapter with a fake for testing. */
55
+ static fake(): import('./fake.js').FakeMailAdapter;
56
+ }
57
+ export interface MailConnectionConfig {
58
+ driver: string;
59
+ [key: string]: unknown;
60
+ }
61
+ export interface MailConfig {
62
+ /** The default mailer connection name */
63
+ default: string;
64
+ /** From address used on all outgoing mail */
65
+ from: {
66
+ address: string;
67
+ name?: string;
68
+ };
69
+ /** Named mailer connections */
70
+ mailers: Record<string, MailConnectionConfig>;
71
+ }
72
+ export interface NodemailerConfig {
73
+ driver: 'smtp';
74
+ host: string;
75
+ port: number;
76
+ username?: string;
77
+ password?: string;
78
+ encryption?: 'tls' | 'ssl' | 'none';
79
+ }
80
+ export declare class LogAdapter implements MailAdapter {
81
+ send(mailable: Mailable, options: SendOptions): Promise<void>;
82
+ }
83
+ export declare function nodemailer(config: NodemailerConfig, from: {
84
+ address: string;
85
+ name?: string;
86
+ }): MailAdapterProvider;
87
+ /**
88
+ * Returns a MailServiceProvider class configured for the given mail config.
89
+ *
90
+ * Built-in drivers: log (prints to console — great for dev), smtp (Nodemailer)
91
+ *
92
+ * Usage in bootstrap/providers.ts:
93
+ * import { mail } from '@rudderjs/mail'
94
+ * import configs from '../config/index.js'
95
+ * export default [..., mail(configs.mail), ...]
96
+ */
97
+ export declare function mail(config: MailConfig): new (app: Application) => ServiceProvider;
98
+ export { FailoverAdapter } from './failover.js';
99
+ export { MarkdownMailable } from './markdown.js';
100
+ export { mailPreview } from './preview.js';
101
+ export { FakeMailAdapter } from './fake.js';
102
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}