@c9up/rover 0.1.7 → 0.1.9
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/README.md +1 -1
- package/dist/BaseMail.d.ts +30 -1
- package/dist/BaseMail.d.ts.map +1 -1
- package/dist/BaseMail.js +30 -0
- package/dist/BaseMail.js.map +1 -1
- package/dist/Mail.d.ts +32 -1
- package/dist/Mail.d.ts.map +1 -1
- package/dist/Mail.js +35 -1
- package/dist/Mail.js.map +1 -1
- package/dist/MessageBuilder.d.ts +197 -7
- package/dist/MessageBuilder.d.ts.map +1 -1
- package/dist/MessageBuilder.js +402 -8
- package/dist/MessageBuilder.js.map +1 -1
- package/dist/RoverProvider.d.ts +1 -1
- package/dist/RoverProvider.d.ts.map +1 -1
- package/dist/RoverProvider.js +6 -6
- package/dist/RoverProvider.js.map +1 -1
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +34 -0
- package/dist/config.js.map +1 -1
- package/dist/format.d.ts +10 -0
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +28 -1
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +18 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/testing/FakeMail.d.ts +33 -0
- package/dist/testing/FakeMail.d.ts.map +1 -1
- package/dist/testing/FakeMail.js +28 -0
- package/dist/testing/FakeMail.js.map +1 -1
- package/dist/transports/BrevoTransport.d.ts.map +1 -1
- package/dist/transports/BrevoTransport.js +5 -4
- package/dist/transports/BrevoTransport.js.map +1 -1
- package/dist/transports/MailgunTransport.d.ts.map +1 -1
- package/dist/transports/MailgunTransport.js +3 -2
- package/dist/transports/MailgunTransport.js.map +1 -1
- package/dist/transports/ResendTransport.d.ts.map +1 -1
- package/dist/transports/ResendTransport.js +5 -4
- package/dist/transports/ResendTransport.js.map +1 -1
- package/dist/transports/SendGridTransport.d.ts.map +1 -1
- package/dist/transports/SendGridTransport.js +3 -2
- package/dist/transports/SendGridTransport.js.map +1 -1
- package/dist/transports/SesTransport.d.ts.map +1 -1
- package/dist/transports/SesTransport.js +7 -5
- package/dist/transports/SesTransport.js.map +1 -1
- package/dist/transports/SparkPostTransport.d.ts.map +1 -1
- package/dist/transports/SparkPostTransport.js +5 -4
- package/dist/transports/SparkPostTransport.js.map +1 -1
- package/dist/transports/fetchError.d.ts +10 -0
- package/dist/transports/fetchError.d.ts.map +1 -1
- package/dist/transports/fetchError.js +30 -0
- package/dist/transports/fetchError.js.map +1 -1
- 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 +6 -1
- package/src/BaseMail.ts +40 -1
- package/src/Mail.ts +55 -2
- package/src/MessageBuilder.ts +561 -11
- package/src/RoverProvider.ts +10 -7
- package/src/config.ts +46 -0
- package/src/format.ts +33 -1
- package/src/index.ts +28 -2
- package/src/testing/FakeMail.ts +46 -0
- package/src/transports/BrevoTransport.ts +5 -4
- package/src/transports/MailgunTransport.ts +3 -2
- package/src/transports/ResendTransport.ts +5 -4
- package/src/transports/SendGridTransport.ts +3 -2
- package/src/transports/SesTransport.ts +7 -5
- package/src/transports/SparkPostTransport.ts +15 -10
- package/src/transports/fetchError.ts +39 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
2
|
import { registerTransport, } from "../Mail.js";
|
|
3
|
+
import { attachmentsFor } from "../MessageBuilder.js";
|
|
3
4
|
import { RoverError } from "../RoverError.js";
|
|
4
|
-
import { wrapFetchNetworkError } from "./fetchError.js";
|
|
5
|
+
import { fetchWithTimeout, wrapFetchNetworkError } from "./fetchError.js";
|
|
5
6
|
const stripCrlf = (v) => v.replace(/[\r\n]/g, "");
|
|
6
7
|
const normalizeConfig = (v) => stripCrlf(v).trim();
|
|
7
8
|
const MAX_PROVIDER_MESSAGE = 16 * 1024;
|
|
@@ -74,8 +75,8 @@ export class SparkPostTransport {
|
|
|
74
75
|
headers.CC = message.cc.map(stripCrlf).join(", ");
|
|
75
76
|
if (Object.keys(headers).length > 0)
|
|
76
77
|
body.content.headers = headers;
|
|
77
|
-
if (message.
|
|
78
|
-
body.content.attachments = message.
|
|
78
|
+
if (attachmentsFor(message).length > 0) {
|
|
79
|
+
body.content.attachments = attachmentsFor(message).map((att) => ({
|
|
79
80
|
name: stripCrlf(att.filename),
|
|
80
81
|
type: att.contentType
|
|
81
82
|
? stripCrlf(att.contentType)
|
|
@@ -85,7 +86,7 @@ export class SparkPostTransport {
|
|
|
85
86
|
}
|
|
86
87
|
let res;
|
|
87
88
|
try {
|
|
88
|
-
res = await
|
|
89
|
+
res = await fetchWithTimeout("SparkPost", `${this.#baseUrl}/api/v1/transmissions`, {
|
|
89
90
|
method: "POST",
|
|
90
91
|
headers: {
|
|
91
92
|
Authorization: this.#apiKey,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SparkPostTransport.js","sourceRoot":"","sources":["../../src/transports/SparkPostTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAIN,iBAAiB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"SparkPostTransport.js","sourceRoot":"","sources":["../../src/transports/SparkPostTransport.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,4 +10,14 @@ import { RoverError } from "../RoverError.js";
|
|
|
10
10
|
* `code` directly.
|
|
11
11
|
*/
|
|
12
12
|
export declare function wrapFetchNetworkError(provider: string, err: unknown): RoverError;
|
|
13
|
+
/**
|
|
14
|
+
* `fetch` with a deadline.
|
|
15
|
+
*
|
|
16
|
+
* Without one, a stalled provider connection never settles: the send hangs,
|
|
17
|
+
* the queue worker holding it hangs with it, and enough of them stop mail going
|
|
18
|
+
* out at all — with no error to explain the silence. A timeout surfaces as the
|
|
19
|
+
* same `MAIL_PROVIDER_ERROR` shape the retry classifier already understands, so
|
|
20
|
+
* it is retried like any other transient network failure.
|
|
21
|
+
*/
|
|
22
|
+
export declare function fetchWithTimeout(provider: string, url: string, init?: RequestInit, timeoutMs?: number): Promise<Response>;
|
|
13
23
|
//# sourceMappingURL=fetchError.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetchError.d.ts","sourceRoot":"","sources":["../../src/transports/fetchError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,OAAO,GACV,UAAU,CA0BZ"}
|
|
1
|
+
{"version":3,"file":"fetchError.d.ts","sourceRoot":"","sources":["../../src/transports/fetchError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,OAAO,GACV,UAAU,CA0BZ;AAKD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACrC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,WAAgB,EACtB,SAAS,GAAE,MAA2B,GACpC,OAAO,CAAC,QAAQ,CAAC,CAoBnB"}
|
|
@@ -32,4 +32,34 @@ export function wrapFetchNetworkError(provider, err) {
|
|
|
32
32
|
context: ctx,
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
+
/** Default ceiling on one provider request. */
|
|
36
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
37
|
+
/**
|
|
38
|
+
* `fetch` with a deadline.
|
|
39
|
+
*
|
|
40
|
+
* Without one, a stalled provider connection never settles: the send hangs,
|
|
41
|
+
* the queue worker holding it hangs with it, and enough of them stop mail going
|
|
42
|
+
* out at all — with no error to explain the silence. A timeout surfaces as the
|
|
43
|
+
* same `MAIL_PROVIDER_ERROR` shape the retry classifier already understands, so
|
|
44
|
+
* it is retried like any other transient network failure.
|
|
45
|
+
*/
|
|
46
|
+
export async function fetchWithTimeout(provider, url, init = {}, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
47
|
+
if (timeoutMs <= 0)
|
|
48
|
+
return fetch(url, init);
|
|
49
|
+
try {
|
|
50
|
+
return await fetch(url, {
|
|
51
|
+
...init,
|
|
52
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
if (err instanceof Error && err.name === "TimeoutError") {
|
|
57
|
+
throw new RoverError("MAIL_PROVIDER_ERROR", `${provider} did not answer within ${timeoutMs}ms.`, {
|
|
58
|
+
context: { provider, upstreamStatus: "0", networkCode: "ETIMEDOUT" },
|
|
59
|
+
hint: "Raise `requestTimeoutMs` for large attachments, or check connectivity to the provider.",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
throw wrapFetchNetworkError(provider, err);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
35
65
|
//# sourceMappingURL=fetchError.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetchError.js","sourceRoot":"","sources":["../../src/transports/fetchError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CACpC,QAAgB,EAChB,GAAY;IAEZ,IAAI,GAAG,YAAY,UAAU;QAAE,OAAO,GAAG,CAAC;IAC1C,MAAM,GAAG,GAAG,GAA6D,CAAC;IAC1E,MAAM,KAAK,GAAG,GAAG,CAAC,KAAuC,CAAC;IAC1D,MAAM,WAAW,GAChB,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAC3B,CAAC,CAAC,GAAG,CAAC,IAAI;QACV,CAAC,CAAC,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ;YAChC,CAAC,CAAC,KAAK,CAAC,IAAI;YACZ,CAAC,CAAC,SAAS,CAAC;IACf,MAAM,OAAO,GACZ,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC;IAChE,MAAM,GAAG,GAA2B;QACnC,QAAQ;QACR,cAAc,EAAE,GAAG;QACnB,eAAe,EAAE,OAAO;KACxB,CAAC;IACF,IAAI,WAAW;QAAE,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC;IAC/C,OAAO,IAAI,UAAU,CACpB,qBAAqB,EACrB,GAAG,QAAQ,gBAAgB,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EACnE;QACC,IAAI,EAAE,iIAAiI;QACvI,OAAO,EAAE,GAAG;KACZ,CACD,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"fetchError.js","sourceRoot":"","sources":["../../src/transports/fetchError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CACpC,QAAgB,EAChB,GAAY;IAEZ,IAAI,GAAG,YAAY,UAAU;QAAE,OAAO,GAAG,CAAC;IAC1C,MAAM,GAAG,GAAG,GAA6D,CAAC;IAC1E,MAAM,KAAK,GAAG,GAAG,CAAC,KAAuC,CAAC;IAC1D,MAAM,WAAW,GAChB,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAC3B,CAAC,CAAC,GAAG,CAAC,IAAI;QACV,CAAC,CAAC,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ;YAChC,CAAC,CAAC,KAAK,CAAC,IAAI;YACZ,CAAC,CAAC,SAAS,CAAC;IACf,MAAM,OAAO,GACZ,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC;IAChE,MAAM,GAAG,GAA2B;QACnC,QAAQ;QACR,cAAc,EAAE,GAAG;QACnB,eAAe,EAAE,OAAO;KACxB,CAAC;IACF,IAAI,WAAW;QAAE,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC;IAC/C,OAAO,IAAI,UAAU,CACpB,qBAAqB,EACrB,GAAG,QAAQ,gBAAgB,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EACnE;QACC,IAAI,EAAE,iIAAiI;QACvI,OAAO,EAAE,GAAG;KACZ,CACD,CAAC;AACH,CAAC;AAED,+CAA+C;AAC/C,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACrC,QAAgB,EAChB,GAAW,EACX,OAAoB,EAAE,EACtB,YAAoB,kBAAkB;IAEtC,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5C,IAAI,CAAC;QACJ,OAAO,MAAM,KAAK,CAAC,GAAG,EAAE;YACvB,GAAG,IAAI;YACP,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;SACtC,CAAC,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACzD,MAAM,IAAI,UAAU,CACnB,qBAAqB,EACrB,GAAG,QAAQ,0BAA0B,SAAS,KAAK,EACnD;gBACC,OAAO,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,WAAW,EAAE,WAAW,EAAE;gBACpE,IAAI,EAAE,wFAAwF;aAC9F,CACD,CAAC;QACH,CAAC;QACD,MAAM,qBAAqB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC5C,CAAC;AACF,CAAC"}
|
package/index.darwin-arm64.node
CHANGED
|
Binary file
|
package/index.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
package/index.linux-x64-gnu.node
CHANGED
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c9up/rover",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Mail transport for Ream — SMTP, log, and pluggable transports",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -67,15 +67,20 @@
|
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@biomejs/biome": "^2.4.10",
|
|
70
|
+
"@c9up/helix": "^0.1.9",
|
|
70
71
|
"@types/node": "*",
|
|
71
72
|
"@types/nodemailer": "^7.0.0",
|
|
72
73
|
"typescript": "^6.0.2",
|
|
73
74
|
"vitest": "^4.1.2"
|
|
74
75
|
},
|
|
75
76
|
"peerDependencies": {
|
|
77
|
+
"@c9up/helix": "^0.1.9",
|
|
76
78
|
"@c9up/ream": "^0.1.0"
|
|
77
79
|
},
|
|
78
80
|
"peerDependenciesMeta": {
|
|
81
|
+
"@c9up/helix": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
79
84
|
"@c9up/ream": {
|
|
80
85
|
"optional": true
|
|
81
86
|
}
|
package/src/BaseMail.ts
CHANGED
|
@@ -2,6 +2,12 @@ import { type MailMessage, MessageBuilder } from "./MessageBuilder.js";
|
|
|
2
2
|
|
|
3
3
|
export type MailAddress = string | { address: string; name?: string };
|
|
4
4
|
|
|
5
|
+
/** The little a mail needs of a mailer to dispatch itself. */
|
|
6
|
+
export interface MailSender<R> {
|
|
7
|
+
send(mail: BaseMail): Promise<R>;
|
|
8
|
+
sendLater(mail: BaseMail): Promise<unknown>;
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
/**
|
|
6
12
|
* Abstract base for class-based mail messages (Adonis parity).
|
|
7
13
|
*
|
|
@@ -17,7 +23,14 @@ export type MailAddress = string | { address: string; name?: string };
|
|
|
17
23
|
* await mail.send(new WelcomeMail(user));
|
|
18
24
|
*/
|
|
19
25
|
export abstract class BaseMail {
|
|
20
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The message this mail builds.
|
|
28
|
+
*
|
|
29
|
+
* Public, because a test asserts on it from outside —
|
|
30
|
+
* `mails.assertSent(WelcomeMail, (mail) => mail.message.hasTo(user.email))`
|
|
31
|
+
* — and a caller may add a recipient before dispatching.
|
|
32
|
+
*/
|
|
33
|
+
readonly message: MessageBuilder = new MessageBuilder();
|
|
21
34
|
|
|
22
35
|
from?: MailAddress;
|
|
23
36
|
replyTo?: MailAddress;
|
|
@@ -33,6 +46,32 @@ export abstract class BaseMail {
|
|
|
33
46
|
|
|
34
47
|
abstract prepare(): void | Promise<void>;
|
|
35
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Send this mail through `mailer`.
|
|
51
|
+
*
|
|
52
|
+
* await new WelcomeMail(user).send(mail.use('smtp'))
|
|
53
|
+
*
|
|
54
|
+
* The mail object is the dispatchable unit, so it can be handed around and
|
|
55
|
+
* sent by whoever holds a mailer.
|
|
56
|
+
*/
|
|
57
|
+
async send<R>(mailer: MailSender<R>): Promise<R> {
|
|
58
|
+
return mailer.send(this);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Queue this mail for background delivery (Adonis `sendLater`). */
|
|
62
|
+
async sendLater(mailer: MailSender<unknown>): Promise<void> {
|
|
63
|
+
await mailer.sendLater(this);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Build the message AND render its templates ahead of time (Adonis
|
|
68
|
+
* `buildWithContents`), so the contents can be inspected before sending —
|
|
69
|
+
* which is what an assertion on the rendered html needs.
|
|
70
|
+
*/
|
|
71
|
+
async buildWithContents(viewsRoot?: string): Promise<MailMessage> {
|
|
72
|
+
return this.build(viewsRoot);
|
|
73
|
+
}
|
|
74
|
+
|
|
36
75
|
async build(viewsRoot?: string): Promise<MailMessage> {
|
|
37
76
|
if (this.from !== undefined) {
|
|
38
77
|
applyAddress(this.from, (address, name) =>
|
package/src/Mail.ts
CHANGED
|
@@ -59,6 +59,11 @@ export type MailSendOutcome = MailSendResult | undefined;
|
|
|
59
59
|
|
|
60
60
|
export interface MailTransport {
|
|
61
61
|
send(message: MailMessage): Promise<MailSendOutcome>;
|
|
62
|
+
/**
|
|
63
|
+
* Release what this transport holds open — an SMTP connection pool, mostly.
|
|
64
|
+
* Optional: an HTTP-API transport has nothing to close.
|
|
65
|
+
*/
|
|
66
|
+
close?(): Promise<void>;
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
/**
|
|
@@ -124,7 +129,16 @@ export interface MailFailedEvent {
|
|
|
124
129
|
export interface MailConfig {
|
|
125
130
|
default: string;
|
|
126
131
|
from: string;
|
|
127
|
-
transports
|
|
132
|
+
transports?: Record<
|
|
133
|
+
string,
|
|
134
|
+
{ transport: string; retry?: RetryConfig; [key: string]: unknown }
|
|
135
|
+
>;
|
|
136
|
+
/**
|
|
137
|
+
* AdonisJS spelling of `transports`. Both are accepted and mean the same
|
|
138
|
+
* thing, so a migrated `config/mail.ts` runs with its imports rewritten and
|
|
139
|
+
* nothing else.
|
|
140
|
+
*/
|
|
141
|
+
mailers?: Record<
|
|
128
142
|
string,
|
|
129
143
|
{ transport: string; retry?: RetryConfig; [key: string]: unknown }
|
|
130
144
|
>;
|
|
@@ -243,12 +257,20 @@ export class SmtpTransport implements MailTransport {
|
|
|
243
257
|
headers: Object.keys(message.headers).length
|
|
244
258
|
? message.headers
|
|
245
259
|
: undefined,
|
|
260
|
+
encoding: message.encoding,
|
|
261
|
+
// `list` is NOT passed: `build()` already rendered the `List-*`
|
|
262
|
+
// headers into `headers`, so handing nodemailer the structured form
|
|
263
|
+
// too would emit each of them twice.
|
|
264
|
+
icalEvent: message.icalEvent,
|
|
246
265
|
attachments: message.attachments.length
|
|
247
266
|
? message.attachments.map((att) => ({
|
|
248
267
|
filename: att.filename,
|
|
249
268
|
content: att.content,
|
|
250
269
|
contentType: att.contentType,
|
|
251
270
|
cid: att.cid,
|
|
271
|
+
contentDisposition: att.contentDisposition,
|
|
272
|
+
encoding: att.encoding,
|
|
273
|
+
headers: att.headers,
|
|
252
274
|
}))
|
|
253
275
|
: undefined,
|
|
254
276
|
});
|
|
@@ -258,6 +280,14 @@ export class SmtpTransport implements MailTransport {
|
|
|
258
280
|
throw wrapSmtpError(err);
|
|
259
281
|
}
|
|
260
282
|
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Drain nodemailer's connection pool. Idempotent — nodemailer tolerates a
|
|
286
|
+
* second close, and a shutdown path may run twice.
|
|
287
|
+
*/
|
|
288
|
+
async close(): Promise<void> {
|
|
289
|
+
this.#transporter.close();
|
|
290
|
+
}
|
|
261
291
|
}
|
|
262
292
|
|
|
263
293
|
/**
|
|
@@ -377,7 +407,10 @@ export class Mail {
|
|
|
377
407
|
setViewsRoot(config.viewsRoot);
|
|
378
408
|
}
|
|
379
409
|
|
|
380
|
-
|
|
410
|
+
// `mailers` (AdonisJS) and `transports` (rover) are the same map under two
|
|
411
|
+
// names; a config that sets both gets both, last name wins per key.
|
|
412
|
+
const declared = { ...config.transports, ...config.mailers };
|
|
413
|
+
for (const [name, transportConfig] of Object.entries(declared)) {
|
|
381
414
|
const factory = transportFactories[transportConfig.transport];
|
|
382
415
|
if (!factory) {
|
|
383
416
|
throw new RoverError(
|
|
@@ -681,6 +714,26 @@ export class Mail {
|
|
|
681
714
|
return t;
|
|
682
715
|
}
|
|
683
716
|
|
|
717
|
+
/**
|
|
718
|
+
* Close one transport's open connections (Adonis `close`).
|
|
719
|
+
*
|
|
720
|
+
* An SMTP pool keeps sockets alive between sends; a process that exits
|
|
721
|
+
* without closing them leaves the server holding connections until it times
|
|
722
|
+
* them out. Unknown or already-closed names are a no-op — shutdown is not
|
|
723
|
+
* the place to throw.
|
|
724
|
+
*/
|
|
725
|
+
async close(name?: string): Promise<void> {
|
|
726
|
+
const transportName = name ?? this.#defaultTransport;
|
|
727
|
+
await this.#transports.get(transportName)?.close?.();
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/** Close every built transport (Adonis `closeAll`). What a shutdown hook calls. */
|
|
731
|
+
async closeAll(): Promise<void> {
|
|
732
|
+
await Promise.all(
|
|
733
|
+
[...this.#transports.values()].map((transport) => transport.close?.()),
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
|
|
684
737
|
/**
|
|
685
738
|
* Enter fake mode. Every subsequent `send()` / `sendLater()` — including via
|
|
686
739
|
* `use(name)` — is captured by the returned `FakeMail` instead of hitting a
|