UNPKG

wretch

Version:

A tiny wrapper built around fetch with an intuitive syntax.

99 lines 4 kB
/* Defaults */ const defaultSkip = (url, opts) => (opts.skipCache || opts.method !== "GET"); const defaultKey = (url, opts) => opts.method + "@" + url; const defaultClear = (url, opts) => false; const defaultInvalidate = (url, opts) => null; const defaultCondition = response => response.ok; export const throttlingCache = ({ throttle = 1000, skip = defaultSkip, key = defaultKey, clear = defaultClear, invalidate = defaultInvalidate, condition = defaultCondition, flagResponseOnCacheHit = "__cached" } = {}) => { const cache = new Map(); const inflight = new Map(); const throttling = new Set(); const throttleRequest = _key => { if (throttle && !throttling.has(_key)) { throttling.add(_key); setTimeout(() => { throttling.delete(_key); }, throttle); } }; const middleware = next => (url, opts) => { const _key = key(url, opts); let invalidatePatterns = invalidate(url, opts); if (invalidatePatterns) { if (!(invalidatePatterns instanceof Array)) { invalidatePatterns = [invalidatePatterns]; } invalidatePatterns.forEach(pattern => { if (typeof pattern === "string") { cache.delete(pattern); } else if (pattern instanceof RegExp) { cache.forEach((_, key) => { if (pattern.test(key)) { cache.delete(key); } }); } }); } if (clear(url, opts)) { cache.clear(); } if (skip(url, opts)) { return next(url, opts); } if (throttling.has(_key)) { // If the cache contains a previous response and we are throttling, serve it and bypass the chain. if (cache.has(_key)) { const cachedClone = cache.get(_key).clone(); if (flagResponseOnCacheHit) { // Flag the Response as cached Object.defineProperty(cachedClone, flagResponseOnCacheHit, { value: _key, enumerable: false }); } return Promise.resolve(cachedClone); // If the request in already in-flight, wait until it is resolved } else if (inflight.has(_key)) { return new Promise((resolve, reject) => { inflight.get(_key).push([resolve, reject]); }); } } // Init. the pending promises Map if (!inflight.has(_key)) inflight.set(_key, []); // If we are not throttling, activate the throttle for X milliseconds throttleRequest(_key); // We call the next middleware in the chain. return next(url, opts) .then(response => { // Add a cloned response to the cache if (condition(response.clone())) { cache.set(_key, response.clone()); } // Resolve pending promises inflight.get(_key).forEach(([resolve]) => resolve(response.clone())); // Remove the inflight pending promises inflight.delete(_key); // Return the original response return response; }) .catch(error => { // Reject pending promises on error inflight.get(_key).forEach(([resolve, reject]) => reject(error)); inflight.delete(_key); throw error; }); }; // Programmatically cache a response middleware.cacheResponse = function (key, response) { throttleRequest(key); cache.set(key, response); }; middleware.cache = cache; middleware.inflight = inflight; middleware.throttling = throttling; return middleware; }; //# sourceMappingURL=throttlingCache.js.map