1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 |
- import retry from 'retry';
- import isNetworkError from 'is-network-error';
- export 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;
- };
- export default async function pRetry(input, options) {
- return new Promise((resolve, reject) => {
- options = {...options};
- options.onFailedAttempt ??= () => {};
- options.shouldRetry ??= () => true;
- options.retries ??= 10;
- const operation = retry.operation(options);
- const abortHandler = () => {
- operation.stop();
- reject(options.signal?.reason);
- };
- if (options.signal && !options.signal.aborted) {
- options.signal.addEventListener('abort', abortHandler, {once: true});
- }
- 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 {
- if (!(error instanceof Error)) {
- throw new TypeError(`Non-error was thrown: "${error}". You should only throw errors.`);
- }
- if (error instanceof AbortError) {
- throw error.originalError;
- }
- if (error instanceof TypeError && !isNetworkError(error)) {
- throw error;
- }
- decorateErrorWithCounts(error, attemptNumber, options);
- if (!(await options.shouldRetry(error))) {
- operation.stop();
- reject(error);
- }
- await options.onFailedAttempt(error);
- if (!operation.retry(error)) {
- throw operation.mainError();
- }
- } catch (finalError) {
- decorateErrorWithCounts(finalError, attemptNumber, options);
- cleanUp();
- reject(finalError);
- }
- }
- });
- });
- }
|