You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
85 lines
2.0 KiB
85 lines
2.0 KiB
'use strict'; |
|
const retry = require('retry'); |
|
|
|
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 { |
|
constructor(message) { |
|
super(); |
|
|
|
if (message instanceof Error) { |
|
this.originalError = message; |
|
({message} = message); |
|
} else { |
|
this.originalError = new Error(message); |
|
this.originalError.stack = this.stack; |
|
} |
|
|
|
this.name = 'AbortError'; |
|
this.message = message; |
|
} |
|
} |
|
|
|
const decorateErrorWithCounts = (error, attemptNumber, options) => { |
|
// Minus 1 from attemptNumber because the first attempt does not count as a retry |
|
const retriesLeft = options.retries - (attemptNumber - 1); |
|
|
|
error.attemptNumber = attemptNumber; |
|
error.retriesLeft = retriesLeft; |
|
return error; |
|
}; |
|
|
|
const isNetworkError = errorMessage => networkErrorMsgs.includes(errorMessage); |
|
|
|
const pRetry = (input, options) => new Promise((resolve, reject) => { |
|
options = { |
|
onFailedAttempt: () => {}, |
|
retries: 10, |
|
...options |
|
}; |
|
|
|
const operation = retry.operation(options); |
|
|
|
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 (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); |
|
|
|
try { |
|
await options.onFailedAttempt(error); |
|
} catch (error) { |
|
reject(error); |
|
return; |
|
} |
|
|
|
if (!operation.retry(error)) { |
|
reject(operation.mainError()); |
|
} |
|
} |
|
} |
|
}); |
|
}); |
|
|
|
module.exports = pRetry; |
|
// TODO: remove this in the next major version |
|
module.exports.default = pRetry; |
|
|
|
module.exports.AbortError = AbortError;
|
|
|