@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 +21 -0
- package/README.md +156 -0
- package/dist/failover.d.ts +26 -0
- package/dist/failover.d.ts.map +1 -0
- package/dist/failover.js +48 -0
- package/dist/failover.js.map +1 -0
- package/dist/fake.d.ts +62 -0
- package/dist/fake.d.ts.map +1 -0
- package/dist/fake.js +113 -0
- package/dist/fake.js.map +1 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +215 -0
- package/dist/index.js.map +1 -0
- package/dist/mailable.d.ts +21 -0
- package/dist/mailable.d.ts.map +1 -0
- package/dist/mailable.js +24 -0
- package/dist/mailable.js.map +1 -0
- package/dist/markdown.d.ts +39 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +194 -0
- package/dist/markdown.js.map +1 -0
- package/dist/preview.d.ts +21 -0
- package/dist/preview.d.ts.map +1 -0
- package/dist/preview.js +62 -0
- package/dist/preview.js.map +1 -0
- package/dist/queued.d.ts +11 -0
- package/dist/queued.d.ts.map +1 -0
- package/dist/queued.js +36 -0
- package/dist/queued.js.map +1 -0
- package/package.json +41 -0
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"}
|
package/dist/failover.js
ADDED
|
@@ -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
|
package/dist/fake.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fake.js","sourceRoot":"","sources":["../src/fake.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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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"}
|