New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.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 4.0.0-beta.1 to 4.0.0-beta.2

16

CHANGELOG.md
# Changelog
### v4.0.0 (2018/7/14)
**Breaking Changes**
- Response caching is now built in, and enabled by default.
### v3.0.0 (2018/4/12)

@@ -16,3 +22,3 @@

* Resolves a problem where you were unable to use the signature `fetch(input, init)`.
- Resolves a problem where you were unable to use the signature `fetch(input, init)`.

@@ -23,3 +29,3 @@ ### v2.1.0 (2018/2/7)

* `responseType` can now be specified as a function. This is useful for backends that don't
- `responseType` can now be specified as a function. This is useful for backends that don't
respect the `Accept` header. "Enterprisey" backends frequently return text stack traces

@@ -32,3 +38,3 @@ for errors, as an example.

* `dedupeOptions` is now optional. The `responseType` is `"json"` by default, unless the
- `dedupeOptions` is now optional. The `responseType` is `"json"` by default, unless the
status code is 204, in which case it will be `"text"`.

@@ -40,4 +46,4 @@

* `init` is now optional
* A `requestKey` will be generated for you if it is omitted
- `init` is now optional
- A `requestKey` will be generated for you if it is omitted

@@ -44,0 +50,0 @@ ### v0.1.0 (2018/2/4)

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

var requestCache = {};
var responseCache = {};
var activeRequests = {};
var responseCacheStore = {};

@@ -28,17 +28,27 @@ function getRequestKey() {

function isRequestInFlight(requestKey) {
return Boolean(requestCache[requestKey]);
return Boolean(activeRequests[requestKey]);
}
function isResponseCached(requestKey) {
return Boolean(responseCache[requestKey]);
function clearActiveRequests() {
activeRequests = {};
}
function clearRequestCache() {
requestCache = {};
}
var responseCache = {
get: function get(requestKey) {
return responseCacheStore[requestKey];
},
set: function set(requestKey, res) {
responseCacheStore[requestKey] = res;
return responseCacheStore[requestKey];
},
has: function has(requestKey) {
// `undefined` is not a valid JSON key, so we can reliably use
// it to determine if the value exists or not.dfs
return typeof responseCacheStore[requestKey] !== 'undefined';
},
clear: function clear() {
responseCacheStore = {};
}
};
function clearResponseCache() {
responseCache = {};
}
// This loops through all of the handlers for the request and either

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

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

@@ -66,3 +74,3 @@ handler.reject(err);

// clear the handlers for the next request.
requestCache[requestKey] = null;
activeRequests[requestKey] = null;
}

@@ -134,9 +142,6 @@

if (appliedCachePolicy !== 'network-only') {
cachedResponse = responseCache[requestKeyToUse];
cachedResponse = responseCacheStore[requestKeyToUse];
if (cachedResponse) {
var resp = new Response(cachedResponse.body, cachedResponse);
resp.data = cachedResponse.data;
resp.fromCache = true;
return Promise.resolve(resp);
return Promise.resolve(cachedResponse);
} else if (cachePolicy === 'cache-only') {

@@ -150,7 +155,7 @@ var cacheError = new CacheMissError('Response for fetch request not found in cache.');

if (dedupe) {
if (!requestCache[requestKeyToUse]) {
requestCache[requestKeyToUse] = [];
if (!activeRequests[requestKeyToUse]) {
activeRequests[requestKeyToUse] = [];
}
var handlers = requestCache[requestKeyToUse];
var handlers = activeRequests[requestKeyToUse];
var requestInFlight = Boolean(handlers.length);

@@ -186,3 +191,3 @@ var requestHandler = {};

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

@@ -220,5 +225,4 @@ if (dedupe) {

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

@@ -225,0 +229,0 @@

@@ -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 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})});
!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 j={},P={};function K(){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,u=void 0===i?"":i;return[void 0===t?"":t,(void 0===r?"":r).toUpperCase(),n,u].join("||")}var t={get:function(e){return P[e]},set:function(e,t){return P[e]=t,P[e]},has:function(e){return void 0!==P[e]},clear:function(){P={}}};function E(e){var t=e.requestKey,r=e.res,o=e.err;(j[t]||[]).forEach(function(e){r?e.resolve(r):e.reject(o)}),j[t]=null}function w(){var e=Error.apply(this,arguments);e.name=this.name="CacheMissError",this.message=e.message,this.stack=e.stack}w.prototype=Object.create(Error.prototype,{constructor:{value:w,writable:!0,configurable:!0}}),e.getRequestKey=K,e.isRequestInFlight=function(e){return!!j[e]},e.clearActiveRequests=function(){j={}},e.responseCache=t,e.fetchDedupe=function(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},r=arguments[2],o=void 0,n=void 0;n=r?(o=r,t):t&&(t.responseType||t.dedupe||t.cachePolicy||t.requestKey)?(o=t,{}):(o={},t);var i=o.requestKey,u=o.responseType,s=void 0===u?"":u,c=o.dedupe,a=void 0===c||c,f=o.cachePolicy,d=(n.method||e.method||"").toUpperCase(),v=void 0;v=f||("GET"==d||"OPTIONS"==d||"HEAD"==d||""==d?"cache-first":"network-only");var h=i||K({url:e.url||e,method:n.method||e.method||"",body:n.body||e.body||""}),p=void 0;if("network-only"!==v){if(p=P[h])return Promise.resolve(p);if("cache-only"===f){var l=new w("Response for fetch request not found in cache.");return Promise.reject(l)}}var y=void 0;if(a){j[h]||(j[h]=[]);var m=j[h],q=!!m.length,b={};if(y=new Promise(function(e,t){b.resolve=e,b.reject=t}),m.push(b),q)return y}var g=fetch(e,n).then(function(t){var e=void 0;return e=s instanceof Function?s(t):s||(204===t.status?"text":"json"),t[e]().then(function(e){if(t.data=e,P[h]=t,!a)return t;E({requestKey:h,res:t})},function(){if(t.data=null,!a)return t;E({requestKey:h,res:t})})},function(e){if(!a)return Promise.reject(e);E({requestKey:h,err:e})});return a?y:g},Object.defineProperty(e,"__esModule",{value:!0})});

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

var requestCache = {};
var responseCache = {};
var activeRequests = {};
var responseCacheStore = {};

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

export function isRequestInFlight(requestKey) {
return Boolean(requestCache[requestKey]);
return Boolean(activeRequests[requestKey]);
}
export function isResponseCached(requestKey) {
return Boolean(responseCache[requestKey]);
export function clearActiveRequests() {
activeRequests = {};
}
export function clearRequestCache() {
requestCache = {};
}
export var responseCache = {
get: function get(requestKey) {
return responseCacheStore[requestKey];
},
set: function set(requestKey, res) {
responseCacheStore[requestKey] = res;
return responseCacheStore[requestKey];
},
has: function has(requestKey) {
// `undefined` is not a valid JSON key, so we can reliably use
// it to determine if the value exists or not.dfs
return typeof responseCacheStore[requestKey] !== 'undefined';
},
clear: function clear() {
responseCacheStore = {};
}
};
export function clearResponseCache() {
responseCache = {};
}
// This loops through all of the handlers for the request and either

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

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

@@ -59,3 +67,3 @@ handler.reject(err);

// clear the handlers for the next request.
requestCache[requestKey] = null;
activeRequests[requestKey] = null;
}

@@ -127,9 +135,6 @@

if (appliedCachePolicy !== 'network-only') {
cachedResponse = responseCache[requestKeyToUse];
cachedResponse = responseCacheStore[requestKeyToUse];
if (cachedResponse) {
var resp = new Response(cachedResponse.body, cachedResponse);
resp.data = cachedResponse.data;
resp.fromCache = true;
return Promise.resolve(resp);
return Promise.resolve(cachedResponse);
} else if (cachePolicy === 'cache-only') {

@@ -143,7 +148,7 @@ var cacheError = new CacheMissError('Response for fetch request not found in cache.');

if (dedupe) {
if (!requestCache[requestKeyToUse]) {
requestCache[requestKeyToUse] = [];
if (!activeRequests[requestKeyToUse]) {
activeRequests[requestKeyToUse] = [];
}
var handlers = requestCache[requestKeyToUse];
var handlers = activeRequests[requestKeyToUse];
var requestInFlight = Boolean(handlers.length);

@@ -179,3 +184,3 @@ var requestHandler = {};

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

@@ -182,0 +187,0 @@ if (dedupe) {

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

exports.isRequestInFlight = isRequestInFlight;
exports.isResponseCached = isResponseCached;
exports.clearRequestCache = clearRequestCache;
exports.clearResponseCache = clearResponseCache;
exports.clearActiveRequests = clearActiveRequests;
exports.fetchDedupe = fetchDedupe;
let requestCache = {};
let responseCache = {};
let activeRequests = {};
let responseCacheStore = {};

@@ -26,27 +24,38 @@ function getRequestKey({

function isRequestInFlight(requestKey) {
return Boolean(requestCache[requestKey]);
return Boolean(activeRequests[requestKey]);
}
function isResponseCached(requestKey) {
return Boolean(responseCache[requestKey]);
function clearActiveRequests() {
activeRequests = {};
}
function clearRequestCache() {
requestCache = {};
}
const responseCache = exports.responseCache = {
get(requestKey) {
return responseCacheStore[requestKey];
},
function clearResponseCache() {
responseCache = {};
}
set(requestKey, res) {
responseCacheStore[requestKey] = res;
return responseCacheStore[requestKey];
},
has(requestKey) {
// `undefined` is not a valid JSON key, so we can reliably use
// it to determine if the value exists or not.dfs
return typeof responseCacheStore[requestKey] !== 'undefined';
},
clear() {
responseCacheStore = {};
}
};
// This loops through all of the handlers for the request and either
// resolves or rejects them.
function resolveRequest({ requestKey, res, err }) {
const handlers = requestCache[requestKey] || [];
const handlers = activeRequests[requestKey] || [];
handlers.forEach(handler => {
if (res) {
const resToSend = new Response(res.body, res);
resToSend.data = res.data;
handler.resolve(resToSend);
handler.resolve(res);
} else {

@@ -59,3 +68,3 @@ handler.reject(err);

// clear the handlers for the next request.
requestCache[requestKey] = null;
activeRequests[requestKey] = null;
}

@@ -116,9 +125,6 @@

if (appliedCachePolicy !== 'network-only') {
cachedResponse = responseCache[requestKeyToUse];
cachedResponse = responseCacheStore[requestKeyToUse];
if (cachedResponse) {
var resp = new Response(cachedResponse.body, cachedResponse);
resp.data = cachedResponse.data;
resp.fromCache = true;
return Promise.resolve(resp);
return Promise.resolve(cachedResponse);
} else if (cachePolicy === 'cache-only') {

@@ -132,7 +138,7 @@ const cacheError = new CacheMissError(`Response for fetch request not found in cache.`);

if (dedupe) {
if (!requestCache[requestKeyToUse]) {
requestCache[requestKeyToUse] = [];
if (!activeRequests[requestKeyToUse]) {
activeRequests[requestKeyToUse] = [];
}
const handlers = requestCache[requestKeyToUse];
const handlers = activeRequests[requestKeyToUse];
const requestInFlight = Boolean(handlers.length);

@@ -168,3 +174,3 @@ const requestHandler = {};

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

@@ -171,0 +177,0 @@ if (dedupe) {

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

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

"isomorphic-fetch": "^2.2.1",
"jest": "^22.1.4",
"jest": "^23.6.0",
"rimraf": "^2.6.2",

@@ -64,0 +64,0 @@ "rollup": "^0.45.1",

@@ -5,14 +5,21 @@ # Fetch Dedupe

[![npm version](https://img.shields.io/npm/v/fetch-dedupe.svg)](https://www.npmjs.com/package/fetch-dedupe)
[![Test Coverage](https://codeclimate.com/github/jmeas/fetch-dedupe/badges/coverage.svg)](https://codeclimate.com/github/jmeas/fetch-dedupe)
[![Test Coverage](https://codeclimate.com/github/jamesplease/fetch-dedupe/badges/coverage.svg)](https://codeclimate.com/github/jamesplease/fetch-dedupe)
[![gzip size](http://img.badgesize.io/https://unpkg.com/fetch-dedupe/dist/fetch-dedupe.min.js?compression=gzip)](https://unpkg.com/fetch-dedupe/dist/fetch-dedupe.min.js)
A (very) thin wrapper around
A thin wrapper around
[`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
that prevents duplicate requests.
that implements request deduplication and response caching.
### Motivation
A common feature of libraries or frameworks that build abstractions around HTTP requests is that
they deduplicate requests that are exactly the same. This library extracts that functionality.
Making a single HTTP request is not difficult to do in JavaScript. However, complex web applications often make many
requests as the user navigates through the app.
Features such as request deduplication and response caching can often save the developer of apps like these from headache and
bugs.
`fetch-dedupe` is a wrapper around fetch that includes request deduplication and response caching for you, and it's a delight
to use.
### Installation

@@ -88,4 +95,9 @@

- `getRequestKey()`
- `responseCache`
- `.get()`
- `.set()`
- `.has()`
- `.clear()`
- `isRequestInFlight()`
- `clearRequestCache()`
- `clearActiveRequests()`

@@ -190,2 +202,22 @@ ##### `fetchDedupe( input [, init] [, dedupeOptions] )`

##### `responseCache.get( requestKey )`
Returns the cached response for `requestKey`. If the response does not exist, then `undefined`
will be returned instead.
##### `responseCache.set( requestKey, res )`
Call this to manually update the cached value of `requestKey` with `res`.
> Note: this is an advanced method, and you generally do not need to manually update the store.
##### `responseCache.has( 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.
##### `responseCache.clear()`
Remove all responses from the cache.
##### `isRequestInFlight( requestKey )`

@@ -213,23 +245,6 @@

##### `isResponseCached( requestKey )`
##### `clearActiveRequests()`
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.
Removes all of the tracked in-flight requests.
> 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()`
Wipe the cache of in-flight requests.
> Warning: this is **not** safe to use in application code. It is mostly useful for testing.
##### `clearResponseCache()`
Wipe the cache of responses.
> Warning: this is **not** safe to use in application code. It is mostly useful for testing.
### Guides

@@ -239,5 +254,4 @@

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.
Any time tbat 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.

@@ -269,9 +283,9 @@ Interactions with the cache can be controlled with the `cachePolicy` option. There are three possible

If the response cannot be interpreted as the `responseType`, then it will be set as `null`.
If the response cannot be parsed as the `responseType`, then it will be set as `null`.
There are two common situations for this:
- The backend returns an empty string when you specify `responseType: 'json'`
- The response body is an empty string when you specify `responseType: 'json'`
- The backend returns a raw text string when you specify `responseType: 'json'`
- The response body is a raw text string when you specify `responseType: 'json'` (i.e.; invalid JSON)

@@ -306,9 +320,9 @@ You can use the `responseType` option to have fine-grained control over the parsing of the

##### `res.bodyUsed` is `false` when the body has already been used
### Requirements
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`.
- a global [fetch()](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) method. If your browser does not support it, then we
recommend GitHub's [fetch polyfill](https://github.com/github/fetch).
As a workaround, when using `fetch-dedupe`, the body will always be used by the time you receive
the Response.
> Note: Node users can try and use [node-fetch](https://github.com/bitinn/node-fetch), although we aren't currently targeting Node support with this
> library.

@@ -319,3 +333,3 @@ ### Implementors

- [React Request](https://github.com/jmeas/react-request)
- [React Request](https://github.com/jamesplease/react-request)

@@ -322,0 +336,0 @@ Are you using it on a project? Add it to this list by opening a Pull Request

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