Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fetch-dedupe

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fetch-dedupe - npm Package Compare versions

Comparing version 3.0.0 to 4.0.0-beta.1

83

dist/fetch-dedupe.js

@@ -7,6 +7,4 @@ (function (global, factory) {

// This is a cache of in-flight requests. Each request key maps to an
// array of Promises. When the request resolves, each promise in the
// array is pushed to.
var requests = {};
var requestCache = {};
var responseCache = {};

@@ -30,9 +28,17 @@ function getRequestKey() {

function isRequestInFlight(requestKey) {
return Boolean(requests[requestKey]);
return Boolean(requestCache[requestKey]);
}
function isResponseCached(requestKey) {
return Boolean(responseCache[requestKey]);
}
function clearRequestCache() {
requests = {};
requestCache = {};
}
function clearResponseCache() {
responseCache = {};
}
// This loops through all of the handlers for the request and either

@@ -45,7 +51,9 @@ // resolves or rejects them.

var handlers = requests[requestKey] || [];
var handlers = requestCache[requestKey] || [];
handlers.forEach(function (handler) {
if (res) {
handler.resolve(res);
var resToSend = new Response(res.body, res);
resToSend.data = res.data;
handler.resolve(resToSend);
} else {

@@ -58,5 +66,20 @@ handler.reject(err);

// clear the handlers for the next request.
requests[requestKey] = null;
requestCache[requestKey] = null;
}
function CacheMissError() {
var err = Error.apply(this, arguments);
err.name = this.name = 'CacheMissError';
this.message = err.message;
this.stack = err.stack;
}
CacheMissError.prototype = Object.create(Error.prototype, {
constructor: {
value: CacheMissError,
writable: true,
configurable: true
}
});
function fetchDedupe(input) {

@@ -71,3 +94,3 @@ var init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

initToUse = init;
} else if (init.responseType) {
} else if (init && (init.responseType || init.dedupe || init.cachePolicy || init.requestKey)) {
opts = init;

@@ -85,6 +108,18 @@ initToUse = {};

_opts$dedupe = _opts.dedupe,
dedupe = _opts$dedupe === undefined ? true : _opts$dedupe;
dedupe = _opts$dedupe === undefined ? true : _opts$dedupe,
cachePolicy = _opts.cachePolicy;
var method = initToUse.method || input.method || '';
var upperCaseMethod = method.toUpperCase();
var appliedCachePolicy = void 0;
if (cachePolicy) {
appliedCachePolicy = cachePolicy;
} else {
var isReadRequest = upperCaseMethod === 'GET' || upperCaseMethod === 'OPTIONS' || upperCaseMethod === 'HEAD' || upperCaseMethod === '';
appliedCachePolicy = isReadRequest ? 'cache-first' : 'network-only';
}
// Build the default request key if one is not passed
var requestKeyToUse = requestKey || getRequestKey({

@@ -99,9 +134,24 @@ // If `input` is a request, then we use that URL

var cachedResponse = void 0;
if (appliedCachePolicy !== 'network-only') {
cachedResponse = responseCache[requestKeyToUse];
if (cachedResponse) {
var resp = new Response(cachedResponse.body, cachedResponse);
resp.data = cachedResponse.data;
resp.fromCache = true;
return Promise.resolve(resp);
} else if (cachePolicy === 'cache-only') {
var cacheError = new CacheMissError('Response for fetch request not found in cache.');
return Promise.reject(cacheError);
}
}
var proxyReq = void 0;
if (dedupe) {
if (!requests[requestKeyToUse]) {
requests[requestKeyToUse] = [];
if (!requestCache[requestKeyToUse]) {
requestCache[requestKeyToUse] = [];
}
var handlers = requests[requestKeyToUse];
var handlers = requestCache[requestKeyToUse];
var requestInFlight = Boolean(handlers.length);

@@ -137,2 +187,3 @@ var requestHandler = {};

res.data = data;
responseCache[requestKeyToUse] = res;

@@ -170,3 +221,5 @@ if (dedupe) {

exports.isRequestInFlight = isRequestInFlight;
exports.isResponseCached = isResponseCached;
exports.clearRequestCache = clearRequestCache;
exports.clearResponseCache = clearResponseCache;
exports.fetchDedupe = fetchDedupe;

@@ -173,0 +226,0 @@

2

dist/fetch-dedupe.min.js

@@ -1,1 +0,1 @@

!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.FetchDedupe={})}(this,function(e){"use strict";var l={};function y(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},t=e.url,n=e.method,r=e.responseType,o=void 0===r?"":r,u=e.body,i=void 0===u?"":u;return[void 0===t?"":t,(void 0===n?"":n).toUpperCase(),o,i].join("||")}function m(e){var t=e.requestKey,n=e.res,r=e.err;(l[t]||[]).forEach(function(e){n?e.resolve(n):e.reject(r)}),l[t]=null}e.getRequestKey=y,e.isRequestInFlight=function(e){return!!l[e]},e.clearRequestCache=function(){l={}},e.fetchDedupe=function(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},n=arguments[2],r=void 0,o=void 0;n?(r=n,o=t):t.responseType?(r=t,o={}):(r={},o=t);var u=r.responseType,i=void 0===u?"":u,d=r.dedupe,s=void 0===d||d,f=r.requestKey||y({url:e.url||e,method:o.method||e.method||"",body:o.body||e.body||""}),c=void 0;if(s){l[f]||(l[f]=[]);var v=l[f],a=!!v.length,p={};if(c=new Promise(function(e,t){p.resolve=e,p.reject=t}),v.push(p),a)return c}var h=fetch(e,o).then(function(t){var e=void 0;return e=i instanceof Function?i(t):i||(204===t.status?"text":"json"),t[e]().then(function(e){if(t.data=e,!s)return t;m({requestKey:f,res:t})},function(){if(t.data=null,!s)return t;m({requestKey:f,res:t})})},function(e){if(!s)return Promise.reject(e);m({requestKey:f,err:e})});return s?c:h},Object.defineProperty(e,"__esModule",{value:!0})});
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.FetchDedupe={})}(this,function(e){"use strict";var P={},R={};function w(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},t=e.url,r=e.method,o=e.responseType,n=void 0===o?"":o,i=e.body,s=void 0===i?"":i;return[void 0===t?"":t,(void 0===r?"":r).toUpperCase(),n,s].join("||")}function C(e){var t=e.requestKey,r=e.res,o=e.err;(P[t]||[]).forEach(function(e){if(r){var t=new Response(r.body,r);t.data=r.data,e.resolve(t)}else e.reject(o)}),P[t]=null}function K(){var e=Error.apply(this,arguments);e.name=this.name="CacheMissError",this.message=e.message,this.stack=e.stack}K.prototype=Object.create(Error.prototype,{constructor:{value:K,writable:!0,configurable:!0}}),e.getRequestKey=w,e.isRequestInFlight=function(e){return!!P[e]},e.isResponseCached=function(e){return!!R[e]},e.clearRequestCache=function(){P={}},e.clearResponseCache=function(){R={}},e.fetchDedupe=function(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},r=arguments[2],o=void 0,n=void 0;r?(o=r,n=t):t&&(t.responseType||t.dedupe||t.cachePolicy||t.requestKey)?(o=t,n={}):(o={},n=t);var i=o.requestKey,s=o.responseType,u=void 0===s?"":s,c=o.dedupe,a=void 0===c||c,d=o.cachePolicy,f=(n.method||e.method||"").toUpperCase(),h=void 0;h=d||("GET"===f||"OPTIONS"===f||"HEAD"===f||""===f?"cache-first":"network-only");var p=i||w({url:e.url||e,method:n.method||e.method||"",body:n.body||e.body||""}),v=void 0;if("network-only"!==h){if(v=R[p]){var l=new Response(v.body,v);return l.data=v.data,l.fromCache=!0,Promise.resolve(l)}if("cache-only"===d){var y=new K("Response for fetch request not found in cache.");return Promise.reject(y)}}var m=void 0;if(a){P[p]||(P[p]=[]);var b=P[p],q=!!b.length,j={};if(m=new Promise(function(e,t){j.resolve=e,j.reject=t}),b.push(j),q)return m}var g=fetch(e,n).then(function(t){var e=void 0;return e=u instanceof Function?u(t):u||(204===t.status?"text":"json"),t[e]().then(function(e){if(t.data=e,R[p]=t,!a)return t;C({requestKey:p,res:t})},function(){if(t.data=null,!a)return t;C({requestKey:p,res:t})})},function(e){if(!a)return Promise.reject(e);C({requestKey:p,err:e})});return a?m:g},Object.defineProperty(e,"__esModule",{value:!0})});

@@ -1,5 +0,3 @@

// This is a cache of in-flight requests. Each request key maps to an
// array of Promises. When the request resolves, each promise in the
// array is pushed to.
var requests = {};
var requestCache = {};
var responseCache = {};

@@ -23,9 +21,17 @@ export function getRequestKey() {

export function isRequestInFlight(requestKey) {
return Boolean(requests[requestKey]);
return Boolean(requestCache[requestKey]);
}
export function isResponseCached(requestKey) {
return Boolean(responseCache[requestKey]);
}
export function clearRequestCache() {
requests = {};
requestCache = {};
}
export function clearResponseCache() {
responseCache = {};
}
// This loops through all of the handlers for the request and either

@@ -38,7 +44,9 @@ // resolves or rejects them.

var handlers = requests[requestKey] || [];
var handlers = requestCache[requestKey] || [];
handlers.forEach(function (handler) {
if (res) {
handler.resolve(res);
var resToSend = new Response(res.body, res);
resToSend.data = res.data;
handler.resolve(resToSend);
} else {

@@ -51,5 +59,20 @@ handler.reject(err);

// clear the handlers for the next request.
requests[requestKey] = null;
requestCache[requestKey] = null;
}
function CacheMissError() {
var err = Error.apply(this, arguments);
err.name = this.name = 'CacheMissError';
this.message = err.message;
this.stack = err.stack;
}
CacheMissError.prototype = Object.create(Error.prototype, {
constructor: {
value: CacheMissError,
writable: true,
configurable: true
}
});
export function fetchDedupe(input) {

@@ -64,3 +87,3 @@ var init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

initToUse = init;
} else if (init.responseType) {
} else if (init && (init.responseType || init.dedupe || init.cachePolicy || init.requestKey)) {
opts = init;

@@ -78,6 +101,18 @@ initToUse = {};

_opts$dedupe = _opts.dedupe,
dedupe = _opts$dedupe === undefined ? true : _opts$dedupe;
dedupe = _opts$dedupe === undefined ? true : _opts$dedupe,
cachePolicy = _opts.cachePolicy;
var method = initToUse.method || input.method || '';
var upperCaseMethod = method.toUpperCase();
var appliedCachePolicy = void 0;
if (cachePolicy) {
appliedCachePolicy = cachePolicy;
} else {
var isReadRequest = upperCaseMethod === 'GET' || upperCaseMethod === 'OPTIONS' || upperCaseMethod === 'HEAD' || upperCaseMethod === '';
appliedCachePolicy = isReadRequest ? 'cache-first' : 'network-only';
}
// Build the default request key if one is not passed
var requestKeyToUse = requestKey || getRequestKey({

@@ -92,9 +127,24 @@ // If `input` is a request, then we use that URL

var cachedResponse = void 0;
if (appliedCachePolicy !== 'network-only') {
cachedResponse = responseCache[requestKeyToUse];
if (cachedResponse) {
var resp = new Response(cachedResponse.body, cachedResponse);
resp.data = cachedResponse.data;
resp.fromCache = true;
return Promise.resolve(resp);
} else if (cachePolicy === 'cache-only') {
var cacheError = new CacheMissError('Response for fetch request not found in cache.');
return Promise.reject(cacheError);
}
}
var proxyReq = void 0;
if (dedupe) {
if (!requests[requestKeyToUse]) {
requests[requestKeyToUse] = [];
if (!requestCache[requestKeyToUse]) {
requestCache[requestKeyToUse] = [];
}
var handlers = requests[requestKeyToUse];
var handlers = requestCache[requestKeyToUse];
var requestInFlight = Boolean(handlers.length);

@@ -130,2 +180,3 @@ var requestHandler = {};

res.data = data;
responseCache[requestKeyToUse] = res;

@@ -132,0 +183,0 @@ if (dedupe) {

@@ -6,8 +6,8 @@ 'use strict';

exports.isRequestInFlight = isRequestInFlight;
exports.isResponseCached = isResponseCached;
exports.clearRequestCache = clearRequestCache;
exports.clearResponseCache = clearResponseCache;
exports.fetchDedupe = fetchDedupe;
// This is a cache of in-flight requests. Each request key maps to an
// array of Promises. When the request resolves, each promise in the
// array is pushed to.
let requests = {};
let requestCache = {};
let responseCache = {};

@@ -26,17 +26,27 @@ function getRequestKey({

function isRequestInFlight(requestKey) {
return Boolean(requests[requestKey]);
return Boolean(requestCache[requestKey]);
}
function isResponseCached(requestKey) {
return Boolean(responseCache[requestKey]);
}
function clearRequestCache() {
requests = {};
requestCache = {};
}
function clearResponseCache() {
responseCache = {};
}
// This loops through all of the handlers for the request and either
// resolves or rejects them.
function resolveRequest({ requestKey, res, err }) {
const handlers = requests[requestKey] || [];
const handlers = requestCache[requestKey] || [];
handlers.forEach(handler => {
if (res) {
handler.resolve(res);
const resToSend = new Response(res.body, res);
resToSend.data = res.data;
handler.resolve(resToSend);
} else {

@@ -49,5 +59,20 @@ handler.reject(err);

// clear the handlers for the next request.
requests[requestKey] = null;
requestCache[requestKey] = null;
}
function CacheMissError() {
var err = Error.apply(this, arguments);
err.name = this.name = 'CacheMissError';
this.message = err.message;
this.stack = err.stack;
}
CacheMissError.prototype = Object.create(Error.prototype, {
constructor: {
value: CacheMissError,
writable: true,
configurable: true
}
});
function fetchDedupe(input, init = {}, dedupeOptions) {

@@ -58,3 +83,3 @@ let opts, initToUse;

initToUse = init;
} else if (init.responseType) {
} else if (init && (init.responseType || init.dedupe || init.cachePolicy || init.requestKey)) {
opts = init;

@@ -67,4 +92,15 @@ initToUse = {};

const { requestKey, responseType = '', dedupe = true } = opts;
const { requestKey, responseType = '', dedupe = true, cachePolicy } = opts;
const method = initToUse.method || input.method || '';
const upperCaseMethod = method.toUpperCase();
let appliedCachePolicy;
if (cachePolicy) {
appliedCachePolicy = cachePolicy;
} else {
const isReadRequest = upperCaseMethod === 'GET' || upperCaseMethod === 'OPTIONS' || upperCaseMethod === 'HEAD' || upperCaseMethod === '';
appliedCachePolicy = isReadRequest ? 'cache-first' : 'network-only';
}
// Build the default request key if one is not passed

@@ -80,9 +116,24 @@ let requestKeyToUse = requestKey || getRequestKey({

let cachedResponse;
if (appliedCachePolicy !== 'network-only') {
cachedResponse = responseCache[requestKeyToUse];
if (cachedResponse) {
var resp = new Response(cachedResponse.body, cachedResponse);
resp.data = cachedResponse.data;
resp.fromCache = true;
return Promise.resolve(resp);
} else if (cachePolicy === 'cache-only') {
const cacheError = new CacheMissError(`Response for fetch request not found in cache.`);
return Promise.reject(cacheError);
}
}
let proxyReq;
if (dedupe) {
if (!requests[requestKeyToUse]) {
requests[requestKeyToUse] = [];
if (!requestCache[requestKeyToUse]) {
requestCache[requestKeyToUse] = [];
}
const handlers = requests[requestKeyToUse];
const handlers = requestCache[requestKeyToUse];
const requestInFlight = Boolean(handlers.length);

@@ -118,2 +169,3 @@ const requestHandler = {};

res.data = data;
responseCache[requestKeyToUse] = res;

@@ -120,0 +172,0 @@ if (dedupe) {

{
"name": "fetch-dedupe",
"version": "3.0.0",
"version": "4.0.0-beta.1",
"description": "A thin wrapper around fetch that prevents duplicate requests.",

@@ -20,3 +20,3 @@ "main": "lib/index.js",

"type": "git",
"url": "https://github.com/jmeas/fetch-dedupe.git"
"url": "https://github.com/jamesplease/fetch-dedupe.git"
},

@@ -44,3 +44,3 @@ "keywords": [

"bugs": {
"url": "https://github.com/jmeas/fetch-dedupe/issues"
"url": "https://github.com/jamesplease/fetch-dedupe/issues"
},

@@ -47,0 +47,0 @@ "files": [

@@ -114,2 +114,5 @@ # Fetch Dedupe

* `cachePolicy` *(String)*: Determines interactions with the cache. Valid options are `"cache-first"`, `"cache-only"`,
and `"network-only"`. For more, refer to the section on Caching.
Given the two possible value types of `input`, optional second argument, there are a way few ways that you can

@@ -208,2 +211,11 @@ call `fetchDedupe`. Let's run through valid calls to `fetchDedupe`:

##### `isResponseCached( requestKey )`
Pass in a `requestKey` to see if there is a cache entry for the request. This can be used
to determine if a call to `fetchDedupe` will hit the cache or not.
> Now: We **strongly** recommend that you manually pass in `requestKey` to `fetchDedupe`
if you intend to use this method. In other words, _do not_ rely on being able to
reliably reproduce the request key that is created when a `requestKey` is not passed in.
##### `clearRequestCache()`

@@ -215,2 +227,37 @@

##### `clearResponseCache()`
Wipe the cache of responses.
> Warning: this is **not** safe to use in application code. It is mostly useful for testing.
### Guides
##### Caching
The way the cache works is like this: any time a response from the server is received, it will be
cached using the request's request key. Subsequent requests are matched with existing cached server
responses using their request key.
Interactions with the cache can be controlled with the `cachePolicy` option. There are three possible
values:
**`cache-first`**
This is the default behavior.
Requests will first look at the cache to see if a response for the same request key exists. If a response is
found, then it will be returned, and no network request will be made.
If no response exists in the cache, then a network request will be made.
**`network-only`**
The cache is ignored, and a network request is always made.
**`cache-only`**
If a response exists in the cache, then it will be returned. If no response
exists in the cache, then an error will be passed into the render prop function.
### FAQ & Troubleshooting

@@ -247,2 +294,19 @@

##### Is the data duplicated?
Although you receive a new `Response` object with every call to `fetch-dedupe`, the body will be read,
so the response's body stream will be empty. In addition, the `data` property between every
`response` is shared. Accordingly, the data returned by the server is never duplicated.
This is an optimization that allows `fetch-dedupe` to be used in applications that fetch
large payloads.
##### `res.bodyUsed` is `false` when the body has already been used
As of Feb 2018, there is a bug in several browsers and `node-fetch`, where the value of `bodyUsed`
will be `false` when it should, in fact, be `true`.
As a workaround, when using `fetch-dedupe`, the body will always be used by the time you receive
the Response.
### Implementors

@@ -249,0 +313,0 @@

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