86 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			86 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
'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;
 |