Socket
Socket
Sign inDemoInstall

fetch-retry

Package Overview
Dependencies
Maintainers
1
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fetch-retry - npm Package Compare versions

Comparing version 2.2.3 to 3.0.0

6

index.d.ts

@@ -20,4 +20,4 @@ declare module 'fetch-retry' {

function fetch(url: String, options?: IRequestInitWithRetry): Promise<Response>;
export = fetch;
}
function fetchBuilder(fetch: (url: String, options?: RequestInit) => Promise<Response>): ((url: String, options?: IRequestInitWithRetry) => Promise<Response>);
export = fetchBuilder;
}
'use strict';
require('isomorphic-fetch');
require('es6-promise').polyfill();
module.exports = function(url, options) {
var retries = 3;
var retryDelay = 1000;
var retryOn = [];
module.exports = function (fetch) {
if (typeof fetch !== 'function') {
throw new ArgumentError('fetch must be a function');
}
if (options && options.retries !== undefined) {
if (isPositiveInteger(options.retries)) {
retries = options.retries;
} else {
throw new ArgumentError('retries must be a positive integer');
return function fetchRetry(url, options) {
var retries = 3;
var retryDelay = 1000;
var retryOn = [];
if (options && options.retries !== undefined) {
if (isPositiveInteger(options.retries)) {
retries = options.retries;
} else {
throw new ArgumentError('retries must be a positive integer');
}
}
}
if (options && options.retryDelay !== undefined) {
if (isPositiveInteger(options.retryDelay) || (typeof options.retryDelay === 'function')) {
retryDelay = options.retryDelay;
} else {
throw new ArgumentError('retryDelay must be a positive integer or a function returning a positive integer');
if (options && options.retryDelay !== undefined) {
if (isPositiveInteger(options.retryDelay) || (typeof options.retryDelay === 'function')) {
retryDelay = options.retryDelay;
} else {
throw new ArgumentError('retryDelay must be a positive integer or a function returning a positive integer');
}
}
}
if (options && options.retryOn) {
if (Array.isArray(options.retryOn) || (typeof options.retryOn === 'function')) {
retryOn = options.retryOn;
} else {
throw new ArgumentError('retryOn property expects an array or function');
if (options && options.retryOn) {
if (Array.isArray(options.retryOn) || (typeof options.retryOn === 'function')) {
retryOn = options.retryOn;
} else {
throw new ArgumentError('retryOn property expects an array or function');
}
}
}
return new Promise(function(resolve, reject) {
var wrappedFetch = function(attempt) {
fetch(url, options)
.then(function(response) {
if (Array.isArray(retryOn) && retryOn.indexOf(response.status) === -1) {
resolve(response);
} else if (typeof retryOn === 'function') {
if (retryOn(attempt, null, response)) {
retry(attempt, null, response);
} else {
return new Promise(function (resolve, reject) {
var wrappedFetch = function (attempt) {
fetch(url, options)
.then(function (response) {
if (Array.isArray(retryOn) && retryOn.indexOf(response.status) === -1) {
resolve(response);
}
} else {
if (attempt < retries) {
retry(attempt, null, response);
} else if (typeof retryOn === 'function') {
if (retryOn(attempt, null, response)) {
retry(attempt, null, response);
} else {
resolve(response);
}
} else {
resolve(response);
if (attempt < retries) {
retry(attempt, null, response);
} else {
resolve(response);
}
}
}
})
.catch(function(error) {
if (typeof retryOn === 'function') {
if (retryOn(attempt, error, null)) {
})
.catch(function (error) {
if (typeof retryOn === 'function') {
if (retryOn(attempt, error, null)) {
retry(attempt, error, null);
} else {
reject(error);
}
} else if (attempt < retries) {
retry(attempt, error, null);

@@ -61,20 +70,16 @@ } else {

}
} else if (attempt < retries) {
retry(attempt, error, null);
} else {
reject(error);
}
});
};
});
};
function retry(attempt, error, response) {
var delay = (typeof retryDelay === 'function') ?
retryDelay(attempt, error, response) : retryDelay;
setTimeout(function() {
wrappedFetch(++attempt);
}, delay);
}
function retry(attempt, error, response) {
var delay = (typeof retryDelay === 'function') ?
retryDelay(attempt, error, response) : retryDelay;
setTimeout(function () {
wrappedFetch(++attempt);
}, delay);
}
wrappedFetch(0);
});
wrappedFetch(0);
});
};
};

@@ -81,0 +86,0 @@

{
"name": "fetch-retry",
"version": "2.2.3",
"version": "3.0.0",
"description": "Adds retry functionality to the Fetch API",

@@ -22,4 +22,3 @@ "repository": "https://github.com/jonbern/fetch-retry.git",

"dependencies": {
"es6-promise": "^4.2.8",
"isomorphic-fetch": "^2.2.1"
"es6-promise": "^4.2.8"
},

@@ -33,7 +32,7 @@ "devDependencies": {

"express": "^4.17.1",
"isomorphic-fetch": "^2.2.1",
"mocha": "^5.2.0",
"nyc": "^14.1.1",
"proxyquire": "^2.1.3",
"sinon": "^6.3.5"
}
}

@@ -5,3 +5,3 @@ # fetch-retry

It wraps [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) and retries requests that fail due to network issues. It can also be configured to retry requests on specific HTTP status codes.
It wraps any `Fetch` API package (eg: [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch), [cross-fetch](https://github.com/lquixada/cross-fetch), [isomorphic-unfetch](https://github.com/developit/unfetch) and etc.) and retries requests that fail due to network issues. It can also be configured to retry requests on specific HTTP status codes.

@@ -22,3 +22,4 @@ [![Build Status](https://travis-ci.org/jonbern/fetch-retry.svg?branch=master)](https://travis-ci.org/jonbern/fetch-retry)

```javascript
var fetch = require('fetch-retry');
var originalFetch = require('isomorphic-fetch');
var fetch = require('fetch-retry')(originalFetch);
```

@@ -25,0 +26,0 @@

@@ -7,3 +7,4 @@ 'use strict';

const childProcess = require('child_process');
const fetchRetry = require('../../');
const fetch = require('isomorphic-fetch');
const fetchRetry = require('../../')(fetch);

@@ -10,0 +11,0 @@ describe('fetch-retry integration tests', () => {

'use strict';
require('isomorphic-fetch');
var proxyquire = require('proxyquire').noPreserveCache();
var fetchBuilder = require('../../');
var sinon = require('sinon');

@@ -8,4 +7,21 @@ var expect = require('expectations');

describe('fetch-retry', function() {
describe('fetchBuilder', function () {
it('should accept fetch function as argument', function () {
expect(function () {
fetchBuilder();
}).toThrow({
name: 'ArgumentError',
message: 'fetch must be a function'
});
});
it('should return fetchRetry function', function () {
expect(typeof fetchBuilder(function () { })).toBe('function');
});
});
describe('fetch-retry', function () {
var fetch;
var fetchRetry;

@@ -24,3 +40,3 @@

beforeEach(function() {
beforeEach(function () {
delay = 1000;

@@ -30,7 +46,7 @@ clock = sinon.useFakeTimers();

afterEach(function() {
afterEach(function () {
clock.restore();
});
beforeEach(function() {
beforeEach(function () {
deferred1 = defer();

@@ -41,3 +57,3 @@ deferred2 = defer();

global.fetch = sinon.stub();
fetch = sinon.stub();
fetch.onCall(0).returns(deferred1.promise);

@@ -48,18 +64,14 @@ fetch.onCall(1).returns(deferred2.promise);

var stubs = {
'isomorphic-fetch': fetch
};
fetchRetry = proxyquire('../../', stubs);
fetchRetry = fetchBuilder(fetch);
});
describe('#url', function() {
describe('#url', function () {
var expectedUrl = 'http://some-url.com';
beforeEach(function() {
beforeEach(function () {
fetchRetry(expectedUrl);
});
it('passes #url to fetch', function() {
it('passes #url to fetch', function () {
expect(fetch.getCall(0).args[0]).toBe(expectedUrl);

@@ -70,9 +82,9 @@ });

describe('#options', function() {
describe('#options', function () {
describe('when #options is provided', function() {
describe('when #options is provided', function () {
var options;
beforeEach(function() {
beforeEach(function () {
options = {

@@ -86,3 +98,3 @@ retries: 3,

it('passes options to fetch', function() {
it('passes options to fetch', function () {
expect(fetch.getCall(0).args[1]).toEqual(options);

@@ -94,3 +106,3 @@ });

it('throws exception', () => {
expect(function() {
expect(function () {
options.retryOn = 503;

@@ -108,11 +120,11 @@ fetchRetry('http://someUrl', options);

describe('when #options is undefined or null', function() {
describe('when #options is undefined or null', function () {
[undefined, null].forEach(function(testCase) {
[undefined, null].forEach(function (testCase) {
beforeEach(function() {
beforeEach(function () {
fetchRetry('http://someUrl', testCase);
});
it('does not pass through options to fetch', function() {
it('does not pass through options to fetch', function () {
expect(fetch.getCall(0).args[1]).toEqual(undefined);

@@ -127,7 +139,7 @@ });

describe('#options.retries', function() {
describe('#options.retries', function () {
describe('when #options.retries=3 (default)', function() {
describe('when #options.retries=3 (default)', function () {
beforeEach(function() {
beforeEach(function () {
thenCallback = sinon.spy();

@@ -141,15 +153,15 @@ catchCallback = sinon.spy();

describe('when first call is a success', function() {
describe('when first call is a success', function () {
beforeEach(function() {
beforeEach(function () {
deferred1.resolve({ status: 200 });
});
describe('when resolved', function() {
describe('when resolved', function () {
it('invokes the then callback', function() {
it('invokes the then callback', function () {
expect(thenCallback.called).toBe(true);
});
it('calls fetch once', function() {
it('calls fetch once', function () {
expect(fetch.callCount).toBe(1);

@@ -162,11 +174,11 @@ });

describe('when first call is a failure', function() {
describe('when first call is a failure', function () {
beforeEach(function() {
beforeEach(function () {
deferred1.reject();
});
describe('when second call is a success', function() {
describe('when second call is a success', function () {
beforeEach(function() {
beforeEach(function () {
clock.tick(delay);

@@ -176,9 +188,9 @@ deferred2.resolve({ status: 200 });

describe('when resolved', function() {
describe('when resolved', function () {
it('invokes the then callback', function() {
it('invokes the then callback', function () {
expect(thenCallback.called).toBe(true);
});
it('calls fetch twice', function() {
it('calls fetch twice', function () {
expect(fetch.callCount).toBe(2);

@@ -191,5 +203,5 @@ });

describe('when second call is a failure', function() {
describe('when second call is a failure', function () {
beforeEach(function() {
beforeEach(function () {
deferred2.reject();

@@ -199,5 +211,5 @@ clock.tick(delay);

describe('when third call is a success', function() {
describe('when third call is a success', function () {
beforeEach(function() {
beforeEach(function () {
deferred3.resolve({ status: 200 });

@@ -207,9 +219,9 @@ clock.tick(delay);

describe('when resolved', function() {
describe('when resolved', function () {
it('invokes the then callback', function() {
it('invokes the then callback', function () {
expect(thenCallback.called).toBe(true);
});
it('calls fetch three times', function() {
it('calls fetch three times', function () {
expect(fetch.callCount).toBe(3);

@@ -222,5 +234,5 @@ });

describe('when third call is a failure', function() {
describe('when third call is a failure', function () {
beforeEach(function() {
beforeEach(function () {
deferred3.reject();

@@ -230,5 +242,5 @@ clock.tick(delay);

describe('when fourth call is a success', function() {
describe('when fourth call is a success', function () {
beforeEach(function() {
beforeEach(function () {
deferred4.resolve({ status: 200 });

@@ -238,9 +250,9 @@ clock.tick(delay);

describe('when resolved', function() {
describe('when resolved', function () {
it('invokes the then callback', function() {
it('invokes the then callback', function () {
expect(thenCallback.called).toBe(true);
});
it('calls fetch four times', function() {
it('calls fetch four times', function () {
expect(fetch.callCount).toBe(4);

@@ -253,5 +265,5 @@ });

describe('when fourth call is a failure', function() {
describe('when fourth call is a failure', function () {
beforeEach(function() {
beforeEach(function () {
deferred4.reject();

@@ -261,9 +273,9 @@ clock.tick(delay);

describe('when rejected', function() {
describe('when rejected', function () {
it('invokes the catch callback', function() {
it('invokes the catch callback', function () {
expect(catchCallback.called).toBe(true);
});
it('does not call fetch again', function() {
it('does not call fetch again', function () {
expect(fetch.callCount).toBe(4);

@@ -284,5 +296,5 @@ });

describe('when #options.retries=1', function() {
describe('when #options.retries=1', function () {
beforeEach(function() {
beforeEach(function () {
thenCallback = sinon.spy();

@@ -296,15 +308,15 @@ catchCallback = sinon.spy();

describe('when first call is a success', function() {
describe('when first call is a success', function () {
beforeEach(function() {
beforeEach(function () {
deferred1.resolve({ status: 200 });
});
describe('when resolved', function() {
describe('when resolved', function () {
it('invokes the then callback', function() {
it('invokes the then callback', function () {
expect(thenCallback.called).toBe(true);
});
it('calls fetch once', function() {
it('calls fetch once', function () {
expect(fetch.callCount).toBe(1);

@@ -317,5 +329,5 @@ });

describe('when first call is a failure', function() {
describe('when first call is a failure', function () {
beforeEach(function() {
beforeEach(function () {
deferred1.reject();

@@ -325,5 +337,5 @@ clock.tick(delay);

describe('when second call is a success', function() {
describe('when second call is a success', function () {
beforeEach(function() {
beforeEach(function () {
deferred2.resolve({ status: 200 });

@@ -333,9 +345,9 @@ clock.tick(delay);

describe('when resolved', function() {
describe('when resolved', function () {
it('invokes the then callback', function() {
it('invokes the then callback', function () {
expect(thenCallback.called).toBe(true);
});
it('calls fetch twice', function() {
it('calls fetch twice', function () {
expect(fetch.callCount).toBe(2);

@@ -348,5 +360,5 @@ });

describe('when second call is a failure', function() {
describe('when second call is a failure', function () {
beforeEach(function() {
beforeEach(function () {
deferred2.reject();

@@ -356,9 +368,9 @@ clock.tick(delay);

describe('when rejected', function() {
describe('when rejected', function () {
it('invokes the catch callback', function() {
it('invokes the catch callback', function () {
expect(catchCallback.called).toBe(true);
});
it('does not call fetch again', function() {
it('does not call fetch again', function () {
expect(fetch.callCount).toBe(2);

@@ -375,5 +387,5 @@ });

describe('when #options.retries=0', function() {
describe('when #options.retries=0', function () {
beforeEach(function() {
beforeEach(function () {
thenCallback = sinon.spy();

@@ -387,15 +399,15 @@ catchCallback = sinon.spy();

describe('when first call is a success', function() {
describe('when first call is a success', function () {
beforeEach(function() {
beforeEach(function () {
deferred1.resolve({ status: 200 });
});
describe('when resolved', function() {
describe('when resolved', function () {
it('invokes the then callback', function() {
it('invokes the then callback', function () {
expect(thenCallback.called).toBe(true);
});
it('calls fetch once', function() {
it('calls fetch once', function () {
expect(fetch.callCount).toBe(1);

@@ -408,5 +420,5 @@ });

describe('when first call is a failure', function() {
describe('when first call is a failure', function () {
beforeEach(function() {
beforeEach(function () {
deferred1.reject();

@@ -417,3 +429,3 @@ });

it('invokes the catch callback', function() {
it('invokes the catch callback', function () {
expect(catchCallback.called).toBe(true);

@@ -448,5 +460,5 @@ });

describe('#options.retryDelay', function() {
describe('#options.retryDelay', function () {
describe('when #options.retryDelay is a number', function() {
describe('when #options.retryDelay is a number', function () {

@@ -456,3 +468,3 @@ var options;

beforeEach(function() {
beforeEach(function () {
retryDelay = 5000;

@@ -469,15 +481,15 @@ options = {

describe('when first call is unsuccessful', function() {
describe('when first call is unsuccessful', function () {
beforeEach(function() {
beforeEach(function () {
deferred1.reject();
});
describe('after specified time', function() {
describe('after specified time', function () {
beforeEach(function() {
beforeEach(function () {
clock.tick(retryDelay);
});
it('invokes fetch again', function() {
it('invokes fetch again', function () {
expect(fetch.callCount).toBe(2);

@@ -488,9 +500,9 @@ });

describe('after less than specified time', function() {
describe('after less than specified time', function () {
beforeEach(function() {
beforeEach(function () {
clock.tick(1000);
});
it('does not invoke fetch again', function() {
it('does not invoke fetch again', function () {
expect(fetch.callCount).toBe(1);

@@ -505,3 +517,3 @@ });

describe('when #options.retryDelay is 0', function() {
describe('when #options.retryDelay is 0', function () {

@@ -511,3 +523,3 @@ var options;

beforeEach(function() {
beforeEach(function () {
retryDelay = 0;

@@ -524,15 +536,15 @@ options = {

describe('when first call is unsuccessful', function() {
describe('when first call is unsuccessful', function () {
beforeEach(function() {
beforeEach(function () {
deferred1.reject();
});
describe('after one event loop tick', function() {
describe('after one event loop tick', function () {
beforeEach(function() {
beforeEach(function () {
clock.tick(0);
});
it('invokes fetch again', function() {
it('invokes fetch again', function () {
expect(fetch.callCount).toBe(2);

@@ -565,3 +577,3 @@ });

describe('when #options.retryDelay is a function', function() {
describe('when #options.retryDelay is a function', function () {

@@ -571,3 +583,3 @@ var options;

beforeEach(function() {
beforeEach(function () {
retryDelay = sinon.stub().returns(5000);

@@ -584,11 +596,11 @@ options = {

describe('when first call is unsuccessful', function() {
describe('when first call is unsuccessful', function () {
beforeEach(function() {
beforeEach(function () {
deferred1.reject(new Error('first error'));
});
describe('when the second call is a success', function() {
describe('when the second call is a success', function () {
beforeEach(function() {
beforeEach(function () {
deferred2.resolve({ status: 200 });

@@ -598,3 +610,3 @@ clock.tick(5000);

it('invokes the retryDelay function', function() {
it('invokes the retryDelay function', function () {
expect(retryDelay.called).toBe(true);

@@ -608,5 +620,5 @@ expect(retryDelay.lastCall.args[0]).toEqual(0);

describe('when second call is a failure', function() {
describe('when second call is a failure', function () {
beforeEach(function() {
beforeEach(function () {
deferred2.reject(new Error('second error'));

@@ -616,5 +628,5 @@ clock.tick(5000);

describe('when the third call is a success', function() {
describe('when the third call is a success', function () {
beforeEach(function() {
beforeEach(function () {
deferred3.resolve({ status: 200 });

@@ -624,3 +636,3 @@ clock.tick(5000);

it('invokes the retryDelay function again', function() {
it('invokes the retryDelay function again', function () {
expect(retryDelay.callCount).toBe(2);

@@ -648,3 +660,3 @@ expect(retryDelay.lastCall.args[0]).toEqual(1);

beforeEach(function() {
beforeEach(function () {
retryOn = [503, 404];

@@ -666,3 +678,3 @@ options = {

beforeEach(() => {
deferred1.resolve({status: 503});
deferred1.resolve({ status: 503 });
});

@@ -683,3 +695,3 @@

beforeEach(() => {
deferred2.resolve({status: 200});
deferred2.resolve({ status: 200 });
});

@@ -689,7 +701,7 @@

it('invokes the then callback', function() {
it('invokes the then callback', function () {
expect(thenCallback.called).toBe(true);
});
it('has called fetch twice', function() {
it('has called fetch twice', function () {
expect(fetch.callCount).toBe(2);

@@ -708,3 +720,3 @@ });

describe('when #options.retryOn is a function', function() {
describe('when #options.retryOn is a function', function () {

@@ -714,3 +726,3 @@ var options;

beforeEach(function() {
beforeEach(function () {
retryOn = sinon.stub();

@@ -729,7 +741,7 @@ options = {

describe('when first attempt is rejected due to network error', function() {
describe('when first attempt is rejected due to network error', function () {
describe('when #retryOn() returns true', () => {
beforeEach(function() {
beforeEach(function () {
retryOn.returns(true);

@@ -739,5 +751,5 @@ deferred1.reject(new Error('first error'));

describe('when rejected', function() {
describe('when rejected', function () {
it('invokes #retryOn function with an error', function() {
it('invokes #retryOn function with an error', function () {
expect(retryOn.called).toBe(true);

@@ -750,15 +762,15 @@ expect(retryOn.lastCall.args.length).toBe(3);

describe('after specified time', function() {
describe('after specified time', function () {
beforeEach(function() {
beforeEach(function () {
clock.tick(delay);
});
it('invokes fetch again', function() {
it('invokes fetch again', function () {
expect(fetch.callCount).toBe(2);
});
describe('when the second call is unsuccessful', function() {
describe('when the second call is unsuccessful', function () {
beforeEach(function() {
beforeEach(function () {
deferred2.reject(new Error('second error'));

@@ -768,5 +780,5 @@ clock.tick(delay);

describe('when rejected', function() {
describe('when rejected', function () {
it('invokes the #retryOn function twice', function() {
it('invokes the #retryOn function twice', function () {
expect(retryOn.callCount).toBe(2);

@@ -788,3 +800,3 @@ expect(retryOn.lastCall.args[0]).toBe(1);

beforeEach(function() {
beforeEach(function () {
retryOn.returns(false);

@@ -794,5 +806,5 @@ deferred1.reject(new Error('first error'));

describe('when rejected', function() {
describe('when rejected', function () {
it('invokes #retryOn function with an error', function() {
it('invokes #retryOn function with an error', function () {
expect(retryOn.called).toBe(true);

@@ -805,13 +817,13 @@ expect(retryOn.lastCall.args.length).toBe(3);

describe('after specified time', function() {
describe('after specified time', function () {
beforeEach(function() {
beforeEach(function () {
clock.tick(delay);
});
it('invokes the catch callback', function() {
it('invokes the catch callback', function () {
expect(catchCallback.called).toBe(true);
});
it('does not call fetch again', function() {
it('does not call fetch again', function () {
expect(fetch.callCount).toBe(1);

@@ -828,7 +840,7 @@ });

describe('when first attempt is resolved', function() {
describe('when first attempt is resolved', function () {
describe('when #retryOn() returns true', () => {
beforeEach(function() {
beforeEach(function () {
retryOn.returns(true);

@@ -840,7 +852,7 @@ deferred1.resolve({ status: 200 });

beforeEach(function() {
beforeEach(function () {
clock.tick(delay);
});
it('calls fetch again', function() {
it('calls fetch again', function () {
expect(fetch.callCount).toBe(2);

@@ -851,3 +863,3 @@ });

beforeEach(function() {
beforeEach(function () {
deferred2.resolve({ status: 200 });

@@ -857,3 +869,3 @@ clock.tick(delay);

it('invokes the #retryOn function with the response', function() {
it('invokes the #retryOn function with the response', function () {
expect(retryOn.called).toBe(true);

@@ -874,3 +886,3 @@ expect(retryOn.lastCall.args.length).toBe(3);

beforeEach(function() {
beforeEach(function () {
retryOn.returns(false);

@@ -882,7 +894,7 @@ deferred1.resolve({ status: 502 });

it('invokes the then callback', function() {
it('invokes the then callback', function () {
expect(thenCallback.called).toBe(true);
});
it('calls fetch 1 time only', function() {
it('calls fetch 1 time only', function () {
expect(fetch.callCount).toBe(1);

@@ -899,3 +911,3 @@ });

describe('when #options.retryOn is not an array or function', function() {
describe('when #options.retryOn is not an array or function', function () {

@@ -907,3 +919,3 @@ var options;

it('throws exception', () => {
expect(function() {
expect(function () {
options.retryOn = 503;

@@ -927,3 +939,3 @@ fetchRetry('http://someUrl', options);

var resolve, reject;
var promise = new Promise(function() {
var promise = new Promise(function () {
resolve = arguments[0];

@@ -930,0 +942,0 @@ reject = arguments[1];

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc