@upyo/retry 0.5.0-dev.197 → 0.5.0-dev.199
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 +51 -21
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +51 -21
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -113,6 +113,7 @@ var RetryTransport = class {
|
|
|
113
113
|
* @param transport The transport to wrap.
|
|
114
114
|
* @param config Retry configuration.
|
|
115
115
|
* @throws {RangeError} If retry configuration is invalid.
|
|
116
|
+
* @since 0.5.0
|
|
116
117
|
*/
|
|
117
118
|
constructor(transport, config = {}) {
|
|
118
119
|
this.wrappedTransport = transport;
|
|
@@ -126,25 +127,16 @@ var RetryTransport = class {
|
|
|
126
127
|
* @param options Optional transport options.
|
|
127
128
|
* @returns The final delivery receipt.
|
|
128
129
|
* @throws {DOMException} If the operation is aborted.
|
|
130
|
+
* @since 0.5.0
|
|
129
131
|
*/
|
|
130
132
|
async send(message, options) {
|
|
131
133
|
options?.signal?.throwIfAborted();
|
|
132
134
|
let lastThrownError;
|
|
133
135
|
for (let attempt = 1; attempt <= this.config.maxAttempts; attempt++) {
|
|
134
136
|
options?.signal?.throwIfAborted();
|
|
137
|
+
let receipt;
|
|
135
138
|
try {
|
|
136
|
-
|
|
137
|
-
if (receipt.successful) return this.withSuccessMetadata(receipt, attempt);
|
|
138
|
-
const failedReceipt = this.withFailureMetadata(receipt, attempt);
|
|
139
|
-
if (attempt >= this.config.maxAttempts || !this.shouldRetryReceipt(failedReceipt)) return failedReceipt;
|
|
140
|
-
await this.waitBeforeRetry({
|
|
141
|
-
attempt,
|
|
142
|
-
nextAttempt: attempt + 1,
|
|
143
|
-
maxAttempts: this.config.maxAttempts,
|
|
144
|
-
delayMilliseconds: this.calculateDelay(attempt, failedReceipt),
|
|
145
|
-
receipt: failedReceipt,
|
|
146
|
-
reason: "retry"
|
|
147
|
-
}, options?.signal);
|
|
139
|
+
receipt = await this.wrappedTransport.send(message, options);
|
|
148
140
|
} catch (error) {
|
|
149
141
|
options?.signal?.throwIfAborted();
|
|
150
142
|
lastThrownError = error;
|
|
@@ -157,7 +149,20 @@ var RetryTransport = class {
|
|
|
157
149
|
error,
|
|
158
150
|
reason: "retry"
|
|
159
151
|
}, options?.signal);
|
|
152
|
+
continue;
|
|
160
153
|
}
|
|
154
|
+
options?.signal?.throwIfAborted();
|
|
155
|
+
if (receipt.successful) return this.withSuccessMetadata(receipt, attempt);
|
|
156
|
+
const failedReceipt = this.withFailureMetadata(receipt, attempt);
|
|
157
|
+
if (attempt >= this.config.maxAttempts || !this.shouldRetryReceipt(failedReceipt)) return failedReceipt;
|
|
158
|
+
await this.waitBeforeRetry({
|
|
159
|
+
attempt,
|
|
160
|
+
nextAttempt: attempt + 1,
|
|
161
|
+
maxAttempts: this.config.maxAttempts,
|
|
162
|
+
delayMilliseconds: this.calculateDelay(attempt, failedReceipt),
|
|
163
|
+
receipt: failedReceipt,
|
|
164
|
+
reason: "retry"
|
|
165
|
+
}, options?.signal);
|
|
161
166
|
}
|
|
162
167
|
return this.createThrownFailure(lastThrownError, this.config.maxAttempts);
|
|
163
168
|
}
|
|
@@ -170,6 +175,7 @@ var RetryTransport = class {
|
|
|
170
175
|
* @param options Optional transport options.
|
|
171
176
|
* @returns An async iterable of receipts.
|
|
172
177
|
* @throws {DOMException} If the operation is aborted.
|
|
178
|
+
* @since 0.5.0
|
|
173
179
|
*/
|
|
174
180
|
async *sendMany(messages, options) {
|
|
175
181
|
const iterator = toAsyncIterator(messages);
|
|
@@ -193,12 +199,16 @@ var RetryTransport = class {
|
|
|
193
199
|
if (inputDone) return;
|
|
194
200
|
combinedSignal.signal.throwIfAborted();
|
|
195
201
|
pullingInput = true;
|
|
196
|
-
let
|
|
202
|
+
let inputPull;
|
|
197
203
|
try {
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
204
|
+
inputPull = Promise.resolve(iterator.next());
|
|
205
|
+
} catch (error) {
|
|
206
|
+
inputPull = Promise.reject(error);
|
|
201
207
|
}
|
|
208
|
+
inputPull = inputPull.finally(() => {
|
|
209
|
+
pullingInput = false;
|
|
210
|
+
});
|
|
211
|
+
const next = await raceWithAbort(inputPull, combinedSignal.signal);
|
|
202
212
|
if (closed) return;
|
|
203
213
|
if (next.done) {
|
|
204
214
|
inputDone = true;
|
|
@@ -323,7 +333,7 @@ var RetryTransport = class {
|
|
|
323
333
|
}
|
|
324
334
|
calculateDelay(attempt, failure) {
|
|
325
335
|
const retryAfterMilliseconds = getRetryAfterMilliseconds(failure);
|
|
326
|
-
const cappedRetryAfter = retryAfterMilliseconds == null ? void 0 : Math.min(retryAfterMilliseconds, this.config.backoff.maxDelayMilliseconds);
|
|
336
|
+
const cappedRetryAfter = retryAfterMilliseconds == null || !Number.isFinite(retryAfterMilliseconds) || retryAfterMilliseconds <= 0 ? void 0 : Math.min(retryAfterMilliseconds, this.config.backoff.maxDelayMilliseconds);
|
|
327
337
|
if (cappedRetryAfter != null) return cappedRetryAfter;
|
|
328
338
|
const computedDelay = Math.min(this.config.backoff.baseDelayMilliseconds * Math.pow(this.config.backoff.factor, attempt - 1), this.config.backoff.maxDelayMilliseconds);
|
|
329
339
|
if (this.config.jitter === false || this.config.jitter === "none") return computedDelay;
|
|
@@ -390,17 +400,37 @@ function toAsyncIterator(values) {
|
|
|
390
400
|
if (Symbol.asyncIterator in values) return values[Symbol.asyncIterator]();
|
|
391
401
|
const iterator = values[Symbol.iterator]();
|
|
392
402
|
return {
|
|
393
|
-
next() {
|
|
394
|
-
return Promise.resolve(iterator.next());
|
|
403
|
+
async next() {
|
|
404
|
+
return await Promise.resolve(iterator.next());
|
|
395
405
|
},
|
|
396
|
-
return(value) {
|
|
397
|
-
return Promise.resolve(iterator.return?.(value) ?? {
|
|
406
|
+
async return(value) {
|
|
407
|
+
return await Promise.resolve(iterator.return?.(value) ?? {
|
|
398
408
|
done: true,
|
|
399
409
|
value
|
|
400
410
|
});
|
|
401
411
|
}
|
|
402
412
|
};
|
|
403
413
|
}
|
|
414
|
+
function raceWithAbort(promise, signal) {
|
|
415
|
+
if (signal.aborted) return Promise.reject(signal.reason);
|
|
416
|
+
return new Promise((resolve, reject) => {
|
|
417
|
+
const abort = () => {
|
|
418
|
+
cleanup();
|
|
419
|
+
reject(signal.reason);
|
|
420
|
+
};
|
|
421
|
+
const cleanup = () => {
|
|
422
|
+
signal.removeEventListener("abort", abort);
|
|
423
|
+
};
|
|
424
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
425
|
+
promise.then((value) => {
|
|
426
|
+
cleanup();
|
|
427
|
+
resolve(value);
|
|
428
|
+
}, (error) => {
|
|
429
|
+
cleanup();
|
|
430
|
+
reject(error);
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
}
|
|
404
434
|
function toReceiptError(value) {
|
|
405
435
|
return isReceiptError(value) ? value : void 0;
|
|
406
436
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -207,6 +207,7 @@ declare class RetryTransport<TProviderId extends string = string> implements Tra
|
|
|
207
207
|
* @param transport The transport to wrap.
|
|
208
208
|
* @param config Retry configuration.
|
|
209
209
|
* @throws {RangeError} If retry configuration is invalid.
|
|
210
|
+
* @since 0.5.0
|
|
210
211
|
*/
|
|
211
212
|
constructor(transport: Transport<TProviderId>, config?: RetryConfig<TProviderId>);
|
|
212
213
|
/**
|
|
@@ -216,6 +217,7 @@ declare class RetryTransport<TProviderId extends string = string> implements Tra
|
|
|
216
217
|
* @param options Optional transport options.
|
|
217
218
|
* @returns The final delivery receipt.
|
|
218
219
|
* @throws {DOMException} If the operation is aborted.
|
|
220
|
+
* @since 0.5.0
|
|
219
221
|
*/
|
|
220
222
|
send(message: Message, options?: TransportOptions): Promise<Receipt<TProviderId>>;
|
|
221
223
|
/**
|
|
@@ -227,6 +229,7 @@ declare class RetryTransport<TProviderId extends string = string> implements Tra
|
|
|
227
229
|
* @param options Optional transport options.
|
|
228
230
|
* @returns An async iterable of receipts.
|
|
229
231
|
* @throws {DOMException} If the operation is aborted.
|
|
232
|
+
* @since 0.5.0
|
|
230
233
|
*/
|
|
231
234
|
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt<TProviderId>>;
|
|
232
235
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -207,6 +207,7 @@ declare class RetryTransport<TProviderId extends string = string> implements Tra
|
|
|
207
207
|
* @param transport The transport to wrap.
|
|
208
208
|
* @param config Retry configuration.
|
|
209
209
|
* @throws {RangeError} If retry configuration is invalid.
|
|
210
|
+
* @since 0.5.0
|
|
210
211
|
*/
|
|
211
212
|
constructor(transport: Transport<TProviderId>, config?: RetryConfig<TProviderId>);
|
|
212
213
|
/**
|
|
@@ -216,6 +217,7 @@ declare class RetryTransport<TProviderId extends string = string> implements Tra
|
|
|
216
217
|
* @param options Optional transport options.
|
|
217
218
|
* @returns The final delivery receipt.
|
|
218
219
|
* @throws {DOMException} If the operation is aborted.
|
|
220
|
+
* @since 0.5.0
|
|
219
221
|
*/
|
|
220
222
|
send(message: Message, options?: TransportOptions): Promise<Receipt<TProviderId>>;
|
|
221
223
|
/**
|
|
@@ -227,6 +229,7 @@ declare class RetryTransport<TProviderId extends string = string> implements Tra
|
|
|
227
229
|
* @param options Optional transport options.
|
|
228
230
|
* @returns An async iterable of receipts.
|
|
229
231
|
* @throws {DOMException} If the operation is aborted.
|
|
232
|
+
* @since 0.5.0
|
|
230
233
|
*/
|
|
231
234
|
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt<TProviderId>>;
|
|
232
235
|
/**
|
package/dist/index.js
CHANGED
|
@@ -90,6 +90,7 @@ var RetryTransport = class {
|
|
|
90
90
|
* @param transport The transport to wrap.
|
|
91
91
|
* @param config Retry configuration.
|
|
92
92
|
* @throws {RangeError} If retry configuration is invalid.
|
|
93
|
+
* @since 0.5.0
|
|
93
94
|
*/
|
|
94
95
|
constructor(transport, config = {}) {
|
|
95
96
|
this.wrappedTransport = transport;
|
|
@@ -103,25 +104,16 @@ var RetryTransport = class {
|
|
|
103
104
|
* @param options Optional transport options.
|
|
104
105
|
* @returns The final delivery receipt.
|
|
105
106
|
* @throws {DOMException} If the operation is aborted.
|
|
107
|
+
* @since 0.5.0
|
|
106
108
|
*/
|
|
107
109
|
async send(message, options) {
|
|
108
110
|
options?.signal?.throwIfAborted();
|
|
109
111
|
let lastThrownError;
|
|
110
112
|
for (let attempt = 1; attempt <= this.config.maxAttempts; attempt++) {
|
|
111
113
|
options?.signal?.throwIfAborted();
|
|
114
|
+
let receipt;
|
|
112
115
|
try {
|
|
113
|
-
|
|
114
|
-
if (receipt.successful) return this.withSuccessMetadata(receipt, attempt);
|
|
115
|
-
const failedReceipt = this.withFailureMetadata(receipt, attempt);
|
|
116
|
-
if (attempt >= this.config.maxAttempts || !this.shouldRetryReceipt(failedReceipt)) return failedReceipt;
|
|
117
|
-
await this.waitBeforeRetry({
|
|
118
|
-
attempt,
|
|
119
|
-
nextAttempt: attempt + 1,
|
|
120
|
-
maxAttempts: this.config.maxAttempts,
|
|
121
|
-
delayMilliseconds: this.calculateDelay(attempt, failedReceipt),
|
|
122
|
-
receipt: failedReceipt,
|
|
123
|
-
reason: "retry"
|
|
124
|
-
}, options?.signal);
|
|
116
|
+
receipt = await this.wrappedTransport.send(message, options);
|
|
125
117
|
} catch (error) {
|
|
126
118
|
options?.signal?.throwIfAborted();
|
|
127
119
|
lastThrownError = error;
|
|
@@ -134,7 +126,20 @@ var RetryTransport = class {
|
|
|
134
126
|
error,
|
|
135
127
|
reason: "retry"
|
|
136
128
|
}, options?.signal);
|
|
129
|
+
continue;
|
|
137
130
|
}
|
|
131
|
+
options?.signal?.throwIfAborted();
|
|
132
|
+
if (receipt.successful) return this.withSuccessMetadata(receipt, attempt);
|
|
133
|
+
const failedReceipt = this.withFailureMetadata(receipt, attempt);
|
|
134
|
+
if (attempt >= this.config.maxAttempts || !this.shouldRetryReceipt(failedReceipt)) return failedReceipt;
|
|
135
|
+
await this.waitBeforeRetry({
|
|
136
|
+
attempt,
|
|
137
|
+
nextAttempt: attempt + 1,
|
|
138
|
+
maxAttempts: this.config.maxAttempts,
|
|
139
|
+
delayMilliseconds: this.calculateDelay(attempt, failedReceipt),
|
|
140
|
+
receipt: failedReceipt,
|
|
141
|
+
reason: "retry"
|
|
142
|
+
}, options?.signal);
|
|
138
143
|
}
|
|
139
144
|
return this.createThrownFailure(lastThrownError, this.config.maxAttempts);
|
|
140
145
|
}
|
|
@@ -147,6 +152,7 @@ var RetryTransport = class {
|
|
|
147
152
|
* @param options Optional transport options.
|
|
148
153
|
* @returns An async iterable of receipts.
|
|
149
154
|
* @throws {DOMException} If the operation is aborted.
|
|
155
|
+
* @since 0.5.0
|
|
150
156
|
*/
|
|
151
157
|
async *sendMany(messages, options) {
|
|
152
158
|
const iterator = toAsyncIterator(messages);
|
|
@@ -170,12 +176,16 @@ var RetryTransport = class {
|
|
|
170
176
|
if (inputDone) return;
|
|
171
177
|
combinedSignal.signal.throwIfAborted();
|
|
172
178
|
pullingInput = true;
|
|
173
|
-
let
|
|
179
|
+
let inputPull;
|
|
174
180
|
try {
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
181
|
+
inputPull = Promise.resolve(iterator.next());
|
|
182
|
+
} catch (error) {
|
|
183
|
+
inputPull = Promise.reject(error);
|
|
178
184
|
}
|
|
185
|
+
inputPull = inputPull.finally(() => {
|
|
186
|
+
pullingInput = false;
|
|
187
|
+
});
|
|
188
|
+
const next = await raceWithAbort(inputPull, combinedSignal.signal);
|
|
179
189
|
if (closed) return;
|
|
180
190
|
if (next.done) {
|
|
181
191
|
inputDone = true;
|
|
@@ -300,7 +310,7 @@ var RetryTransport = class {
|
|
|
300
310
|
}
|
|
301
311
|
calculateDelay(attempt, failure) {
|
|
302
312
|
const retryAfterMilliseconds = getRetryAfterMilliseconds(failure);
|
|
303
|
-
const cappedRetryAfter = retryAfterMilliseconds == null ? void 0 : Math.min(retryAfterMilliseconds, this.config.backoff.maxDelayMilliseconds);
|
|
313
|
+
const cappedRetryAfter = retryAfterMilliseconds == null || !Number.isFinite(retryAfterMilliseconds) || retryAfterMilliseconds <= 0 ? void 0 : Math.min(retryAfterMilliseconds, this.config.backoff.maxDelayMilliseconds);
|
|
304
314
|
if (cappedRetryAfter != null) return cappedRetryAfter;
|
|
305
315
|
const computedDelay = Math.min(this.config.backoff.baseDelayMilliseconds * Math.pow(this.config.backoff.factor, attempt - 1), this.config.backoff.maxDelayMilliseconds);
|
|
306
316
|
if (this.config.jitter === false || this.config.jitter === "none") return computedDelay;
|
|
@@ -367,17 +377,37 @@ function toAsyncIterator(values) {
|
|
|
367
377
|
if (Symbol.asyncIterator in values) return values[Symbol.asyncIterator]();
|
|
368
378
|
const iterator = values[Symbol.iterator]();
|
|
369
379
|
return {
|
|
370
|
-
next() {
|
|
371
|
-
return Promise.resolve(iterator.next());
|
|
380
|
+
async next() {
|
|
381
|
+
return await Promise.resolve(iterator.next());
|
|
372
382
|
},
|
|
373
|
-
return(value) {
|
|
374
|
-
return Promise.resolve(iterator.return?.(value) ?? {
|
|
383
|
+
async return(value) {
|
|
384
|
+
return await Promise.resolve(iterator.return?.(value) ?? {
|
|
375
385
|
done: true,
|
|
376
386
|
value
|
|
377
387
|
});
|
|
378
388
|
}
|
|
379
389
|
};
|
|
380
390
|
}
|
|
391
|
+
function raceWithAbort(promise, signal) {
|
|
392
|
+
if (signal.aborted) return Promise.reject(signal.reason);
|
|
393
|
+
return new Promise((resolve, reject) => {
|
|
394
|
+
const abort = () => {
|
|
395
|
+
cleanup();
|
|
396
|
+
reject(signal.reason);
|
|
397
|
+
};
|
|
398
|
+
const cleanup = () => {
|
|
399
|
+
signal.removeEventListener("abort", abort);
|
|
400
|
+
};
|
|
401
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
402
|
+
promise.then((value) => {
|
|
403
|
+
cleanup();
|
|
404
|
+
resolve(value);
|
|
405
|
+
}, (error) => {
|
|
406
|
+
cleanup();
|
|
407
|
+
reject(error);
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
}
|
|
381
411
|
function toReceiptError(value) {
|
|
382
412
|
return isReceiptError(value) ? value : void 0;
|
|
383
413
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upyo/retry",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.199",
|
|
4
4
|
"description": "Retry and backoff decorator transport for Upyo email library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"email",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
"sideEffects": false,
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@upyo/core": "0.5.0-dev.
|
|
57
|
+
"@upyo/core": "0.5.0-dev.199+f10145fc"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"tsdown": "^0.12.7",
|