Comparing version 4.6.2 to 6.1.0
154
index.d.ts
@@ -1,4 +0,4 @@ | ||
import {OperationOptions} from 'retry'; | ||
import {type OperationOptions} from 'retry'; | ||
declare class AbortErrorClass extends Error { | ||
export class AbortError extends Error { | ||
readonly name: 'AbortError'; | ||
@@ -10,3 +10,3 @@ readonly originalError: Error; | ||
@param message - Error message or custom error. | ||
@param message - An error message or a custom error. | ||
*/ | ||
@@ -16,93 +16,99 @@ constructor(message: string | Error); | ||
declare namespace pRetry { | ||
interface FailedAttemptError extends Error { | ||
readonly attemptNumber: number; | ||
readonly retriesLeft: number; | ||
} | ||
export type FailedAttemptError = { | ||
readonly attemptNumber: number; | ||
readonly retriesLeft: number; | ||
} & Error; | ||
interface Options extends OperationOptions { | ||
/** | ||
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. | ||
export type Options = { | ||
/** | ||
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. | ||
The `onFailedAttempt` function can return a promise. For example, to add a [delay](https://github.com/sindresorhus/delay): | ||
The `onFailedAttempt` function can return a promise. For example, to add a [delay](https://github.com/sindresorhus/delay): | ||
``` | ||
import pRetry = require('p-retry'); | ||
import delay = require('delay'); | ||
``` | ||
import pRetry from 'p-retry'; | ||
import delay from 'delay'; | ||
const run = async () => { ... }; | ||
const run = async () => { ... }; | ||
(async () => { | ||
const result = await pRetry(run, { | ||
onFailedAttempt: async error => { | ||
console.log('Waiting for 1 second before retrying'); | ||
await delay(1000); | ||
} | ||
}); | ||
})(); | ||
``` | ||
const result = await pRetry(run, { | ||
onFailedAttempt: async error => { | ||
console.log('Waiting for 1 second before retrying'); | ||
await delay(1000); | ||
} | ||
}); | ||
``` | ||
If the `onFailedAttempt` function throws, all retries will be aborted and the original promise will reject with the thrown error. | ||
*/ | ||
readonly onFailedAttempt?: (error: FailedAttemptError) => void | Promise<void>; | ||
} | ||
If the `onFailedAttempt` function throws, all retries will be aborted and the original promise will reject with the thrown error. | ||
*/ | ||
readonly onFailedAttempt?: (error: FailedAttemptError) => void | Promise<void>; | ||
type AbortError = AbortErrorClass; | ||
} | ||
declare const pRetry: { | ||
/** | ||
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. | ||
You can abort retrying using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). | ||
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) | ||
``` | ||
import pRetry from 'p-retry'; | ||
@param input - Receives the number of attempts as the first argument and is expected to return a `Promise` or any value. | ||
@param options - Options are passed to the [`retry`](https://github.com/tim-kos/node-retry#retryoperationoptions) module. | ||
const run = async () => { … }; | ||
const controller = new AbortController(); | ||
@example | ||
cancelButton.addEventListener('click', () => { | ||
controller.abort(new Error('User clicked cancel button')); | ||
}); | ||
try { | ||
await pRetry(run, {signal: controller.signal}); | ||
} catch (error) { | ||
console.log(error.message); | ||
//=> 'User clicked cancel button' | ||
} | ||
``` | ||
import pRetry = require('p-retry'); | ||
import fetch from 'node-fetch'; | ||
*/ | ||
readonly signal?: AbortSignal; | ||
} & OperationOptions; | ||
const run = async () => { | ||
const response = await fetch('https://sindresorhus.com/unicorn'); | ||
/** | ||
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. | ||
// Abort retrying if the resource doesn't exist | ||
if (response.status === 404) { | ||
throw new pRetry.AbortError(response.statusText); | ||
} | ||
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) | ||
return response.blob(); | ||
}; | ||
@param input - Receives the number of attempts as the first argument and is expected to return a `Promise` or any value. | ||
@param options - Options are passed to the [`retry`](https://github.com/tim-kos/node-retry#retryoperationoptions) module. | ||
(async () => { | ||
console.log(await pRetry(run, {retries: 5})); | ||
@example | ||
``` | ||
import pRetry, {AbortError} from 'p-retry'; | ||
import fetch from 'node-fetch'; | ||
// With the `onFailedAttempt` option: | ||
const result = await pRetry(run, { | ||
onFailedAttempt: error => { | ||
console.log(`Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left.`); | ||
// 1st request => Attempt 1 failed. There are 4 retries left. | ||
// 2nd request => Attempt 2 failed. There are 3 retries left. | ||
// … | ||
}, | ||
retries: 5 | ||
}); | ||
const run = async () => { | ||
const response = await fetch('https://sindresorhus.com/unicorn'); | ||
console.log(result); | ||
})(); | ||
``` | ||
*/ | ||
<T>( | ||
input: (attemptCount: number) => PromiseLike<T> | T, | ||
options?: pRetry.Options | ||
): Promise<T>; | ||
// Abort retrying if the resource doesn't exist | ||
if (response.status === 404) { | ||
throw new AbortError(response.statusText); | ||
} | ||
AbortError: typeof AbortErrorClass; | ||
// TODO: remove this in the next major version | ||
default: typeof pRetry; | ||
return response.blob(); | ||
}; | ||
export = pRetry; | ||
console.log(await pRetry(run, {retries: 5})); | ||
// With the `onFailedAttempt` option: | ||
const result = await pRetry(run, { | ||
onFailedAttempt: error => { | ||
console.log(`Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left.`); | ||
// 1st request => Attempt 1 failed. There are 4 retries left. | ||
// 2nd request => Attempt 2 failed. There are 3 retries left. | ||
// … | ||
}, | ||
retries: 5 | ||
}); | ||
console.log(result); | ||
``` | ||
*/ | ||
export default function pRetry<T>( | ||
input: (attemptCount: number) => PromiseLike<T> | T, | ||
options?: Options | ||
): Promise<T>; |
99
index.js
@@ -1,12 +0,5 @@ | ||
'use strict'; | ||
const retry = require('retry'); | ||
import retry from 'retry'; | ||
import isNetworkError from 'is-network-error'; | ||
const networkErrorMsgs = [ | ||
'Failed to fetch', // Chrome | ||
'NetworkError when attempting to fetch resource.', // Firefox | ||
'The Internet connection appears to be offline.', // Safari | ||
'Network request failed' // `cross-fetch` | ||
]; | ||
class AbortError extends Error { | ||
export class AbortError extends Error { | ||
constructor(message) { | ||
@@ -37,50 +30,58 @@ super(); | ||
const isNetworkError = errorMessage => networkErrorMsgs.includes(errorMessage); | ||
export default async function pRetry(input, options) { | ||
return new Promise((resolve, reject) => { | ||
options = { | ||
onFailedAttempt() {}, | ||
retries: 10, | ||
...options, | ||
}; | ||
const pRetry = (input, options) => new Promise((resolve, reject) => { | ||
options = { | ||
onFailedAttempt: () => {}, | ||
retries: 10, | ||
...options | ||
}; | ||
const operation = retry.operation(options); | ||
const operation = retry.operation(options); | ||
const abortHandler = () => { | ||
operation.stop(); | ||
reject(options.signal?.reason); | ||
}; | ||
operation.attempt(async attemptNumber => { | ||
try { | ||
resolve(await input(attemptNumber)); | ||
} catch (error) { | ||
if (!(error instanceof Error)) { | ||
reject(new TypeError(`Non-error was thrown: "${error}". You should only throw errors.`)); | ||
return; | ||
} | ||
if (options.signal && !options.signal.aborted) { | ||
options.signal.addEventListener('abort', abortHandler, {once: true}); | ||
} | ||
if (error instanceof AbortError) { | ||
operation.stop(); | ||
reject(error.originalError); | ||
} else if (error instanceof TypeError && !isNetworkError(error.message)) { | ||
operation.stop(); | ||
reject(error); | ||
} else { | ||
decorateErrorWithCounts(error, attemptNumber, options); | ||
const cleanUp = () => { | ||
options.signal?.removeEventListener('abort', abortHandler); | ||
operation.stop(); | ||
}; | ||
operation.attempt(async attemptNumber => { | ||
try { | ||
const result = await input(attemptNumber); | ||
cleanUp(); | ||
resolve(result); | ||
} catch (error) { | ||
try { | ||
await options.onFailedAttempt(error); | ||
} catch (error) { | ||
reject(error); | ||
return; | ||
} | ||
if (!(error instanceof Error)) { | ||
throw new TypeError(`Non-error was thrown: "${error}". You should only throw errors.`); | ||
} | ||
if (!operation.retry(error)) { | ||
reject(operation.mainError()); | ||
if (error instanceof AbortError) { | ||
throw error.originalError; | ||
} | ||
if (error instanceof TypeError && !isNetworkError(error)) { | ||
throw error; | ||
} | ||
await options.onFailedAttempt(decorateErrorWithCounts(error, attemptNumber, options)); | ||
if (!operation.retry(error)) { | ||
throw operation.mainError(); | ||
} | ||
} catch (finalError) { | ||
decorateErrorWithCounts(finalError, attemptNumber, options); | ||
cleanUp(); | ||
reject(finalError); | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
}); | ||
module.exports = pRetry; | ||
// TODO: remove this in the next major version | ||
module.exports.default = pRetry; | ||
module.exports.AbortError = AbortError; | ||
} |
{ | ||
"name": "p-retry", | ||
"version": "4.6.2", | ||
"version": "6.1.0", | ||
"description": "Retry a promise-returning or async function", | ||
"license": "MIT", | ||
"repository": "sindresorhus/p-retry", | ||
"funding": "https://github.com/sponsors/sindresorhus", | ||
"author": { | ||
"name": "Sindre Sorhus", | ||
"email": "sindresorhus@gmail.com", | ||
"url": "sindresorhus.com" | ||
"url": "https://sindresorhus.com" | ||
}, | ||
"type": "module", | ||
"exports": "./index.js", | ||
"engines": { | ||
"node": ">=8" | ||
"node": ">=16.17" | ||
}, | ||
@@ -42,11 +45,12 @@ "scripts": { | ||
"dependencies": { | ||
"@types/retry": "0.12.0", | ||
"@types/retry": "0.12.2", | ||
"is-network-error": "^1.0.0", | ||
"retry": "^0.13.1" | ||
}, | ||
"devDependencies": { | ||
"ava": "^2.4.0", | ||
"delay": "^4.1.0", | ||
"tsd": "^0.10.0", | ||
"xo": "^0.25.3" | ||
"ava": "^5.3.1", | ||
"delay": "^6.0.0", | ||
"tsd": "^0.28.1", | ||
"xo": "^0.56.0" | ||
} | ||
} |
102
readme.md
@@ -9,5 +9,5 @@ # p-retry | ||
```sh | ||
npm install p-retry | ||
``` | ||
$ npm install p-retry | ||
``` | ||
@@ -17,4 +17,4 @@ ## Usage | ||
```js | ||
const pRetry = require('p-retry'); | ||
const fetch = require('node-fetch'); | ||
import pRetry, {AbortError} from 'p-retry'; | ||
import fetch from 'node-fetch'; | ||
@@ -26,3 +26,3 @@ const run = async () => { | ||
if (response.status === 404) { | ||
throw new pRetry.AbortError(response.statusText); | ||
throw new AbortError(response.statusText); | ||
} | ||
@@ -33,5 +33,3 @@ | ||
(async () => { | ||
console.log(await pRetry(run, {retries: 5})); | ||
})(); | ||
console.log(await pRetry(run, {retries: 5})); | ||
``` | ||
@@ -45,6 +43,4 @@ | ||
It does not retry on most `TypeError`'s, 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) | ||
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) | ||
#### input | ||
@@ -69,2 +65,4 @@ | ||
```js | ||
import pRetry from 'p-retry'; | ||
const run = async () => { | ||
@@ -80,15 +78,13 @@ const response = await fetch('https://sindresorhus.com/unicorn'); | ||
(async () => { | ||
const result = await pRetry(run, { | ||
onFailedAttempt: error => { | ||
console.log(`Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left.`); | ||
// 1st request => Attempt 1 failed. There are 4 retries left. | ||
// 2nd request => Attempt 2 failed. There are 3 retries left. | ||
// … | ||
}, | ||
retries: 5 | ||
}); | ||
const result = await pRetry(run, { | ||
onFailedAttempt: error => { | ||
console.log(`Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left.`); | ||
// 1st request => Attempt 1 failed. There are 4 retries left. | ||
// 2nd request => Attempt 2 failed. There are 3 retries left. | ||
// … | ||
}, | ||
retries: 5 | ||
}); | ||
console.log(result); | ||
})(); | ||
console.log(result); | ||
``` | ||
@@ -99,14 +95,12 @@ | ||
```js | ||
const pRetry = require('p-retry'); | ||
const logger = require('./some-logger'); | ||
import pRetry from 'p-retry'; | ||
import logger from './some-logger'; | ||
const run = async () => { … }; | ||
(async () => { | ||
const result = await pRetry(run, { | ||
onFailedAttempt: async error => { | ||
await logger.log(error); | ||
} | ||
}); | ||
})(); | ||
const result = await pRetry(run, { | ||
onFailedAttempt: async error => { | ||
await logger.log(error); | ||
} | ||
}); | ||
``` | ||
@@ -116,5 +110,29 @@ | ||
### pRetry.AbortError(message) | ||
### pRetry.AbortError(error) | ||
##### signal | ||
Type: [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) | ||
You can abort retrying using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). | ||
```js | ||
import pRetry from 'p-retry'; | ||
const run = async () => { … }; | ||
const controller = new AbortController(); | ||
cancelButton.addEventListener('click', () => { | ||
controller.abort(new Error('User clicked cancel button')); | ||
}); | ||
try { | ||
await pRetry(run, {signal: controller.signal}); | ||
} catch (error) { | ||
console.log(error.message); | ||
//=> 'User clicked cancel button' | ||
} | ||
``` | ||
### AbortError(message) | ||
### AbortError(error) | ||
Abort retrying and reject the promise. | ||
@@ -126,3 +144,3 @@ | ||
Error message. | ||
An error message. | ||
@@ -133,3 +151,3 @@ ### error | ||
Custom error. | ||
A custom error. | ||
@@ -141,3 +159,3 @@ ## Tip | ||
```js | ||
const pRetry = require('p-retry'); | ||
import pRetry from 'p-retry'; | ||
@@ -148,9 +166,7 @@ const run = async emoji => { | ||
(async () => { | ||
// Without arguments | ||
await pRetry(run, {retries: 5}); | ||
// Without arguments | ||
await pRetry(run, {retries: 5}); | ||
// With arguments | ||
await pRetry(() => run('🦄'), {retries: 5}); | ||
})(); | ||
// With arguments | ||
await pRetry(() => run('🦄'), {retries: 5}); | ||
``` | ||
@@ -157,0 +173,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
11879
159
165
Yes
3
+ Addedis-network-error@^1.0.0
+ Added@types/retry@0.12.2(transitive)
+ Addedis-network-error@1.1.0(transitive)
- Removed@types/retry@0.12.0(transitive)
Updated@types/retry@0.12.2