@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.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +91 -0
  3. package/dist/BaseMail.d.ts +28 -0
  4. package/dist/BaseMail.d.ts.map +1 -0
  5. package/dist/BaseMail.js +42 -0
  6. package/dist/BaseMail.js.map +1 -0
  7. package/dist/Mail.d.ts +166 -0
  8. package/dist/Mail.d.ts.map +1 -0
  9. package/dist/Mail.js +451 -0
  10. package/dist/Mail.js.map +1 -0
  11. package/dist/MessageBuilder.d.ts +38 -0
  12. package/dist/MessageBuilder.d.ts.map +1 -0
  13. package/dist/MessageBuilder.js +70 -0
  14. package/dist/MessageBuilder.js.map +1 -0
  15. package/dist/RoverError.d.ts +30 -0
  16. package/dist/RoverError.d.ts.map +1 -0
  17. package/dist/RoverError.js +33 -0
  18. package/dist/RoverError.js.map +1 -0
  19. package/dist/RoverProvider.d.ts +24 -0
  20. package/dist/RoverProvider.d.ts.map +1 -0
  21. package/dist/RoverProvider.js +51 -0
  22. package/dist/RoverProvider.js.map +1 -0
  23. package/dist/config.d.ts +4 -0
  24. package/dist/config.d.ts.map +1 -0
  25. package/dist/config.js +4 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/index.d.ts +9 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +7 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/queue/MailJob.d.ts +41 -0
  32. package/dist/queue/MailJob.d.ts.map +1 -0
  33. package/dist/queue/MailJob.js +75 -0
  34. package/dist/queue/MailJob.js.map +1 -0
  35. package/dist/retry.d.ts +20 -0
  36. package/dist/retry.d.ts.map +1 -0
  37. package/dist/retry.js +101 -0
  38. package/dist/retry.js.map +1 -0
  39. package/dist/services/main.d.ts +20 -0
  40. package/dist/services/main.d.ts.map +1 -0
  41. package/dist/services/main.js +33 -0
  42. package/dist/services/main.js.map +1 -0
  43. package/dist/templating/SimpleTemplate.d.ts +7 -0
  44. package/dist/templating/SimpleTemplate.d.ts.map +1 -0
  45. package/dist/templating/SimpleTemplate.js +192 -0
  46. package/dist/templating/SimpleTemplate.js.map +1 -0
  47. package/dist/templating/loadNapi.d.ts +21 -0
  48. package/dist/templating/loadNapi.d.ts.map +1 -0
  49. package/dist/templating/loadNapi.js +115 -0
  50. package/dist/templating/loadNapi.js.map +1 -0
  51. package/dist/testing/FakeMail.d.ts +31 -0
  52. package/dist/testing/FakeMail.d.ts.map +1 -0
  53. package/dist/testing/FakeMail.js +90 -0
  54. package/dist/testing/FakeMail.js.map +1 -0
  55. package/dist/transports/MailgunTransport.d.ts +7 -0
  56. package/dist/transports/MailgunTransport.d.ts.map +1 -0
  57. package/dist/transports/MailgunTransport.js +149 -0
  58. package/dist/transports/MailgunTransport.js.map +1 -0
  59. package/dist/transports/ResendTransport.d.ts +7 -0
  60. package/dist/transports/ResendTransport.d.ts.map +1 -0
  61. package/dist/transports/ResendTransport.js +111 -0
  62. package/dist/transports/ResendTransport.js.map +1 -0
  63. package/dist/transports/SendGridTransport.d.ts +7 -0
  64. package/dist/transports/SendGridTransport.d.ts.map +1 -0
  65. package/dist/transports/SendGridTransport.js +165 -0
  66. package/dist/transports/SendGridTransport.js.map +1 -0
  67. package/dist/transports/SesTransport.d.ts +7 -0
  68. package/dist/transports/SesTransport.d.ts.map +1 -0
  69. package/dist/transports/SesTransport.js +334 -0
  70. package/dist/transports/SesTransport.js.map +1 -0
  71. package/dist/transports/fetchError.d.ts +13 -0
  72. package/dist/transports/fetchError.d.ts.map +1 -0
  73. package/dist/transports/fetchError.js +35 -0
  74. package/dist/transports/fetchError.js.map +1 -0
  75. package/dist/webhooks/context.d.ts +35 -0
  76. package/dist/webhooks/context.d.ts.map +1 -0
  77. package/dist/webhooks/context.js +7 -0
  78. package/dist/webhooks/context.js.map +1 -0
  79. package/dist/webhooks/mailgun.d.ts +13 -0
  80. package/dist/webhooks/mailgun.d.ts.map +1 -0
  81. package/dist/webhooks/mailgun.js +102 -0
  82. package/dist/webhooks/mailgun.js.map +1 -0
  83. package/dist/webhooks/resend.d.ts +16 -0
  84. package/dist/webhooks/resend.d.ts.map +1 -0
  85. package/dist/webhooks/resend.js +121 -0
  86. package/dist/webhooks/resend.js.map +1 -0
  87. package/dist/webhooks/sendgrid.d.ts +13 -0
  88. package/dist/webhooks/sendgrid.d.ts.map +1 -0
  89. package/dist/webhooks/sendgrid.js +104 -0
  90. package/dist/webhooks/sendgrid.js.map +1 -0
  91. package/index.darwin-arm64.node +0 -0
  92. package/index.darwin-x64.node +0 -0
  93. package/index.linux-arm64-gnu.node +0 -0
  94. package/index.linux-x64-gnu.node +0 -0
  95. package/index.win32-x64-msvc.node +0 -0
  96. package/package.json +97 -0
  97. package/scripts/copy-napi.mjs +62 -0
  98. package/src/BaseMail.ts +52 -0
  99. package/src/Mail.ts +663 -0
  100. package/src/MessageBuilder.ts +101 -0
  101. package/src/RoverError.ts +44 -0
  102. package/src/RoverProvider.ts +72 -0
  103. package/src/config.ts +7 -0
  104. package/src/index.ts +30 -0
  105. package/src/queue/MailJob.ts +131 -0
  106. package/src/retry.ts +135 -0
  107. package/src/services/main.ts +41 -0
  108. package/src/templating/SimpleTemplate.ts +240 -0
  109. package/src/templating/loadNapi.ts +169 -0
  110. package/src/testing/FakeMail.ts +112 -0
  111. package/src/transports/MailgunTransport.ts +204 -0
  112. package/src/transports/ResendTransport.ts +147 -0
  113. package/src/transports/SendGridTransport.ts +246 -0
  114. package/src/transports/SesTransport.ts +394 -0
  115. package/src/transports/fetchError.ts +42 -0
  116. package/src/webhooks/context.ts +41 -0
  117. package/src/webhooks/mailgun.ts +134 -0
  118. package/src/webhooks/resend.ts +164 -0
  119. package/src/webhooks/sendgrid.ts +133 -0
package/dist/Mail.js ADDED
@@ -0,0 +1,451 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { RoverError } from "./RoverError.js";
3
+ import nodemailer from "nodemailer";
4
+ import { BaseMail } from "./BaseMail.js";
5
+ import { MessageBuilder, } from "./MessageBuilder.js";
6
+ import { MAIL_JOB_NAME, MailJobHandler, } from "./queue/MailJob.js";
7
+ import { computeBackoffMs, isRetryableError, resolveRetryConfig, } from "./retry.js";
8
+ import { setViewsRoot } from "./templating/SimpleTemplate.js";
9
+ import { FakeMail } from "./testing/FakeMail.js";
10
+ export { MessageBuilder };
11
+ /**
12
+ * Message builder — fluent API for composing an email.
13
+ */
14
+ /**
15
+ * SMTP transport — thin wrapper around `nodemailer`. Nodemailer handles
16
+ * the SMTP state machine, TLS / STARTTLS negotiation, AUTH LOGIN /
17
+ * PLAIN / XOAUTH2, dot-stuffing, MIME assembly, attachments, and address
18
+ * encoding for 10+ years. We forward the `MailMessage` shape and surface
19
+ * the `messageId` returned by the server.
20
+ */
21
+ export class SmtpTransport {
22
+ #transporter;
23
+ constructor(config) {
24
+ // Host is required — defaulting to "localhost" silently hid env-var
25
+ // misconfigurations (SMTP_HOST unset) until the SMTP connection
26
+ // timed out against a nonexistent local server. Fail at
27
+ // construction with an actionable message instead.
28
+ if (config.host !== undefined && typeof config.host !== "string") {
29
+ throw new RoverError("MAIL_PROVIDER_CONFIG", `SMTP host must be a string, got ${typeof config.host}`, { hint: "Set config.host to your SMTP server hostname." });
30
+ }
31
+ if (typeof config.host !== "string" || config.host.length === 0) {
32
+ throw new RoverError("MAIL_PROVIDER_CONFIG", "SMTP host is required", {
33
+ hint: "Set config.host (e.g. process.env.SMTP_HOST). Use the fake / log transports for local development.",
34
+ });
35
+ }
36
+ const host = config.host;
37
+ const port = typeof config.port === "number" ? config.port : 587;
38
+ const secure = typeof config.secure === "boolean" ? config.secure : false;
39
+ const user = typeof config.user === "string" ? config.user : undefined;
40
+ const pass = typeof config.pass === "string" ? config.pass : undefined;
41
+ const requireTLS = typeof config.requireTLS === "boolean" ? config.requireTLS : undefined;
42
+ // Partial auth config (only user OR only pass) is almost always a typo —
43
+ // fail fast rather than connect anonymously and let the server reject.
44
+ if ((user && !pass) || (!user && pass)) {
45
+ throw new RoverError("MAIL_PROVIDER_CONFIG", "SMTP auth requires both `user` and `pass` or neither", {
46
+ hint: "Check your env vars — one half of the credential pair is missing.",
47
+ });
48
+ }
49
+ // Allow DI for tests: if the config carries a pre-built transporter,
50
+ // use it instead of nodemailer.createTransport.
51
+ const injected = config._transporter;
52
+ if (injected && typeof injected === "object" && "sendMail" in injected) {
53
+ this.#transporter = injected;
54
+ }
55
+ else {
56
+ this.#transporter = nodemailer.createTransport({
57
+ host,
58
+ port,
59
+ secure,
60
+ requireTLS,
61
+ auth: user && pass ? { user, pass } : undefined,
62
+ });
63
+ }
64
+ }
65
+ async send(message) {
66
+ if (message.to.length === 0 &&
67
+ message.cc.length === 0 &&
68
+ message.bcc.length === 0) {
69
+ throw new RoverError("MAIL_PROVIDER_CONFIG", "Mail message has no recipients", { hint: "Set at least one `to`, `cc`, or `bcc` before sending." });
70
+ }
71
+ try {
72
+ const info = await this.#transporter.sendMail({
73
+ from: message.from,
74
+ to: message.to,
75
+ cc: message.cc.length ? message.cc : undefined,
76
+ bcc: message.bcc.length ? message.bcc : undefined,
77
+ replyTo: message.replyTo,
78
+ subject: message.subject,
79
+ html: message.html,
80
+ text: message.text,
81
+ headers: Object.keys(message.headers).length
82
+ ? message.headers
83
+ : undefined,
84
+ attachments: message.attachments.length
85
+ ? message.attachments.map((att) => ({
86
+ filename: att.filename,
87
+ content: att.content,
88
+ contentType: att.contentType,
89
+ }))
90
+ : undefined,
91
+ });
92
+ if (info.messageId)
93
+ return { providerId: info.messageId };
94
+ return undefined;
95
+ }
96
+ catch (err) {
97
+ throw wrapSmtpError(err);
98
+ }
99
+ }
100
+ }
101
+ /**
102
+ * Normalise nodemailer / socket errors into the uniform `MAIL_PROVIDER_ERROR`
103
+ * shape used by the rest of the library. Preserves the `code` field (errno)
104
+ * in `context.networkCode` so `isRetryableError` can classify transient
105
+ * network failures without losing the root cause.
106
+ */
107
+ function wrapSmtpError(err) {
108
+ if (err instanceof RoverError)
109
+ return err;
110
+ const anyErr = err;
111
+ const status = typeof anyErr.responseCode === "number" ? anyErr.responseCode : 0;
112
+ const ctx = {
113
+ provider: "smtp",
114
+ upstreamStatus: String(status),
115
+ providerMessage: anyErr.message ?? "unknown",
116
+ };
117
+ if (typeof anyErr.code === "string") {
118
+ ctx.networkCode = anyErr.code;
119
+ }
120
+ return new RoverError("MAIL_PROVIDER_ERROR", `SMTP failed: ${anyErr.message ?? "unknown"}`, {
121
+ hint: "Inspect `context.networkCode` (ECONNRESET/ETIMEDOUT/...) or `context.upstreamStatus` (SMTP response code) to decide retry eligibility.",
122
+ context: ctx,
123
+ });
124
+ }
125
+ /**
126
+ * Log transport — logs emails to console (development).
127
+ */
128
+ export class LogTransport {
129
+ async send(message) {
130
+ console.log(`[MAIL] To: ${message.to.join(", ")} | Subject: ${message.subject}`);
131
+ if (message.text)
132
+ console.log(` Body: ${message.text.slice(0, 200)}`);
133
+ return undefined;
134
+ }
135
+ }
136
+ const transportFactories = {
137
+ smtp: (config) => new SmtpTransport(config),
138
+ log: () => new LogTransport(),
139
+ };
140
+ /**
141
+ * Register a custom transport factory globally.
142
+ *
143
+ * @example
144
+ * registerTransport('mailgun', (config) => new MailgunTransport(config))
145
+ * // Now config/mail.ts can reference `transport: 'mailgun'`
146
+ */
147
+ export function registerTransport(name, factory) {
148
+ transportFactories[name] = factory;
149
+ }
150
+ /**
151
+ * Mail manager — send emails via configured transport.
152
+ */
153
+ export class Mail {
154
+ #transports = new Map();
155
+ #defaultTransport;
156
+ #defaultFrom;
157
+ #fakeSnapshot = null;
158
+ #queue = null;
159
+ #queueName;
160
+ #queueMaxAttempts;
161
+ #globalRetry;
162
+ #transportRetry = new Map();
163
+ #hooks;
164
+ #emitter;
165
+ constructor(config, options) {
166
+ this.#defaultTransport = config.default;
167
+ this.#defaultFrom = config.from;
168
+ // Bay uses a single `name` for both `register(name, handler)` and
169
+ // `dispatch(name, payload)`. Default to the canonical mail job name.
170
+ this.#queueName = config.queue?.name ?? MAIL_JOB_NAME;
171
+ this.#queueMaxAttempts = config.queue?.maxAttempts ?? 3;
172
+ this.#globalRetry = config.retry;
173
+ this.#hooks = options?.hooks ?? {};
174
+ this.#emitter = options?.emitter ?? null;
175
+ if (config.viewsRoot !== undefined) {
176
+ setViewsRoot(config.viewsRoot);
177
+ }
178
+ for (const [name, transportConfig] of Object.entries(config.transports)) {
179
+ const factory = transportFactories[transportConfig.transport];
180
+ if (!factory) {
181
+ throw new RoverError("MAIL_UNKNOWN_TRANSPORT", `Unknown mail transport type '${transportConfig.transport}' (configured under name '${name}')`, {
182
+ hint: "Register the transport with registerTransport() before constructing Mail, or fix the typo in config.mail.transports[*].transport.",
183
+ });
184
+ }
185
+ this.#transports.set(name, factory(transportConfig));
186
+ if (transportConfig.retry) {
187
+ this.#transportRetry.set(name, transportConfig.retry);
188
+ }
189
+ }
190
+ if (options?.queue) {
191
+ this.#queue = options.queue;
192
+ this.#queue.register(this.#queueName, new MailJobHandler(this));
193
+ }
194
+ }
195
+ async send(arg, transport) {
196
+ const transportName = transport ?? this.#defaultTransport;
197
+ // Validate transport up-front before running any callback / prepare() side effects.
198
+ if (!this.#transports.has(transportName)) {
199
+ throw new Error(`Mail transport '${transportName}' not configured`);
200
+ }
201
+ const message = await this.#buildMessage(arg);
202
+ await this.dispatchMessage(message, transportName);
203
+ }
204
+ /**
205
+ * Enqueue a send onto the Bay queue. Returns the job id. Throws
206
+ * `MAIL_QUEUE_REQUIRED` if no `QueueManager` was wired through the
207
+ * constructor options.
208
+ */
209
+ async sendLater(arg, options) {
210
+ if (this.#queue === null) {
211
+ throw new RoverError("MAIL_QUEUE_REQUIRED", "mail.sendLater() requires @c9up/bay QueueManager", {
212
+ hint: "Register @c9up/bay and pass the QueueManager to Mail via RoverProvider, or use mail.send() for synchronous delivery.",
213
+ });
214
+ }
215
+ const message = await this.#buildMessage(arg);
216
+ const queueName = options?.queue ?? this.#queueName;
217
+ return this.#queue.dispatch(queueName, { message, transport: options?.transport }, { maxAttempts: this.#queueMaxAttempts });
218
+ }
219
+ /**
220
+ * Shared transport-resolve-then-send helper used by `send()` and `MailJobHandler`.
221
+ * Applies in-process retry with exponential backoff per `RetryConfig` resolution
222
+ * (per-transport > global > library default). Fires `onSent` / `onFailed` hooks.
223
+ *
224
+ * `overrideRetry` (optional) wins over config-level retry; `MailJobHandler` passes
225
+ * `{ maxAttempts: 1 }` so queue-level and sync-level retries don't compound.
226
+ */
227
+ async dispatchMessage(message, transportName, overrideRetry) {
228
+ // Defense-in-depth: queue payloads bypass `#buildMessage`, so a
229
+ // malformed message deserialised from storage would otherwise reach
230
+ // the transport with the cryptic provider error A2 was meant to
231
+ // eliminate. Cheap re-validation on every dispatch keeps the
232
+ // failure mode uniform across send() and queue dequeue.
233
+ validateMailMessage(message);
234
+ const name = transportName ?? this.#defaultTransport;
235
+ const t = this.#transports.get(name);
236
+ if (!t)
237
+ throw new Error(`Mail transport '${name}' not configured`);
238
+ const retry = overrideRetry
239
+ ? resolveRetryConfig(undefined, overrideRetry)
240
+ : resolveRetryConfig(this.#globalRetry, this.#transportRetry.get(name));
241
+ const generatedId = randomBytes(16).toString("hex");
242
+ let lastError;
243
+ for (let attempt = 1; attempt <= retry.maxAttempts; attempt += 1) {
244
+ let sendResult;
245
+ try {
246
+ sendResult = await t.send(message);
247
+ }
248
+ catch (err) {
249
+ lastError = err;
250
+ const retryable = isRetryableError(err);
251
+ if (!retryable || attempt === retry.maxAttempts) {
252
+ const annotated = this.#withAttempts(err, attempt);
253
+ this.#fireFailed({
254
+ messageId: generatedId,
255
+ to: message.to.slice(),
256
+ cc: message.cc.slice(),
257
+ bcc: message.bcc.slice(),
258
+ transportName: name,
259
+ error: errorDescriptor(annotated, attempt),
260
+ timestamp: Date.now(),
261
+ });
262
+ throw annotated;
263
+ }
264
+ const delay = computeBackoffMs(attempt, retry, err);
265
+ await new Promise((resolve) => setTimeout(resolve, delay));
266
+ continue;
267
+ }
268
+ // Success path lives outside the try/catch so a throw inside the
269
+ // success hook cannot be mistaken for a transport failure.
270
+ const providerIdRaw = sendResult &&
271
+ typeof sendResult === "object" &&
272
+ "providerId" in sendResult
273
+ ? sendResult.providerId
274
+ : undefined;
275
+ // `??` would accept empty-string providerId; explicit truthy check
276
+ // so a provider returning `{ providerId: "" }` falls back to the
277
+ // internally generated correlation id.
278
+ const providerId = providerIdRaw && providerIdRaw.length > 0 ? providerIdRaw : undefined;
279
+ this.#fireSent({
280
+ messageId: providerId ?? generatedId,
281
+ to: message.to.slice(),
282
+ cc: message.cc.slice(),
283
+ bcc: message.bcc.slice(),
284
+ transportName: name,
285
+ timestamp: Date.now(),
286
+ });
287
+ return;
288
+ }
289
+ // Unreachable — the loop always returns or throws — but TS wants a throw.
290
+ throw lastError;
291
+ }
292
+ /**
293
+ * Return a shallow clone of `err` with `context.attempts` annotated. We
294
+ * avoid mutating the original error so outer retry layers / shared error
295
+ * references don't see their attempt counter overwritten.
296
+ */
297
+ #withAttempts(err, attempts) {
298
+ if (!(err instanceof RoverError))
299
+ return err;
300
+ const clone = new RoverError(err.code, err.message, {
301
+ hint: err.hint,
302
+ sourceFile: err.sourceFile,
303
+ sourceLine: err.sourceLine,
304
+ docsUrl: err.docsUrl,
305
+ pipelineStage: err.pipelineStage,
306
+ context: { ...err.context, attempts: String(attempts) },
307
+ });
308
+ if (err.stack)
309
+ clone.stack = err.stack;
310
+ return clone;
311
+ }
312
+ #fireSent(event) {
313
+ // Hooks are in user-land and may throw; their failure must not poison
314
+ // delivery outcome. Emitter errors are already defensively swallowed.
315
+ try {
316
+ this.#hooks.onSent?.(event);
317
+ }
318
+ catch (err) {
319
+ process.stderr.write(`[rover] onSent hook threw: ${err instanceof Error ? err.message : String(err)}\n`);
320
+ }
321
+ if (this.#emitter) {
322
+ try {
323
+ this.#emitter.emit("mail.sent", event);
324
+ }
325
+ catch {
326
+ // Event bus failure ≠ mail delivery failure — swallow.
327
+ }
328
+ }
329
+ }
330
+ #fireFailed(event) {
331
+ try {
332
+ this.#hooks.onFailed?.(event);
333
+ }
334
+ catch (err) {
335
+ process.stderr.write(`[rover] onFailed hook threw: ${err instanceof Error ? err.message : String(err)}\n`);
336
+ }
337
+ if (this.#emitter) {
338
+ try {
339
+ this.#emitter.emit("mail.failed", event);
340
+ }
341
+ catch {
342
+ // Event bus failure ≠ mail delivery failure — swallow.
343
+ }
344
+ }
345
+ }
346
+ async #buildMessage(arg) {
347
+ let result;
348
+ if (arg instanceof BaseMail) {
349
+ const built = await arg.build();
350
+ result = built.from ? built : { ...built, from: this.#defaultFrom };
351
+ }
352
+ else {
353
+ const builder = new MessageBuilder();
354
+ builder.from(this.#defaultFrom);
355
+ arg(builder);
356
+ result = await builder.build();
357
+ }
358
+ validateMailMessage(result);
359
+ return result;
360
+ }
361
+ /** Get a specific transport. */
362
+ use(name) {
363
+ const t = this.#transports.get(name);
364
+ if (!t)
365
+ throw new Error(`Mail transport '${name}' not configured`);
366
+ return t;
367
+ }
368
+ /**
369
+ * Swap the default transport with a `FakeMail` that captures every send.
370
+ * Call `restore()` to re-install the original. Throws if a fake is already
371
+ * active — nested fakes always indicate a forgotten `restore()`.
372
+ */
373
+ fake() {
374
+ if (this.#fakeSnapshot !== null) {
375
+ throw new Error("Mail.fake() already active — call restore() first");
376
+ }
377
+ const transportName = this.#defaultTransport;
378
+ const original = this.#transports.get(transportName);
379
+ if (!original) {
380
+ throw new Error(`Cannot fake default transport '${transportName}' — not configured`);
381
+ }
382
+ const fake = new FakeMail();
383
+ this.#fakeSnapshot = { transportName, original };
384
+ this.#transports.set(transportName, fake);
385
+ return fake;
386
+ }
387
+ /** Undo the swap installed by `fake()`. No-op if no fake is active. */
388
+ restore() {
389
+ if (this.#fakeSnapshot === null)
390
+ return;
391
+ const { transportName, original } = this.#fakeSnapshot;
392
+ this.#transports.set(transportName, original);
393
+ this.#fakeSnapshot = null;
394
+ }
395
+ }
396
+ /**
397
+ * Reject empty `from` and empty recipients before any transport is reached —
398
+ * keeps the failure mode uniform across SMTP / Mailgun / SendGrid / SES /
399
+ * Resend / log instead of relying on each transport to surface a (possibly
400
+ * cryptic) "no recipients" provider error.
401
+ */
402
+ function validateMailMessage(message) {
403
+ // `MailMessage.from` is typed `string` but config-loader paths
404
+ // (`app.config.get<MailConfig>(...)`) cast at runtime — a missing key
405
+ // can yield `undefined` despite the type. Whitespace-only is also
406
+ // invalid per RFC 5321 reverse-path semantics.
407
+ if (typeof message.from !== "string" || message.from.trim() === "") {
408
+ throw new RoverError("MAIL_INVALID_MESSAGE", "Mail message has no `from` address", {
409
+ hint: "Set `config.from`, an instance `from`, or call `message.from(...)` in the builder.",
410
+ });
411
+ }
412
+ // Defense-in-depth on each array: `MailJob.validatePayload` only requires
413
+ // `to` to be an array (cc/bcc are optional at the queue boundary), so a
414
+ // deserialised job payload may reach here with `cc`/`bcc` as `undefined`.
415
+ // `.some(...)` on `undefined` throws `TypeError` instead of the
416
+ // structured `MAIL_INVALID_MESSAGE` this validator is supposed to surface.
417
+ const hasRecipient = (Array.isArray(message.to) && message.to.some(isNonEmptyAddress)) ||
418
+ (Array.isArray(message.cc) && message.cc.some(isNonEmptyAddress)) ||
419
+ (Array.isArray(message.bcc) && message.bcc.some(isNonEmptyAddress));
420
+ if (!hasRecipient) {
421
+ throw new RoverError("MAIL_INVALID_MESSAGE", "Mail message has no recipients", {
422
+ hint: "Call `message.to(...)`, `cc(...)`, or `bcc(...)` with a non-empty address before sending.",
423
+ });
424
+ }
425
+ }
426
+ function isNonEmptyAddress(addr) {
427
+ return typeof addr === "string" && addr.trim() !== "";
428
+ }
429
+ function errorDescriptor(err, attempts) {
430
+ if (err instanceof RoverError) {
431
+ const statusStr = err.context.upstreamStatus;
432
+ const upstream = Number(statusStr);
433
+ return {
434
+ code: err.code,
435
+ message: err.message,
436
+ upstreamStatus: Number.isFinite(upstream) ? upstream : undefined,
437
+ upstreamStatusRaw: statusStr,
438
+ attempts,
439
+ };
440
+ }
441
+ if (err instanceof Error) {
442
+ const errnoCode = err.code;
443
+ return {
444
+ code: typeof errnoCode === "string" ? errnoCode : "UNKNOWN",
445
+ message: err.message,
446
+ attempts,
447
+ };
448
+ }
449
+ return { code: "UNKNOWN", message: String(err), attempts };
450
+ }
451
+ //# sourceMappingURL=Mail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Mail.js","sourceRoot":"","sources":["../src/Mail.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export interface MailMessage {
2
+ from: string;
3
+ to: string[];
4
+ cc: string[];
5
+ bcc: string[];
6
+ replyTo?: string;
7
+ subject: string;
8
+ html?: string;
9
+ text?: string;
10
+ attachments: MailAttachment[];
11
+ headers: Record<string, string>;
12
+ }
13
+ export interface MailAttachment {
14
+ filename: string;
15
+ content: Buffer | string;
16
+ contentType?: string;
17
+ }
18
+ export declare class MessageBuilder {
19
+ #private;
20
+ from(address: string): this;
21
+ to(address: string): this;
22
+ cc(address: string): this;
23
+ bcc(address: string): this;
24
+ replyTo(address: string): this;
25
+ subject(text: string): this;
26
+ html(content: string): this;
27
+ text(content: string): this;
28
+ attach(filename: string, content: Buffer | string, contentType?: string): this;
29
+ header(key: string, value: string): this;
30
+ /**
31
+ * Queue an HTML template render. The render happens lazily at `build()` time
32
+ * so the fluent chain stays synchronous; `build()` is async and awaits the
33
+ * render before returning the finalised `MailMessage`.
34
+ */
35
+ htmlView(viewPath: string, data?: Record<string, unknown>): this;
36
+ build(): Promise<MailMessage>;
37
+ }
38
+ //# sourceMappingURL=MessageBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageBuilder.d.ts","sourceRoot":"","sources":["../src/MessageBuilder.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,cAAc;;IAY1B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIzB,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIzB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI1B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI9B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3B,MAAM,CACL,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,WAAW,CAAC,EAAE,MAAM,GAClB,IAAI;IAKP,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKxC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK1D,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;CAUnC"}
@@ -0,0 +1,70 @@
1
+ import { renderFile as renderTemplateFile } from "./templating/SimpleTemplate.js";
2
+ export class MessageBuilder {
3
+ #msg = {
4
+ from: "",
5
+ to: [],
6
+ cc: [],
7
+ bcc: [],
8
+ subject: "",
9
+ attachments: [],
10
+ headers: {},
11
+ };
12
+ #pendingView = null;
13
+ from(address) {
14
+ this.#msg.from = address;
15
+ return this;
16
+ }
17
+ to(address) {
18
+ this.#msg.to.push(address);
19
+ return this;
20
+ }
21
+ cc(address) {
22
+ this.#msg.cc.push(address);
23
+ return this;
24
+ }
25
+ bcc(address) {
26
+ this.#msg.bcc.push(address);
27
+ return this;
28
+ }
29
+ replyTo(address) {
30
+ this.#msg.replyTo = address;
31
+ return this;
32
+ }
33
+ subject(text) {
34
+ this.#msg.subject = text;
35
+ return this;
36
+ }
37
+ html(content) {
38
+ this.#msg.html = content;
39
+ return this;
40
+ }
41
+ text(content) {
42
+ this.#msg.text = content;
43
+ return this;
44
+ }
45
+ attach(filename, content, contentType) {
46
+ this.#msg.attachments.push({ filename, content, contentType });
47
+ return this;
48
+ }
49
+ header(key, value) {
50
+ this.#msg.headers[key] = value;
51
+ return this;
52
+ }
53
+ /**
54
+ * Queue an HTML template render. The render happens lazily at `build()` time
55
+ * so the fluent chain stays synchronous; `build()` is async and awaits the
56
+ * render before returning the finalised `MailMessage`.
57
+ */
58
+ htmlView(viewPath, data) {
59
+ this.#pendingView = { path: viewPath, data: data ?? {} };
60
+ return this;
61
+ }
62
+ async build() {
63
+ if (this.#pendingView !== null) {
64
+ this.#msg.html = await renderTemplateFile(this.#pendingView.path, this.#pendingView.data);
65
+ this.#pendingView = null;
66
+ }
67
+ return this.#msg;
68
+ }
69
+ }
70
+ //# sourceMappingURL=MessageBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageBuilder.js","sourceRoot":"","sources":["../src/MessageBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,IAAI,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAqBlF,MAAM,OAAO,cAAc;IAC1B,IAAI,GAAgB;QACnB,IAAI,EAAE,EAAE;QACR,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,GAAG,EAAE,EAAE;QACP,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,EAAE;QACf,OAAO,EAAE,EAAE;KACX,CAAC;IACF,YAAY,GAA2D,IAAI,CAAC;IAE5E,IAAI,CAAC,OAAe;QACnB,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC;QACzB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,EAAE,CAAC,OAAe;QACjB,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACb,CAAC;IACD,EAAE,CAAC,OAAe;QACjB,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACb,CAAC;IACD,GAAG,CAAC,OAAe;QAClB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5B,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,CAAC,OAAe;QACtB,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QAC5B,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,CAAC,IAAY;QACnB,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACzB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,IAAI,CAAC,OAAe;QACnB,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC;QACzB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,IAAI,CAAC,OAAe;QACnB,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC;QACzB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,MAAM,CACL,QAAgB,EAChB,OAAwB,EACxB,WAAoB;QAEpB,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/D,OAAO,IAAI,CAAC;IACb,CAAC;IAED,MAAM,CAAC,GAAW,EAAE,KAAa;QAChC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC/B,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;OAIG;IACH,QAAQ,CAAC,QAAgB,EAAE,IAA8B;QACxD,IAAI,CAAC,YAAY,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;QACzD,OAAO,IAAI,CAAC;IACb,CAAC;IAED,KAAK,CAAC,KAAK;QACV,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,MAAM,kBAAkB,CACxC,IAAI,CAAC,YAAY,CAAC,IAAI,EACtB,IAAI,CAAC,YAAY,CAAC,IAAI,CACtB,CAAC;YACF,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC1B,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC;IAClB,CAAC;CACD"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Rover's own structured error — keeps the package framework-agnostic (no
3
+ * dependency on `@c9up/ream`'s `ReamError`). Mirrors the error shape Rover
4
+ * carries through its transports and retry logic.
5
+ */
6
+ export declare class RoverError extends Error {
7
+ /** Error code (e.g. "ROVER_SMTP_ERROR"). */
8
+ readonly code: string;
9
+ /** Additional context key-value pairs. */
10
+ readonly context: Record<string, string>;
11
+ /** Actionable hint for the developer. */
12
+ readonly hint?: string;
13
+ /** Source file where the error originated. */
14
+ readonly sourceFile?: string;
15
+ /** Line number in the source file. */
16
+ readonly sourceLine?: number;
17
+ /** URL to the error documentation page. */
18
+ readonly docsUrl?: string;
19
+ /** Pipeline stage where the error occurred (if applicable). */
20
+ readonly pipelineStage?: string;
21
+ constructor(code: string, message: string, options?: {
22
+ context?: Record<string, string>;
23
+ hint?: string;
24
+ sourceFile?: string;
25
+ sourceLine?: number;
26
+ docsUrl?: string;
27
+ pipelineStage?: string;
28
+ });
29
+ }
30
+ //# sourceMappingURL=RoverError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RoverError.d.ts","sourceRoot":"","sources":["../src/RoverError.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACpC,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,yCAAyC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,sCAAsC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;gBAG/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,CAAC;KACvB;CAYF"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Rover's own structured error — keeps the package framework-agnostic (no
3
+ * dependency on `@c9up/ream`'s `ReamError`). Mirrors the error shape Rover
4
+ * carries through its transports and retry logic.
5
+ */
6
+ export class RoverError extends Error {
7
+ /** Error code (e.g. "ROVER_SMTP_ERROR"). */
8
+ code;
9
+ /** Additional context key-value pairs. */
10
+ context;
11
+ /** Actionable hint for the developer. */
12
+ hint;
13
+ /** Source file where the error originated. */
14
+ sourceFile;
15
+ /** Line number in the source file. */
16
+ sourceLine;
17
+ /** URL to the error documentation page. */
18
+ docsUrl;
19
+ /** Pipeline stage where the error occurred (if applicable). */
20
+ pipelineStage;
21
+ constructor(code, message, options) {
22
+ super(message);
23
+ this.name = "RoverError";
24
+ this.code = code;
25
+ this.context = options?.context ?? {};
26
+ this.hint = options?.hint;
27
+ this.sourceFile = options?.sourceFile;
28
+ this.sourceLine = options?.sourceLine;
29
+ this.docsUrl = options?.docsUrl;
30
+ this.pipelineStage = options?.pipelineStage;
31
+ }
32
+ }
33
+ //# sourceMappingURL=RoverError.js.map