@upyo/mailgun 0.5.0-dev.136 → 0.5.0-dev.154

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/dist/index.cjs CHANGED
@@ -1,3 +1,27 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ const __upyo_core = __toESM(require("@upyo/core"));
1
25
 
2
26
  //#region src/config.ts
3
27
  /**
@@ -76,6 +100,8 @@ var MailgunHttpClient = class {
76
100
  * @param url The URL to make the request to.
77
101
  * @param options Fetch options.
78
102
  * @returns Promise that resolves to the parsed response.
103
+ * @throws {MailgunApiError} If Mailgun returns an error response or all
104
+ * retry attempts are exhausted.
79
105
  */
80
106
  async makeRequest(url, options) {
81
107
  let lastError = null;
@@ -87,7 +113,7 @@ var MailgunHttpClient = class {
87
113
  try {
88
114
  errorMessage = JSON.parse(text)?.message;
89
115
  } catch {}
90
- throw new MailgunApiError(errorMessage || text || `HTTP ${response.status}`, response.status);
116
+ throw new MailgunApiError(errorMessage || truncateErrorBody(text) || `HTTP ${response.status}`, response.status, (0, __upyo_core.parseRetryAfter)(response.headers.get("Retry-After")), attempt + 1);
91
117
  }
92
118
  try {
93
119
  return JSON.parse(text);
@@ -96,8 +122,12 @@ var MailgunHttpClient = class {
96
122
  }
97
123
  } catch (error) {
98
124
  lastError = error instanceof Error ? error : new Error(String(error));
125
+ if (isAbortError$1(error)) throw error;
99
126
  if (error instanceof MailgunApiError && error.statusCode && error.statusCode >= 400 && error.statusCode < 500) throw error;
100
- if (attempt === this.config.retries) throw error;
127
+ if (attempt === this.config.retries) {
128
+ if (lastError instanceof MailgunApiError) throw lastError;
129
+ throw new MailgunApiError(lastError.message, void 0, void 0, attempt + 1);
130
+ }
101
131
  const delay = Math.pow(2, attempt) * 1e3;
102
132
  await new Promise((resolve) => setTimeout(resolve, delay));
103
133
  }
@@ -109,6 +139,8 @@ var MailgunHttpClient = class {
109
139
  * @param url The URL to make the request to.
110
140
  * @param options Fetch options.
111
141
  * @returns Promise that resolves to the fetch response.
142
+ * @throws {Error} If the configured request timeout is reached.
143
+ * @throws {DOMException} If the caller aborts the request.
112
144
  */
113
145
  async fetchWithAuth(url, options) {
114
146
  const headers = new Headers(options.headers);
@@ -125,22 +157,54 @@ var MailgunHttpClient = class {
125
157
  headers,
126
158
  signal
127
159
  });
160
+ } catch (error) {
161
+ if (isAbortError$1(error) && controller.signal.aborted && !options.signal?.aborted) throw new Error(`Mailgun API request timed out after ${this.config.timeout} ms.`);
162
+ throw error;
128
163
  } finally {
129
164
  clearTimeout(timeoutId);
130
165
  }
131
166
  }
132
167
  };
133
168
  /**
134
- * Custom error class for Mailgun API errors.
169
+ * Error thrown when a Mailgun API request fails.
170
+ *
171
+ * @since 0.5.0
135
172
  */
136
173
  var MailgunApiError = class extends Error {
174
+ /**
175
+ * HTTP status code returned by Mailgun, if the request reached the API.
176
+ */
137
177
  statusCode;
138
- constructor(message, statusCode) {
178
+ /**
179
+ * Retry delay from Mailgun's `Retry-After` response header.
180
+ */
181
+ retryAfterMilliseconds;
182
+ /**
183
+ * Number of attempts made before this error was produced.
184
+ */
185
+ attempts;
186
+ /**
187
+ * Creates a Mailgun API error.
188
+ *
189
+ * @param message Error message.
190
+ * @param statusCode HTTP status code returned by Mailgun.
191
+ * @param retryAfterMilliseconds Retry delay from the response.
192
+ * @param attempts Number of attempts made before this error.
193
+ */
194
+ constructor(message, statusCode, retryAfterMilliseconds, attempts) {
139
195
  super(message);
140
196
  this.name = "MailgunApiError";
141
197
  this.statusCode = statusCode;
198
+ this.retryAfterMilliseconds = retryAfterMilliseconds;
199
+ this.attempts = attempts;
142
200
  }
143
201
  };
202
+ function isAbortError$1(error) {
203
+ return error instanceof Error && error.name === "AbortError";
204
+ }
205
+ function truncateErrorBody(text) {
206
+ return text.length > 500 ? `${text.slice(0, 500)}...` : text;
207
+ }
144
208
 
145
209
  //#endregion
146
210
  //#region src/message-converter.ts
@@ -267,6 +331,7 @@ function isStandardHeader(headerName) {
267
331
  * ```
268
332
  */
269
333
  var MailgunTransport = class {
334
+ id = "mailgun";
270
335
  /**
271
336
  * The resolved Mailgun configuration used by this transport.
272
337
  */
@@ -322,14 +387,13 @@ var MailgunTransport = class {
322
387
  const response = await this.httpClient.sendMessage(formData, options?.signal);
323
388
  return {
324
389
  successful: true,
325
- messageId: response.id
390
+ messageId: response.id,
391
+ provider: "mailgun"
326
392
  };
327
393
  } catch (error) {
394
+ if (isAbortError(error) && options?.signal?.aborted) throw error;
328
395
  const errorMessage = error instanceof Error ? error.message : String(error);
329
- return {
330
- successful: false,
331
- errorMessages: [errorMessage]
332
- };
396
+ return createMailgunFailure(errorMessage, error);
333
397
  }
334
398
  }
335
399
  /**
@@ -397,6 +461,18 @@ var MailgunTransport = class {
397
461
  }
398
462
  }
399
463
  };
464
+ function createMailgunFailure(message, error) {
465
+ if (error instanceof MailgunApiError) return (0, __upyo_core.createFailedReceipt)(message, {
466
+ provider: "mailgun",
467
+ statusCode: error.statusCode,
468
+ retryAfterMilliseconds: error.retryAfterMilliseconds,
469
+ attempts: error.attempts
470
+ });
471
+ return (0, __upyo_core.createFailedReceipt)(message, { provider: "mailgun" });
472
+ }
473
+ function isAbortError(error) {
474
+ return error instanceof Error && error.name === "AbortError";
475
+ }
400
476
 
401
477
  //#endregion
402
478
  exports.MailgunApiError = MailgunApiError;
package/dist/index.d.cts CHANGED
@@ -132,7 +132,8 @@ type ResolvedMailgunConfig = Required<MailgunConfig>;
132
132
  * }
133
133
  * ```
134
134
  */
135
- declare class MailgunTransport implements Transport {
135
+ declare class MailgunTransport implements Transport<"mailgun"> {
136
+ readonly id = "mailgun";
136
137
  /**
137
138
  * The resolved Mailgun configuration used by this transport.
138
139
  */
@@ -177,7 +178,7 @@ declare class MailgunTransport implements Transport {
177
178
  * @returns A promise that resolves to a receipt indicating success or
178
179
  * failure.
179
180
  */
180
- send(message: Message, options?: TransportOptions): Promise<Receipt>;
181
+ send(message: Message, options?: TransportOptions): Promise<Receipt<"mailgun">>;
181
182
  /**
182
183
  * Sends multiple email messages efficiently via Mailgun API.
183
184
  *
@@ -230,17 +231,38 @@ declare class MailgunTransport implements Transport {
230
231
  * cancellation.
231
232
  * @returns An async iterable of receipts, one for each message.
232
233
  */
233
- sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
234
+ sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt<"mailgun">>;
234
235
  }
235
236
  //#endregion
236
237
  //#region src/http-client.d.ts
237
238
 
238
239
  /**
239
- * Custom error class for Mailgun API errors.
240
+ * Error thrown when a Mailgun API request fails.
241
+ *
242
+ * @since 0.5.0
240
243
  */
241
244
  declare class MailgunApiError extends Error {
245
+ /**
246
+ * HTTP status code returned by Mailgun, if the request reached the API.
247
+ */
242
248
  statusCode?: number;
243
- constructor(message: string, statusCode?: number);
249
+ /**
250
+ * Retry delay from Mailgun's `Retry-After` response header.
251
+ */
252
+ retryAfterMilliseconds?: number;
253
+ /**
254
+ * Number of attempts made before this error was produced.
255
+ */
256
+ attempts?: number;
257
+ /**
258
+ * Creates a Mailgun API error.
259
+ *
260
+ * @param message Error message.
261
+ * @param statusCode HTTP status code returned by Mailgun.
262
+ * @param retryAfterMilliseconds Retry delay from the response.
263
+ * @param attempts Number of attempts made before this error.
264
+ */
265
+ constructor(message: string, statusCode?: number, retryAfterMilliseconds?: number, attempts?: number);
244
266
  }
245
267
  //#endregion
246
268
  export { MailgunApiError, MailgunConfig, MailgunTransport };
package/dist/index.d.ts CHANGED
@@ -132,7 +132,8 @@ type ResolvedMailgunConfig = Required<MailgunConfig>;
132
132
  * }
133
133
  * ```
134
134
  */
135
- declare class MailgunTransport implements Transport {
135
+ declare class MailgunTransport implements Transport<"mailgun"> {
136
+ readonly id = "mailgun";
136
137
  /**
137
138
  * The resolved Mailgun configuration used by this transport.
138
139
  */
@@ -177,7 +178,7 @@ declare class MailgunTransport implements Transport {
177
178
  * @returns A promise that resolves to a receipt indicating success or
178
179
  * failure.
179
180
  */
180
- send(message: Message, options?: TransportOptions): Promise<Receipt>;
181
+ send(message: Message, options?: TransportOptions): Promise<Receipt<"mailgun">>;
181
182
  /**
182
183
  * Sends multiple email messages efficiently via Mailgun API.
183
184
  *
@@ -230,17 +231,38 @@ declare class MailgunTransport implements Transport {
230
231
  * cancellation.
231
232
  * @returns An async iterable of receipts, one for each message.
232
233
  */
233
- sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
234
+ sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt<"mailgun">>;
234
235
  }
235
236
  //#endregion
236
237
  //#region src/http-client.d.ts
237
238
 
238
239
  /**
239
- * Custom error class for Mailgun API errors.
240
+ * Error thrown when a Mailgun API request fails.
241
+ *
242
+ * @since 0.5.0
240
243
  */
241
244
  declare class MailgunApiError extends Error {
245
+ /**
246
+ * HTTP status code returned by Mailgun, if the request reached the API.
247
+ */
242
248
  statusCode?: number;
243
- constructor(message: string, statusCode?: number);
249
+ /**
250
+ * Retry delay from Mailgun's `Retry-After` response header.
251
+ */
252
+ retryAfterMilliseconds?: number;
253
+ /**
254
+ * Number of attempts made before this error was produced.
255
+ */
256
+ attempts?: number;
257
+ /**
258
+ * Creates a Mailgun API error.
259
+ *
260
+ * @param message Error message.
261
+ * @param statusCode HTTP status code returned by Mailgun.
262
+ * @param retryAfterMilliseconds Retry delay from the response.
263
+ * @param attempts Number of attempts made before this error.
264
+ */
265
+ constructor(message: string, statusCode?: number, retryAfterMilliseconds?: number, attempts?: number);
244
266
  }
245
267
  //#endregion
246
268
  export { MailgunApiError, MailgunConfig, MailgunTransport };
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { createFailedReceipt, parseRetryAfter } from "@upyo/core";
2
+
1
3
  //#region src/config.ts
2
4
  /**
3
5
  * Creates a resolved Mailgun configuration by applying default values to optional fields.
@@ -75,6 +77,8 @@ var MailgunHttpClient = class {
75
77
  * @param url The URL to make the request to.
76
78
  * @param options Fetch options.
77
79
  * @returns Promise that resolves to the parsed response.
80
+ * @throws {MailgunApiError} If Mailgun returns an error response or all
81
+ * retry attempts are exhausted.
78
82
  */
79
83
  async makeRequest(url, options) {
80
84
  let lastError = null;
@@ -86,7 +90,7 @@ var MailgunHttpClient = class {
86
90
  try {
87
91
  errorMessage = JSON.parse(text)?.message;
88
92
  } catch {}
89
- throw new MailgunApiError(errorMessage || text || `HTTP ${response.status}`, response.status);
93
+ throw new MailgunApiError(errorMessage || truncateErrorBody(text) || `HTTP ${response.status}`, response.status, parseRetryAfter(response.headers.get("Retry-After")), attempt + 1);
90
94
  }
91
95
  try {
92
96
  return JSON.parse(text);
@@ -95,8 +99,12 @@ var MailgunHttpClient = class {
95
99
  }
96
100
  } catch (error) {
97
101
  lastError = error instanceof Error ? error : new Error(String(error));
102
+ if (isAbortError$1(error)) throw error;
98
103
  if (error instanceof MailgunApiError && error.statusCode && error.statusCode >= 400 && error.statusCode < 500) throw error;
99
- if (attempt === this.config.retries) throw error;
104
+ if (attempt === this.config.retries) {
105
+ if (lastError instanceof MailgunApiError) throw lastError;
106
+ throw new MailgunApiError(lastError.message, void 0, void 0, attempt + 1);
107
+ }
100
108
  const delay = Math.pow(2, attempt) * 1e3;
101
109
  await new Promise((resolve) => setTimeout(resolve, delay));
102
110
  }
@@ -108,6 +116,8 @@ var MailgunHttpClient = class {
108
116
  * @param url The URL to make the request to.
109
117
  * @param options Fetch options.
110
118
  * @returns Promise that resolves to the fetch response.
119
+ * @throws {Error} If the configured request timeout is reached.
120
+ * @throws {DOMException} If the caller aborts the request.
111
121
  */
112
122
  async fetchWithAuth(url, options) {
113
123
  const headers = new Headers(options.headers);
@@ -124,22 +134,54 @@ var MailgunHttpClient = class {
124
134
  headers,
125
135
  signal
126
136
  });
137
+ } catch (error) {
138
+ if (isAbortError$1(error) && controller.signal.aborted && !options.signal?.aborted) throw new Error(`Mailgun API request timed out after ${this.config.timeout} ms.`);
139
+ throw error;
127
140
  } finally {
128
141
  clearTimeout(timeoutId);
129
142
  }
130
143
  }
131
144
  };
132
145
  /**
133
- * Custom error class for Mailgun API errors.
146
+ * Error thrown when a Mailgun API request fails.
147
+ *
148
+ * @since 0.5.0
134
149
  */
135
150
  var MailgunApiError = class extends Error {
151
+ /**
152
+ * HTTP status code returned by Mailgun, if the request reached the API.
153
+ */
136
154
  statusCode;
137
- constructor(message, statusCode) {
155
+ /**
156
+ * Retry delay from Mailgun's `Retry-After` response header.
157
+ */
158
+ retryAfterMilliseconds;
159
+ /**
160
+ * Number of attempts made before this error was produced.
161
+ */
162
+ attempts;
163
+ /**
164
+ * Creates a Mailgun API error.
165
+ *
166
+ * @param message Error message.
167
+ * @param statusCode HTTP status code returned by Mailgun.
168
+ * @param retryAfterMilliseconds Retry delay from the response.
169
+ * @param attempts Number of attempts made before this error.
170
+ */
171
+ constructor(message, statusCode, retryAfterMilliseconds, attempts) {
138
172
  super(message);
139
173
  this.name = "MailgunApiError";
140
174
  this.statusCode = statusCode;
175
+ this.retryAfterMilliseconds = retryAfterMilliseconds;
176
+ this.attempts = attempts;
141
177
  }
142
178
  };
179
+ function isAbortError$1(error) {
180
+ return error instanceof Error && error.name === "AbortError";
181
+ }
182
+ function truncateErrorBody(text) {
183
+ return text.length > 500 ? `${text.slice(0, 500)}...` : text;
184
+ }
143
185
 
144
186
  //#endregion
145
187
  //#region src/message-converter.ts
@@ -266,6 +308,7 @@ function isStandardHeader(headerName) {
266
308
  * ```
267
309
  */
268
310
  var MailgunTransport = class {
311
+ id = "mailgun";
269
312
  /**
270
313
  * The resolved Mailgun configuration used by this transport.
271
314
  */
@@ -321,14 +364,13 @@ var MailgunTransport = class {
321
364
  const response = await this.httpClient.sendMessage(formData, options?.signal);
322
365
  return {
323
366
  successful: true,
324
- messageId: response.id
367
+ messageId: response.id,
368
+ provider: "mailgun"
325
369
  };
326
370
  } catch (error) {
371
+ if (isAbortError(error) && options?.signal?.aborted) throw error;
327
372
  const errorMessage = error instanceof Error ? error.message : String(error);
328
- return {
329
- successful: false,
330
- errorMessages: [errorMessage]
331
- };
373
+ return createMailgunFailure(errorMessage, error);
332
374
  }
333
375
  }
334
376
  /**
@@ -396,6 +438,18 @@ var MailgunTransport = class {
396
438
  }
397
439
  }
398
440
  };
441
+ function createMailgunFailure(message, error) {
442
+ if (error instanceof MailgunApiError) return createFailedReceipt(message, {
443
+ provider: "mailgun",
444
+ statusCode: error.statusCode,
445
+ retryAfterMilliseconds: error.retryAfterMilliseconds,
446
+ attempts: error.attempts
447
+ });
448
+ return createFailedReceipt(message, { provider: "mailgun" });
449
+ }
450
+ function isAbortError(error) {
451
+ return error instanceof Error && error.name === "AbortError";
452
+ }
399
453
 
400
454
  //#endregion
401
455
  export { MailgunApiError, MailgunTransport };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upyo/mailgun",
3
- "version": "0.5.0-dev.136",
3
+ "version": "0.5.0-dev.154",
4
4
  "description": "Mailgun transport for Upyo email library",
5
5
  "keywords": [
6
6
  "email",
@@ -53,18 +53,13 @@
53
53
  },
54
54
  "sideEffects": false,
55
55
  "peerDependencies": {
56
- "@upyo/core": "0.5.0-dev.136+adacf579"
56
+ "@upyo/core": "0.5.0-dev.154+2f72d353"
57
57
  },
58
58
  "devDependencies": {
59
- "@dotenvx/dotenvx": "^1.47.3",
60
59
  "tsdown": "^0.12.7",
61
60
  "typescript": "5.8.3"
62
61
  },
63
62
  "scripts": {
64
- "build": "tsdown",
65
- "prepublish": "tsdown",
66
- "test": "tsdown && dotenvx run --ignore=MISSING_ENV_FILE -- node --experimental-transform-types --test",
67
- "test:bun": "tsdown && bun test --timeout=30000 --env-file=.env",
68
- "test:deno": "deno test --allow-env --allow-net --env-file=.env"
63
+ "prepublish": "mise run --no-deps :build"
69
64
  }
70
65
  }