@upyo/sendgrid 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
  /**
@@ -78,11 +102,15 @@ var SendGridHttpClient = class {
78
102
  } catch {
79
103
  errorData = { message: text || `HTTP ${response.status}` };
80
104
  }
81
- throw new SendGridApiError(errorData.message || `HTTP ${response.status}`, response.status, errorData.errors);
105
+ throw new SendGridApiError(errorData.message || `HTTP ${response.status}`, response.status, errorData.errors, (0, __upyo_core.parseRetryAfter)(response.headers.get("Retry-After")), attempt + 1);
82
106
  } catch (error) {
83
107
  lastError = error instanceof Error ? error : new Error(String(error));
108
+ if (isAbortError$1(error)) throw error;
84
109
  if (error instanceof SendGridApiError && error.statusCode && error.statusCode >= 400 && error.statusCode < 500) throw error;
85
- if (attempt === this.config.retries) throw error;
110
+ if (attempt === this.config.retries) {
111
+ if (lastError instanceof SendGridApiError) throw lastError;
112
+ throw new SendGridApiError(lastError.message, void 0, void 0, void 0, attempt + 1);
113
+ }
86
114
  const delay = Math.pow(2, attempt) * 1e3;
87
115
  await new Promise((resolve) => setTimeout(resolve, delay));
88
116
  }
@@ -101,18 +129,16 @@ var SendGridHttpClient = class {
101
129
  for (const [key, value] of Object.entries(this.config.headers)) headers.set(key, value);
102
130
  const controller = new AbortController();
103
131
  const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
104
- let signal = controller.signal;
105
- if (options.signal) {
106
- signal = options.signal;
107
- if (options.signal.aborted) controller.abort();
108
- else options.signal.addEventListener("abort", () => controller.abort());
109
- }
132
+ const signal = options.signal == null ? controller.signal : AbortSignal.any([controller.signal, options.signal]);
110
133
  try {
111
134
  return await globalThis.fetch(url, {
112
135
  ...options,
113
136
  headers,
114
137
  signal
115
138
  });
139
+ } catch (error) {
140
+ if (isAbortError$1(error) && controller.signal.aborted && !options.signal?.aborted) throw new Error(`SendGrid API request timed out after ${this.config.timeout} ms.`);
141
+ throw error;
116
142
  } finally {
117
143
  clearTimeout(timeoutId);
118
144
  }
@@ -130,18 +156,48 @@ var SendGridHttpClient = class {
130
156
  }
131
157
  };
132
158
  /**
133
- * Custom error class for SendGrid API errors.
159
+ * Error thrown when a SendGrid API request fails.
160
+ *
161
+ * @since 0.5.0
134
162
  */
135
163
  var SendGridApiError = class extends Error {
164
+ /**
165
+ * HTTP status code returned by SendGrid, if the request reached the API.
166
+ */
136
167
  statusCode;
168
+ /**
169
+ * Provider-supplied SendGrid error details.
170
+ */
137
171
  errors;
138
- constructor(message, statusCode, errors) {
172
+ /**
173
+ * Retry delay from SendGrid's `Retry-After` response header.
174
+ */
175
+ retryAfterMilliseconds;
176
+ /**
177
+ * Number of attempts made before this error was produced.
178
+ */
179
+ attempts;
180
+ /**
181
+ * Creates a SendGrid API error.
182
+ *
183
+ * @param message Error message.
184
+ * @param statusCode HTTP status code returned by SendGrid.
185
+ * @param errors Provider-supplied SendGrid error details.
186
+ * @param retryAfterMilliseconds Retry delay from the response.
187
+ * @param attempts Number of attempts made before this error.
188
+ */
189
+ constructor(message, statusCode, errors, retryAfterMilliseconds, attempts) {
139
190
  super(message);
140
191
  this.name = "SendGridApiError";
141
192
  this.statusCode = statusCode;
142
193
  this.errors = errors;
194
+ this.retryAfterMilliseconds = retryAfterMilliseconds;
195
+ this.attempts = attempts;
143
196
  }
144
197
  };
198
+ function isAbortError$1(error) {
199
+ return error instanceof Error && error.name === "AbortError";
200
+ }
145
201
 
146
202
  //#endregion
147
203
  //#region src/message-converter.ts
@@ -312,6 +368,7 @@ function isStandardHeader(headerName) {
312
368
  * ```
313
369
  */
314
370
  var SendGridTransport = class {
371
+ id = "sendgrid";
315
372
  /**
316
373
  * The resolved SendGrid configuration used by this transport.
317
374
  */
@@ -368,14 +425,13 @@ var SendGridTransport = class {
368
425
  const messageId = this.extractMessageId(response);
369
426
  return {
370
427
  successful: true,
371
- messageId
428
+ messageId,
429
+ provider: "sendgrid"
372
430
  };
373
431
  } catch (error) {
432
+ if (isAbortError(error) && options?.signal?.aborted) throw error;
374
433
  const errorMessage = error instanceof Error ? error.message : String(error);
375
- return {
376
- successful: false,
377
- errorMessages: [errorMessage]
378
- };
434
+ return createSendGridFailure(errorMessage, error);
379
435
  }
380
436
  }
381
437
  /**
@@ -459,6 +515,19 @@ var SendGridTransport = class {
459
515
  return `sendgrid-${timestamp}-${random}`;
460
516
  }
461
517
  };
518
+ function createSendGridFailure(message, error) {
519
+ if (error instanceof SendGridApiError) return (0, __upyo_core.createFailedReceipt)(message, {
520
+ provider: "sendgrid",
521
+ statusCode: error.statusCode,
522
+ retryAfterMilliseconds: error.retryAfterMilliseconds,
523
+ providerDetails: error.errors,
524
+ attempts: error.attempts
525
+ });
526
+ return (0, __upyo_core.createFailedReceipt)(message, { provider: "sendgrid" });
527
+ }
528
+ function isAbortError(error) {
529
+ return error instanceof Error && error.name === "AbortError";
530
+ }
462
531
 
463
532
  //#endregion
464
533
  exports.SendGridApiError = SendGridApiError;
package/dist/index.d.cts CHANGED
@@ -123,7 +123,8 @@ type ResolvedSendGridConfig = Required<SendGridConfig>;
123
123
  * }
124
124
  * ```
125
125
  */
126
- declare class SendGridTransport implements Transport {
126
+ declare class SendGridTransport implements Transport<"sendgrid"> {
127
+ readonly id = "sendgrid";
127
128
  /**
128
129
  * The resolved SendGrid configuration used by this transport.
129
130
  */
@@ -168,7 +169,7 @@ declare class SendGridTransport implements Transport {
168
169
  * @returns A promise that resolves to a receipt indicating success or
169
170
  * failure.
170
171
  */
171
- send(message: Message, options?: TransportOptions): Promise<Receipt>;
172
+ send(message: Message, options?: TransportOptions): Promise<Receipt<"sendgrid">>;
172
173
  /**
173
174
  * Sends multiple email messages efficiently via SendGrid API.
174
175
  *
@@ -221,7 +222,7 @@ declare class SendGridTransport implements Transport {
221
222
  * cancellation.
222
223
  * @returns An async iterable of receipts, one for each message.
223
224
  */
224
- sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
225
+ sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt<"sendgrid">>;
225
226
  /**
226
227
  * Extracts or generates a message ID from the SendGrid response.
227
228
  *
@@ -237,20 +238,45 @@ declare class SendGridTransport implements Transport {
237
238
  //#region src/http-client.d.ts
238
239
 
239
240
  /**
240
- * Custom error class for SendGrid API errors.
241
+ * Error thrown when a SendGrid API request fails.
242
+ *
243
+ * @since 0.5.0
241
244
  */
242
245
  declare class SendGridApiError extends Error {
246
+ /**
247
+ * HTTP status code returned by SendGrid, if the request reached the API.
248
+ */
243
249
  readonly statusCode?: number;
250
+ /**
251
+ * Provider-supplied SendGrid error details.
252
+ */
244
253
  readonly errors?: {
245
254
  readonly message: string;
246
255
  readonly field?: string;
247
256
  readonly help?: string;
248
257
  }[];
258
+ /**
259
+ * Retry delay from SendGrid's `Retry-After` response header.
260
+ */
261
+ readonly retryAfterMilliseconds?: number;
262
+ /**
263
+ * Number of attempts made before this error was produced.
264
+ */
265
+ readonly attempts?: number;
266
+ /**
267
+ * Creates a SendGrid API error.
268
+ *
269
+ * @param message Error message.
270
+ * @param statusCode HTTP status code returned by SendGrid.
271
+ * @param errors Provider-supplied SendGrid error details.
272
+ * @param retryAfterMilliseconds Retry delay from the response.
273
+ * @param attempts Number of attempts made before this error.
274
+ */
249
275
  constructor(message: string, statusCode?: number, errors?: Array<{
250
276
  message: string;
251
277
  field?: string;
252
278
  help?: string;
253
- }>);
279
+ }>, retryAfterMilliseconds?: number, attempts?: number);
254
280
  }
255
281
  //#endregion
256
282
  export { SendGridApiError, SendGridConfig, SendGridTransport };
package/dist/index.d.ts CHANGED
@@ -123,7 +123,8 @@ type ResolvedSendGridConfig = Required<SendGridConfig>;
123
123
  * }
124
124
  * ```
125
125
  */
126
- declare class SendGridTransport implements Transport {
126
+ declare class SendGridTransport implements Transport<"sendgrid"> {
127
+ readonly id = "sendgrid";
127
128
  /**
128
129
  * The resolved SendGrid configuration used by this transport.
129
130
  */
@@ -168,7 +169,7 @@ declare class SendGridTransport implements Transport {
168
169
  * @returns A promise that resolves to a receipt indicating success or
169
170
  * failure.
170
171
  */
171
- send(message: Message, options?: TransportOptions): Promise<Receipt>;
172
+ send(message: Message, options?: TransportOptions): Promise<Receipt<"sendgrid">>;
172
173
  /**
173
174
  * Sends multiple email messages efficiently via SendGrid API.
174
175
  *
@@ -221,7 +222,7 @@ declare class SendGridTransport implements Transport {
221
222
  * cancellation.
222
223
  * @returns An async iterable of receipts, one for each message.
223
224
  */
224
- sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
225
+ sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt<"sendgrid">>;
225
226
  /**
226
227
  * Extracts or generates a message ID from the SendGrid response.
227
228
  *
@@ -237,20 +238,45 @@ declare class SendGridTransport implements Transport {
237
238
  //#region src/http-client.d.ts
238
239
 
239
240
  /**
240
- * Custom error class for SendGrid API errors.
241
+ * Error thrown when a SendGrid API request fails.
242
+ *
243
+ * @since 0.5.0
241
244
  */
242
245
  declare class SendGridApiError extends Error {
246
+ /**
247
+ * HTTP status code returned by SendGrid, if the request reached the API.
248
+ */
243
249
  readonly statusCode?: number;
250
+ /**
251
+ * Provider-supplied SendGrid error details.
252
+ */
244
253
  readonly errors?: {
245
254
  readonly message: string;
246
255
  readonly field?: string;
247
256
  readonly help?: string;
248
257
  }[];
258
+ /**
259
+ * Retry delay from SendGrid's `Retry-After` response header.
260
+ */
261
+ readonly retryAfterMilliseconds?: number;
262
+ /**
263
+ * Number of attempts made before this error was produced.
264
+ */
265
+ readonly attempts?: number;
266
+ /**
267
+ * Creates a SendGrid API error.
268
+ *
269
+ * @param message Error message.
270
+ * @param statusCode HTTP status code returned by SendGrid.
271
+ * @param errors Provider-supplied SendGrid error details.
272
+ * @param retryAfterMilliseconds Retry delay from the response.
273
+ * @param attempts Number of attempts made before this error.
274
+ */
249
275
  constructor(message: string, statusCode?: number, errors?: Array<{
250
276
  message: string;
251
277
  field?: string;
252
278
  help?: string;
253
- }>);
279
+ }>, retryAfterMilliseconds?: number, attempts?: number);
254
280
  }
255
281
  //#endregion
256
282
  export { SendGridApiError, SendGridConfig, SendGridTransport };
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 SendGrid configuration by applying default values to optional fields.
@@ -77,11 +79,15 @@ var SendGridHttpClient = class {
77
79
  } catch {
78
80
  errorData = { message: text || `HTTP ${response.status}` };
79
81
  }
80
- throw new SendGridApiError(errorData.message || `HTTP ${response.status}`, response.status, errorData.errors);
82
+ throw new SendGridApiError(errorData.message || `HTTP ${response.status}`, response.status, errorData.errors, parseRetryAfter(response.headers.get("Retry-After")), attempt + 1);
81
83
  } catch (error) {
82
84
  lastError = error instanceof Error ? error : new Error(String(error));
85
+ if (isAbortError$1(error)) throw error;
83
86
  if (error instanceof SendGridApiError && error.statusCode && error.statusCode >= 400 && error.statusCode < 500) throw error;
84
- if (attempt === this.config.retries) throw error;
87
+ if (attempt === this.config.retries) {
88
+ if (lastError instanceof SendGridApiError) throw lastError;
89
+ throw new SendGridApiError(lastError.message, void 0, void 0, void 0, attempt + 1);
90
+ }
85
91
  const delay = Math.pow(2, attempt) * 1e3;
86
92
  await new Promise((resolve) => setTimeout(resolve, delay));
87
93
  }
@@ -100,18 +106,16 @@ var SendGridHttpClient = class {
100
106
  for (const [key, value] of Object.entries(this.config.headers)) headers.set(key, value);
101
107
  const controller = new AbortController();
102
108
  const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
103
- let signal = controller.signal;
104
- if (options.signal) {
105
- signal = options.signal;
106
- if (options.signal.aborted) controller.abort();
107
- else options.signal.addEventListener("abort", () => controller.abort());
108
- }
109
+ const signal = options.signal == null ? controller.signal : AbortSignal.any([controller.signal, options.signal]);
109
110
  try {
110
111
  return await globalThis.fetch(url, {
111
112
  ...options,
112
113
  headers,
113
114
  signal
114
115
  });
116
+ } catch (error) {
117
+ if (isAbortError$1(error) && controller.signal.aborted && !options.signal?.aborted) throw new Error(`SendGrid API request timed out after ${this.config.timeout} ms.`);
118
+ throw error;
115
119
  } finally {
116
120
  clearTimeout(timeoutId);
117
121
  }
@@ -129,18 +133,48 @@ var SendGridHttpClient = class {
129
133
  }
130
134
  };
131
135
  /**
132
- * Custom error class for SendGrid API errors.
136
+ * Error thrown when a SendGrid API request fails.
137
+ *
138
+ * @since 0.5.0
133
139
  */
134
140
  var SendGridApiError = class extends Error {
141
+ /**
142
+ * HTTP status code returned by SendGrid, if the request reached the API.
143
+ */
135
144
  statusCode;
145
+ /**
146
+ * Provider-supplied SendGrid error details.
147
+ */
136
148
  errors;
137
- constructor(message, statusCode, errors) {
149
+ /**
150
+ * Retry delay from SendGrid's `Retry-After` response header.
151
+ */
152
+ retryAfterMilliseconds;
153
+ /**
154
+ * Number of attempts made before this error was produced.
155
+ */
156
+ attempts;
157
+ /**
158
+ * Creates a SendGrid API error.
159
+ *
160
+ * @param message Error message.
161
+ * @param statusCode HTTP status code returned by SendGrid.
162
+ * @param errors Provider-supplied SendGrid error details.
163
+ * @param retryAfterMilliseconds Retry delay from the response.
164
+ * @param attempts Number of attempts made before this error.
165
+ */
166
+ constructor(message, statusCode, errors, retryAfterMilliseconds, attempts) {
138
167
  super(message);
139
168
  this.name = "SendGridApiError";
140
169
  this.statusCode = statusCode;
141
170
  this.errors = errors;
171
+ this.retryAfterMilliseconds = retryAfterMilliseconds;
172
+ this.attempts = attempts;
142
173
  }
143
174
  };
175
+ function isAbortError$1(error) {
176
+ return error instanceof Error && error.name === "AbortError";
177
+ }
144
178
 
145
179
  //#endregion
146
180
  //#region src/message-converter.ts
@@ -311,6 +345,7 @@ function isStandardHeader(headerName) {
311
345
  * ```
312
346
  */
313
347
  var SendGridTransport = class {
348
+ id = "sendgrid";
314
349
  /**
315
350
  * The resolved SendGrid configuration used by this transport.
316
351
  */
@@ -367,14 +402,13 @@ var SendGridTransport = class {
367
402
  const messageId = this.extractMessageId(response);
368
403
  return {
369
404
  successful: true,
370
- messageId
405
+ messageId,
406
+ provider: "sendgrid"
371
407
  };
372
408
  } catch (error) {
409
+ if (isAbortError(error) && options?.signal?.aborted) throw error;
373
410
  const errorMessage = error instanceof Error ? error.message : String(error);
374
- return {
375
- successful: false,
376
- errorMessages: [errorMessage]
377
- };
411
+ return createSendGridFailure(errorMessage, error);
378
412
  }
379
413
  }
380
414
  /**
@@ -458,6 +492,19 @@ var SendGridTransport = class {
458
492
  return `sendgrid-${timestamp}-${random}`;
459
493
  }
460
494
  };
495
+ function createSendGridFailure(message, error) {
496
+ if (error instanceof SendGridApiError) return createFailedReceipt(message, {
497
+ provider: "sendgrid",
498
+ statusCode: error.statusCode,
499
+ retryAfterMilliseconds: error.retryAfterMilliseconds,
500
+ providerDetails: error.errors,
501
+ attempts: error.attempts
502
+ });
503
+ return createFailedReceipt(message, { provider: "sendgrid" });
504
+ }
505
+ function isAbortError(error) {
506
+ return error instanceof Error && error.name === "AbortError";
507
+ }
461
508
 
462
509
  //#endregion
463
510
  export { SendGridApiError, SendGridTransport };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upyo/sendgrid",
3
- "version": "0.5.0-dev.136",
3
+ "version": "0.5.0-dev.154",
4
4
  "description": "SendGrid 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
  }