fetch-dedupe
Advanced tools
Comparing version 4.0.0-beta.3 to 4.0.0-beta.4
191
es/index.js
@@ -1,34 +0,112 @@ | ||
import CacheMissError from './cache-miss-error'; | ||
import responseCache from './response-cache'; | ||
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 { responseCache, CacheMissError }; | ||
var responseCacheStore = {}; | ||
var activeRequests = {}; | ||
var accessFn = function accessFn() { | ||
return true; | ||
}; | ||
export function getRequestKey() { | ||
var responseCache = { | ||
get: function get(requestKey) { | ||
if (responseCache.has(requestKey)) { | ||
var cacheObject = responseCacheStore[requestKey]; | ||
cacheObject.accessCount += 1; | ||
cacheObject.lastAccessedAt = Date.now(); | ||
return cacheObject.res; | ||
} else { | ||
return undefined; | ||
} | ||
}, | ||
set: function set(requestKey, res) { | ||
responseCacheStore[requestKey] = { | ||
res: res, | ||
createdAt: Date.now(), | ||
accessCount: 0, | ||
lastAccessedAt: null | ||
}; | ||
return responseCache; | ||
}, | ||
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'; | ||
}, | ||
delete: function _delete(requestKey) { | ||
if (!responseCache.has(requestKey)) { | ||
return false; | ||
} else { | ||
delete responseCache[requestKey]; | ||
return true; | ||
} | ||
}, | ||
clear: function clear() { | ||
responseCacheStore = {}; | ||
}, | ||
useCachedResponse: function useCachedResponse(fn) { | ||
if (typeof fn === 'function') { | ||
accssFn = fn; | ||
} else { | ||
throw new TypeError('The first argument to `responseCache.useCachedResponse()` must be a function.'); | ||
} | ||
} | ||
}; | ||
function shouldUseCachedValue(requestKey) { | ||
if (responseCache.has(requestKey)) { | ||
var cacheObject = responseCache.get(requestKey); | ||
var shouldAccess = accessFn(); | ||
if (!shouldAccess) { | ||
responseCache.delete(requestKey); | ||
} | ||
return shouldAccess; | ||
} else { | ||
return false; | ||
} | ||
} | ||
var activeRequestsStore = {}; | ||
function getRequestKey() { | ||
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
_ref$url = _ref.url, | ||
url = _ref$url === undefined ? '' : _ref$url, | ||
url = _ref$url === void 0 ? '' : _ref$url, | ||
_ref$method = _ref.method, | ||
method = _ref$method === undefined ? '' : _ref$method, | ||
method = _ref$method === void 0 ? '' : _ref$method, | ||
_ref$responseType = _ref.responseType, | ||
responseType = _ref$responseType === undefined ? '' : _ref$responseType, | ||
responseType = _ref$responseType === void 0 ? '' : _ref$responseType, | ||
_ref$body = _ref.body, | ||
body = _ref$body === undefined ? '' : _ref$body; | ||
body = _ref$body === void 0 ? '' : _ref$body; | ||
return [url, method.toUpperCase(), responseType, body].join('||'); | ||
} | ||
var activeRequests = { | ||
// Returns `true` if a request with `requestKey` is in flight, | ||
// and `false` otherwise. | ||
isRequestInFlight: function isRequestInFlight(requestKey) { | ||
var handlers = activeRequestsStore[requestKey]; | ||
// Returns `true` if a request with `requestKey` is in flight, | ||
// and `false` otherwise. | ||
export function isRequestInFlight(requestKey) { | ||
return Boolean(activeRequests[requestKey]); | ||
} | ||
if (handlers && handlers.length) { | ||
return Boolean(handlers.length); | ||
} else { | ||
return false; | ||
} | ||
}, | ||
clear: function clear() { | ||
activeRequestsStore = {}; | ||
} | ||
}; | ||
// resolves or rejects them. | ||
export function clearActiveRequests() { | ||
activeRequests = {}; | ||
} | ||
// This loops through all of the handlers for the request and either | ||
// resolves or rejects them. | ||
function resolveRequest(_ref2) { | ||
@@ -38,5 +116,3 @@ var requestKey = _ref2.requestKey, | ||
err = _ref2.err; | ||
var handlers = activeRequests[requestKey] || []; | ||
var handlers = activeRequestsStore[requestKey] || []; | ||
handlers.forEach(function (handler) { | ||
@@ -48,15 +124,13 @@ if (res) { | ||
} | ||
}); | ||
}); // This list of handlers has been, well, handled. So we | ||
// clear the handlers for the next request. | ||
// This list of handlers has been, well, handled. So we | ||
// clear the handlers for the next request. | ||
activeRequests[requestKey] = null; | ||
activeRequestsStore[requestKey] = null; | ||
} | ||
export function fetchDedupe(input) { | ||
function fetchDedupe(input) { | ||
var init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var dedupeOptions = arguments[2]; | ||
var dedupeOptions = arguments.length > 2 ? arguments[2] : undefined; | ||
var opts, initToUse; | ||
var opts = void 0, | ||
initToUse = void 0; | ||
if (dedupeOptions) { | ||
@@ -76,12 +150,10 @@ opts = dedupeOptions; | ||
_opts$responseType = _opts.responseType, | ||
responseType = _opts$responseType === undefined ? '' : _opts$responseType, | ||
responseType = _opts$responseType === void 0 ? '' : _opts$responseType, | ||
_opts$dedupe = _opts.dedupe, | ||
dedupe = _opts$dedupe === undefined ? true : _opts$dedupe, | ||
dedupe = _opts$dedupe === void 0 ? true : _opts$dedupe, | ||
cachePolicy = _opts.cachePolicy; | ||
var method = initToUse.method || input.method || ''; | ||
var upperCaseMethod = method.toUpperCase(); | ||
var appliedCachePolicy; | ||
var appliedCachePolicy = void 0; | ||
if (cachePolicy) { | ||
@@ -91,6 +163,6 @@ appliedCachePolicy = cachePolicy; | ||
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 | ||
appliedCachePolicy = isReadRequest ? 'cache-first' : 'network-only'; | ||
} | ||
// Build the default request key if one is not passed | ||
var requestKeyToUse = requestKey || getRequestKey({ | ||
@@ -106,6 +178,6 @@ // If `input` is a request, then we use that URL | ||
if (appliedCachePolicy !== 'network-only') { | ||
if (responseCache._useCachedValue(requestKeyToUse)) { | ||
if (shouldUseCachedValue(requestKeyToUse)) { | ||
return Promise.resolve(responseCache.get(requestKeyToUse)); | ||
} else if (cachePolicy === 'cache-only') { | ||
var cacheError = new CacheMissError('Response for fetch request not found in cache.'); | ||
var cacheError = new CacheMissError("Response for fetch request not found in cache."); | ||
return Promise.reject(cacheError); | ||
@@ -115,9 +187,10 @@ } | ||
var proxyReq = void 0; | ||
var proxyReq; | ||
if (dedupe) { | ||
if (!activeRequests[requestKeyToUse]) { | ||
activeRequests[requestKeyToUse] = []; | ||
if (!activeRequestsStore[requestKeyToUse]) { | ||
activeRequestsStore[requestKeyToUse] = []; | ||
} | ||
var handlers = activeRequests[requestKeyToUse]; | ||
var handlers = activeRequestsStore[requestKeyToUse]; | ||
var requestInFlight = Boolean(handlers.length); | ||
@@ -129,3 +202,2 @@ var requestHandler = {}; | ||
}); | ||
handlers.push(requestHandler); | ||
@@ -139,3 +211,4 @@ | ||
var request = fetch(input, initToUse).then(function (res) { | ||
var responseTypeToUse = void 0; | ||
var responseTypeToUse; | ||
if (responseType instanceof Function) { | ||
@@ -149,6 +222,7 @@ responseTypeToUse = responseType(res); | ||
responseTypeToUse = 'json'; | ||
} | ||
// The response body is a ReadableStream. ReadableStreams can only be read a single | ||
} // The response body is a ReadableStream. ReadableStreams can only be read a single | ||
// time, so we must handle that in a central location, here, before resolving | ||
// the fetch. | ||
return res[responseTypeToUse]().then(function (data) { | ||
@@ -159,3 +233,6 @@ res.data = data; | ||
if (dedupe) { | ||
resolveRequest({ requestKey: requestKeyToUse, res: res }); | ||
resolveRequest({ | ||
requestKey: requestKeyToUse, | ||
res: res | ||
}); | ||
} else { | ||
@@ -168,3 +245,6 @@ return res; | ||
if (dedupe) { | ||
resolveRequest({ requestKey: requestKeyToUse, res: res }); | ||
resolveRequest({ | ||
requestKey: requestKeyToUse, | ||
res: res | ||
}); | ||
} else { | ||
@@ -176,3 +256,6 @@ return res; | ||
if (dedupe) { | ||
resolveRequest({ requestKey: requestKeyToUse, err: err }); | ||
resolveRequest({ | ||
requestKey: requestKeyToUse, | ||
err: err | ||
}); | ||
} else { | ||
@@ -188,2 +271,4 @@ return Promise.reject(err); | ||
} | ||
} | ||
} | ||
export { CacheMissError, activeRequests, fetchDedupe, getRequestKey, responseCache }; |
243
lib/index.js
'use strict'; | ||
exports.__esModule = true; | ||
exports.CacheMissError = exports.responseCache = undefined; | ||
exports.getRequestKey = getRequestKey; | ||
exports.isRequestInFlight = isRequestInFlight; | ||
exports.clearActiveRequests = clearActiveRequests; | ||
exports.fetchDedupe = fetchDedupe; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
var _cacheMissError = require('./cache-miss-error'); | ||
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 | ||
} | ||
}); | ||
var _cacheMissError2 = _interopRequireDefault(_cacheMissError); | ||
var responseCacheStore = {}; | ||
var _responseCache = require('./response-cache'); | ||
var accessFn = function accessFn() { | ||
return true; | ||
}; | ||
var _responseCache2 = _interopRequireDefault(_responseCache); | ||
var responseCache = { | ||
get: function get(requestKey) { | ||
if (responseCache.has(requestKey)) { | ||
var cacheObject = responseCacheStore[requestKey]; | ||
cacheObject.accessCount += 1; | ||
cacheObject.lastAccessedAt = Date.now(); | ||
return cacheObject.res; | ||
} else { | ||
return undefined; | ||
} | ||
}, | ||
set: function set(requestKey, res) { | ||
responseCacheStore[requestKey] = { | ||
res: res, | ||
createdAt: Date.now(), | ||
accessCount: 0, | ||
lastAccessedAt: null | ||
}; | ||
return responseCache; | ||
}, | ||
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'; | ||
}, | ||
delete: function _delete(requestKey) { | ||
if (!responseCache.has(requestKey)) { | ||
return false; | ||
} else { | ||
delete responseCache[requestKey]; | ||
return true; | ||
} | ||
}, | ||
clear: function clear() { | ||
responseCacheStore = {}; | ||
}, | ||
useCachedResponse: function useCachedResponse(fn) { | ||
if (typeof fn === 'function') { | ||
accssFn = fn; | ||
} else { | ||
throw new TypeError('The first argument to `responseCache.useCachedResponse()` must be a function.'); | ||
} | ||
} | ||
}; | ||
function shouldUseCachedValue(requestKey) { | ||
if (responseCache.has(requestKey)) { | ||
var cacheObject = responseCache.get(requestKey); | ||
var shouldAccess = accessFn(); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
if (!shouldAccess) { | ||
responseCache.delete(requestKey); | ||
} | ||
exports.responseCache = _responseCache2.default; | ||
exports.CacheMissError = _cacheMissError2.default; | ||
return shouldAccess; | ||
} else { | ||
return false; | ||
} | ||
} | ||
var activeRequestsStore = {}; | ||
function getRequestKey() { | ||
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
_ref$url = _ref.url, | ||
url = _ref$url === void 0 ? '' : _ref$url, | ||
_ref$method = _ref.method, | ||
method = _ref$method === void 0 ? '' : _ref$method, | ||
_ref$responseType = _ref.responseType, | ||
responseType = _ref$responseType === void 0 ? '' : _ref$responseType, | ||
_ref$body = _ref.body, | ||
body = _ref$body === void 0 ? '' : _ref$body; | ||
let activeRequests = {}; | ||
function getRequestKey({ | ||
url = '', | ||
method = '', | ||
responseType = '', | ||
body = '' | ||
} = {}) { | ||
return [url, method.toUpperCase(), responseType, body].join('||'); | ||
} | ||
var activeRequests = { | ||
// Returns `true` if a request with `requestKey` is in flight, | ||
// and `false` otherwise. | ||
isRequestInFlight: function isRequestInFlight(requestKey) { | ||
var handlers = activeRequestsStore[requestKey]; | ||
// Returns `true` if a request with `requestKey` is in flight, | ||
// and `false` otherwise. | ||
function isRequestInFlight(requestKey) { | ||
return Boolean(activeRequests[requestKey]); | ||
} | ||
function clearActiveRequests() { | ||
activeRequests = {}; | ||
} | ||
// This loops through all of the handlers for the request and either | ||
if (handlers && handlers.length) { | ||
return Boolean(handlers.length); | ||
} else { | ||
return false; | ||
} | ||
}, | ||
clear: function clear() { | ||
activeRequestsStore = {}; | ||
} | ||
}; | ||
// resolves or rejects them. | ||
function resolveRequest({ requestKey, res, err }) { | ||
const handlers = activeRequests[requestKey] || []; | ||
handlers.forEach(handler => { | ||
function resolveRequest(_ref2) { | ||
var requestKey = _ref2.requestKey, | ||
res = _ref2.res, | ||
err = _ref2.err; | ||
var handlers = activeRequestsStore[requestKey] || []; | ||
handlers.forEach(function (handler) { | ||
if (res) { | ||
@@ -56,11 +127,13 @@ handler.resolve(res); | ||
} | ||
}); | ||
}); // This list of handlers has been, well, handled. So we | ||
// clear the handlers for the next request. | ||
// This list of handlers has been, well, handled. So we | ||
// clear the handlers for the next request. | ||
activeRequests[requestKey] = null; | ||
activeRequestsStore[requestKey] = null; | ||
} | ||
function fetchDedupe(input, init = {}, dedupeOptions) { | ||
let opts, initToUse; | ||
function fetchDedupe(input) { | ||
var init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var dedupeOptions = arguments.length > 2 ? arguments[2] : undefined; | ||
var opts, initToUse; | ||
if (dedupeOptions) { | ||
@@ -77,17 +150,22 @@ opts = dedupeOptions; | ||
const { requestKey, responseType = '', dedupe = true, cachePolicy } = opts; | ||
var _opts = opts, | ||
requestKey = _opts.requestKey, | ||
_opts$responseType = _opts.responseType, | ||
responseType = _opts$responseType === void 0 ? '' : _opts$responseType, | ||
_opts$dedupe = _opts.dedupe, | ||
dedupe = _opts$dedupe === void 0 ? true : _opts$dedupe, | ||
cachePolicy = _opts.cachePolicy; | ||
var method = initToUse.method || input.method || ''; | ||
var upperCaseMethod = method.toUpperCase(); | ||
var appliedCachePolicy; | ||
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 === ''; | ||
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 | ||
appliedCachePolicy = isReadRequest ? 'cache-first' : 'network-only'; | ||
} | ||
// Build the default request key if one is not passed | ||
let requestKeyToUse = requestKey || getRequestKey({ | ||
var requestKeyToUse = requestKey || getRequestKey({ | ||
// If `input` is a request, then we use that URL | ||
@@ -102,6 +180,6 @@ url: input.url || input, | ||
if (appliedCachePolicy !== 'network-only') { | ||
if (_responseCache2.default._useCachedValue(requestKeyToUse)) { | ||
return Promise.resolve(_responseCache2.default.get(requestKeyToUse)); | ||
if (shouldUseCachedValue(requestKeyToUse)) { | ||
return Promise.resolve(responseCache.get(requestKeyToUse)); | ||
} else if (cachePolicy === 'cache-only') { | ||
const cacheError = new _cacheMissError2.default(`Response for fetch request not found in cache.`); | ||
var cacheError = new CacheMissError("Response for fetch request not found in cache."); | ||
return Promise.reject(cacheError); | ||
@@ -111,16 +189,16 @@ } | ||
let proxyReq; | ||
var proxyReq; | ||
if (dedupe) { | ||
if (!activeRequests[requestKeyToUse]) { | ||
activeRequests[requestKeyToUse] = []; | ||
if (!activeRequestsStore[requestKeyToUse]) { | ||
activeRequestsStore[requestKeyToUse] = []; | ||
} | ||
const handlers = activeRequests[requestKeyToUse]; | ||
const requestInFlight = Boolean(handlers.length); | ||
const requestHandler = {}; | ||
proxyReq = new Promise((resolve, reject) => { | ||
var handlers = activeRequestsStore[requestKeyToUse]; | ||
var requestInFlight = Boolean(handlers.length); | ||
var requestHandler = {}; | ||
proxyReq = new Promise(function (resolve, reject) { | ||
requestHandler.resolve = resolve; | ||
requestHandler.reject = reject; | ||
}); | ||
handlers.push(requestHandler); | ||
@@ -133,4 +211,5 @@ | ||
const request = fetch(input, initToUse).then(res => { | ||
let responseTypeToUse; | ||
var request = fetch(input, initToUse).then(function (res) { | ||
var responseTypeToUse; | ||
if (responseType instanceof Function) { | ||
@@ -144,20 +223,27 @@ responseTypeToUse = responseType(res); | ||
responseTypeToUse = 'json'; | ||
} | ||
// The response body is a ReadableStream. ReadableStreams can only be read a single | ||
} // The response body is a ReadableStream. ReadableStreams can only be read a single | ||
// time, so we must handle that in a central location, here, before resolving | ||
// the fetch. | ||
return res[responseTypeToUse]().then(data => { | ||
return res[responseTypeToUse]().then(function (data) { | ||
res.data = data; | ||
_responseCache2.default.set(requestKeyToUse, res); | ||
responseCache.set(requestKeyToUse, res); | ||
if (dedupe) { | ||
resolveRequest({ requestKey: requestKeyToUse, res }); | ||
resolveRequest({ | ||
requestKey: requestKeyToUse, | ||
res: res | ||
}); | ||
} else { | ||
return res; | ||
} | ||
}, () => { | ||
}, function () { | ||
res.data = null; | ||
if (dedupe) { | ||
resolveRequest({ requestKey: requestKeyToUse, res }); | ||
resolveRequest({ | ||
requestKey: requestKeyToUse, | ||
res: res | ||
}); | ||
} else { | ||
@@ -167,5 +253,8 @@ return res; | ||
}); | ||
}, err => { | ||
}, function (err) { | ||
if (dedupe) { | ||
resolveRequest({ requestKey: requestKeyToUse, err }); | ||
resolveRequest({ | ||
requestKey: requestKeyToUse, | ||
err: err | ||
}); | ||
} else { | ||
@@ -181,2 +270,8 @@ return Promise.reject(err); | ||
} | ||
} | ||
} | ||
exports.CacheMissError = CacheMissError; | ||
exports.activeRequests = activeRequests; | ||
exports.fetchDedupe = fetchDedupe; | ||
exports.getRequestKey = getRequestKey; | ||
exports.responseCache = responseCache; |
{ | ||
"name": "fetch-dedupe", | ||
"version": "4.0.0-beta.3", | ||
"version": "4.0.0-beta.4", | ||
"description": "A thin wrapper around fetch that prevents duplicate requests.", | ||
@@ -9,10 +9,6 @@ "main": "lib/index.js", | ||
"clean": "rimraf dist es tmp lib", | ||
"test": "jest", | ||
"test": "NODE_ENV=test jest", | ||
"test:watch": "jest --watch", | ||
"prepublish": "in-publish && npm run build || not-in-publish", | ||
"build": "npm run clean && npm run build:umd && npm run build:umd:min && npm run build:es && npm run build:commonjs", | ||
"build:commonjs": "cross-env BABEL_ENV=commonjs babel src --out-dir lib", | ||
"build:es": "cross-env BABEL_ENV=es babel src --out-dir es", | ||
"build:umd": "cross-env NODE_ENV=development BABEL_ENV=build rollup -c -i src/index.js -o dist/fetch-dedupe.js", | ||
"build:umd:min": "cross-env NODE_ENV=production BABEL_ENV=buildProd rollup -c -i src/index.js -o dist/fetch-dedupe.min.js" | ||
"build": "rollup -c" | ||
}, | ||
@@ -52,8 +48,6 @@ "repository": { | ||
"devDependencies": { | ||
"babel-cli": "^6.26.0", | ||
"babel-core": "^6.26.0", | ||
"babel-jest": "^22.1.0", | ||
"babel-plugin-external-helpers": "^6.22.0", | ||
"babel-preset-env": "^1.6.1", | ||
"babel-preset-stage-3": "^6.24.1", | ||
"@babel/core": "^7.2.2", | ||
"@babel/preset-env": "^7.2.3", | ||
"@rollup/plugin-replace": "^2.2.1", | ||
"babel-jest": "^24.9.0", | ||
"cross-env": "^5.1.3", | ||
@@ -63,10 +57,10 @@ "fetch-mock": "^6.0.0", | ||
"isomorphic-fetch": "^2.2.1", | ||
"jest": "^23.6.0", | ||
"jest": "^24.9.0", | ||
"rimraf": "^2.6.2", | ||
"rollup": "^0.45.1", | ||
"rollup-plugin-babel": "^2.7.1", | ||
"rollup-plugin-commonjs": "^8.2.6", | ||
"rollup-plugin-node-resolve": "^3.0.0", | ||
"rollup-plugin-uglify": "^2.0.1" | ||
"rollup": "^1.0.0", | ||
"rollup-plugin-babel": "^4.2.0", | ||
"rollup-plugin-commonjs": "^9.2.0", | ||
"rollup-plugin-node-resolve": "^4.0.0", | ||
"rollup-plugin-uglify": "^6.0.3" | ||
} | ||
} |
@@ -13,3 +13,3 @@ # Fetch Dedupe | ||
### Motivation | ||
## Motivation | ||
@@ -25,3 +25,3 @@ Making a single HTTP request is not difficult to do in JavaScript. However, complex web applications often make many | ||
### Installation | ||
## Installation | ||
@@ -40,3 +40,3 @@ Install using [npm](https://www.npmjs.com): | ||
### Getting Started | ||
## Getting Started | ||
@@ -67,3 +67,3 @@ This example demonstrates using Fetch Dedupe with the | ||
#### Important: Read this! | ||
### Important: Read this! | ||
@@ -92,3 +92,3 @@ When using `fetch`, you typically read the body yourself by calling, say, `.json()` on the | ||
### API | ||
## API | ||
@@ -105,5 +105,6 @@ This library exports the following methods: | ||
- `.clear()` | ||
- `.setCacheCheck()` | ||
- `isRequestInFlight()` | ||
- `clearActiveRequests()` | ||
- `.useCachedResponse()` | ||
- `activeRequests` | ||
- `isRequestInFlight()` | ||
- `clear()` | ||
@@ -233,13 +234,19 @@ ##### `fetchDedupe( input [, init] [, dedupeOptions] )` | ||
##### `responseCache.setCacheCheck( fn )` | ||
##### `responseCache.useCachedResponse( fn )` | ||
By default, fetch-dedupe caches responses indefinitely. You can customize this behavior by calling this | ||
method a single time when your app is initialized. | ||
By default, fetch-dedupe caches responses indefinitely. You can customize this behavior using this method. | ||
This function accepts a single argument, `fn,` which is a function. This function will be called any time | ||
that a request is made that has a cached response. It receives two arguments: `cachedResponse, timestamp`. Return | ||
`true` to use the cached response, or `false` to remove the value from the cache and make a network request | ||
This method accepts a single argument, `fn,` which is a function. `fn` will be called any time | ||
that a request is made that has a cached response. It's called with a single argument, `cacheObject`, an object | ||
with the following properties: | ||
- `res`: The cached response object. | ||
- `createdAt`: A timestamp (in milliseconds) when the value was added to the cache. | ||
- `lastAccessedAt`: A timestamp (in milliseconds) when the value was last read from the cache. | ||
- `accessCount`: An integer representing the number of times that the value has been read from the cache. | ||
Return `true` to use the cached response, or `false` to remove the value from the cache and make a network request | ||
instead. | ||
For instance, to invalidate cached responses after 10 seconds: | ||
For instance, to invalidate cached responses that are more than 10 minutes old: | ||
@@ -254,9 +261,9 @@ ```js | ||
responseCache.setCacheCheck((cachedResponse, timestamp) => { | ||
const currentTimestamp = Number(new Date()); | ||
return currentTimestamp - timestamp <= TEN_MINUTES; | ||
responseCache.useCachedResponse(({ createdAt }) => { | ||
const currentTimestamp = Date.now(); | ||
return currentTimestamp - createdAt <= TEN_MINUTES; | ||
}); | ||
``` | ||
##### `isRequestInFlight( requestKey )` | ||
##### `activeRequests.isRequestInFlight( requestKey )` | ||
@@ -268,3 +275,3 @@ Pass in a `requestKey` to see if there's already a request in flight for it. This | ||
```js | ||
import { isRequestInFlight, getRequestKey } from 'fetch-dedupe'; | ||
import { activeRequests, getRequestKey } from 'fetch-dedupe'; | ||
@@ -277,19 +284,16 @@ const key = getRequestKey({ | ||
// Is there already a request in flight for this? | ||
const readingBooksAlready = isRequestInFlight(key); | ||
const readingBooksAlready = activeRequests(key); | ||
``` | ||
> 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. | ||
##### `activeRequests.clear()` | ||
##### `clearActiveRequests()` | ||
Removes all of the tracked in-flight requests. In-flight requests are not cancelled: calling this | ||
method only ensures that subsequent identical requests are not deduped. | ||
Removes all of the tracked in-flight requests. | ||
> Note: you typically should not need to use this method. | ||
> Note: it is very unlikely that you would ever need to call this method. | ||
## Guides | ||
### Guides | ||
### Caching | ||
##### Caching | ||
Any time tbat a response from the server is received, it will be cached using the request's request key. | ||
@@ -319,5 +323,5 @@ Subsequent requests are matched with existing cached server responses using their request key. | ||
### FAQ & Troubleshooting | ||
## FAQ & Troubleshooting | ||
##### Why is `response.data` set to `null` sometimes? | ||
#### Why is `response.data` set to `null` sometimes? | ||
@@ -335,3 +339,3 @@ If the response cannot be parsed as the `responseType`, then it will be set as `null`. | ||
##### Why is `responseType` even an option? | ||
#### Why is `responseType` even an option? | ||
@@ -347,3 +351,3 @@ The argument that is returned to you in the `.then` callback of a call to `fetch()` is a | ||
##### What request body types are supported? | ||
#### What request body types are supported? | ||
@@ -353,3 +357,3 @@ Just strings for now, which should work for the majority of APIs. Support for other body types | ||
##### Is the data duplicated? | ||
#### Is the data duplicated? | ||
@@ -363,3 +367,3 @@ Although you receive a new `Response` object with every call to `fetch-dedupe`, the body will be read, | ||
### Requirements | ||
## Requirements | ||
@@ -372,12 +376,4 @@ - a global [fetch()](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) method. If your browser does not support it, then we | ||
### Implementors | ||
## Acknowledgements | ||
These are projects that build abstractions around HTTP requests using Fetch Dedupe under the hood. | ||
- [React Request](https://github.com/jamesplease/react-request) | ||
Are you using it on a project? Add it to this list by opening a Pull Request | ||
### Acknowledgements | ||
[Apollo](https://www.apollographql.com/) inspired me to write this library. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
15
0
3
30970
6
470
364