@common.js/p-retry 6.2.0 → 8.0.0

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 (4) hide show
  1. package/README.md +1 -1
  2. package/index.d.ts +167 -34
  3. package/index.js +329 -127
  4. package/package.json +15 -11
package/README.md CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  The [p-retry](https://www.npmjs.com/package/p-retry) package exported as CommonJS modules.
4
4
 
5
- Exported from [p-retry@6.2.0](https://www.npmjs.com/package/p-retry/v/6.2.0) using https://github.com/etienne-martin/common.js.
5
+ Exported from [p-retry@8.0.0](https://www.npmjs.com/package/p-retry/v/8.0.0) using https://github.com/etienne-martin/common.js.
package/index.d.ts CHANGED
@@ -1,36 +1,78 @@
1
- import {type OperationOptions} from 'retry';
2
-
3
1
  export class AbortError extends Error {
4
2
  readonly name: 'AbortError';
5
3
  readonly originalError: Error;
6
4
 
7
5
  /**
8
- Abort retrying and reject the promise.
6
+ Abort retrying and reject the promise. No callback functions will be called.
9
7
 
10
8
  @param message - An error message or a custom error.
11
9
  */
12
10
  constructor(message: string | Error);
13
11
  }
14
12
 
15
- export type FailedAttemptError = {
13
+ export type RetryContext = {
14
+ readonly error: Error;
16
15
  readonly attemptNumber: number;
17
16
  readonly retriesLeft: number;
18
- } & Error;
17
+ readonly retriesConsumed: number;
18
+
19
+ /**
20
+ The delay in milliseconds before the next retry attempt.
21
+
22
+ This is calculated based on `minTimeout`, `factor`, `maxTimeout`, and `randomize` options.
23
+
24
+ Note: The actual delay may be shorter if it would exceed `maxRetryTime`.
25
+ This is `0` when the retry is skipped or when no retry will occur based on the checks completed before the current callback runs.
26
+ */
27
+ readonly retryDelay: number;
28
+ };
19
29
 
20
30
  export type Options = {
21
31
  /**
22
- Callback invoked on each retry. Receives the error thrown by `input` as the first argument with properties `attemptNumber` and `retriesLeft` which indicate the current attempt number and the number of attempts left, respectively.
32
+ Callback invoked on each failure. Receives a context object containing the error and retry state information.
33
+
34
+ The function is called after `shouldConsumeRetry` and before `shouldRetry`, for all errors except `AbortError`.
35
+
36
+ The function is not called on `AbortError`.
37
+
38
+ @example
39
+ ```
40
+ import pRetry from 'p-retry';
41
+
42
+ const run = async () => {
43
+ const response = await fetch('https://sindresorhus.com/unicorn');
44
+
45
+ if (!response.ok) {
46
+ throw new Error(response.statusText);
47
+ }
48
+
49
+ return response.json();
50
+ };
51
+
52
+ const result = await pRetry(run, {
53
+ onFailedAttempt: ({error, attemptNumber, retriesLeft, retriesConsumed, retryDelay}) => {
54
+ console.log(`Attempt ${attemptNumber} failed. Retrying in ${retryDelay}ms. ${retriesLeft} retries left.`);
55
+ // 1st request => Attempt 1 failed. Retrying in 1000ms. 5 retries left.
56
+ // 2nd request => Attempt 2 failed. Retrying in 2000ms. 4 retries left.
57
+ // …
58
+ },
59
+ retries: 5
60
+ });
61
+
62
+ console.log(result);
63
+ ```
23
64
 
24
65
  The `onFailedAttempt` function can return a promise. For example, to add a [delay](https://github.com/sindresorhus/delay):
25
66
 
67
+ @example
26
68
  ```
27
69
  import pRetry from 'p-retry';
28
70
  import delay from 'delay';
29
71
 
30
- const run = async () => { ... };
72
+ const run = async () => { };
31
73
 
32
74
  const result = await pRetry(run, {
33
- onFailedAttempt: async error => {
75
+ onFailedAttempt: async () => {
34
76
  console.log('Waiting for 1 second before retrying');
35
77
  await delay(1000);
36
78
  }
@@ -39,14 +81,14 @@ export type Options = {
39
81
 
40
82
  If the `onFailedAttempt` function throws, all retries will be aborted and the original promise will reject with the thrown error.
41
83
  */
42
- readonly onFailedAttempt?: (error: FailedAttemptError) => void | Promise<void>;
84
+ readonly onFailedAttempt?: (context: RetryContext) => void | Promise<void>;
43
85
 
44
86
  /**
45
- Decide if a retry should occur based on the error. Returning true triggers a retry, false aborts with the error.
87
+ Decide if a retry should occur based on the context. Returning true triggers a retry, false aborts with the error.
46
88
 
47
- It is not called for `TypeError` (except network errors) and `AbortError`.
89
+ The function is called after `onFailedAttempt` and `shouldConsumeRetry`.
48
90
 
49
- @param error - The error thrown by the input function.
91
+ The function is not called on `AbortError`, `TypeError` (except network errors), or if `retries` or `maxRetryTime` are exhausted.
50
92
 
51
93
  @example
52
94
  ```
@@ -55,13 +97,91 @@ export type Options = {
55
97
  const run = async () => { … };
56
98
 
57
99
  const result = await pRetry(run, {
58
- shouldRetry: error => !(error instanceof CustomError);
100
+ shouldRetry: ({error, attemptNumber, retriesLeft}) => !(error instanceof CustomError)
59
101
  });
60
102
  ```
61
103
 
62
104
  In the example above, the operation will be retried unless the error is an instance of `CustomError`.
105
+
106
+ If the `shouldRetry` function throws, all retries will be aborted and the original promise will reject with the thrown error.
107
+ */
108
+ readonly shouldRetry?: (context: RetryContext) => boolean | Promise<boolean>;
109
+
110
+ /**
111
+ Decide if this failure should consume a retry from the `retries` budget.
112
+
113
+ When `false` is returned, the failure will not consume a retry or increment backoff values, but is still subject to `maxRetryTime`.
114
+
115
+ The function is called before `onFailedAttempt` and `shouldRetry`.
116
+
117
+ The function is not called on `AbortError`.
118
+
119
+ @example
120
+ ```
121
+ import pRetry from 'p-retry';
122
+
123
+ const run = async () => { … };
124
+
125
+ const result = await pRetry(run, {
126
+ retries: 2,
127
+ shouldConsumeRetry: ({error, retriesLeft}) => {
128
+ console.log(`Retries left: ${retriesLeft}`);
129
+ return !(error instanceof RateLimitError);
130
+ },
131
+ });
132
+ ```
133
+
134
+ In the example above, `RateLimitError`s will not decrement the available `retries`.
135
+
136
+ If the `shouldConsumeRetry` function throws, all retries will be aborted and the original promise will reject with the thrown error.
63
137
  */
64
- readonly shouldRetry?: (error: FailedAttemptError) => boolean | Promise<boolean>;
138
+ readonly shouldConsumeRetry?: (context: RetryContext) => boolean | Promise<boolean>;
139
+
140
+ /**
141
+ The maximum amount of times to retry the operation.
142
+
143
+ @default 10
144
+ */
145
+ readonly retries?: number;
146
+
147
+ /**
148
+ The exponential factor to use.
149
+
150
+ @default 2
151
+ */
152
+ readonly factor?: number;
153
+
154
+ /**
155
+ The number of milliseconds before starting the first retry.
156
+
157
+ Set this to `0` to retry immediately with no delay.
158
+
159
+ @default 1000
160
+ */
161
+ readonly minTimeout?: number;
162
+
163
+ /**
164
+ The maximum number of milliseconds between two retries.
165
+
166
+ @default Infinity
167
+ */
168
+ readonly maxTimeout?: number;
169
+
170
+ /**
171
+ Randomizes the timeouts by multiplying with a factor between 1 and 2.
172
+
173
+ @default false
174
+ */
175
+ readonly randomize?: boolean;
176
+
177
+ /**
178
+ The maximum time (in milliseconds) that the retried operation is allowed to run.
179
+
180
+ @default Infinity
181
+
182
+ Measured with a monotonic clock (`performance.now()`) so system clock adjustments do not affect the limit.
183
+ */
184
+ readonly maxRetryTime?: number;
65
185
 
66
186
  /**
67
187
  You can abort retrying using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).
@@ -84,22 +204,31 @@ export type Options = {
84
204
  }
85
205
  ```
86
206
  */
87
- readonly signal?: AbortSignal;
88
- } & OperationOptions;
207
+ readonly signal?: AbortSignal | undefined;
208
+
209
+ /**
210
+ Prevents retry timeouts from keeping the process alive.
211
+
212
+ Only affects platforms with a `.unref()` method on timeouts, such as Node.js.
213
+
214
+ @default false
215
+ */
216
+ readonly unref?: boolean;
217
+ };
89
218
 
90
219
  /**
91
220
  Returns a `Promise` that is fulfilled when calling `input` returns a fulfilled promise. If calling `input` returns a rejected promise, `input` is called again until the max retries are reached, it then rejects with the last rejection reason.
92
221
 
93
- Does not retry on most `TypeErrors`, with the exception of network errors. This is done on a best case basis as different browsers have different [messages](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful) to indicate this.
94
- See [whatwg/fetch#526 (comment)](https://github.com/whatwg/fetch/issues/526#issuecomment-554604080)
222
+ Does not retry on most `TypeErrors`, with the exception of network errors. This is done on a best case basis as different browsers have different [messages](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful) to indicate this. See [whatwg/fetch#526 (comment)](https://github.com/whatwg/fetch/issues/526#issuecomment-554604080)
223
+
224
+ Non-network `TypeError`s always abort retries, even if `shouldConsumeRetry` or `shouldRetry` would otherwise allow another attempt.
95
225
 
96
226
  @param input - Receives the number of attempts as the first argument and is expected to return a `Promise` or any value.
97
- @param options - Options are passed to the [`retry`](https://github.com/tim-kos/node-retry#retryoperationoptions) module.
227
+ @param options - Options for configuring the retry behavior.
98
228
 
99
229
  @example
100
230
  ```
101
231
  import pRetry, {AbortError} from 'p-retry';
102
- import fetch from 'node-fetch';
103
232
 
104
233
  const run = async () => {
105
234
  const response = await fetch('https://sindresorhus.com/unicorn');
@@ -113,22 +242,26 @@ const run = async () => {
113
242
  };
114
243
 
115
244
  console.log(await pRetry(run, {retries: 5}));
116
-
117
- // With the `onFailedAttempt` option:
118
- const result = await pRetry(run, {
119
- onFailedAttempt: error => {
120
- console.log(`Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left.`);
121
- // 1st request => Attempt 1 failed. There are 4 retries left.
122
- // 2nd request => Attempt 2 failed. There are 3 retries left.
123
- // …
124
- },
125
- retries: 5
126
- });
127
-
128
- console.log(result);
129
245
  ```
130
246
  */
131
247
  export default function pRetry<T>(
132
- input: (attemptCount: number) => PromiseLike<T> | T,
248
+ input: (attemptNumber: number) => PromiseLike<T> | T,
133
249
  options?: Options
134
250
  ): Promise<T>;
251
+
252
+ /**
253
+ Wrap a function so that each call is automatically retried on failure.
254
+
255
+ @example
256
+ ```
257
+ import {makeRetriable} from 'p-retry';
258
+
259
+ const fetchWithRetry = makeRetriable(fetch, {retries: 5});
260
+
261
+ const response = await fetchWithRetry('https://sindresorhus.com/unicorn');
262
+ ```
263
+ */
264
+ export function makeRetriable<Arguments extends readonly unknown[], Result>(
265
+ function_: (...arguments_: Arguments) => PromiseLike<Result> | Result,
266
+ options?: Options
267
+ ): (...arguments_: Arguments) => Promise<Result>;
package/index.js CHANGED
@@ -14,9 +14,11 @@ _export(exports, {
14
14
  },
15
15
  default: function() {
16
16
  return pRetry;
17
+ },
18
+ makeRetriable: function() {
19
+ return makeRetriable;
17
20
  }
18
21
  });
19
- var _retry = /*#__PURE__*/ _interopRequireDefault(require("retry"));
20
22
  var _isNetworkError = /*#__PURE__*/ _interopRequireDefault(require("is-network-error"));
21
23
  function _assertThisInitialized(self) {
22
24
  if (self === void 0) {
@@ -310,6 +312,42 @@ var __generator = (void 0) && (void 0).__generator || function(thisArg, body) {
310
312
  };
311
313
  }
312
314
  };
315
+ var _options, _options1, _options2, _options3, _options4, _options5, _options6, _options7, _options8;
316
+ function validateRetries(retries) {
317
+ if (typeof retries === "number") {
318
+ if (retries < 0) {
319
+ throw new TypeError("Expected `retries` to be a non-negative number.");
320
+ }
321
+ if (Number.isNaN(retries)) {
322
+ throw new TypeError("Expected `retries` to be a valid number or Infinity, got NaN.");
323
+ }
324
+ } else if (retries !== undefined) {
325
+ throw new TypeError("Expected `retries` to be a number or Infinity.");
326
+ }
327
+ }
328
+ function validateNumberOption(name, value) {
329
+ var ref = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {}, _min = ref.min, min = _min === void 0 ? 0 : _min, _allowInfinity = ref.allowInfinity, allowInfinity = _allowInfinity === void 0 ? false : _allowInfinity;
330
+ if (value === undefined) {
331
+ return;
332
+ }
333
+ if (typeof value !== "number" || Number.isNaN(value)) {
334
+ throw new TypeError("Expected `".concat(name, "` to be a number").concat(allowInfinity ? " or Infinity" : "", "."));
335
+ }
336
+ if (!allowInfinity && !Number.isFinite(value)) {
337
+ throw new TypeError("Expected `".concat(name, "` to be a finite number."));
338
+ }
339
+ if (value < min) {
340
+ throw new TypeError("Expected `".concat(name, "` to be ≥ ").concat(min, "."));
341
+ }
342
+ }
343
+ function validateFunctionOption(name, value) {
344
+ if (value === undefined) {
345
+ return;
346
+ }
347
+ if (typeof value !== "function") {
348
+ throw new TypeError("Expected `".concat(name, "` to be a function."));
349
+ }
350
+ }
313
351
  var AbortError = /*#__PURE__*/ function(Error1) {
314
352
  "use strict";
315
353
  _inherits(AbortError, Error1);
@@ -331,139 +369,303 @@ var AbortError = /*#__PURE__*/ function(Error1) {
331
369
  }
332
370
  return AbortError;
333
371
  }(_wrapNativeSuper(Error));
334
- var decorateErrorWithCounts = function(error, attemptNumber, options) {
335
- // Minus 1 from attemptNumber because the first attempt does not count as a retry
336
- var retriesLeft = options.retries - (attemptNumber - 1);
337
- error.attemptNumber = attemptNumber;
338
- error.retriesLeft = retriesLeft;
339
- return error;
340
- };
341
- function pRetry(input, options) {
372
+ function calculateDelay(retriesConsumed, options) {
373
+ var attempt = Math.max(1, retriesConsumed + 1);
374
+ var random = options.randomize ? Math.random() + 1 : 1;
375
+ var timeout = Math.round(random * options.minTimeout * Math.pow(options.factor, attempt - 1));
376
+ timeout = Math.min(timeout, options.maxTimeout);
377
+ return timeout;
378
+ }
379
+ function calculateRemainingTime(start, max) {
380
+ if (!Number.isFinite(max)) {
381
+ return max;
382
+ }
383
+ return max - (performance.now() - start);
384
+ }
385
+ function delayForRetry(delay, options) {
386
+ return _delayForRetry.apply(this, arguments);
387
+ }
388
+ function _delayForRetry() {
389
+ _delayForRetry = _asyncToGenerator(function(delay, options) {
390
+ return __generator(this, function(_state) {
391
+ switch(_state.label){
392
+ case 0:
393
+ if (delay <= 0) {
394
+ return [
395
+ 2
396
+ ];
397
+ }
398
+ return [
399
+ 4,
400
+ new Promise(function(resolve, reject) {
401
+ var ref;
402
+ var onAbort = function() {
403
+ var ref;
404
+ clearTimeout(timeoutToken);
405
+ (ref = options.signal) === null || ref === void 0 ? void 0 : ref.removeEventListener("abort", onAbort);
406
+ reject(options.signal.reason);
407
+ };
408
+ var timeoutToken = setTimeout(function() {
409
+ var ref;
410
+ (ref = options.signal) === null || ref === void 0 ? void 0 : ref.removeEventListener("abort", onAbort);
411
+ resolve();
412
+ }, delay);
413
+ if (options.unref) {
414
+ var ref1;
415
+ (ref1 = timeoutToken.unref) === null || ref1 === void 0 ? void 0 : ref1.call(timeoutToken);
416
+ }
417
+ (ref = options.signal) === null || ref === void 0 ? void 0 : ref.addEventListener("abort", onAbort, {
418
+ once: true
419
+ });
420
+ })
421
+ ];
422
+ case 1:
423
+ _state.sent();
424
+ return [
425
+ 2
426
+ ];
427
+ }
428
+ });
429
+ });
430
+ return _delayForRetry.apply(this, arguments);
431
+ }
432
+ function onAttemptFailure(_) {
433
+ return _onAttemptFailure.apply(this, arguments);
434
+ }
435
+ function _onAttemptFailure() {
436
+ _onAttemptFailure = _asyncToGenerator(function(param) {
437
+ var error, attemptNumber, retriesConsumed, startTime, options, ref, ref1, normalizedError, retriesLeft, _maxRetryTime, maxRetryTime, delayTime, remainingTimeBeforeCallbacks, context, consumeRetryContext, consumeRetry, effectiveDelay, context1, remainingTime, remainingTimeAfterShouldRetry, ref2, finalDelay;
438
+ return __generator(this, function(_state) {
439
+ switch(_state.label){
440
+ case 0:
441
+ error = param.error, attemptNumber = param.attemptNumber, retriesConsumed = param.retriesConsumed, startTime = param.startTime, options = param.options;
442
+ normalizedError = _instanceof(error, Error) ? error : new TypeError('Non-error was thrown: "'.concat(error, '". You should only throw errors.'));
443
+ if (_instanceof(normalizedError, AbortError)) {
444
+ throw normalizedError.originalError;
445
+ }
446
+ retriesLeft = Number.isFinite(options.retries) ? Math.max(0, options.retries - retriesConsumed) : options.retries;
447
+ maxRetryTime = (_maxRetryTime = options.maxRetryTime) !== null && _maxRetryTime !== void 0 ? _maxRetryTime : Number.POSITIVE_INFINITY;
448
+ delayTime = calculateDelay(retriesConsumed, options);
449
+ remainingTimeBeforeCallbacks = calculateRemainingTime(startTime, maxRetryTime);
450
+ if (!(remainingTimeBeforeCallbacks <= 0)) return [
451
+ 3,
452
+ 2
453
+ ];
454
+ context = Object.freeze({
455
+ error: normalizedError,
456
+ attemptNumber: attemptNumber,
457
+ retriesLeft: retriesLeft,
458
+ retriesConsumed: retriesConsumed,
459
+ retryDelay: 0
460
+ });
461
+ return [
462
+ 4,
463
+ options.onFailedAttempt(context)
464
+ ];
465
+ case 1:
466
+ _state.sent();
467
+ throw normalizedError;
468
+ case 2:
469
+ consumeRetryContext = Object.freeze({
470
+ error: normalizedError,
471
+ attemptNumber: attemptNumber,
472
+ retriesLeft: retriesLeft,
473
+ retriesConsumed: retriesConsumed,
474
+ retryDelay: retriesLeft > 0 ? delayTime : 0
475
+ });
476
+ return [
477
+ 4,
478
+ options.shouldConsumeRetry(consumeRetryContext)
479
+ ];
480
+ case 3:
481
+ consumeRetry = _state.sent();
482
+ effectiveDelay = consumeRetry && retriesLeft > 0 ? delayTime : 0;
483
+ context1 = Object.freeze({
484
+ error: normalizedError,
485
+ attemptNumber: attemptNumber,
486
+ retriesLeft: retriesLeft,
487
+ retriesConsumed: retriesConsumed,
488
+ retryDelay: effectiveDelay
489
+ });
490
+ return [
491
+ 4,
492
+ options.onFailedAttempt(context1)
493
+ ];
494
+ case 4:
495
+ _state.sent();
496
+ if (calculateRemainingTime(startTime, maxRetryTime) <= 0) {
497
+ throw normalizedError;
498
+ }
499
+ remainingTime = calculateRemainingTime(startTime, maxRetryTime);
500
+ if (remainingTime <= 0 || retriesLeft <= 0) {
501
+ throw normalizedError;
502
+ }
503
+ if (_instanceof(normalizedError, TypeError) && !(0, _isNetworkError.default)(normalizedError)) {
504
+ throw normalizedError;
505
+ }
506
+ return [
507
+ 4,
508
+ options.shouldRetry(context1)
509
+ ];
510
+ case 5:
511
+ if (!_state.sent()) {
512
+ throw normalizedError;
513
+ }
514
+ remainingTimeAfterShouldRetry = calculateRemainingTime(startTime, maxRetryTime);
515
+ if (remainingTimeAfterShouldRetry <= 0) {
516
+ throw normalizedError;
517
+ }
518
+ if (!consumeRetry) {
519
+ ;
520
+ (ref2 = options.signal) === null || ref2 === void 0 ? void 0 : ref2.throwIfAborted();
521
+ return [
522
+ 2,
523
+ false
524
+ ];
525
+ }
526
+ finalDelay = Math.min(effectiveDelay, remainingTimeAfterShouldRetry);
527
+ (ref = options.signal) === null || ref === void 0 ? void 0 : ref.throwIfAborted();
528
+ return [
529
+ 4,
530
+ delayForRetry(finalDelay, options)
531
+ ];
532
+ case 6:
533
+ _state.sent();
534
+ (ref1 = options.signal) === null || ref1 === void 0 ? void 0 : ref1.throwIfAborted();
535
+ return [
536
+ 2,
537
+ true
538
+ ];
539
+ }
540
+ });
541
+ });
542
+ return _onAttemptFailure.apply(this, arguments);
543
+ }
544
+ function pRetry(input) {
342
545
  return _pRetry.apply(this, arguments);
343
546
  }
344
547
  function _pRetry() {
345
- _pRetry = _asyncToGenerator(function(input, options) {
548
+ _pRetry = _asyncToGenerator(function(input) {
549
+ var options, ref, _retries, _factor, _minTimeout, _maxTimeout, _maxRetryTime, _randomize, _onFailedAttempt, _shouldRetry, _shouldConsumeRetry, attemptNumber, retriesConsumed, startTime, ref1, ref2, result, error;
550
+ var _arguments = arguments;
346
551
  return __generator(this, function(_state) {
347
- return [
348
- 2,
349
- new Promise(function(resolve, reject) {
350
- options = _objectSpread({
351
- onFailedAttempt: function onFailedAttempt() {},
352
- retries: 10,
353
- shouldRetry: function() {
354
- return true;
355
- }
356
- }, options);
357
- var operation = _retry.default.operation(options);
358
- var abortHandler = function() {
359
- var ref;
360
- operation.stop();
361
- reject((ref = options.signal) === null || ref === void 0 ? void 0 : ref.reason);
362
- };
363
- if (options.signal && !options.signal.aborted) {
364
- options.signal.addEventListener("abort", abortHandler, {
365
- once: true
366
- });
552
+ switch(_state.label){
553
+ case 0:
554
+ options = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
555
+ options = _objectSpread({}, options);
556
+ validateRetries(options.retries);
557
+ if (Object.hasOwn(options, "forever")) {
558
+ throw new Error("The `forever` option is no longer supported. For many use-cases, you can set `retries: Infinity` instead.");
367
559
  }
368
- var cleanUp = function() {
369
- var ref;
370
- (ref = options.signal) === null || ref === void 0 ? void 0 : ref.removeEventListener("abort", abortHandler);
371
- operation.stop();
560
+ (_retries = (_options = options).retries) !== null && _retries !== void 0 ? _retries : _options.retries = 10;
561
+ (_factor = (_options1 = options).factor) !== null && _factor !== void 0 ? _factor : _options1.factor = 2;
562
+ (_minTimeout = (_options2 = options).minTimeout) !== null && _minTimeout !== void 0 ? _minTimeout : _options2.minTimeout = 1000;
563
+ (_maxTimeout = (_options3 = options).maxTimeout) !== null && _maxTimeout !== void 0 ? _maxTimeout : _options3.maxTimeout = Number.POSITIVE_INFINITY;
564
+ (_maxRetryTime = (_options4 = options).maxRetryTime) !== null && _maxRetryTime !== void 0 ? _maxRetryTime : _options4.maxRetryTime = Number.POSITIVE_INFINITY;
565
+ (_randomize = (_options5 = options).randomize) !== null && _randomize !== void 0 ? _randomize : _options5.randomize = false;
566
+ (_onFailedAttempt = (_options6 = options).onFailedAttempt) !== null && _onFailedAttempt !== void 0 ? _onFailedAttempt : _options6.onFailedAttempt = function() {};
567
+ (_shouldRetry = (_options7 = options).shouldRetry) !== null && _shouldRetry !== void 0 ? _shouldRetry : _options7.shouldRetry = function() {
568
+ return true;
372
569
  };
373
- operation.attempt(function() {
374
- var _ref = _asyncToGenerator(function(attemptNumber) {
375
- var result, error, finalError;
376
- return __generator(this, function(_state) {
377
- switch(_state.label){
378
- case 0:
379
- _state.trys.push([
380
- 0,
381
- 2,
382
- ,
383
- 8
384
- ]);
385
- return [
386
- 4,
387
- input(attemptNumber)
388
- ];
389
- case 1:
390
- result = _state.sent();
391
- cleanUp();
392
- resolve(result);
393
- return [
394
- 3,
395
- 8
396
- ];
397
- case 2:
398
- error = _state.sent();
399
- _state.label = 3;
400
- case 3:
401
- _state.trys.push([
402
- 3,
403
- 6,
404
- ,
405
- 7
406
- ]);
407
- if (!_instanceof(error, Error)) {
408
- throw new TypeError('Non-error was thrown: "'.concat(error, '". You should only throw errors.'));
409
- }
410
- if (_instanceof(error, AbortError)) {
411
- throw error.originalError;
412
- }
413
- if (_instanceof(error, TypeError) && !(0, _isNetworkError.default)(error)) {
414
- throw error;
415
- }
416
- decorateErrorWithCounts(error, attemptNumber, options);
417
- return [
418
- 4,
419
- options.shouldRetry(error)
420
- ];
421
- case 4:
422
- if (!_state.sent()) {
423
- operation.stop();
424
- reject(error);
425
- }
426
- return [
427
- 4,
428
- options.onFailedAttempt(error)
429
- ];
430
- case 5:
431
- _state.sent();
432
- if (!operation.retry(error)) {
433
- throw operation.mainError();
434
- }
435
- return [
436
- 3,
437
- 7
438
- ];
439
- case 6:
440
- finalError = _state.sent();
441
- decorateErrorWithCounts(finalError, attemptNumber, options);
442
- cleanUp();
443
- reject(finalError);
444
- return [
445
- 3,
446
- 7
447
- ];
448
- case 7:
449
- return [
450
- 3,
451
- 8
452
- ];
453
- case 8:
454
- return [
455
- 2
456
- ];
457
- }
458
- });
459
- });
460
- return function(attemptNumber) {
461
- return _ref.apply(this, arguments);
462
- };
463
- }());
464
- })
465
- ];
570
+ (_shouldConsumeRetry = (_options8 = options).shouldConsumeRetry) !== null && _shouldConsumeRetry !== void 0 ? _shouldConsumeRetry : _options8.shouldConsumeRetry = function() {
571
+ return true;
572
+ };
573
+ // Validate numeric options and normalize edge cases
574
+ validateFunctionOption("onFailedAttempt", options.onFailedAttempt);
575
+ validateFunctionOption("shouldRetry", options.shouldRetry);
576
+ validateFunctionOption("shouldConsumeRetry", options.shouldConsumeRetry);
577
+ validateNumberOption("factor", options.factor, {
578
+ min: 0,
579
+ allowInfinity: false
580
+ });
581
+ validateNumberOption("minTimeout", options.minTimeout, {
582
+ min: 0,
583
+ allowInfinity: false
584
+ });
585
+ validateNumberOption("maxTimeout", options.maxTimeout, {
586
+ min: 0,
587
+ allowInfinity: true
588
+ });
589
+ validateNumberOption("maxRetryTime", options.maxRetryTime, {
590
+ min: 0,
591
+ allowInfinity: true
592
+ });
593
+ // Treat non-positive factor as 1 to avoid zero backoff or negative behavior
594
+ if (!(options.factor > 0)) {
595
+ options.factor = 1;
596
+ }
597
+ (ref = options.signal) === null || ref === void 0 ? void 0 : ref.throwIfAborted();
598
+ attemptNumber = 0;
599
+ retriesConsumed = 0;
600
+ startTime = performance.now();
601
+ _state.label = 1;
602
+ case 1:
603
+ if (!(Number.isFinite(options.retries) ? retriesConsumed <= options.retries : true)) return [
604
+ 3,
605
+ 7
606
+ ];
607
+ attemptNumber++;
608
+ _state.label = 2;
609
+ case 2:
610
+ _state.trys.push([
611
+ 2,
612
+ 4,
613
+ ,
614
+ 6
615
+ ]);
616
+ (ref1 = options.signal) === null || ref1 === void 0 ? void 0 : ref1.throwIfAborted();
617
+ return [
618
+ 4,
619
+ input(attemptNumber)
620
+ ];
621
+ case 3:
622
+ result = _state.sent();
623
+ (ref2 = options.signal) === null || ref2 === void 0 ? void 0 : ref2.throwIfAborted();
624
+ return [
625
+ 2,
626
+ result
627
+ ];
628
+ case 4:
629
+ error = _state.sent();
630
+ return [
631
+ 4,
632
+ onAttemptFailure({
633
+ error: error,
634
+ attemptNumber: attemptNumber,
635
+ retriesConsumed: retriesConsumed,
636
+ startTime: startTime,
637
+ options: options
638
+ })
639
+ ];
640
+ case 5:
641
+ if (_state.sent()) {
642
+ retriesConsumed++;
643
+ }
644
+ return [
645
+ 3,
646
+ 6
647
+ ];
648
+ case 6:
649
+ return [
650
+ 3,
651
+ 1
652
+ ];
653
+ case 7:
654
+ // Should not reach here, but in case it does, throw an error
655
+ throw new Error("Retry attempts exhausted without throwing an error.");
656
+ }
466
657
  });
467
658
  });
468
659
  return _pRetry.apply(this, arguments);
469
660
  }
661
+ function makeRetriable(function_, options) {
662
+ return function() {
663
+ for(var _len = arguments.length, arguments_ = new Array(_len), _key = 0; _key < _len; _key++){
664
+ arguments_[_key] = arguments[_key];
665
+ }
666
+ var _this = this;
667
+ return pRetry(function() {
668
+ return function_.apply(_this, arguments_);
669
+ }, options);
670
+ };
671
+ }
package/package.json CHANGED
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "name": "@common.js/p-retry",
3
- "version": "6.2.0",
3
+ "version": "8.0.0",
4
4
  "description": "p-retry package exported as CommonJS modules",
5
5
  "license": "MIT",
6
6
  "repository": "etienne-martin/common.js",
7
7
  "funding": "https://github.com/sponsors/sindresorhus",
8
8
  "type": "commonjs",
9
- "types": "./index.d.ts",
10
9
  "sideEffects": false,
11
10
  "engines": {
12
- "node": ">=16.17"
11
+ "node": ">=22"
13
12
  },
14
13
  "scripts": {
15
14
  "test": "xo && ava && tsd"
@@ -19,16 +18,21 @@
19
18
  "index.d.ts"
20
19
  ],
21
20
  "dependencies": {
22
- "@types/retry": "0.12.2",
23
- "@common.js/is-network-error": "^1.0.0",
24
- "retry": "^0.13.1"
21
+ "@common.js/is-network-error": "^1.3.0"
25
22
  },
26
23
  "devDependencies": {
27
- "ava": "^5.3.1",
28
- "delay": "^6.0.0",
29
- "tsd": "^0.28.1",
30
- "xo": "^0.56.0"
24
+ "ava": "^6.4.1",
25
+ "delay": "^7.0.0",
26
+ "execa": "^9.6.1",
27
+ "tsd": "^0.33.0",
28
+ "xo": "^1.2.3"
29
+ },
30
+ "xo": {
31
+ "rules": {
32
+ "no-await-in-loop": "off"
33
+ }
31
34
  },
32
35
  "homepage": "https://github.com/etienne-martin/common.js#readme",
33
- "main": "./index.js"
36
+ "main": "./index.js",
37
+ "types": "./index.d.ts"
34
38
  }