@c9up/rover 0.1.3
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 +91 -0
- package/dist/BaseMail.d.ts +28 -0
- package/dist/BaseMail.d.ts.map +1 -0
- package/dist/BaseMail.js +42 -0
- package/dist/BaseMail.js.map +1 -0
- package/dist/Mail.d.ts +166 -0
- package/dist/Mail.d.ts.map +1 -0
- package/dist/Mail.js +451 -0
- package/dist/Mail.js.map +1 -0
- package/dist/MessageBuilder.d.ts +38 -0
- package/dist/MessageBuilder.d.ts.map +1 -0
- package/dist/MessageBuilder.js +70 -0
- package/dist/MessageBuilder.js.map +1 -0
- package/dist/RoverError.d.ts +30 -0
- package/dist/RoverError.d.ts.map +1 -0
- package/dist/RoverError.js +33 -0
- package/dist/RoverError.js.map +1 -0
- package/dist/RoverProvider.d.ts +24 -0
- package/dist/RoverProvider.d.ts.map +1 -0
- package/dist/RoverProvider.js +51 -0
- package/dist/RoverProvider.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/queue/MailJob.d.ts +41 -0
- package/dist/queue/MailJob.d.ts.map +1 -0
- package/dist/queue/MailJob.js +75 -0
- package/dist/queue/MailJob.js.map +1 -0
- package/dist/retry.d.ts +20 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +101 -0
- package/dist/retry.js.map +1 -0
- package/dist/services/main.d.ts +20 -0
- package/dist/services/main.d.ts.map +1 -0
- package/dist/services/main.js +33 -0
- package/dist/services/main.js.map +1 -0
- package/dist/templating/SimpleTemplate.d.ts +7 -0
- package/dist/templating/SimpleTemplate.d.ts.map +1 -0
- package/dist/templating/SimpleTemplate.js +192 -0
- package/dist/templating/SimpleTemplate.js.map +1 -0
- package/dist/templating/loadNapi.d.ts +21 -0
- package/dist/templating/loadNapi.d.ts.map +1 -0
- package/dist/templating/loadNapi.js +115 -0
- package/dist/templating/loadNapi.js.map +1 -0
- package/dist/testing/FakeMail.d.ts +31 -0
- package/dist/testing/FakeMail.d.ts.map +1 -0
- package/dist/testing/FakeMail.js +90 -0
- package/dist/testing/FakeMail.js.map +1 -0
- package/dist/transports/MailgunTransport.d.ts +7 -0
- package/dist/transports/MailgunTransport.d.ts.map +1 -0
- package/dist/transports/MailgunTransport.js +149 -0
- package/dist/transports/MailgunTransport.js.map +1 -0
- package/dist/transports/ResendTransport.d.ts +7 -0
- package/dist/transports/ResendTransport.d.ts.map +1 -0
- package/dist/transports/ResendTransport.js +111 -0
- package/dist/transports/ResendTransport.js.map +1 -0
- package/dist/transports/SendGridTransport.d.ts +7 -0
- package/dist/transports/SendGridTransport.d.ts.map +1 -0
- package/dist/transports/SendGridTransport.js +165 -0
- package/dist/transports/SendGridTransport.js.map +1 -0
- package/dist/transports/SesTransport.d.ts +7 -0
- package/dist/transports/SesTransport.d.ts.map +1 -0
- package/dist/transports/SesTransport.js +334 -0
- package/dist/transports/SesTransport.js.map +1 -0
- package/dist/transports/fetchError.d.ts +13 -0
- package/dist/transports/fetchError.d.ts.map +1 -0
- package/dist/transports/fetchError.js +35 -0
- package/dist/transports/fetchError.js.map +1 -0
- package/dist/webhooks/context.d.ts +35 -0
- package/dist/webhooks/context.d.ts.map +1 -0
- package/dist/webhooks/context.js +7 -0
- package/dist/webhooks/context.js.map +1 -0
- package/dist/webhooks/mailgun.d.ts +13 -0
- package/dist/webhooks/mailgun.d.ts.map +1 -0
- package/dist/webhooks/mailgun.js +102 -0
- package/dist/webhooks/mailgun.js.map +1 -0
- package/dist/webhooks/resend.d.ts +16 -0
- package/dist/webhooks/resend.d.ts.map +1 -0
- package/dist/webhooks/resend.js +121 -0
- package/dist/webhooks/resend.js.map +1 -0
- package/dist/webhooks/sendgrid.d.ts +13 -0
- package/dist/webhooks/sendgrid.d.ts.map +1 -0
- package/dist/webhooks/sendgrid.js +104 -0
- package/dist/webhooks/sendgrid.js.map +1 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +97 -0
- package/scripts/copy-napi.mjs +62 -0
- package/src/BaseMail.ts +52 -0
- package/src/Mail.ts +663 -0
- package/src/MessageBuilder.ts +101 -0
- package/src/RoverError.ts +44 -0
- package/src/RoverProvider.ts +72 -0
- package/src/config.ts +7 -0
- package/src/index.ts +30 -0
- package/src/queue/MailJob.ts +131 -0
- package/src/retry.ts +135 -0
- package/src/services/main.ts +41 -0
- package/src/templating/SimpleTemplate.ts +240 -0
- package/src/templating/loadNapi.ts +169 -0
- package/src/testing/FakeMail.ts +112 -0
- package/src/transports/MailgunTransport.ts +204 -0
- package/src/transports/ResendTransport.ts +147 -0
- package/src/transports/SendGridTransport.ts +246 -0
- package/src/transports/SesTransport.ts +394 -0
- package/src/transports/fetchError.ts +42 -0
- package/src/webhooks/context.ts +41 -0
- package/src/webhooks/mailgun.ts +134 -0
- package/src/webhooks/resend.ts +164 -0
- package/src/webhooks/sendgrid.ts +133 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 C9up
|
|
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,91 @@
|
|
|
1
|
+
# @c9up/rover
|
|
2
|
+
|
|
3
|
+
Mail transport for Ream — SMTP, log, and pluggable transports (Mailgun, SendGrid, SES, Resend), plus class-based mailers (`BaseMail`), async templating (`htmlView`), in-process retry with exponential backoff, and an optional queue path via `@c9up/bay`.
|
|
4
|
+
|
|
5
|
+
## Quick start
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Mail } from '@c9up/rover'
|
|
9
|
+
import '@c9up/rover/transports/mailgun' // opt-in side-effect registration
|
|
10
|
+
|
|
11
|
+
const mail = new Mail({
|
|
12
|
+
default: 'mailgun',
|
|
13
|
+
from: 'noreply@acme.com',
|
|
14
|
+
transports: {
|
|
15
|
+
mailgun: { transport: 'mailgun', apiKey: process.env.MAILGUN_KEY!, domain: 'mg.acme.com' },
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
await mail.send((m) => m.to('user@example.com').subject('Welcome').html('<p>Hi</p>'))
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Webhooks
|
|
23
|
+
|
|
24
|
+
Providers push delivery events (`delivered`, `bounced`, `failed`) to a URL you configure. Rover ships signature-verifying middleware for Mailgun, SendGrid, and Resend that emit canonical `mail.*` events into your event bus.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { createMailgunWebhookHandler } from '@c9up/rover/webhooks/mailgun'
|
|
28
|
+
import { Emitter } from '@c9up/ream/events'
|
|
29
|
+
|
|
30
|
+
const emitter = app.container.resolve(Emitter)
|
|
31
|
+
|
|
32
|
+
router.post(
|
|
33
|
+
'/webhooks/mailgun',
|
|
34
|
+
createMailgunWebhookHandler({
|
|
35
|
+
signingKey: process.env.MAILGUN_SIGNING_KEY!,
|
|
36
|
+
emitter,
|
|
37
|
+
// maxAgeSeconds: 300 (default) — rejects replayed-old signatures
|
|
38
|
+
}),
|
|
39
|
+
)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The handler verifies the HMAC over `timestamp + token`, enforces a ±5 minute replay window, and emits `mail.delivered` / `mail.bounced` / `mail.failed` on the bus. Invalid / stale / missing signatures return `401` without leaking internals.
|
|
43
|
+
|
|
44
|
+
SendGrid (Ed25519 via `x-twilio-email-event-webhook-signature`) and Resend (Svix HMAC via `svix-signature`) use the same API shape:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { createSendGridWebhookHandler } from '@c9up/rover/webhooks/sendgrid'
|
|
48
|
+
import { createResendWebhookHandler } from '@c9up/rover/webhooks/resend'
|
|
49
|
+
|
|
50
|
+
router.post('/webhooks/sendgrid', createSendGridWebhookHandler({
|
|
51
|
+
publicKey: process.env.SENDGRID_WEBHOOK_PUBLIC_KEY!,
|
|
52
|
+
emitter,
|
|
53
|
+
}))
|
|
54
|
+
|
|
55
|
+
router.post('/webhooks/resend', createResendWebhookHandler({
|
|
56
|
+
secret: process.env.RESEND_WEBHOOK_SECRET!, // accepts both `whsec_...` and raw base64
|
|
57
|
+
emitter,
|
|
58
|
+
}))
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `rawBody()` contract
|
|
62
|
+
|
|
63
|
+
All three handlers rely on `ctx.request.rawBody()` returning the exact bytes the provider signed. If your framework re-parses the body into JSON before the handler sees it and your `rawBody()` returns a re-serialised copy, bytes differ and every signature fails. Mount these handlers on a route where the raw body is preserved (e.g. skip the JSON body parser on `/webhooks/**`).
|
|
64
|
+
|
|
65
|
+
## Queue / `sendLater`
|
|
66
|
+
|
|
67
|
+
Register `@c9up/bay`'s `QueueManager` in your container and Rover will pick it up automatically:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import '@c9up/rover/transports/mailgun'
|
|
71
|
+
import { QueueManager, MemoryDriver } from '@c9up/bay'
|
|
72
|
+
|
|
73
|
+
app.container.singleton('QueueManager', () => new QueueManager(new MemoryDriver()))
|
|
74
|
+
|
|
75
|
+
// Then in a request handler:
|
|
76
|
+
await mail.sendLater((m) => m.to('user@acme.com').subject('Welcome').html('<p>Hi</p>'))
|
|
77
|
+
// returns a job id string; the actual send runs on the Bay worker.
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Without a `QueueManager` wired, `sendLater` throws `MAIL_QUEUE_REQUIRED`.
|
|
81
|
+
|
|
82
|
+
## Testing
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import '@c9up/rover/transports/mailgun' // register whatever transports your app uses
|
|
86
|
+
|
|
87
|
+
const fake = mail.fake()
|
|
88
|
+
await userRegistration({ email: 'user@acme.com' })
|
|
89
|
+
fake.assertSent({ to: 'user@acme.com', subject: 'Welcome' })
|
|
90
|
+
mail.restore()
|
|
91
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type MailMessage, MessageBuilder } from "./MessageBuilder.js";
|
|
2
|
+
export type MailAddress = string | {
|
|
3
|
+
address: string;
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Abstract base for class-based mail messages (Adonis parity).
|
|
8
|
+
*
|
|
9
|
+
* class WelcomeMail extends BaseMail {
|
|
10
|
+
* from = { address: "noreply@acme.com", name: "Acme" };
|
|
11
|
+
* subject = "Welcome!";
|
|
12
|
+
* constructor(private user: User) { super(); }
|
|
13
|
+
* async prepare() {
|
|
14
|
+
* this.message.to(this.user.email).html(...);
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* await mail.send(new WelcomeMail(user));
|
|
19
|
+
*/
|
|
20
|
+
export declare abstract class BaseMail {
|
|
21
|
+
protected readonly message: MessageBuilder;
|
|
22
|
+
from?: MailAddress;
|
|
23
|
+
subject?: string;
|
|
24
|
+
constructor();
|
|
25
|
+
abstract prepare(): void | Promise<void>;
|
|
26
|
+
build(): Promise<MailMessage>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=BaseMail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseMail.d.ts","sourceRoot":"","sources":["../src/BaseMail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEvE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE;;;;;;;;;;;;;GAaG;AACH,8BAAsB,QAAQ;IAC7B,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAwB;IAElE,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;;IAUjB,QAAQ,CAAC,OAAO,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAElC,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;CAUnC"}
|
package/dist/BaseMail.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { MessageBuilder } from "./MessageBuilder.js";
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base for class-based mail messages (Adonis parity).
|
|
4
|
+
*
|
|
5
|
+
* class WelcomeMail extends BaseMail {
|
|
6
|
+
* from = { address: "noreply@acme.com", name: "Acme" };
|
|
7
|
+
* subject = "Welcome!";
|
|
8
|
+
* constructor(private user: User) { super(); }
|
|
9
|
+
* async prepare() {
|
|
10
|
+
* this.message.to(this.user.email).html(...);
|
|
11
|
+
* }
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* await mail.send(new WelcomeMail(user));
|
|
15
|
+
*/
|
|
16
|
+
export class BaseMail {
|
|
17
|
+
message = new MessageBuilder();
|
|
18
|
+
from;
|
|
19
|
+
subject;
|
|
20
|
+
constructor() {
|
|
21
|
+
if (new.target === BaseMail) {
|
|
22
|
+
throw new Error("BaseMail is abstract and cannot be instantiated directly");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async build() {
|
|
26
|
+
if (this.from !== undefined) {
|
|
27
|
+
this.message.from(formatAddress(this.from));
|
|
28
|
+
}
|
|
29
|
+
if (this.subject !== undefined) {
|
|
30
|
+
this.message.subject(this.subject);
|
|
31
|
+
}
|
|
32
|
+
await this.prepare();
|
|
33
|
+
return this.message.build();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function formatAddress(addr) {
|
|
37
|
+
if (typeof addr === "string") {
|
|
38
|
+
return addr;
|
|
39
|
+
}
|
|
40
|
+
return `"${addr.name}" <${addr.address}>`;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=BaseMail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseMail.js","sourceRoot":"","sources":["../src/BaseMail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAIvE;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAgB,QAAQ;IACV,OAAO,GAAmB,IAAI,cAAc,EAAE,CAAC;IAElE,IAAI,CAAe;IACnB,OAAO,CAAU;IAEjB;QACC,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CACd,0DAA0D,CAC1D,CAAC;QACH,CAAC;IACF,CAAC;IAID,KAAK,CAAC,KAAK;QACV,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;CACD;AAED,SAAS,aAAa,CAAC,IAAiB;IACvC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,OAAO,GAAG,CAAC;AAC3C,CAAC"}
|
package/dist/Mail.d.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { BaseMail } from "./BaseMail.js";
|
|
2
|
+
import { type MailAttachment, type MailMessage, MessageBuilder } from "./MessageBuilder.js";
|
|
3
|
+
import { type BayQueueLike } from "./queue/MailJob.js";
|
|
4
|
+
import { type RetryConfig } from "./retry.js";
|
|
5
|
+
import { FakeMail } from "./testing/FakeMail.js";
|
|
6
|
+
export type { MailAttachment, MailMessage };
|
|
7
|
+
export { MessageBuilder };
|
|
8
|
+
/**
|
|
9
|
+
* Mail — send emails via pluggable transports.
|
|
10
|
+
*
|
|
11
|
+
* Like AdonisJS Mail:
|
|
12
|
+
* await mail.send((message) => {
|
|
13
|
+
* message.to('user@example.com')
|
|
14
|
+
* message.subject('Welcome')
|
|
15
|
+
* message.html('<h1>Hello</h1>')
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* Transports: SMTP, log (dev), custom.
|
|
19
|
+
* Configured via config/mail.ts.
|
|
20
|
+
*/
|
|
21
|
+
export interface MailSendResult {
|
|
22
|
+
/** Provider-returned message id (Mailgun `mg-abc`, SendGrid `X-Message-Id`, etc.). */
|
|
23
|
+
providerId?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Return shape of `MailTransport.send`. Transports that don't surface a
|
|
27
|
+
* provider id may `return` nothing (implicit `undefined`); transports that
|
|
28
|
+
* do return `{ providerId }`. `undefined` rather than `void` in the union
|
|
29
|
+
* keeps the linter happy (`noConfusingVoidType`) while preserving the
|
|
30
|
+
* "no result" semantics.
|
|
31
|
+
*/
|
|
32
|
+
export type MailSendOutcome = MailSendResult | undefined;
|
|
33
|
+
export interface MailTransport {
|
|
34
|
+
send(message: MailMessage): Promise<MailSendOutcome>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Structural interface for the event bus `Emitter` — peer-dep friendly.
|
|
38
|
+
* Rover never hard-imports the event bus.
|
|
39
|
+
*/
|
|
40
|
+
export interface EmitterLike {
|
|
41
|
+
emit(event: string, data: unknown): void;
|
|
42
|
+
}
|
|
43
|
+
export interface MailSentEvent {
|
|
44
|
+
messageId: string;
|
|
45
|
+
to: string[];
|
|
46
|
+
cc: string[];
|
|
47
|
+
bcc: string[];
|
|
48
|
+
transportName: string;
|
|
49
|
+
timestamp: number;
|
|
50
|
+
}
|
|
51
|
+
export interface MailFailedEvent {
|
|
52
|
+
messageId: string;
|
|
53
|
+
to: string[];
|
|
54
|
+
cc: string[];
|
|
55
|
+
bcc: string[];
|
|
56
|
+
transportName: string;
|
|
57
|
+
error: {
|
|
58
|
+
code: string;
|
|
59
|
+
message: string;
|
|
60
|
+
upstreamStatus?: number;
|
|
61
|
+
upstreamStatusRaw?: string;
|
|
62
|
+
attempts: number;
|
|
63
|
+
};
|
|
64
|
+
timestamp: number;
|
|
65
|
+
}
|
|
66
|
+
export interface MailConfig {
|
|
67
|
+
default: string;
|
|
68
|
+
from: string;
|
|
69
|
+
transports: Record<string, {
|
|
70
|
+
transport: string;
|
|
71
|
+
retry?: RetryConfig;
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}>;
|
|
74
|
+
/** Root directory for `htmlView(path, data)` template lookups. Default: `"resources/views/emails"`. */
|
|
75
|
+
viewsRoot?: string;
|
|
76
|
+
/** Optional Bay queue tuning for `sendLater()`. */
|
|
77
|
+
queue?: {
|
|
78
|
+
name?: string;
|
|
79
|
+
maxAttempts?: number;
|
|
80
|
+
};
|
|
81
|
+
/** Process-wide retry defaults. Overridden per-transport via `transports[name].retry`. */
|
|
82
|
+
retry?: RetryConfig;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Event hooks invoked by the internal dispatch loop. Default implementations
|
|
86
|
+
* are no-ops; when an event-bus `EmitterLike` is wired, the hooks emit
|
|
87
|
+
* `mail.sent` / `mail.failed`. Tests inject spies.
|
|
88
|
+
*/
|
|
89
|
+
export interface MailHooks {
|
|
90
|
+
onSent?(event: MailSentEvent): void;
|
|
91
|
+
onFailed?(event: MailFailedEvent): void;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Message builder — fluent API for composing an email.
|
|
95
|
+
*/
|
|
96
|
+
/**
|
|
97
|
+
* SMTP transport — thin wrapper around `nodemailer`. Nodemailer handles
|
|
98
|
+
* the SMTP state machine, TLS / STARTTLS negotiation, AUTH LOGIN /
|
|
99
|
+
* PLAIN / XOAUTH2, dot-stuffing, MIME assembly, attachments, and address
|
|
100
|
+
* encoding for 10+ years. We forward the `MailMessage` shape and surface
|
|
101
|
+
* the `messageId` returned by the server.
|
|
102
|
+
*/
|
|
103
|
+
export declare class SmtpTransport implements MailTransport {
|
|
104
|
+
#private;
|
|
105
|
+
constructor(config: Record<string, unknown>);
|
|
106
|
+
send(message: MailMessage): Promise<MailSendOutcome>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Log transport — logs emails to console (development).
|
|
110
|
+
*/
|
|
111
|
+
export declare class LogTransport implements MailTransport {
|
|
112
|
+
send(message: MailMessage): Promise<MailSendOutcome>;
|
|
113
|
+
}
|
|
114
|
+
export type MailTransportFactory = (config: Record<string, unknown>) => MailTransport;
|
|
115
|
+
/**
|
|
116
|
+
* Register a custom transport factory globally.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* registerTransport('mailgun', (config) => new MailgunTransport(config))
|
|
120
|
+
* // Now config/mail.ts can reference `transport: 'mailgun'`
|
|
121
|
+
*/
|
|
122
|
+
export declare function registerTransport(name: string, factory: MailTransportFactory): void;
|
|
123
|
+
/**
|
|
124
|
+
* Mail manager — send emails via configured transport.
|
|
125
|
+
*/
|
|
126
|
+
export declare class Mail {
|
|
127
|
+
#private;
|
|
128
|
+
constructor(config: MailConfig, options?: {
|
|
129
|
+
queue?: BayQueueLike;
|
|
130
|
+
hooks?: MailHooks;
|
|
131
|
+
emitter?: EmitterLike;
|
|
132
|
+
});
|
|
133
|
+
/** Send an email using the fluent message builder. */
|
|
134
|
+
send(callback: (message: MessageBuilder) => void, transport?: string): Promise<void>;
|
|
135
|
+
/** Send an email using a class-based `BaseMail` instance. */
|
|
136
|
+
send(instance: BaseMail, transport?: string): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Enqueue a send onto the Bay queue. Returns the job id. Throws
|
|
139
|
+
* `MAIL_QUEUE_REQUIRED` if no `QueueManager` was wired through the
|
|
140
|
+
* constructor options.
|
|
141
|
+
*/
|
|
142
|
+
sendLater(arg: ((message: MessageBuilder) => void) | BaseMail, options?: {
|
|
143
|
+
transport?: string;
|
|
144
|
+
queue?: string;
|
|
145
|
+
}): Promise<string>;
|
|
146
|
+
/**
|
|
147
|
+
* Shared transport-resolve-then-send helper used by `send()` and `MailJobHandler`.
|
|
148
|
+
* Applies in-process retry with exponential backoff per `RetryConfig` resolution
|
|
149
|
+
* (per-transport > global > library default). Fires `onSent` / `onFailed` hooks.
|
|
150
|
+
*
|
|
151
|
+
* `overrideRetry` (optional) wins over config-level retry; `MailJobHandler` passes
|
|
152
|
+
* `{ maxAttempts: 1 }` so queue-level and sync-level retries don't compound.
|
|
153
|
+
*/
|
|
154
|
+
dispatchMessage(message: MailMessage, transportName?: string, overrideRetry?: RetryConfig): Promise<void>;
|
|
155
|
+
/** Get a specific transport. */
|
|
156
|
+
use(name: string): MailTransport;
|
|
157
|
+
/**
|
|
158
|
+
* Swap the default transport with a `FakeMail` that captures every send.
|
|
159
|
+
* Call `restore()` to re-install the original. Throws if a fake is already
|
|
160
|
+
* active — nested fakes always indicate a forgotten `restore()`.
|
|
161
|
+
*/
|
|
162
|
+
fake(): FakeMail;
|
|
163
|
+
/** Undo the swap installed by `fake()`. No-op if no fake is active. */
|
|
164
|
+
restore(): void;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=Mail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Mail.d.ts","sourceRoot":"","sources":["../src/Mail.ts"],"names":[],"mappings":"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"}
|