@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.
Files changed (76) hide show
  1. package/README.md +1 -1
  2. package/dist/BaseMail.d.ts +30 -1
  3. package/dist/BaseMail.d.ts.map +1 -1
  4. package/dist/BaseMail.js +30 -0
  5. package/dist/BaseMail.js.map +1 -1
  6. package/dist/Mail.d.ts +32 -1
  7. package/dist/Mail.d.ts.map +1 -1
  8. package/dist/Mail.js +35 -1
  9. package/dist/Mail.js.map +1 -1
  10. package/dist/MessageBuilder.d.ts +197 -7
  11. package/dist/MessageBuilder.d.ts.map +1 -1
  12. package/dist/MessageBuilder.js +402 -8
  13. package/dist/MessageBuilder.js.map +1 -1
  14. package/dist/RoverProvider.d.ts +1 -1
  15. package/dist/RoverProvider.d.ts.map +1 -1
  16. package/dist/RoverProvider.js +6 -6
  17. package/dist/RoverProvider.js.map +1 -1
  18. package/dist/config.d.ts +38 -0
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js +34 -0
  21. package/dist/config.js.map +1 -1
  22. package/dist/format.d.ts +10 -0
  23. package/dist/format.d.ts.map +1 -1
  24. package/dist/format.js +28 -1
  25. package/dist/format.js.map +1 -1
  26. package/dist/index.d.ts +18 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +16 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/testing/FakeMail.d.ts +33 -0
  31. package/dist/testing/FakeMail.d.ts.map +1 -1
  32. package/dist/testing/FakeMail.js +28 -0
  33. package/dist/testing/FakeMail.js.map +1 -1
  34. package/dist/transports/BrevoTransport.d.ts.map +1 -1
  35. package/dist/transports/BrevoTransport.js +5 -4
  36. package/dist/transports/BrevoTransport.js.map +1 -1
  37. package/dist/transports/MailgunTransport.d.ts.map +1 -1
  38. package/dist/transports/MailgunTransport.js +3 -2
  39. package/dist/transports/MailgunTransport.js.map +1 -1
  40. package/dist/transports/ResendTransport.d.ts.map +1 -1
  41. package/dist/transports/ResendTransport.js +5 -4
  42. package/dist/transports/ResendTransport.js.map +1 -1
  43. package/dist/transports/SendGridTransport.d.ts.map +1 -1
  44. package/dist/transports/SendGridTransport.js +3 -2
  45. package/dist/transports/SendGridTransport.js.map +1 -1
  46. package/dist/transports/SesTransport.d.ts.map +1 -1
  47. package/dist/transports/SesTransport.js +7 -5
  48. package/dist/transports/SesTransport.js.map +1 -1
  49. package/dist/transports/SparkPostTransport.d.ts.map +1 -1
  50. package/dist/transports/SparkPostTransport.js +5 -4
  51. package/dist/transports/SparkPostTransport.js.map +1 -1
  52. package/dist/transports/fetchError.d.ts +10 -0
  53. package/dist/transports/fetchError.d.ts.map +1 -1
  54. package/dist/transports/fetchError.js +30 -0
  55. package/dist/transports/fetchError.js.map +1 -1
  56. package/index.darwin-arm64.node +0 -0
  57. package/index.darwin-x64.node +0 -0
  58. package/index.linux-arm64-gnu.node +0 -0
  59. package/index.linux-x64-gnu.node +0 -0
  60. package/index.win32-x64-msvc.node +0 -0
  61. package/package.json +6 -1
  62. package/src/BaseMail.ts +40 -1
  63. package/src/Mail.ts +55 -2
  64. package/src/MessageBuilder.ts +561 -11
  65. package/src/RoverProvider.ts +10 -7
  66. package/src/config.ts +46 -0
  67. package/src/format.ts +33 -1
  68. package/src/index.ts +28 -2
  69. package/src/testing/FakeMail.ts +46 -0
  70. package/src/transports/BrevoTransport.ts +5 -4
  71. package/src/transports/MailgunTransport.ts +3 -2
  72. package/src/transports/ResendTransport.ts +5 -4
  73. package/src/transports/SendGridTransport.ts +3 -2
  74. package/src/transports/SesTransport.ts +7 -5
  75. package/src/transports/SparkPostTransport.ts +15 -10
  76. 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.attachments.length > 0) {
78
- body.content.attachments = message.attachments.map((att) => ({
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 fetch(`${this.#baseUrl}/api/v1/transmissions`, {
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":"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"}
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"}
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c9up/rover",
3
- "version": "0.1.7",
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
- protected readonly message: MessageBuilder = new MessageBuilder();
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: Record<
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
- for (const [name, transportConfig] of Object.entries(config.transports)) {
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