@upyo/ses 0.5.0-dev.136 → 0.5.0-dev.156
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 +60 -14
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +38 -14
- package/package.json +3 -8
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
|
/**
|
|
@@ -72,11 +96,15 @@ var SesHttpClient = class {
|
|
|
72
96
|
} catch {
|
|
73
97
|
errorData = { message: text || `HTTP ${response.status}` };
|
|
74
98
|
}
|
|
75
|
-
throw new SesApiError(errorData.message || `HTTP ${response.status}`, response.status, errorData.errors);
|
|
99
|
+
throw new SesApiError(errorData.message || `HTTP ${response.status}`, response.status, errorData.errors, (0, __upyo_core.parseRetryAfter)(response.headers.get("Retry-After")), attempt + 1);
|
|
76
100
|
} catch (error) {
|
|
77
101
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
102
|
+
if (isAbortError$1(error)) throw error;
|
|
78
103
|
if (error instanceof SesApiError && error.statusCode && error.statusCode >= 400 && error.statusCode < 500) throw error;
|
|
79
|
-
if (attempt === this.config.retries)
|
|
104
|
+
if (attempt === this.config.retries) {
|
|
105
|
+
if (lastError instanceof SesApiError) throw lastError;
|
|
106
|
+
throw new SesApiError(lastError.message, void 0, void 0, void 0, attempt + 1);
|
|
107
|
+
}
|
|
80
108
|
const delay = Math.pow(2, attempt) * 1e3;
|
|
81
109
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
82
110
|
}
|
|
@@ -92,18 +120,16 @@ var SesHttpClient = class {
|
|
|
92
120
|
for (const [key, value] of Object.entries(this.config.headers)) signedHeaders.set(key, value);
|
|
93
121
|
const controller = new AbortController();
|
|
94
122
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
95
|
-
|
|
96
|
-
if (options.signal) {
|
|
97
|
-
signal = options.signal;
|
|
98
|
-
if (options.signal.aborted) controller.abort();
|
|
99
|
-
else options.signal.addEventListener("abort", () => controller.abort());
|
|
100
|
-
}
|
|
123
|
+
const signal = options.signal == null ? controller.signal : AbortSignal.any([controller.signal, options.signal]);
|
|
101
124
|
try {
|
|
102
125
|
return await globalThis.fetch(url, {
|
|
103
126
|
...options,
|
|
104
127
|
headers: this.headersToRecord(signedHeaders),
|
|
105
128
|
signal
|
|
106
129
|
});
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (isAbortError$1(error) && controller.signal.aborted && !options.signal?.aborted) throw new Error(`SES API request timed out after ${this.config.timeout} ms.`);
|
|
132
|
+
throw error;
|
|
107
133
|
} finally {
|
|
108
134
|
clearTimeout(timeoutId);
|
|
109
135
|
}
|
|
@@ -190,14 +216,21 @@ var SesHttpClient = class {
|
|
|
190
216
|
return record;
|
|
191
217
|
}
|
|
192
218
|
};
|
|
219
|
+
function isAbortError$1(error) {
|
|
220
|
+
return error instanceof Error && error.name === "AbortError";
|
|
221
|
+
}
|
|
193
222
|
var SesApiError = class extends Error {
|
|
194
223
|
statusCode;
|
|
195
224
|
errors;
|
|
196
|
-
|
|
225
|
+
retryAfterMilliseconds;
|
|
226
|
+
attempts;
|
|
227
|
+
constructor(message, statusCode, errors, retryAfterMilliseconds, attempts) {
|
|
197
228
|
super(message);
|
|
198
229
|
this.name = "SesApiError";
|
|
199
230
|
this.statusCode = statusCode;
|
|
200
231
|
this.errors = errors;
|
|
232
|
+
this.retryAfterMilliseconds = retryAfterMilliseconds;
|
|
233
|
+
this.attempts = attempts;
|
|
201
234
|
}
|
|
202
235
|
};
|
|
203
236
|
|
|
@@ -311,6 +344,7 @@ function formatAddress(address) {
|
|
|
311
344
|
* @since 0.2.0
|
|
312
345
|
*/
|
|
313
346
|
var SesTransport = class {
|
|
347
|
+
id = "ses";
|
|
314
348
|
/** Resolved configuration with defaults applied */
|
|
315
349
|
config;
|
|
316
350
|
/** HTTP client for SES API requests */
|
|
@@ -360,14 +394,13 @@ var SesTransport = class {
|
|
|
360
394
|
const messageId = this.extractMessageId(response);
|
|
361
395
|
return {
|
|
362
396
|
successful: true,
|
|
363
|
-
messageId
|
|
397
|
+
messageId,
|
|
398
|
+
provider: "ses"
|
|
364
399
|
};
|
|
365
400
|
} catch (error) {
|
|
401
|
+
if (isAbortError(error) && options?.signal?.aborted) throw error;
|
|
366
402
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
367
|
-
return
|
|
368
|
-
successful: false,
|
|
369
|
-
errorMessages: [errorMessage]
|
|
370
|
-
};
|
|
403
|
+
return createSesFailure(errorMessage, error);
|
|
371
404
|
}
|
|
372
405
|
}
|
|
373
406
|
/**
|
|
@@ -430,6 +463,19 @@ var SesTransport = class {
|
|
|
430
463
|
return `ses-${timestamp}-${random}`;
|
|
431
464
|
}
|
|
432
465
|
};
|
|
466
|
+
function createSesFailure(message, error) {
|
|
467
|
+
if (error instanceof SesApiError) return (0, __upyo_core.createFailedReceipt)(message, {
|
|
468
|
+
provider: "ses",
|
|
469
|
+
statusCode: error.statusCode,
|
|
470
|
+
retryAfterMilliseconds: error.retryAfterMilliseconds,
|
|
471
|
+
providerDetails: error.errors,
|
|
472
|
+
attempts: error.attempts
|
|
473
|
+
});
|
|
474
|
+
return (0, __upyo_core.createFailedReceipt)(message, { provider: "ses" });
|
|
475
|
+
}
|
|
476
|
+
function isAbortError(error) {
|
|
477
|
+
return error instanceof Error && error.name === "AbortError";
|
|
478
|
+
}
|
|
433
479
|
|
|
434
480
|
//#endregion
|
|
435
481
|
exports.SesTransport = SesTransport;
|
package/dist/index.d.cts
CHANGED
|
@@ -188,7 +188,8 @@ type ResolvedSesConfig = Required<Omit<SesConfig, "configurationSetName" | "defa
|
|
|
188
188
|
*
|
|
189
189
|
* @since 0.2.0
|
|
190
190
|
*/
|
|
191
|
-
declare class SesTransport implements Transport {
|
|
191
|
+
declare class SesTransport implements Transport<"ses"> {
|
|
192
|
+
readonly id = "ses";
|
|
192
193
|
/** Resolved configuration with defaults applied */
|
|
193
194
|
config: ResolvedSesConfig;
|
|
194
195
|
/** HTTP client for SES API requests */
|
|
@@ -226,7 +227,7 @@ declare class SesTransport implements Transport {
|
|
|
226
227
|
* @param options Optional transport options (e.g., abort signal).
|
|
227
228
|
* @returns A promise that resolves to a receipt with the result.
|
|
228
229
|
*/
|
|
229
|
-
send(message: Message, options?: TransportOptions): Promise<Receipt
|
|
230
|
+
send(message: Message, options?: TransportOptions): Promise<Receipt<"ses">>;
|
|
230
231
|
/**
|
|
231
232
|
* Sends multiple email messages concurrently through Amazon SES.
|
|
232
233
|
*
|
|
@@ -256,7 +257,7 @@ declare class SesTransport implements Transport {
|
|
|
256
257
|
* @param options Optional transport options (e.g., abort signal).
|
|
257
258
|
* @returns Individual receipts for each message as they complete.
|
|
258
259
|
*/
|
|
259
|
-
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt
|
|
260
|
+
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt<"ses">>;
|
|
260
261
|
private sendConcurrent;
|
|
261
262
|
private extractMessageId;
|
|
262
263
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -188,7 +188,8 @@ type ResolvedSesConfig = Required<Omit<SesConfig, "configurationSetName" | "defa
|
|
|
188
188
|
*
|
|
189
189
|
* @since 0.2.0
|
|
190
190
|
*/
|
|
191
|
-
declare class SesTransport implements Transport {
|
|
191
|
+
declare class SesTransport implements Transport<"ses"> {
|
|
192
|
+
readonly id = "ses";
|
|
192
193
|
/** Resolved configuration with defaults applied */
|
|
193
194
|
config: ResolvedSesConfig;
|
|
194
195
|
/** HTTP client for SES API requests */
|
|
@@ -226,7 +227,7 @@ declare class SesTransport implements Transport {
|
|
|
226
227
|
* @param options Optional transport options (e.g., abort signal).
|
|
227
228
|
* @returns A promise that resolves to a receipt with the result.
|
|
228
229
|
*/
|
|
229
|
-
send(message: Message, options?: TransportOptions): Promise<Receipt
|
|
230
|
+
send(message: Message, options?: TransportOptions): Promise<Receipt<"ses">>;
|
|
230
231
|
/**
|
|
231
232
|
* Sends multiple email messages concurrently through Amazon SES.
|
|
232
233
|
*
|
|
@@ -256,7 +257,7 @@ declare class SesTransport implements Transport {
|
|
|
256
257
|
* @param options Optional transport options (e.g., abort signal).
|
|
257
258
|
* @returns Individual receipts for each message as they complete.
|
|
258
259
|
*/
|
|
259
|
-
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt
|
|
260
|
+
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt<"ses">>;
|
|
260
261
|
private sendConcurrent;
|
|
261
262
|
private extractMessageId;
|
|
262
263
|
}
|
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 SES configuration with default values applied.
|
|
@@ -71,11 +73,15 @@ var SesHttpClient = class {
|
|
|
71
73
|
} catch {
|
|
72
74
|
errorData = { message: text || `HTTP ${response.status}` };
|
|
73
75
|
}
|
|
74
|
-
throw new SesApiError(errorData.message || `HTTP ${response.status}`, response.status, errorData.errors);
|
|
76
|
+
throw new SesApiError(errorData.message || `HTTP ${response.status}`, response.status, errorData.errors, parseRetryAfter(response.headers.get("Retry-After")), attempt + 1);
|
|
75
77
|
} catch (error) {
|
|
76
78
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
79
|
+
if (isAbortError$1(error)) throw error;
|
|
77
80
|
if (error instanceof SesApiError && error.statusCode && error.statusCode >= 400 && error.statusCode < 500) throw error;
|
|
78
|
-
if (attempt === this.config.retries)
|
|
81
|
+
if (attempt === this.config.retries) {
|
|
82
|
+
if (lastError instanceof SesApiError) throw lastError;
|
|
83
|
+
throw new SesApiError(lastError.message, void 0, void 0, void 0, attempt + 1);
|
|
84
|
+
}
|
|
79
85
|
const delay = Math.pow(2, attempt) * 1e3;
|
|
80
86
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
81
87
|
}
|
|
@@ -91,18 +97,16 @@ var SesHttpClient = class {
|
|
|
91
97
|
for (const [key, value] of Object.entries(this.config.headers)) signedHeaders.set(key, value);
|
|
92
98
|
const controller = new AbortController();
|
|
93
99
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
94
|
-
|
|
95
|
-
if (options.signal) {
|
|
96
|
-
signal = options.signal;
|
|
97
|
-
if (options.signal.aborted) controller.abort();
|
|
98
|
-
else options.signal.addEventListener("abort", () => controller.abort());
|
|
99
|
-
}
|
|
100
|
+
const signal = options.signal == null ? controller.signal : AbortSignal.any([controller.signal, options.signal]);
|
|
100
101
|
try {
|
|
101
102
|
return await globalThis.fetch(url, {
|
|
102
103
|
...options,
|
|
103
104
|
headers: this.headersToRecord(signedHeaders),
|
|
104
105
|
signal
|
|
105
106
|
});
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (isAbortError$1(error) && controller.signal.aborted && !options.signal?.aborted) throw new Error(`SES API request timed out after ${this.config.timeout} ms.`);
|
|
109
|
+
throw error;
|
|
106
110
|
} finally {
|
|
107
111
|
clearTimeout(timeoutId);
|
|
108
112
|
}
|
|
@@ -189,14 +193,21 @@ var SesHttpClient = class {
|
|
|
189
193
|
return record;
|
|
190
194
|
}
|
|
191
195
|
};
|
|
196
|
+
function isAbortError$1(error) {
|
|
197
|
+
return error instanceof Error && error.name === "AbortError";
|
|
198
|
+
}
|
|
192
199
|
var SesApiError = class extends Error {
|
|
193
200
|
statusCode;
|
|
194
201
|
errors;
|
|
195
|
-
|
|
202
|
+
retryAfterMilliseconds;
|
|
203
|
+
attempts;
|
|
204
|
+
constructor(message, statusCode, errors, retryAfterMilliseconds, attempts) {
|
|
196
205
|
super(message);
|
|
197
206
|
this.name = "SesApiError";
|
|
198
207
|
this.statusCode = statusCode;
|
|
199
208
|
this.errors = errors;
|
|
209
|
+
this.retryAfterMilliseconds = retryAfterMilliseconds;
|
|
210
|
+
this.attempts = attempts;
|
|
200
211
|
}
|
|
201
212
|
};
|
|
202
213
|
|
|
@@ -310,6 +321,7 @@ function formatAddress(address) {
|
|
|
310
321
|
* @since 0.2.0
|
|
311
322
|
*/
|
|
312
323
|
var SesTransport = class {
|
|
324
|
+
id = "ses";
|
|
313
325
|
/** Resolved configuration with defaults applied */
|
|
314
326
|
config;
|
|
315
327
|
/** HTTP client for SES API requests */
|
|
@@ -359,14 +371,13 @@ var SesTransport = class {
|
|
|
359
371
|
const messageId = this.extractMessageId(response);
|
|
360
372
|
return {
|
|
361
373
|
successful: true,
|
|
362
|
-
messageId
|
|
374
|
+
messageId,
|
|
375
|
+
provider: "ses"
|
|
363
376
|
};
|
|
364
377
|
} catch (error) {
|
|
378
|
+
if (isAbortError(error) && options?.signal?.aborted) throw error;
|
|
365
379
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
366
|
-
return
|
|
367
|
-
successful: false,
|
|
368
|
-
errorMessages: [errorMessage]
|
|
369
|
-
};
|
|
380
|
+
return createSesFailure(errorMessage, error);
|
|
370
381
|
}
|
|
371
382
|
}
|
|
372
383
|
/**
|
|
@@ -429,6 +440,19 @@ var SesTransport = class {
|
|
|
429
440
|
return `ses-${timestamp}-${random}`;
|
|
430
441
|
}
|
|
431
442
|
};
|
|
443
|
+
function createSesFailure(message, error) {
|
|
444
|
+
if (error instanceof SesApiError) return createFailedReceipt(message, {
|
|
445
|
+
provider: "ses",
|
|
446
|
+
statusCode: error.statusCode,
|
|
447
|
+
retryAfterMilliseconds: error.retryAfterMilliseconds,
|
|
448
|
+
providerDetails: error.errors,
|
|
449
|
+
attempts: error.attempts
|
|
450
|
+
});
|
|
451
|
+
return createFailedReceipt(message, { provider: "ses" });
|
|
452
|
+
}
|
|
453
|
+
function isAbortError(error) {
|
|
454
|
+
return error instanceof Error && error.name === "AbortError";
|
|
455
|
+
}
|
|
432
456
|
|
|
433
457
|
//#endregion
|
|
434
458
|
export { SesTransport };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upyo/ses",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.156",
|
|
4
4
|
"description": "Amazon SES transport for Upyo email library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"email",
|
|
@@ -55,18 +55,13 @@
|
|
|
55
55
|
},
|
|
56
56
|
"sideEffects": false,
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@upyo/core": "0.5.0-dev.
|
|
58
|
+
"@upyo/core": "0.5.0-dev.156+edad9790"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@dotenvx/dotenvx": "^1.47.3",
|
|
62
61
|
"tsdown": "^0.12.7",
|
|
63
62
|
"typescript": "5.8.3"
|
|
64
63
|
},
|
|
65
64
|
"scripts": {
|
|
66
|
-
"
|
|
67
|
-
"prepublish": "tsdown",
|
|
68
|
-
"test": "tsdown && dotenvx run --ignore=MISSING_ENV_FILE -- node --experimental-transform-types --test",
|
|
69
|
-
"test:bun": "tsdown && bun test --timeout=30000 --env-file=.env",
|
|
70
|
-
"test:deno": "deno test --allow-env --allow-net --env-file=.env"
|
|
65
|
+
"prepublish": "mise run --no-deps :build"
|
|
71
66
|
}
|
|
72
67
|
}
|