Socket
Socket
Sign inDemoInstall

axios-cache-interceptor

Package Overview
Dependencies
Maintainers
1
Versions
78
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

axios-cache-interceptor - npm Package Compare versions

Comparing version 1.5.0 to 1.5.1

2

dev/index.bundle.d.ts
/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors
* Released under the MIT License.
*/
import{parse as e}from"cache-parser";import{deferred as t}from"fast-defer";import{hash as a}from"object-code";const r=Object.freeze({IfModifiedSince:"if-modified-since",LastModified:"last-modified",IfNoneMatch:"if-none-match",CacheControl:"cache-control",Pragma:"pragma",ETag:"etag",Expires:"expires",Age:"age",XAxiosCacheEtag:"x-axios-cache-etag",XAxiosCacheLastModified:"x-axios-cache-last-modified",XAxiosCacheStaleIfError:"x-axios-cache-stale-if-error"}),i=t=>{if(!t)return"not enough headers";const a=t[r.CacheControl];if(a){const{noCache:i,noStore:n,maxAge:o,maxStale:s,immutable:d,staleWhileRevalidate:c}=e(String(a));if(i||n)return"dont cache";if(d)return{cache:31536e6};if(void 0!==o){const e=t[r.Age];return{cache:e?1e3*(o-Number(e)):1e3*o,stale:void 0!==s?1e3*s:void 0!==c?1e3*c:void 0}}}const i=t[r.Expires];if(i){const e=Date.parse(String(i))-Date.now();return e>=0?{cache:e}:"dont cache"}return"not enough headers"};function n(){return n=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var a=arguments[t];for(var r in a)Object.prototype.hasOwnProperty.call(a,r)&&(e[r]=a[r])}return e},n.apply(this,arguments)}function o(e){return e?t=>e(t)||304===t:e=>e>=200&&e<300||304===e}function s(e="get",t=[]){return e=e.toLowerCase(),t.some(t=>t===e)}function d(e,t){t.headers||(t.headers={});const{etag:a,modifiedSince:i}=t.cache;if(a){var n;const i=!0===a?null==(n=e.data)?void 0:n.headers[r.ETag]:a;i&&(t.headers[r.IfNoneMatch]=i)}i&&(t.headers[r.IfModifiedSince]=!0===i?e.data.headers[r.LastModified]||new Date(e.createdAt).toUTCString():i.toUTCString())}function c(e,t){return 304===e.status&&t?(e.cached=!0,e.data=t.data,e.status=t.status,e.statusText=t.statusText,e.headers=n({},t.headers,e.headers),t):{data:e.data,status:e.status,statusText:e.statusText,headers:e.headers}}function u(e){const a=async i=>{if(i.id=e.generateKey(i),!1===i.cache)return e.debug({id:i.id,msg:"Ignoring cache because config.cache === false",data:i}),i;if(i.cache=n({},e.defaults.cache,i.cache),"object"==typeof i.cache.cachePredicate&&i.cache.cachePredicate.ignoreUrls&&i.url)for(const t of i.cache.cachePredicate.ignoreUrls)if(t instanceof RegExp?(t.lastIndex=0,t.test(i.url)):i.url.includes(t))return e.debug({id:i.id,msg:`Ignored because url (${i.url}) matches ignoreUrls (${i.cache.cachePredicate.ignoreUrls})`,data:{url:i.url,cachePredicate:i.cache.cachePredicate}}),i;var c,u,h,g,l,f;if(i.cache.cacheTakeover&&(null!=(c=i.headers)[u=r.CacheControl]||(c[u]="no-cache"),null!=(h=i.headers)[g=r.Pragma]||(h[g]="no-cache"),null!=(l=i.headers)[f=r.Expires]||(l[f]="0")),!s(i.method,i.cache.methods))return e.debug({id:i.id,msg:`Ignored because method (${i.method}) is not in cache.methods (${i.cache.methods})`}),i;let m=await e.storage.get(i.id,i);const p=i.cache.override;e:if("empty"===m.state||"stale"===m.state||p){if(e.waiting[i.id]&&!p&&(m=await e.storage.get(i.id,i),"empty"!==m.state)){e.debug({id:i.id,msg:"Waiting list had an deferred for this key, waiting for it to finish"});break e}return e.waiting[i.id]=t(),e.waiting[i.id].catch(()=>{}),await e.storage.set(i.id,{state:"loading",previous:p?m.data?"stale":"empty":m.state,data:m.data,createdAt:p&&!m.createdAt?Date.now():m.createdAt},i),"stale"===m.state&&(d(m,i),e.debug({id:i.id,msg:"Updated stale request"})),i.validateStatus=o(i.validateStatus),e.debug({id:i.id,msg:"Sending request, waiting for response",data:{overrideCache:p,state:m.state}}),("stale"===m.state||m.data)&&await(null==i.cache.hydrate?void 0:i.cache.hydrate(m)),i}let w;if("loading"===m.state){const t=e.waiting[i.id];if(!t)return m.data&&await(null==i.cache.hydrate?void 0:i.cache.hydrate(m)),i;e.debug({id:i.id,msg:"Detected concurrent request, waiting for it to finish"});try{w=await t}catch(t){return e.debug({id:i.id,msg:"Deferred rejected, requesting again",data:t}),m.data&&await(null==i.cache.hydrate?void 0:i.cache.hydrate(m)),a(i)}}else w=m.data;return i.transformResponse=void 0,i.adapter=function(){return Promise.resolve({config:i,data:w.data,headers:w.headers,status:w.status,statusText:w.statusText,cached:!0,id:i.id})},e.debug({id:i.id,msg:"Returning cached response"}),i};return{onFulfilled:a,apply:()=>e.interceptors.request.use(a)}}async function h(e,t){if("function"==typeof t)return t(e);const{statusCheck:a,responseMatch:r,containsHeaders:i}=t;if(a&&!await a(e.status)||r&&!await r(e))return!1;if(i)for(const[t,a]of Object.entries(i)){var n;if(!await a(null!=(n=e.headers[t.toLowerCase()])?n:e.headers[t]))return!1}return!0}async function g(e,t,a){if("function"==typeof a)return a(t);for(const[r,i]of Object.entries(a)){if("delete"===i){await e.remove(r,t.config);continue}const a=await e.get(r,t.config);if("loading"===a.state)continue;const n=await i(a,t);"delete"!==n?"ignore"!==n&&await e.set(r,n,t.config):await e.remove(r,t.config)}}function l(t){const a=async(e,a)=>{var r;await t.storage.remove(e,a),null==(r=t.waiting[e])||r.reject(),delete t.waiting[e]},i=async e=>{if(null==e||!e.config)throw t.debug({msg:"Response interceptor received an unknown response.",data:e}),e;e.id=e.config.id,null!=e.cached||(e.cached=!1);const i=e.config,n=i.cache;if(e.cached)return t.debug({id:e.id,msg:"Returned cached response"}),e;if(!n)return t.debug({id:e.id,msg:"Response with config.cache falsy",data:e}),e.cached=!1,e;if(n.update&&await g(t.storage,e,n.update),!s(i.method,n.methods))return t.debug({id:e.id,msg:`Ignored because method (${i.method}) is not in cache.methods (${n.methods})`,data:{config:i,cacheConfig:n}}),e;const o=await t.storage.get(e.id,i);if("loading"!==o.state)return t.debug({id:e.id,msg:"Response not cached and storage isn't loading",data:{cache:o,response:e}}),e;if(!o.data&&!await h(e,n.cachePredicate))return await a(e.id,i),t.debug({id:e.id,msg:"Cache predicate rejected this response"}),e;for(const t of Object.keys(e.headers))t.startsWith("x-axios-cache")&&delete e.headers[t];n.etag&&!0!==n.etag&&(e.headers[r.XAxiosCacheEtag]=n.etag),n.modifiedSince&&(e.headers[r.XAxiosCacheLastModified]=!0===n.modifiedSince?"use-cache-timestamp":n.modifiedSince.toUTCString());let d,u=n.ttl||-1;if(n.interpretHeader){const r=t.headerInterpreter(e.headers);if("dont cache"===r)return await a(e.id,i),t.debug({id:e.id,msg:"Cache header interpreted as 'dont cache'",data:{cache:o,response:e,expirationTime:r}}),e;"not enough headers"!==r&&("number"==typeof r?u=r:(u=r.cache,d=r.stale))}const l=c(e,o.data);"function"==typeof u&&(u=await u(e)),n.staleIfError&&(e.headers[r.XAxiosCacheStaleIfError]=String(u)),t.debug({id:e.id,msg:"Useful response configuration found",data:{cacheConfig:n,cacheResponse:l}});const f={state:"cached",ttl:u,staleTtl:d,createdAt:Date.now(),data:l},m=t.waiting[e.id];return m&&(m.resolve(f.data),delete t.waiting[e.id],t.debug({id:e.id,msg:"Found waiting deferred(s) and resolved them"})),await t.storage.set(e.id,f,i),t.debug({id:e.id,msg:"Response cached",data:{cache:f,response:e}}),e},n=async i=>{if(!i.isAxiosError||!i.config)throw t.debug({msg:"FATAL: Received an non axios error in the rejected response interceptor, ignoring.",data:i}),i;const n=i.config,o=n.id,d=n.cache,c=i.response;if(!d||!o)throw t.debug({msg:"Web request returned an error but cache handling is not enabled",data:{error:i}}),i;if(!s(n.method,d.methods))throw t.debug({id:o,msg:`Ignored because method (${n.method}) is not in cache.methods (${d.methods})`,data:{config:n,cacheConfig:d}}),await a(o,n),i;const u=await t.storage.get(o,n);if("loading"!==u.state||"stale"!==u.previous)throw t.debug({id:o,msg:"Caught an error in the request interceptor",data:{cache:u,error:i,config:n}}),await a(o,n),i;if(d.staleIfError){const a=String(null==c?void 0:c.headers[r.CacheControl]),s=a&&e(a).staleIfError,g="function"==typeof d.staleIfError?await d.staleIfError(c,u,i):!0===d.staleIfError&&s?1e3*s:d.staleIfError;var h;if(t.debug({id:o,msg:"Found cache if stale config for rejected response",data:{error:i,config:n,staleIfError:g}}),!0===g||"number"==typeof g&&u.createdAt+g>Date.now())return null==(h=t.waiting[o])||h.resolve(u.data),delete t.waiting[o],await t.storage.set(o,{state:"stale",createdAt:Date.now(),data:u.data},n),t.debug({id:o,msg:"staleIfError resolved this response with cached data",data:{error:i,config:n,cache:u}}),{cached:!0,config:n,id:o,data:u.data.data,headers:u.data.headers,status:u.data.status,statusText:u.data.statusText}}throw t.debug({id:o,msg:"Received an unknown error that could not be handled",data:{error:i,config:n}}),await a(o,n),i};return{onFulfilled:i,onRejected:n,apply:()=>t.interceptors.response.use(i,n)}}const f=e=>!!e&&!!e["is-storage"];function m(e){const t=e.data.headers;return r.ETag in t||r.LastModified in t||r.XAxiosCacheEtag in t||r.XAxiosCacheLastModified in t}function p(e){return!String(e.data.headers[r.CacheControl]).includes("must-revalidate")&&(!!m(e)||"cached"===e.state&&void 0!==e.staleTtl&&Math.abs(Date.now()-(e.createdAt+e.ttl))<=e.staleTtl)}function w(e){return void 0!==e.ttl&&e.createdAt+e.ttl<=Date.now()}function b({set:e,find:t,remove:a}){return{"is-storage":1,set:e,remove:a,get:async(r,i)=>{let n=await t(r,i);if(!n)return{state:"empty"};if("empty"===n.state||"loading"===n.state)return n;if("cached"===n.state){if(!w(n))return n;if(!p(n))return await a(r,i),{state:"empty"};n={state:"stale",createdAt:n.createdAt,data:n.data,ttl:void 0!==n.staleTtl?n.staleTtl+n.ttl:void 0},await e(r,n,i)}return w(n)?m(n)?n:(await a(r,i),{state:"empty"}):n}}}function v(e=!1,t=!1,a=!1){const r=b({set:(t,i)=>{if(a){let e=Object.keys(r.data);if(e.length>=a)for(r.cleanup(),e=Object.keys(r.data);e.length>=a;)delete r.data[e.shift()]}r.data[t]="double"===e?"function"==typeof structuredClone?structuredClone(i):JSON.parse(JSON.stringify(i)):i},remove:e=>{delete r.data[e]},find:t=>{const a=r.data[t];return e&&void 0!==a?"function"==typeof structuredClone?structuredClone(a):JSON.parse(JSON.stringify(a)):a}});return r.data=Object.create(null),r.cleanup=()=>{const e=Object.keys(r.data);let t,a,i=-1;for(;++i<e.length;)a=e[i],t=r.data[a],"empty"!==t.state?"cached"===t.state&&w(t)&&!p(t)&&r.remove(a):r.remove(a)},t&&(r.cleaner=setInterval(r.cleanup,t)),r}const y=/^\/|\/$/g;function C(e){return t=>{if(t.id)return t.id;const r=e(t);return"string"==typeof r||"number"==typeof r?`${r}`:`${a(r)}`}}const I=C(({baseURL:e,url:t,method:a,params:r,data:i})=>(e=void 0!==e?e.replace(y,""):"",t=void 0!==t?t.replace(y,""):"",{url:e+(e&&t?"/":"")+t,params:r,method:a=void 0!==a?a.toLowerCase():"get",data:i}));function x(e,t={}){var a,r,n,o,s,d,c,h;const g=e;if(g.defaults.cache)throw new Error("setupCache() should be called only once");if(g.storage=t.storage||v(),!f(g.storage))throw new Error("Use buildStorage() function");return g.waiting=t.waiting||{},g.generateKey=t.generateKey||I,g.headerInterpreter=t.headerInterpreter||i,g.requestInterceptor=t.requestInterceptor||u(g),g.responseInterceptor=t.responseInterceptor||l(g),g.debug=t.debug||function(){},g.defaults.cache={update:t.update||{},ttl:null!=(a=t.ttl)?a:3e5,methods:t.methods||["get","head"],cachePredicate:t.cachePredicate||{statusCheck:e=>[200,203,300,301,302,404,405,410,414,501].includes(e)},etag:null==(r=t.etag)||r,modifiedSince:null!=(n=t.modifiedSince)?n:!1===t.etag,interpretHeader:null==(o=t.interpretHeader)||o,cacheTakeover:null==(s=t.cacheTakeover)||s,staleIfError:null==(d=t.staleIfError)||d,override:null!=(c=t.override)&&c,hydrate:null!=(h=t.hydrate)?h:void 0},g.requestInterceptor.apply(),g.responseInterceptor.apply(),g}function S(e,t="axios-cache-"){return b({find:a=>{const r=e.getItem(t+a);return r?JSON.parse(r):void 0},remove:a=>{e.removeItem(t+a)},set:(a,r)=>{const i=()=>e.setItem(t+a,JSON.stringify(r));try{return i()}catch(r){const n=Object.entries(e).filter(e=>e[0].startsWith(t)).map(e=>[e[0],JSON.parse(e[1])]);for(const t of n)"cached"===t[1].state&&w(t[1])&&!p(t[1])&&e.removeItem(t[0]);try{return i()}catch(t){const a=n.sort((e,t)=>(e[1].createdAt||0)-(t[1].createdAt||0));for(const t of a){e.removeItem(t[0]);try{return i()}catch(e){}}}e.removeItem(t+a)}}})}console.error("You are using a development build. Make sure to use the correct build in production\nhttps://axios-cache-interceptor.js.org/guide/getting-started\n\n");export{r as Header,C as buildKeyGenerator,v as buildMemoryStorage,b as buildStorage,S as buildWebStorage,p as canStale,c as createCacheResponse,o as createValidateStatus,i as defaultHeaderInterpreter,I as defaultKeyGenerator,u as defaultRequestInterceptor,l as defaultResponseInterceptor,w as isExpired,s as isMethodIn,f as isStorage,x as setupCache,h as testCachePredicate,g as updateCache,d as updateStaleRequest};
import { parse } from 'cache-parser';
import { deferred } from 'fast-defer';
import { hash } from 'object-code';
const Header = Object.freeze({
/**
* ```txt
* If-Modified-Since: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since
*/
IfModifiedSince: 'if-modified-since',
/**
* ```txt
* Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified
*/
LastModified: 'last-modified',
/**
* ```txt
* If-None-Match: "<etag_value>"
* If-None-Match: "<etag_value>", "<etag_value>", …
* If-None-Match: *
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match
*/
IfNoneMatch: 'if-none-match',
/**
* ```txt
* Cache-Control: max-age=604800
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
*/
CacheControl: 'cache-control',
/**
* ```txt
* Pragma: no - cache;
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Pragma
*/
Pragma: 'pragma',
/**
* ```txt
* ETag: W / '<etag_value>';
* ETag: '<etag_value>';
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
*/
ETag: 'etag',
/**
* ```txt
* Expires: <http-date>
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires
*/
Expires: 'expires',
/**
* ```txt
* Age: <delta-seconds>
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Age
*/
Age: 'age',
/**
* Used internally as metadata to mark the cache item as revalidatable and enabling
* stale cache state Contains a string of ASCII characters that can be used as ETag for
* `If-Match` header Provided by user using `cache.etag` value.
*
* ```txt
* X-Axios-Cache-Etag: "<etag_value>"
* ```
*/
XAxiosCacheEtag: 'x-axios-cache-etag',
/**
* Used internally as metadata to mark the cache item as revalidatable and enabling
* stale cache state may contain `'use-cache-timestamp'` if `cache.modifiedSince` is
* `true`, otherwise will contain a date from `cache.modifiedSince`. If a date is
* provided, it can be used for `If-Modified-Since` header, otherwise the cache
* timestamp can be used for `If-Modified-Since` header.
*
* ```txt
* X-Axios-Cache-Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
* X-Axios-Cache-Last-Modified: use-cache-timestamp
* ```
*/
XAxiosCacheLastModified: 'x-axios-cache-last-modified',
/**
* Used internally as metadata to mark the cache item able to be used if the server
* returns an error. The stale-if-error response directive indicates that the cache can
* reuse a stale response when any error occurs.
*
* ```txt
* XAxiosCacheStaleIfError: <seconds>
* ```
*/
XAxiosCacheStaleIfError: 'x-axios-cache-stale-if-error'
});
const defaultHeaderInterpreter = headers => {
if (!headers) return 'not enough headers';
const cacheControl = headers[Header.CacheControl];
if (cacheControl) {
const {
noCache,
noStore,
maxAge,
maxStale,
immutable,
staleWhileRevalidate
} = parse(String(cacheControl));
// Header told that this response should not be cached.
if (noCache || noStore) {
return 'dont cache';
}
if (immutable) {
// 1 year is sufficient, as Infinity may cause problems with certain storages.
// It might not be the best way, but a year is better than none. Facebook shows
// that a browser session stays at the most 1 month.
return {
cache: 1000 * 60 * 60 * 24 * 365
};
}
if (maxAge !== undefined) {
const age = headers[Header.Age];
return {
cache: age ?
// If age is present, we must subtract it from maxAge
(maxAge - Number(age)) * 1000 : maxAge * 1000,
// Already out of date, must be requested again
stale:
// I couldn't find any documentation about who should be used, as they
// are not meant to overlap each other. But, as we cannot request in the
// background, as the stale-while-revalidate says, and we just increase
// its staleTtl when its present, max-stale is being preferred over
// stale-while-revalidate.
maxStale !== undefined ? maxStale * 1000 : staleWhileRevalidate !== undefined ? staleWhileRevalidate * 1000 : undefined
};
}
}
const expires = headers[Header.Expires];
if (expires) {
const milliseconds = Date.parse(String(expires)) - Date.now();
return milliseconds >= 0 ? {
cache: milliseconds
} : 'dont cache';
}
return 'not enough headers';
};
function _extends() {
_extends = Object.assign ? Object.assign.bind() : function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
/**
* Creates a new validateStatus function that will use the one already used and also
* accept status code 304.
*/
function createValidateStatus(oldValidate) {
return oldValidate ? status => oldValidate(status) || status === 304 : status => status >= 200 && status < 300 || status === 304;
}
/** Checks if the given method is in the methods array */
function isMethodIn(requestMethod = 'get', methodList = []) {
requestMethod = requestMethod.toLowerCase();
return methodList.some(method => method === requestMethod);
}
/**
* This function updates the cache when the request is stale. So, the next request to the
* server will be made with proper header / settings.
*/
function updateStaleRequest(cache, config) {
config.headers || (config.headers = {});
const {
etag,
modifiedSince
} = config.cache;
if (etag) {
var _cache$data;
const etagValue = etag === true ? (_cache$data = cache.data) == null ? void 0 : _cache$data.headers[Header.ETag] : etag;
if (etagValue) {
config.headers[Header.IfNoneMatch] = etagValue;
}
}
if (modifiedSince) {
config.headers[Header.IfModifiedSince] = modifiedSince === true ?
// If last-modified is not present, use the createdAt timestamp
cache.data.headers[Header.LastModified] || new Date(cache.createdAt).toUTCString() : modifiedSince.toUTCString();
}
}
/**
* Creates the new date to the cache by the provided response. Also handles possible 304
* Not Modified by updating response properties.
*/
function createCacheResponse(response, previousCache) {
if (response.status === 304 && previousCache) {
// Set the cache information into the response object
response.cached = true;
response.data = previousCache.data;
response.status = previousCache.status;
response.statusText = previousCache.statusText;
// Update possible new headers
response.headers = _extends({}, previousCache.headers, response.headers);
// return the old cache
return previousCache;
}
// New Response
return {
data: response.data,
status: response.status,
statusText: response.statusText,
headers: response.headers
};
}
function defaultRequestInterceptor(axios) {
const onFulfilled = async config => {
config.id = axios.generateKey(config);
if (config.cache === false) {
{
axios.debug({
id: config.id,
msg: 'Ignoring cache because config.cache === false',
data: config
});
}
return config;
}
// merge defaults with per request configuration
config.cache = _extends({}, axios.defaults.cache, config.cache);
if (typeof config.cache.cachePredicate === 'object' && config.cache.cachePredicate.ignoreUrls && config.url) {
for (const url of config.cache.cachePredicate.ignoreUrls) {
if (url instanceof RegExp ? (
// Handles stateful regexes
// biome-ignore lint: reduces the number of checks
url.lastIndex = 0, url.test(config.url)) : config.url.includes(url)) {
{
axios.debug({
id: config.id,
msg: `Ignored because url (${config.url}) matches ignoreUrls (${config.cache.cachePredicate.ignoreUrls})`,
data: {
url: config.url,
cachePredicate: config.cache.cachePredicate
}
});
}
return config;
}
}
}
// Applies sufficient headers to prevent other cache systems to work along with this one
//
// Its currently used before isMethodIn because if the isMethodIn returns false, the request
// shouldn't be cached an therefore neither in the browser.
if (config.cache.cacheTakeover) {
var _config$headers, _Header$CacheControl, _config$headers$_Head, _config$headers2, _Header$Pragma, _config$headers2$_Hea, _config$headers3, _Header$Expires, _config$headers3$_Hea;
(_config$headers$_Head = (_config$headers = config.headers)[_Header$CacheControl = Header.CacheControl]) != null ? _config$headers$_Head : _config$headers[_Header$CacheControl] = 'no-cache';
(_config$headers2$_Hea = (_config$headers2 = config.headers)[_Header$Pragma = Header.Pragma]) != null ? _config$headers2$_Hea : _config$headers2[_Header$Pragma] = 'no-cache';
(_config$headers3$_Hea = (_config$headers3 = config.headers)[_Header$Expires = Header.Expires]) != null ? _config$headers3$_Hea : _config$headers3[_Header$Expires] = '0';
}
if (!isMethodIn(config.method, config.cache.methods)) {
{
axios.debug({
id: config.id,
msg: `Ignored because method (${config.method}) is not in cache.methods (${config.cache.methods})`
});
}
return config;
}
// Assumes that the storage handled staled responses
let cache = await axios.storage.get(config.id, config);
const overrideCache = config.cache.override;
// Not cached, continue the request, and mark it as fetching
// biome-ignore lint/suspicious/noConfusingLabels: required to break condition in simultaneous accesses
ignoreAndRequest: if (cache.state === 'empty' || cache.state === 'stale' || overrideCache) {
// This checks for simultaneous access to a new key. The js event loop jumps on the
// first await statement, so the second (asynchronous call) request may have already
// started executing.
if (axios.waiting[config.id] && !overrideCache) {
cache = await axios.storage.get(config.id, config);
// @ts-expect-error This check is required when a request has it own cache deleted manually, lets
// say by a `axios.storage.delete(key)` and has a concurrent loading request.
// Because in this case, the cache will be empty and may still has a pending key
// on waiting map.
if (cache.state !== 'empty') {
{
axios.debug({
id: config.id,
msg: 'Waiting list had an deferred for this key, waiting for it to finish'
});
}
break ignoreAndRequest;
}
}
// Create a deferred to resolve other requests for the same key when it's completed
axios.waiting[config.id] = deferred();
// Adds a default reject handler to catch when the request gets aborted without
// others waiting for it.
axios.waiting[config.id].catch(() => undefined);
await axios.storage.set(config.id, {
state: 'loading',
previous: overrideCache ?
// Simply determine if the request is stale or not
// based if it had previous data or not
cache.data ? 'stale' : 'empty' :
// Typescript doesn't know that cache.state here can only be 'empty' or 'stale'
cache.state,
data: cache.data,
// If the cache is empty and asked to override it, use the current timestamp
createdAt: overrideCache && !cache.createdAt ? Date.now() : cache.createdAt
}, config);
if (cache.state === 'stale') {
updateStaleRequest(cache, config);
{
axios.debug({
id: config.id,
msg: 'Updated stale request'
});
}
}
config.validateStatus = createValidateStatus(config.validateStatus);
{
axios.debug({
id: config.id,
msg: 'Sending request, waiting for response',
data: {
overrideCache,
state: cache.state
}
});
}
// Hydrates any UI temporarily, if cache is available
if (cache.state === 'stale' || cache.data) {
await (config.cache.hydrate == null ? void 0 : config.cache.hydrate(cache));
}
return config;
}
let cachedResponse;
if (cache.state === 'loading') {
const deferred = axios.waiting[config.id];
// The deferred may not exists when the process is using a persistent
// storage and cancelled in the middle of a request, this would result in
// a pending loading state in the storage but no current promises to resolve
if (!deferred) {
// Hydrates any UI temporarily, if cache is available
if (cache.data) {
await (config.cache.hydrate == null ? void 0 : config.cache.hydrate(cache));
}
return config;
}
{
axios.debug({
id: config.id,
msg: 'Detected concurrent request, waiting for it to finish'
});
}
try {
cachedResponse = await deferred;
} catch (err) {
{
axios.debug({
id: config.id,
msg: 'Deferred rejected, requesting again',
data: err
});
}
// Hydrates any UI temporarily, if cache is available
/* c8 ignore next 3 */
if (cache.data) {
await (config.cache.hydrate == null ? void 0 : config.cache.hydrate(cache));
}
// The deferred is rejected when the request that we are waiting rejects its cache.
// In this case, we need to redo the request all over again.
return onFulfilled(config);
}
} else {
cachedResponse = cache.data;
}
// The cached data is already transformed after receiving the response from the server.
// Reapplying the transformation on the transformed data will have an unintended effect.
// Since the cached data is already in the desired format, there is no need to apply the transformation function again.
config.transformResponse = undefined;
// Even though the response interceptor receives this one from here,
// it has been configured to ignore cached responses = true
config.adapter = function cachedAdapter() {
return Promise.resolve({
config,
data: cachedResponse.data,
headers: cachedResponse.headers,
status: cachedResponse.status,
statusText: cachedResponse.statusText,
cached: true,
id: config.id
});
};
{
axios.debug({
id: config.id,
msg: 'Returning cached response'
});
}
return config;
};
return {
onFulfilled,
apply: () => axios.interceptors.request.use(onFulfilled)
};
}
/** Tests an response against a {@link CachePredicateObject}. */
async function testCachePredicate(response, predicate) {
if (typeof predicate === 'function') {
return predicate(response);
}
const {
statusCheck,
responseMatch,
containsHeaders
} = predicate;
if (statusCheck && !(await statusCheck(response.status)) || responseMatch && !(await responseMatch(response))) {
return false;
}
if (containsHeaders) {
for (const [header, _predicate] of Object.entries(containsHeaders)) {
var _response$headers$hea;
if (!(await _predicate( // Avoid bugs in case the header is not in lower case
(_response$headers$hea = response.headers[header.toLowerCase()]) != null ? _response$headers$hea : response.headers[header]))) {
return false;
}
}
}
return true;
}
/** Function to update all caches, from CacheProperties.update, with the new data. */
async function updateCache(storage, data, cacheUpdater) {
// Global cache update function.
if (typeof cacheUpdater === 'function') {
return cacheUpdater(data);
}
for (const [cacheKey, updater] of Object.entries(cacheUpdater)) {
if (updater === 'delete') {
await storage.remove(cacheKey, data.config);
continue;
}
const value = await storage.get(cacheKey, data.config);
if (value.state === 'loading') {
continue;
}
const newValue = await updater(value, data);
if (newValue === 'delete') {
await storage.remove(cacheKey, data.config);
continue;
}
if (newValue !== 'ignore') {
await storage.set(cacheKey, newValue, data.config);
}
}
}
function defaultResponseInterceptor(axios) {
/**
* Rejects cache for an response response.
*
* Also update the waiting list for this key by rejecting it.
*/
const rejectResponse = async (responseId, config) => {
var _axios$waiting$respon;
// Updates the cache to empty to prevent infinite loading state
await axios.storage.remove(responseId, config);
// Rejects the deferred, if present
(_axios$waiting$respon = axios.waiting[responseId]) == null || _axios$waiting$respon.reject();
delete axios.waiting[responseId];
};
const onFulfilled = async response => {
var _response$cached;
// When response.config is not present, the response is indeed a error.
if (!(response != null && response.config)) {
{
axios.debug({
msg: 'Response interceptor received an unknown response.',
data: response
});
}
// Re-throws the error
throw response;
}
response.id = response.config.id;
(_response$cached = response.cached) != null ? _response$cached : response.cached = false;
const config = response.config;
// Request interceptor merges defaults with per request configuration
const cacheConfig = config.cache;
// Response is already cached
if (response.cached) {
{
axios.debug({
id: response.id,
msg: 'Returned cached response'
});
}
return response;
}
// Skip cache: either false or weird behavior
// config.cache should always exists, at least from global config merge.
if (!cacheConfig) {
{
axios.debug({
id: response.id,
msg: 'Response with config.cache falsy',
data: response
});
}
response.cached = false;
return response;
}
// Update other entries before updating himself
if (cacheConfig.update) {
await updateCache(axios.storage, response, cacheConfig.update);
}
if (!isMethodIn(config.method, cacheConfig.methods)) {
{
axios.debug({
id: response.id,
msg: `Ignored because method (${config.method}) is not in cache.methods (${cacheConfig.methods})`,
data: {
config,
cacheConfig
}
});
}
return response;
}
const cache = await axios.storage.get(response.id, config);
if (
// If the request interceptor had a problem or it wasn't cached
cache.state !== 'loading') {
{
axios.debug({
id: response.id,
msg: "Response not cached and storage isn't loading",
data: {
cache,
response
}
});
}
return response;
}
// Config told that this response should be cached.
if (
// For 'loading' values (previous: stale), this check already ran in the past.
!cache.data && !(await testCachePredicate(response, cacheConfig.cachePredicate))) {
await rejectResponse(response.id, config);
{
axios.debug({
id: response.id,
msg: 'Cache predicate rejected this response'
});
}
return response;
}
// Avoid remnant headers from remote server to break implementation
for (const header of Object.keys(response.headers)) {
if (header.startsWith('x-axios-cache')) {
delete response.headers[header];
}
}
if (cacheConfig.etag && cacheConfig.etag !== true) {
response.headers[Header.XAxiosCacheEtag] = cacheConfig.etag;
}
if (cacheConfig.modifiedSince) {
response.headers[Header.XAxiosCacheLastModified] = cacheConfig.modifiedSince === true ? 'use-cache-timestamp' : cacheConfig.modifiedSince.toUTCString();
}
let ttl = cacheConfig.ttl || -1; // always set from global config
let staleTtl;
if (cacheConfig.interpretHeader) {
const expirationTime = axios.headerInterpreter(response.headers);
// Cache should not be used
if (expirationTime === 'dont cache') {
await rejectResponse(response.id, config);
{
axios.debug({
id: response.id,
msg: `Cache header interpreted as 'dont cache'`,
data: {
cache,
response,
expirationTime
}
});
}
return response;
}
if (expirationTime !== 'not enough headers') {
if (typeof expirationTime === 'number') {
ttl = expirationTime;
} else {
ttl = expirationTime.cache;
staleTtl = expirationTime.stale;
}
}
}
const data = createCacheResponse(response, cache.data);
if (typeof ttl === 'function') {
ttl = await ttl(response);
}
if (cacheConfig.staleIfError) {
response.headers[Header.XAxiosCacheStaleIfError] = String(ttl);
}
{
axios.debug({
id: response.id,
msg: 'Useful response configuration found',
data: {
cacheConfig,
cacheResponse: data
}
});
}
const newCache = {
state: 'cached',
ttl,
staleTtl,
createdAt: Date.now(),
data
};
// Resolve all other requests waiting for this response
const waiting = axios.waiting[response.id];
if (waiting) {
waiting.resolve(newCache.data);
delete axios.waiting[response.id];
{
axios.debug({
id: response.id,
msg: 'Found waiting deferred(s) and resolved them'
});
}
}
// Define this key as cache on the storage
await axios.storage.set(response.id, newCache, config);
{
axios.debug({
id: response.id,
msg: 'Response cached',
data: {
cache: newCache,
response
}
});
}
// Return the response with cached as false, because it was not cached at all
return response;
};
const onRejected = async error => {
// When response.config is not present, the response is indeed a error.
if (!error.isAxiosError || !error.config) {
{
axios.debug({
msg: 'FATAL: Received an non axios error in the rejected response interceptor, ignoring.',
data: error
});
}
// We should probably re-request the response to avoid an infinite loading state here
// but, since this is an unknown error, we cannot figure out what request ID to use.
// And the only solution is to let the storage actively reject the current loading state.
throw error;
}
const config = error.config;
const id = config.id;
const cacheConfig = config.cache;
const response = error.response;
// config.cache should always exist, at least from global config merge.
if (!cacheConfig || !id) {
{
axios.debug({
msg: 'Web request returned an error but cache handling is not enabled',
data: {
error
}
});
}
throw error;
}
if (!isMethodIn(config.method, cacheConfig.methods)) {
{
axios.debug({
id,
msg: `Ignored because method (${config.method}) is not in cache.methods (${cacheConfig.methods})`,
data: {
config,
cacheConfig
}
});
}
// Rejects all other requests waiting for this response
await rejectResponse(id, config);
throw error;
}
const cache = await axios.storage.get(id, config);
if (
// This will only not be loading if the interceptor broke
cache.state !== 'loading' || cache.previous !== 'stale') {
{
axios.debug({
id,
msg: 'Caught an error in the request interceptor',
data: {
cache,
error,
config
}
});
}
// Rejects all other requests waiting for this response
await rejectResponse(id, config);
throw error;
}
if (cacheConfig.staleIfError) {
const cacheControl = String(response == null ? void 0 : response.headers[Header.CacheControl]);
const staleHeader = cacheControl && parse(cacheControl).staleIfError;
const staleIfError = typeof cacheConfig.staleIfError === 'function' ? await cacheConfig.staleIfError(response, cache, error) : cacheConfig.staleIfError === true && staleHeader ? staleHeader * 1000 //staleIfError is in seconds
: cacheConfig.staleIfError;
{
axios.debug({
id,
msg: 'Found cache if stale config for rejected response',
data: {
error,
config,
staleIfError
}
});
}
if (staleIfError === true ||
// staleIfError is the number of seconds that stale is allowed to be used
typeof staleIfError === 'number' && cache.createdAt + staleIfError > Date.now()) {
var _axios$waiting$id;
// Resolve all other requests waiting for this response
(_axios$waiting$id = axios.waiting[id]) == null || _axios$waiting$id.resolve(cache.data);
delete axios.waiting[id];
// re-mark the cache as stale
await axios.storage.set(id, {
state: 'stale',
createdAt: Date.now(),
data: cache.data
}, config);
{
axios.debug({
id,
msg: 'staleIfError resolved this response with cached data',
data: {
error,
config,
cache
}
});
}
return {
cached: true,
config,
id,
data: cache.data.data,
headers: cache.data.headers,
status: cache.data.status,
statusText: cache.data.statusText
};
}
}
{
axios.debug({
id,
msg: 'Received an unknown error that could not be handled',
data: {
error,
config
}
});
}
// Rejects all other requests waiting for this response
await rejectResponse(id, config);
throw error;
};
return {
onFulfilled,
onRejected,
apply: () => axios.interceptors.response.use(onFulfilled, onRejected)
};
}
/** Returns true if the provided object was created from {@link buildStorage} function. */
const isStorage = obj => !!obj && !!obj['is-storage'];
function hasUniqueIdentifierHeader(value) {
const headers = value.data.headers;
return Header.ETag in headers || Header.LastModified in headers || Header.XAxiosCacheEtag in headers || Header.XAxiosCacheLastModified in headers;
}
/** Returns true if this has sufficient properties to stale instead of expire. */
function canStale(value) {
// Must revalidate is a special case and should not be staled
if (String(value.data.headers[Header.CacheControl])
// We could use cache-control's parse function, but this is way faster and simpler
.includes('must-revalidate')) {
return false;
}
if (hasUniqueIdentifierHeader(value)) {
return true;
}
return value.state === 'cached' && value.staleTtl !== undefined &&
// Only allow stale values after the ttl is already in the past and the staleTtl is in the future.
// In cases that just createdAt + ttl > Date.now(), isn't enough because the staleTtl could be <= 0.
// This logic only returns true when Date.now() is between the (createdAt + ttl) and (createdAt + ttl + staleTtl).
// Following the example below:
// |--createdAt--:--ttl--:---staleTtl--->
// [ past ][now is in here]
Math.abs(Date.now() - (value.createdAt + value.ttl)) <= value.staleTtl;
}
/**
* Checks if the provided cache is expired. You should also check if the cache
* {@link canStale}
*/
function isExpired(value) {
return value.ttl !== undefined && value.createdAt + value.ttl <= Date.now();
}
/**
* All integrated storages are wrappers around the `buildStorage` function. External
* libraries use it and if you want to build your own, `buildStorage` is the way to go!
*
* The exported `buildStorage` function abstracts the storage interface and requires a
* super simple object to build the storage.
*
* **Note**: You can only create an custom storage with this function.
*
* @example
*
* ```js
* const myStorage = buildStorage({
* find: () => {...},
* set: () => {...},
* remove: () => {...}
* });
*
* const axios = setupCache(axios, { storage: myStorage });
* ```
*
* @see https://axios-cache-interceptor.js.org/guide/storages#buildstorage
*/
function buildStorage({
set,
find,
remove
}) {
return {
//@ts-expect-error - we don't want to expose this
'is-storage': 1,
set,
remove,
get: async (key, config) => {
let value = await find(key, config);
if (!value) {
return {
state: 'empty'
};
}
if (value.state === 'empty' || value.state === 'loading') {
return value;
}
// Handle cached values
if (value.state === 'cached') {
if (!isExpired(value)) {
return value;
}
// Tries to stale expired value
if (!canStale(value)) {
await remove(key, config);
return {
state: 'empty'
};
}
value = {
state: 'stale',
createdAt: value.createdAt,
data: value.data,
ttl: value.staleTtl !== undefined ? value.staleTtl + value.ttl : undefined
};
await set(key, value, config);
}
// A second check in case the new stale value was created already expired.
if (!isExpired(value)) {
return value;
}
if (hasUniqueIdentifierHeader(value)) {
return value;
}
await remove(key, config);
return {
state: 'empty'
};
}
};
}
/**
* Creates a simple in-memory storage. This means that if you need to persist data between
* page or server reloads, this will not help.
*
* This is the storage used by default.
*
* If you need to modify it's data, you can do by the `data` property.
*
* @example
*
* ```js
* const memoryStorage = buildMemoryStorage();
*
* setupCache(axios, { storage: memoryStorage });
*
* // Simple example to force delete the request cache
*
* const { id } = axios.get('url');
*
* delete memoryStorage.data[id];
* ```
*
* @param {boolean | 'double'} cloneData Use `true` if the data returned by `find()`
* should be cloned to avoid mutating the original data outside the `set()` method. Use
* `'double'` to also clone before saving value in storage using `set()`. Disabled is
* default
* @param {number | false} cleanupInterval The interval in milliseconds to run a
* setInterval job of cleaning old entries. If false, the job will not be created.
* Disabled is default
* @param {number | false} maxEntries The maximum number of entries to keep in the
* storage. Its hard to determine the size of the entries, so a smart FIFO order is used
* to determine eviction. If false, no check will be done and you may grow up memory
* usage. Disabled is default
*/
function buildMemoryStorage(cloneData = false, cleanupInterval = false, maxEntries = false) {
const storage = buildStorage({
set: (key, value) => {
if (maxEntries) {
let keys = Object.keys(storage.data);
// Tries to cleanup first
if (keys.length >= maxEntries) {
storage.cleanup();
// Recalculates the keys
keys = Object.keys(storage.data);
// Keeps deleting until there's space
while (keys.length >= maxEntries) {
// There's always at least one key here, otherwise it would not be
// in the loop.
delete storage.data[keys.shift()];
}
}
}
storage.data[key] =
// Clone the value before storing to prevent future mutations
// from affecting cached data.
cloneData === 'double' ? /* c8 ignore next 3 */
typeof structuredClone === 'function' ? structuredClone(value) : JSON.parse(JSON.stringify(value)) : value;
},
remove: key => {
delete storage.data[key];
},
find: key => {
const value = storage.data[key];
/* c8 ignore next 7 */
if (cloneData && value !== undefined) {
if (typeof structuredClone === 'function') {
return structuredClone(value);
}
return JSON.parse(JSON.stringify(value));
}
return value;
}
});
storage.data = Object.create(null);
// When this program gets running for more than the specified interval, there's a good
// chance of it being a long-running process or at least have a lot of entries. Therefore,
// "faster" loop is more important than code readability.
storage.cleanup = () => {
const keys = Object.keys(storage.data);
let i = -1;
let value;
let key;
// Looping forward, as older entries are more likely to be expired
// than newer ones.
while (++i < keys.length) {
key = keys[i];
value = storage.data[key];
if (value.state === 'empty') {
// this storage returns void.
storage.remove(key);
continue;
}
// If the value is expired and can't be stale, remove it
if (value.state === 'cached' && isExpired(value) && !canStale(value)) {
// this storage returns void.
storage.remove(key);
}
}
};
if (cleanupInterval) {
storage.cleaner = setInterval(storage.cleanup, cleanupInterval);
}
return storage;
}
// Remove first and last '/' char, if present
const SLASHES_REGEX = /^\/|\/$/g;
/**
* Builds an generator that receives a {@link CacheRequestConfig} and returns a value
* hashed by {@link hash}.
*
* The value is hashed into a signed integer when the returned value from the provided
* generator is not a `string` or a `number`.
*
* You can return any type of data structure.
*
* @example
*
* ```js
* // This generator will return a hash code.
* // The code will only be the same if url, method and data are the same.
* const generator = buildKeyGenerator(({ url, method, data }) => ({
* url,
* method,
* data
* }));
* ```
*/
function buildKeyGenerator(generator) {
return request => {
if (request.id) {
return request.id;
}
const key = generator(request);
if (typeof key === 'string' || typeof key === 'number') {
return `${key}`;
}
return `${hash(key)}`;
};
}
const defaultKeyGenerator = buildKeyGenerator(({
baseURL,
url,
method,
params,
data
}) => {
// Remove trailing slashes to avoid generating different keys for the "same" final url.
if (baseURL !== undefined) {
baseURL = baseURL.replace(SLASHES_REGEX, '');
} else {
// just to have a consistent hash
baseURL = '';
}
if (url !== undefined) {
url = url.replace(SLASHES_REGEX, '');
} else {
// just to have a consistent hash
url = '';
}
if (method !== undefined) {
method = method.toLowerCase();
} else {
// just to have a consistent hash
method = 'get';
}
return {
url: baseURL + (baseURL && url ? '/' : '') + url,
params: params,
method: method,
data: data
};
});
/**
* Apply the caching interceptors for a already created axios instance.
*
* ```ts
* const axios = setupCache(axios, OPTIONS);
* ```
*
* The `setupCache` function receives global options and all [request
* specifics](https://axios-cache-interceptor.js.org/config/request-specifics) ones too.
* This way, you can customize the defaults for all requests.
*
* @param axios The already created axios instance
* @param config The config for the caching interceptors
* @returns The same instance with extended typescript types.
* @see https://axios-cache-interceptor.js.org/config
*/
function setupCache(axios, options = {}) {
var _options$ttl, _options$etag, _options$modifiedSinc, _options$interpretHea, _options$cacheTakeove, _options$staleIfError, _options$override, _options$hydrate;
const axiosCache = axios;
if (axiosCache.defaults.cache) {
throw new Error('setupCache() should be called only once');
}
axiosCache.storage = options.storage || buildMemoryStorage();
if (!isStorage(axiosCache.storage)) {
throw new Error('Use buildStorage() function');
}
axiosCache.waiting = options.waiting || {};
axiosCache.generateKey = options.generateKey || defaultKeyGenerator;
axiosCache.headerInterpreter = options.headerInterpreter || defaultHeaderInterpreter;
axiosCache.requestInterceptor = options.requestInterceptor || defaultRequestInterceptor(axiosCache);
axiosCache.responseInterceptor = options.responseInterceptor || defaultResponseInterceptor(axiosCache);
axiosCache.debug = options.debug || function noop() {};
// CacheRequestConfig values
axiosCache.defaults.cache = {
update: options.update || {},
ttl: (_options$ttl = options.ttl) != null ? _options$ttl : 1000 * 60 * 5,
// Although RFC 7231 also marks POST as cacheable, most users don't know that
// and may have problems about why their "create X" route not working.
methods: options.methods || ['get', 'head'],
cachePredicate: options.cachePredicate || {
// All cacheable status codes defined in RFC 7231
statusCheck: status => [200, 203, 300, 301, 302, 404, 405, 410, 414, 501].includes(status)
},
etag: (_options$etag = options.etag) != null ? _options$etag : true,
// This option is going to be ignored by servers when ETag is enabled
// Checks strict equality to false to avoid undefined-ish values
modifiedSince: (_options$modifiedSinc = options.modifiedSince) != null ? _options$modifiedSinc : options.etag === false,
interpretHeader: (_options$interpretHea = options.interpretHeader) != null ? _options$interpretHea : true,
cacheTakeover: (_options$cacheTakeove = options.cacheTakeover) != null ? _options$cacheTakeove : true,
staleIfError: (_options$staleIfError = options.staleIfError) != null ? _options$staleIfError : true,
override: (_options$override = options.override) != null ? _options$override : false,
hydrate: (_options$hydrate = options.hydrate) != null ? _options$hydrate : undefined
};
// Apply interceptors
axiosCache.requestInterceptor.apply();
axiosCache.responseInterceptor.apply();
return axiosCache;
}
/**
* Creates a simple storage. You can persist his data by using `sessionStorage` or
* `localStorage` with it.
*
* **ImplNote**: Without polyfill, this storage only works on browser environments.
*
* @example
*
* ```js
* const fromLocalStorage = buildWebStorage(localStorage);
* const fromSessionStorage = buildWebStorage(sessionStorage);
*
* const myStorage = new Storage();
* const fromMyStorage = buildWebStorage(myStorage);
* ```
*
* @param storage The type of web storage to use. localStorage or sessionStorage.
* @param prefix The prefix to index the storage. Useful to prevent collision between
* multiple places using the same storage.
*/
function buildWebStorage(storage, prefix = 'axios-cache-') {
return buildStorage({
find: key => {
const json = storage.getItem(prefix + key);
return json ? JSON.parse(json) : undefined;
},
remove: key => {
storage.removeItem(prefix + key);
},
set: (key, value) => {
const save = () => storage.setItem(prefix + key, JSON.stringify(value));
try {
return save();
} catch (error) {
const allValues = Object.entries(storage).filter(item => item[0].startsWith(prefix)).map(item => [item[0], JSON.parse(item[1])]);
// Remove all expired values
for (const _value of allValues) {
if (_value[1].state === 'cached' && isExpired(_value[1]) && !canStale(_value[1])) {
storage.removeItem(_value[0]);
}
}
// Try save again after removing expired values
try {
return save();
} catch (_unused) {
// Storage still full, try removing the oldest value until it can be saved
// Descending sort by createdAt
const sortedItems = allValues.sort((a, b) => (a[1].createdAt || 0) - (b[1].createdAt || 0));
for (const item of sortedItems) {
storage.removeItem(item[0]);
try {
return save();
} catch (_unused2) {
// This key didn't free all the required space
}
}
}
// Clear the cache for the specified key
storage.removeItem(prefix + key);
}
}
});
}
{
console.error('You are using a development build. Make sure to use the correct build in production\nhttps://axios-cache-interceptor.js.org/guide/getting-started\n\n');
}
export { Header, buildKeyGenerator, buildMemoryStorage, buildStorage, buildWebStorage, canStale, createCacheResponse, createValidateStatus, defaultHeaderInterpreter, defaultKeyGenerator, defaultRequestInterceptor, defaultResponseInterceptor, isExpired, isMethodIn, isStorage, setupCache, testCachePredicate, updateCache, updateStaleRequest };
//# sourceMappingURL=index.modern.js.map
/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors
* Released under the MIT License.
*/
import{parse as e}from"cache-parser";import{deferred as t}from"fast-defer";import{hash as a}from"object-code";const r=Object.freeze({IfModifiedSince:"if-modified-since",LastModified:"last-modified",IfNoneMatch:"if-none-match",CacheControl:"cache-control",Pragma:"pragma",ETag:"etag",Expires:"expires",Age:"age",XAxiosCacheEtag:"x-axios-cache-etag",XAxiosCacheLastModified:"x-axios-cache-last-modified",XAxiosCacheStaleIfError:"x-axios-cache-stale-if-error"}),n=t=>{if(!t)return"not enough headers";const a=t[r.CacheControl];if(a){const{noCache:n,noStore:o,maxAge:i,maxStale:s,immutable:c,staleWhileRevalidate:d}=e(String(a));if(n||o)return"dont cache";if(c)return{cache:31536e6};if(void 0!==i){const e=t[r.Age];return{cache:e?1e3*(i-Number(e)):1e3*i,stale:void 0!==s?1e3*s:void 0!==d?1e3*d:void 0}}}const n=t[r.Expires];if(n){const e=Date.parse(String(n))-Date.now();return e>=0?{cache:e}:"dont cache"}return"not enough headers"};function o(){return o=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var a=arguments[t];for(var r in a)Object.prototype.hasOwnProperty.call(a,r)&&(e[r]=a[r])}return e},o.apply(this,arguments)}function i(e){return e?t=>e(t)||304===t:e=>e>=200&&e<300||304===e}function s(e="get",t=[]){return e=e.toLowerCase(),t.some(t=>t===e)}function c(e,t){t.headers||(t.headers={});const{etag:a,modifiedSince:n}=t.cache;if(a){var o;const n=!0===a?null==(o=e.data)?void 0:o.headers[r.ETag]:a;n&&(t.headers[r.IfNoneMatch]=n)}n&&(t.headers[r.IfModifiedSince]=!0===n?e.data.headers[r.LastModified]||new Date(e.createdAt).toUTCString():n.toUTCString())}function d(e,t){return 304===e.status&&t?(e.cached=!0,e.data=t.data,e.status=t.status,e.statusText=t.statusText,e.headers=o({},t.headers,e.headers),t):{data:e.data,status:e.status,statusText:e.statusText,headers:e.headers}}function u(e){const a=async n=>{if(n.id=e.generateKey(n),!1===n.cache)return n;if(n.cache=o({},e.defaults.cache,n.cache),"object"==typeof n.cache.cachePredicate&&n.cache.cachePredicate.ignoreUrls&&n.url)for(const e of n.cache.cachePredicate.ignoreUrls)if(e instanceof RegExp?(e.lastIndex=0,e.test(n.url)):n.url.includes(e))return n;var d,u,l,f,h,g;if(n.cache.cacheTakeover&&(null!=(d=n.headers)[u=r.CacheControl]||(d[u]="no-cache"),null!=(l=n.headers)[f=r.Pragma]||(l[f]="no-cache"),null!=(h=n.headers)[g=r.Expires]||(h[g]="0")),!s(n.method,n.cache.methods))return n;let p=await e.storage.get(n.id,n);const m=n.cache.override;e:if("empty"===p.state||"stale"===p.state||m){if(e.waiting[n.id]&&!m&&(p=await e.storage.get(n.id,n),"empty"!==p.state))break e;return e.waiting[n.id]=t(),e.waiting[n.id].catch(()=>{}),await e.storage.set(n.id,{state:"loading",previous:m?p.data?"stale":"empty":p.state,data:p.data,createdAt:m&&!p.createdAt?Date.now():p.createdAt},n),"stale"===p.state&&c(p,n),n.validateStatus=i(n.validateStatus),("stale"===p.state||p.data)&&await(null==n.cache.hydrate?void 0:n.cache.hydrate(p)),n}let w;if("loading"===p.state){const t=e.waiting[n.id];if(!t)return p.data&&await(null==n.cache.hydrate?void 0:n.cache.hydrate(p)),n;try{w=await t}catch(e){return p.data&&await(null==n.cache.hydrate?void 0:n.cache.hydrate(p)),a(n)}}else w=p.data;return n.transformResponse=void 0,n.adapter=function(){return Promise.resolve({config:n,data:w.data,headers:w.headers,status:w.status,statusText:w.statusText,cached:!0,id:n.id})},n};return{onFulfilled:a,apply:()=>e.interceptors.request.use(a)}}async function l(e,t){if("function"==typeof t)return t(e);const{statusCheck:a,responseMatch:r,containsHeaders:n}=t;if(a&&!await a(e.status)||r&&!await r(e))return!1;if(n)for(const[t,a]of Object.entries(n)){var o;if(!await a(null!=(o=e.headers[t.toLowerCase()])?o:e.headers[t]))return!1}return!0}async function f(e,t,a){if("function"==typeof a)return a(t);for(const[r,n]of Object.entries(a)){if("delete"===n){await e.remove(r,t.config);continue}const a=await e.get(r,t.config);if("loading"===a.state)continue;const o=await n(a,t);"delete"!==o?"ignore"!==o&&await e.set(r,o,t.config):await e.remove(r,t.config)}}function h(t){const a=async(e,a)=>{var r;await t.storage.remove(e,a),null==(r=t.waiting[e])||r.reject(),delete t.waiting[e]},n=async e=>{if(null==e||!e.config)throw e;e.id=e.config.id,null!=e.cached||(e.cached=!1);const n=e.config,o=n.cache;if(e.cached)return e;if(!o)return e.cached=!1,e;if(o.update&&await f(t.storage,e,o.update),!s(n.method,o.methods))return e;const i=await t.storage.get(e.id,n);if("loading"!==i.state)return e;if(!i.data&&!await l(e,o.cachePredicate))return await a(e.id,n),e;for(const t of Object.keys(e.headers))t.startsWith("x-axios-cache")&&delete e.headers[t];o.etag&&!0!==o.etag&&(e.headers[r.XAxiosCacheEtag]=o.etag),o.modifiedSince&&(e.headers[r.XAxiosCacheLastModified]=!0===o.modifiedSince?"use-cache-timestamp":o.modifiedSince.toUTCString());let c,u=o.ttl||-1;if(o.interpretHeader){const r=t.headerInterpreter(e.headers);if("dont cache"===r)return await a(e.id,n),e;"not enough headers"!==r&&("number"==typeof r?u=r:(u=r.cache,c=r.stale))}const h=d(e,i.data);"function"==typeof u&&(u=await u(e)),o.staleIfError&&(e.headers[r.XAxiosCacheStaleIfError]=String(u));const g={state:"cached",ttl:u,staleTtl:c,createdAt:Date.now(),data:h},p=t.waiting[e.id];return p&&(p.resolve(g.data),delete t.waiting[e.id]),await t.storage.set(e.id,g,n),e},o=async n=>{if(!n.isAxiosError||!n.config)throw n;const o=n.config,i=o.id,c=o.cache,d=n.response;if(!c||!i)throw n;if(!s(o.method,c.methods))throw await a(i,o),n;const u=await t.storage.get(i,o);if("loading"!==u.state||"stale"!==u.previous)throw await a(i,o),n;if(c.staleIfError){const a=String(null==d?void 0:d.headers[r.CacheControl]),s=a&&e(a).staleIfError,f="function"==typeof c.staleIfError?await c.staleIfError(d,u,n):!0===c.staleIfError&&s?1e3*s:c.staleIfError;var l;if(!0===f||"number"==typeof f&&u.createdAt+f>Date.now())return null==(l=t.waiting[i])||l.resolve(u.data),delete t.waiting[i],await t.storage.set(i,{state:"stale",createdAt:Date.now(),data:u.data},o),{cached:!0,config:o,id:i,data:u.data.data,headers:u.data.headers,status:u.data.status,statusText:u.data.statusText}}throw await a(i,o),n};return{onFulfilled:n,onRejected:o,apply:()=>t.interceptors.response.use(n,o)}}const g=e=>!!e&&!!e["is-storage"];function p(e){const t=e.data.headers;return r.ETag in t||r.LastModified in t||r.XAxiosCacheEtag in t||r.XAxiosCacheLastModified in t}function m(e){return!String(e.data.headers[r.CacheControl]).includes("must-revalidate")&&(!!p(e)||"cached"===e.state&&void 0!==e.staleTtl&&Math.abs(Date.now()-(e.createdAt+e.ttl))<=e.staleTtl)}function w(e){return void 0!==e.ttl&&e.createdAt+e.ttl<=Date.now()}function v({set:e,find:t,remove:a}){return{"is-storage":1,set:e,remove:a,get:async(r,n)=>{let o=await t(r,n);if(!o)return{state:"empty"};if("empty"===o.state||"loading"===o.state)return o;if("cached"===o.state){if(!w(o))return o;if(!m(o))return await a(r,n),{state:"empty"};o={state:"stale",createdAt:o.createdAt,data:o.data,ttl:void 0!==o.staleTtl?o.staleTtl+o.ttl:void 0},await e(r,o,n)}return w(o)?p(o)?o:(await a(r,n),{state:"empty"}):o}}}function y(e=!1,t=!1,a=!1){const r=v({set:(t,n)=>{if(a){let e=Object.keys(r.data);if(e.length>=a)for(r.cleanup(),e=Object.keys(r.data);e.length>=a;)delete r.data[e.shift()]}r.data[t]="double"===e?"function"==typeof structuredClone?structuredClone(n):JSON.parse(JSON.stringify(n)):n},remove:e=>{delete r.data[e]},find:t=>{const a=r.data[t];return e&&void 0!==a?"function"==typeof structuredClone?structuredClone(a):JSON.parse(JSON.stringify(a)):a}});return r.data=Object.create(null),r.cleanup=()=>{const e=Object.keys(r.data);let t,a,n=-1;for(;++n<e.length;)a=e[n],t=r.data[a],"empty"!==t.state?"cached"===t.state&&w(t)&&!m(t)&&r.remove(a):r.remove(a)},t&&(r.cleaner=setInterval(r.cleanup,t)),r}const x=/^\/|\/$/g;function C(e){return t=>{if(t.id)return t.id;const r=e(t);return"string"==typeof r||"number"==typeof r?`${r}`:`${a(r)}`}}const I=C(({baseURL:e,url:t,method:a,params:r,data:n})=>(e=void 0!==e?e.replace(x,""):"",t=void 0!==t?t.replace(x,""):"",{url:e+(e&&t?"/":"")+t,params:r,method:a=void 0!==a?a.toLowerCase():"get",data:n}));function S(e,t={}){var a,r,o,i,s,c,d,l;const f=e;if(f.defaults.cache)throw new Error("setupCache() should be called only once");if(f.storage=t.storage||y(),!g(f.storage))throw new Error("Use buildStorage() function");return f.waiting=t.waiting||{},f.generateKey=t.generateKey||I,f.headerInterpreter=t.headerInterpreter||n,f.requestInterceptor=t.requestInterceptor||u(f),f.responseInterceptor=t.responseInterceptor||h(f),f.debug=t.debug||function(){},f.defaults.cache={update:t.update||{},ttl:null!=(a=t.ttl)?a:3e5,methods:t.methods||["get","head"],cachePredicate:t.cachePredicate||{statusCheck:e=>[200,203,300,301,302,404,405,410,414,501].includes(e)},etag:null==(r=t.etag)||r,modifiedSince:null!=(o=t.modifiedSince)?o:!1===t.etag,interpretHeader:null==(i=t.interpretHeader)||i,cacheTakeover:null==(s=t.cacheTakeover)||s,staleIfError:null==(c=t.staleIfError)||c,override:null!=(d=t.override)&&d,hydrate:null!=(l=t.hydrate)?l:void 0},f.requestInterceptor.apply(),f.responseInterceptor.apply(),f}function b(e,t="axios-cache-"){return v({find:a=>{const r=e.getItem(t+a);return r?JSON.parse(r):void 0},remove:a=>{e.removeItem(t+a)},set:(a,r)=>{const n=()=>e.setItem(t+a,JSON.stringify(r));try{return n()}catch(r){const o=Object.entries(e).filter(e=>e[0].startsWith(t)).map(e=>[e[0],JSON.parse(e[1])]);for(const t of o)"cached"===t[1].state&&w(t[1])&&!m(t[1])&&e.removeItem(t[0]);try{return n()}catch(t){const a=o.sort((e,t)=>(e[1].createdAt||0)-(t[1].createdAt||0));for(const t of a){e.removeItem(t[0]);try{return n()}catch(e){}}}e.removeItem(t+a)}}})}export{r as Header,C as buildKeyGenerator,y as buildMemoryStorage,v as buildStorage,b as buildWebStorage,m as canStale,d as createCacheResponse,i as createValidateStatus,n as defaultHeaderInterpreter,I as defaultKeyGenerator,u as defaultRequestInterceptor,h as defaultResponseInterceptor,w as isExpired,s as isMethodIn,g as isStorage,S as setupCache,l as testCachePredicate,f as updateCache,c as updateStaleRequest};
import { parse } from 'cache-parser';
import { deferred } from 'fast-defer';
import { hash } from 'object-code';
const Header = Object.freeze({
/**
* ```txt
* If-Modified-Since: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since
*/
IfModifiedSince: 'if-modified-since',
/**
* ```txt
* Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified
*/
LastModified: 'last-modified',
/**
* ```txt
* If-None-Match: "<etag_value>"
* If-None-Match: "<etag_value>", "<etag_value>", …
* If-None-Match: *
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match
*/
IfNoneMatch: 'if-none-match',
/**
* ```txt
* Cache-Control: max-age=604800
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
*/
CacheControl: 'cache-control',
/**
* ```txt
* Pragma: no - cache;
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Pragma
*/
Pragma: 'pragma',
/**
* ```txt
* ETag: W / '<etag_value>';
* ETag: '<etag_value>';
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
*/
ETag: 'etag',
/**
* ```txt
* Expires: <http-date>
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires
*/
Expires: 'expires',
/**
* ```txt
* Age: <delta-seconds>
* ```
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Age
*/
Age: 'age',
/**
* Used internally as metadata to mark the cache item as revalidatable and enabling
* stale cache state Contains a string of ASCII characters that can be used as ETag for
* `If-Match` header Provided by user using `cache.etag` value.
*
* ```txt
* X-Axios-Cache-Etag: "<etag_value>"
* ```
*/
XAxiosCacheEtag: 'x-axios-cache-etag',
/**
* Used internally as metadata to mark the cache item as revalidatable and enabling
* stale cache state may contain `'use-cache-timestamp'` if `cache.modifiedSince` is
* `true`, otherwise will contain a date from `cache.modifiedSince`. If a date is
* provided, it can be used for `If-Modified-Since` header, otherwise the cache
* timestamp can be used for `If-Modified-Since` header.
*
* ```txt
* X-Axios-Cache-Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
* X-Axios-Cache-Last-Modified: use-cache-timestamp
* ```
*/
XAxiosCacheLastModified: 'x-axios-cache-last-modified',
/**
* Used internally as metadata to mark the cache item able to be used if the server
* returns an error. The stale-if-error response directive indicates that the cache can
* reuse a stale response when any error occurs.
*
* ```txt
* XAxiosCacheStaleIfError: <seconds>
* ```
*/
XAxiosCacheStaleIfError: 'x-axios-cache-stale-if-error'
});
const defaultHeaderInterpreter = headers => {
if (!headers) return 'not enough headers';
const cacheControl = headers[Header.CacheControl];
if (cacheControl) {
const {
noCache,
noStore,
maxAge,
maxStale,
immutable,
staleWhileRevalidate
} = parse(String(cacheControl));
// Header told that this response should not be cached.
if (noCache || noStore) {
return 'dont cache';
}
if (immutable) {
// 1 year is sufficient, as Infinity may cause problems with certain storages.
// It might not be the best way, but a year is better than none. Facebook shows
// that a browser session stays at the most 1 month.
return {
cache: 1000 * 60 * 60 * 24 * 365
};
}
if (maxAge !== undefined) {
const age = headers[Header.Age];
return {
cache: age ?
// If age is present, we must subtract it from maxAge
(maxAge - Number(age)) * 1000 : maxAge * 1000,
// Already out of date, must be requested again
stale:
// I couldn't find any documentation about who should be used, as they
// are not meant to overlap each other. But, as we cannot request in the
// background, as the stale-while-revalidate says, and we just increase
// its staleTtl when its present, max-stale is being preferred over
// stale-while-revalidate.
maxStale !== undefined ? maxStale * 1000 : staleWhileRevalidate !== undefined ? staleWhileRevalidate * 1000 : undefined
};
}
}
const expires = headers[Header.Expires];
if (expires) {
const milliseconds = Date.parse(String(expires)) - Date.now();
return milliseconds >= 0 ? {
cache: milliseconds
} : 'dont cache';
}
return 'not enough headers';
};
function _extends() {
_extends = Object.assign ? Object.assign.bind() : function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
/**
* Creates a new validateStatus function that will use the one already used and also
* accept status code 304.
*/
function createValidateStatus(oldValidate) {
return oldValidate ? status => oldValidate(status) || status === 304 : status => status >= 200 && status < 300 || status === 304;
}
/** Checks if the given method is in the methods array */
function isMethodIn(requestMethod = 'get', methodList = []) {
requestMethod = requestMethod.toLowerCase();
return methodList.some(method => method === requestMethod);
}
/**
* This function updates the cache when the request is stale. So, the next request to the
* server will be made with proper header / settings.
*/
function updateStaleRequest(cache, config) {
config.headers || (config.headers = {});
const {
etag,
modifiedSince
} = config.cache;
if (etag) {
var _cache$data;
const etagValue = etag === true ? (_cache$data = cache.data) == null ? void 0 : _cache$data.headers[Header.ETag] : etag;
if (etagValue) {
config.headers[Header.IfNoneMatch] = etagValue;
}
}
if (modifiedSince) {
config.headers[Header.IfModifiedSince] = modifiedSince === true ?
// If last-modified is not present, use the createdAt timestamp
cache.data.headers[Header.LastModified] || new Date(cache.createdAt).toUTCString() : modifiedSince.toUTCString();
}
}
/**
* Creates the new date to the cache by the provided response. Also handles possible 304
* Not Modified by updating response properties.
*/
function createCacheResponse(response, previousCache) {
if (response.status === 304 && previousCache) {
// Set the cache information into the response object
response.cached = true;
response.data = previousCache.data;
response.status = previousCache.status;
response.statusText = previousCache.statusText;
// Update possible new headers
response.headers = _extends({}, previousCache.headers, response.headers);
// return the old cache
return previousCache;
}
// New Response
return {
data: response.data,
status: response.status,
statusText: response.statusText,
headers: response.headers
};
}
function defaultRequestInterceptor(axios) {
const onFulfilled = async config => {
config.id = axios.generateKey(config);
if (config.cache === false) {
return config;
}
// merge defaults with per request configuration
config.cache = _extends({}, axios.defaults.cache, config.cache);
if (typeof config.cache.cachePredicate === 'object' && config.cache.cachePredicate.ignoreUrls && config.url) {
for (const url of config.cache.cachePredicate.ignoreUrls) {
if (url instanceof RegExp ? (
// Handles stateful regexes
// biome-ignore lint: reduces the number of checks
url.lastIndex = 0, url.test(config.url)) : config.url.includes(url)) {
return config;
}
}
}
// Applies sufficient headers to prevent other cache systems to work along with this one
//
// Its currently used before isMethodIn because if the isMethodIn returns false, the request
// shouldn't be cached an therefore neither in the browser.
if (config.cache.cacheTakeover) {
var _config$headers, _Header$CacheControl, _config$headers$_Head, _config$headers2, _Header$Pragma, _config$headers2$_Hea, _config$headers3, _Header$Expires, _config$headers3$_Hea;
(_config$headers$_Head = (_config$headers = config.headers)[_Header$CacheControl = Header.CacheControl]) != null ? _config$headers$_Head : _config$headers[_Header$CacheControl] = 'no-cache';
(_config$headers2$_Hea = (_config$headers2 = config.headers)[_Header$Pragma = Header.Pragma]) != null ? _config$headers2$_Hea : _config$headers2[_Header$Pragma] = 'no-cache';
(_config$headers3$_Hea = (_config$headers3 = config.headers)[_Header$Expires = Header.Expires]) != null ? _config$headers3$_Hea : _config$headers3[_Header$Expires] = '0';
}
if (!isMethodIn(config.method, config.cache.methods)) {
return config;
}
// Assumes that the storage handled staled responses
let cache = await axios.storage.get(config.id, config);
const overrideCache = config.cache.override;
// Not cached, continue the request, and mark it as fetching
// biome-ignore lint/suspicious/noConfusingLabels: required to break condition in simultaneous accesses
ignoreAndRequest: if (cache.state === 'empty' || cache.state === 'stale' || overrideCache) {
// This checks for simultaneous access to a new key. The js event loop jumps on the
// first await statement, so the second (asynchronous call) request may have already
// started executing.
if (axios.waiting[config.id] && !overrideCache) {
cache = await axios.storage.get(config.id, config);
// @ts-expect-error This check is required when a request has it own cache deleted manually, lets
// say by a `axios.storage.delete(key)` and has a concurrent loading request.
// Because in this case, the cache will be empty and may still has a pending key
// on waiting map.
if (cache.state !== 'empty') {
break ignoreAndRequest;
}
}
// Create a deferred to resolve other requests for the same key when it's completed
axios.waiting[config.id] = deferred();
// Adds a default reject handler to catch when the request gets aborted without
// others waiting for it.
axios.waiting[config.id].catch(() => undefined);
await axios.storage.set(config.id, {
state: 'loading',
previous: overrideCache ?
// Simply determine if the request is stale or not
// based if it had previous data or not
cache.data ? 'stale' : 'empty' :
// Typescript doesn't know that cache.state here can only be 'empty' or 'stale'
cache.state,
data: cache.data,
// If the cache is empty and asked to override it, use the current timestamp
createdAt: overrideCache && !cache.createdAt ? Date.now() : cache.createdAt
}, config);
if (cache.state === 'stale') {
updateStaleRequest(cache, config);
}
config.validateStatus = createValidateStatus(config.validateStatus);
// Hydrates any UI temporarily, if cache is available
if (cache.state === 'stale' || cache.data) {
await (config.cache.hydrate == null ? void 0 : config.cache.hydrate(cache));
}
return config;
}
let cachedResponse;
if (cache.state === 'loading') {
const deferred = axios.waiting[config.id];
// The deferred may not exists when the process is using a persistent
// storage and cancelled in the middle of a request, this would result in
// a pending loading state in the storage but no current promises to resolve
if (!deferred) {
// Hydrates any UI temporarily, if cache is available
if (cache.data) {
await (config.cache.hydrate == null ? void 0 : config.cache.hydrate(cache));
}
return config;
}
try {
cachedResponse = await deferred;
} catch (err) {
// Hydrates any UI temporarily, if cache is available
/* c8 ignore next 3 */
if (cache.data) {
await (config.cache.hydrate == null ? void 0 : config.cache.hydrate(cache));
}
// The deferred is rejected when the request that we are waiting rejects its cache.
// In this case, we need to redo the request all over again.
return onFulfilled(config);
}
} else {
cachedResponse = cache.data;
}
// The cached data is already transformed after receiving the response from the server.
// Reapplying the transformation on the transformed data will have an unintended effect.
// Since the cached data is already in the desired format, there is no need to apply the transformation function again.
config.transformResponse = undefined;
// Even though the response interceptor receives this one from here,
// it has been configured to ignore cached responses = true
config.adapter = function cachedAdapter() {
return Promise.resolve({
config,
data: cachedResponse.data,
headers: cachedResponse.headers,
status: cachedResponse.status,
statusText: cachedResponse.statusText,
cached: true,
id: config.id
});
};
return config;
};
return {
onFulfilled,
apply: () => axios.interceptors.request.use(onFulfilled)
};
}
/** Tests an response against a {@link CachePredicateObject}. */
async function testCachePredicate(response, predicate) {
if (typeof predicate === 'function') {
return predicate(response);
}
const {
statusCheck,
responseMatch,
containsHeaders
} = predicate;
if (statusCheck && !(await statusCheck(response.status)) || responseMatch && !(await responseMatch(response))) {
return false;
}
if (containsHeaders) {
for (const [header, _predicate] of Object.entries(containsHeaders)) {
var _response$headers$hea;
if (!(await _predicate( // Avoid bugs in case the header is not in lower case
(_response$headers$hea = response.headers[header.toLowerCase()]) != null ? _response$headers$hea : response.headers[header]))) {
return false;
}
}
}
return true;
}
/** Function to update all caches, from CacheProperties.update, with the new data. */
async function updateCache(storage, data, cacheUpdater) {
// Global cache update function.
if (typeof cacheUpdater === 'function') {
return cacheUpdater(data);
}
for (const [cacheKey, updater] of Object.entries(cacheUpdater)) {
if (updater === 'delete') {
await storage.remove(cacheKey, data.config);
continue;
}
const value = await storage.get(cacheKey, data.config);
if (value.state === 'loading') {
continue;
}
const newValue = await updater(value, data);
if (newValue === 'delete') {
await storage.remove(cacheKey, data.config);
continue;
}
if (newValue !== 'ignore') {
await storage.set(cacheKey, newValue, data.config);
}
}
}
function defaultResponseInterceptor(axios) {
/**
* Rejects cache for an response response.
*
* Also update the waiting list for this key by rejecting it.
*/
const rejectResponse = async (responseId, config) => {
var _axios$waiting$respon;
// Updates the cache to empty to prevent infinite loading state
await axios.storage.remove(responseId, config);
// Rejects the deferred, if present
(_axios$waiting$respon = axios.waiting[responseId]) == null || _axios$waiting$respon.reject();
delete axios.waiting[responseId];
};
const onFulfilled = async response => {
var _response$cached;
// When response.config is not present, the response is indeed a error.
if (!(response != null && response.config)) {
// Re-throws the error
throw response;
}
response.id = response.config.id;
(_response$cached = response.cached) != null ? _response$cached : response.cached = false;
const config = response.config;
// Request interceptor merges defaults with per request configuration
const cacheConfig = config.cache;
// Response is already cached
if (response.cached) {
return response;
}
// Skip cache: either false or weird behavior
// config.cache should always exists, at least from global config merge.
if (!cacheConfig) {
response.cached = false;
return response;
}
// Update other entries before updating himself
if (cacheConfig.update) {
await updateCache(axios.storage, response, cacheConfig.update);
}
if (!isMethodIn(config.method, cacheConfig.methods)) {
return response;
}
const cache = await axios.storage.get(response.id, config);
if (
// If the request interceptor had a problem or it wasn't cached
cache.state !== 'loading') {
return response;
}
// Config told that this response should be cached.
if (
// For 'loading' values (previous: stale), this check already ran in the past.
!cache.data && !(await testCachePredicate(response, cacheConfig.cachePredicate))) {
await rejectResponse(response.id, config);
return response;
}
// Avoid remnant headers from remote server to break implementation
for (const header of Object.keys(response.headers)) {
if (header.startsWith('x-axios-cache')) {
delete response.headers[header];
}
}
if (cacheConfig.etag && cacheConfig.etag !== true) {
response.headers[Header.XAxiosCacheEtag] = cacheConfig.etag;
}
if (cacheConfig.modifiedSince) {
response.headers[Header.XAxiosCacheLastModified] = cacheConfig.modifiedSince === true ? 'use-cache-timestamp' : cacheConfig.modifiedSince.toUTCString();
}
let ttl = cacheConfig.ttl || -1; // always set from global config
let staleTtl;
if (cacheConfig.interpretHeader) {
const expirationTime = axios.headerInterpreter(response.headers);
// Cache should not be used
if (expirationTime === 'dont cache') {
await rejectResponse(response.id, config);
return response;
}
if (expirationTime !== 'not enough headers') {
if (typeof expirationTime === 'number') {
ttl = expirationTime;
} else {
ttl = expirationTime.cache;
staleTtl = expirationTime.stale;
}
}
}
const data = createCacheResponse(response, cache.data);
if (typeof ttl === 'function') {
ttl = await ttl(response);
}
if (cacheConfig.staleIfError) {
response.headers[Header.XAxiosCacheStaleIfError] = String(ttl);
}
const newCache = {
state: 'cached',
ttl,
staleTtl,
createdAt: Date.now(),
data
};
// Resolve all other requests waiting for this response
const waiting = axios.waiting[response.id];
if (waiting) {
waiting.resolve(newCache.data);
delete axios.waiting[response.id];
}
// Define this key as cache on the storage
await axios.storage.set(response.id, newCache, config);
// Return the response with cached as false, because it was not cached at all
return response;
};
const onRejected = async error => {
// When response.config is not present, the response is indeed a error.
if (!error.isAxiosError || !error.config) {
// We should probably re-request the response to avoid an infinite loading state here
// but, since this is an unknown error, we cannot figure out what request ID to use.
// And the only solution is to let the storage actively reject the current loading state.
throw error;
}
const config = error.config;
const id = config.id;
const cacheConfig = config.cache;
const response = error.response;
// config.cache should always exist, at least from global config merge.
if (!cacheConfig || !id) {
throw error;
}
if (!isMethodIn(config.method, cacheConfig.methods)) {
// Rejects all other requests waiting for this response
await rejectResponse(id, config);
throw error;
}
const cache = await axios.storage.get(id, config);
if (
// This will only not be loading if the interceptor broke
cache.state !== 'loading' || cache.previous !== 'stale') {
// Rejects all other requests waiting for this response
await rejectResponse(id, config);
throw error;
}
if (cacheConfig.staleIfError) {
const cacheControl = String(response == null ? void 0 : response.headers[Header.CacheControl]);
const staleHeader = cacheControl && parse(cacheControl).staleIfError;
const staleIfError = typeof cacheConfig.staleIfError === 'function' ? await cacheConfig.staleIfError(response, cache, error) : cacheConfig.staleIfError === true && staleHeader ? staleHeader * 1000 //staleIfError is in seconds
: cacheConfig.staleIfError;
if (staleIfError === true ||
// staleIfError is the number of seconds that stale is allowed to be used
typeof staleIfError === 'number' && cache.createdAt + staleIfError > Date.now()) {
var _axios$waiting$id;
// Resolve all other requests waiting for this response
(_axios$waiting$id = axios.waiting[id]) == null || _axios$waiting$id.resolve(cache.data);
delete axios.waiting[id];
// re-mark the cache as stale
await axios.storage.set(id, {
state: 'stale',
createdAt: Date.now(),
data: cache.data
}, config);
return {
cached: true,
config,
id,
data: cache.data.data,
headers: cache.data.headers,
status: cache.data.status,
statusText: cache.data.statusText
};
}
}
// Rejects all other requests waiting for this response
await rejectResponse(id, config);
throw error;
};
return {
onFulfilled,
onRejected,
apply: () => axios.interceptors.response.use(onFulfilled, onRejected)
};
}
/** Returns true if the provided object was created from {@link buildStorage} function. */
const isStorage = obj => !!obj && !!obj['is-storage'];
function hasUniqueIdentifierHeader(value) {
const headers = value.data.headers;
return Header.ETag in headers || Header.LastModified in headers || Header.XAxiosCacheEtag in headers || Header.XAxiosCacheLastModified in headers;
}
/** Returns true if this has sufficient properties to stale instead of expire. */
function canStale(value) {
// Must revalidate is a special case and should not be staled
if (String(value.data.headers[Header.CacheControl])
// We could use cache-control's parse function, but this is way faster and simpler
.includes('must-revalidate')) {
return false;
}
if (hasUniqueIdentifierHeader(value)) {
return true;
}
return value.state === 'cached' && value.staleTtl !== undefined &&
// Only allow stale values after the ttl is already in the past and the staleTtl is in the future.
// In cases that just createdAt + ttl > Date.now(), isn't enough because the staleTtl could be <= 0.
// This logic only returns true when Date.now() is between the (createdAt + ttl) and (createdAt + ttl + staleTtl).
// Following the example below:
// |--createdAt--:--ttl--:---staleTtl--->
// [ past ][now is in here]
Math.abs(Date.now() - (value.createdAt + value.ttl)) <= value.staleTtl;
}
/**
* Checks if the provided cache is expired. You should also check if the cache
* {@link canStale}
*/
function isExpired(value) {
return value.ttl !== undefined && value.createdAt + value.ttl <= Date.now();
}
/**
* All integrated storages are wrappers around the `buildStorage` function. External
* libraries use it and if you want to build your own, `buildStorage` is the way to go!
*
* The exported `buildStorage` function abstracts the storage interface and requires a
* super simple object to build the storage.
*
* **Note**: You can only create an custom storage with this function.
*
* @example
*
* ```js
* const myStorage = buildStorage({
* find: () => {...},
* set: () => {...},
* remove: () => {...}
* });
*
* const axios = setupCache(axios, { storage: myStorage });
* ```
*
* @see https://axios-cache-interceptor.js.org/guide/storages#buildstorage
*/
function buildStorage({
set,
find,
remove
}) {
return {
//@ts-expect-error - we don't want to expose this
'is-storage': 1,
set,
remove,
get: async (key, config) => {
let value = await find(key, config);
if (!value) {
return {
state: 'empty'
};
}
if (value.state === 'empty' || value.state === 'loading') {
return value;
}
// Handle cached values
if (value.state === 'cached') {
if (!isExpired(value)) {
return value;
}
// Tries to stale expired value
if (!canStale(value)) {
await remove(key, config);
return {
state: 'empty'
};
}
value = {
state: 'stale',
createdAt: value.createdAt,
data: value.data,
ttl: value.staleTtl !== undefined ? value.staleTtl + value.ttl : undefined
};
await set(key, value, config);
}
// A second check in case the new stale value was created already expired.
if (!isExpired(value)) {
return value;
}
if (hasUniqueIdentifierHeader(value)) {
return value;
}
await remove(key, config);
return {
state: 'empty'
};
}
};
}
/**
* Creates a simple in-memory storage. This means that if you need to persist data between
* page or server reloads, this will not help.
*
* This is the storage used by default.
*
* If you need to modify it's data, you can do by the `data` property.
*
* @example
*
* ```js
* const memoryStorage = buildMemoryStorage();
*
* setupCache(axios, { storage: memoryStorage });
*
* // Simple example to force delete the request cache
*
* const { id } = axios.get('url');
*
* delete memoryStorage.data[id];
* ```
*
* @param {boolean | 'double'} cloneData Use `true` if the data returned by `find()`
* should be cloned to avoid mutating the original data outside the `set()` method. Use
* `'double'` to also clone before saving value in storage using `set()`. Disabled is
* default
* @param {number | false} cleanupInterval The interval in milliseconds to run a
* setInterval job of cleaning old entries. If false, the job will not be created.
* Disabled is default
* @param {number | false} maxEntries The maximum number of entries to keep in the
* storage. Its hard to determine the size of the entries, so a smart FIFO order is used
* to determine eviction. If false, no check will be done and you may grow up memory
* usage. Disabled is default
*/
function buildMemoryStorage(cloneData = false, cleanupInterval = false, maxEntries = false) {
const storage = buildStorage({
set: (key, value) => {
if (maxEntries) {
let keys = Object.keys(storage.data);
// Tries to cleanup first
if (keys.length >= maxEntries) {
storage.cleanup();
// Recalculates the keys
keys = Object.keys(storage.data);
// Keeps deleting until there's space
while (keys.length >= maxEntries) {
// There's always at least one key here, otherwise it would not be
// in the loop.
delete storage.data[keys.shift()];
}
}
}
storage.data[key] =
// Clone the value before storing to prevent future mutations
// from affecting cached data.
cloneData === 'double' ? /* c8 ignore next 3 */
typeof structuredClone === 'function' ? structuredClone(value) : JSON.parse(JSON.stringify(value)) : value;
},
remove: key => {
delete storage.data[key];
},
find: key => {
const value = storage.data[key];
/* c8 ignore next 7 */
if (cloneData && value !== undefined) {
if (typeof structuredClone === 'function') {
return structuredClone(value);
}
return JSON.parse(JSON.stringify(value));
}
return value;
}
});
storage.data = Object.create(null);
// When this program gets running for more than the specified interval, there's a good
// chance of it being a long-running process or at least have a lot of entries. Therefore,
// "faster" loop is more important than code readability.
storage.cleanup = () => {
const keys = Object.keys(storage.data);
let i = -1;
let value;
let key;
// Looping forward, as older entries are more likely to be expired
// than newer ones.
while (++i < keys.length) {
key = keys[i];
value = storage.data[key];
if (value.state === 'empty') {
// this storage returns void.
storage.remove(key);
continue;
}
// If the value is expired and can't be stale, remove it
if (value.state === 'cached' && isExpired(value) && !canStale(value)) {
// this storage returns void.
storage.remove(key);
}
}
};
if (cleanupInterval) {
storage.cleaner = setInterval(storage.cleanup, cleanupInterval);
}
return storage;
}
// Remove first and last '/' char, if present
const SLASHES_REGEX = /^\/|\/$/g;
/**
* Builds an generator that receives a {@link CacheRequestConfig} and returns a value
* hashed by {@link hash}.
*
* The value is hashed into a signed integer when the returned value from the provided
* generator is not a `string` or a `number`.
*
* You can return any type of data structure.
*
* @example
*
* ```js
* // This generator will return a hash code.
* // The code will only be the same if url, method and data are the same.
* const generator = buildKeyGenerator(({ url, method, data }) => ({
* url,
* method,
* data
* }));
* ```
*/
function buildKeyGenerator(generator) {
return request => {
if (request.id) {
return request.id;
}
const key = generator(request);
if (typeof key === 'string' || typeof key === 'number') {
return `${key}`;
}
return `${hash(key)}`;
};
}
const defaultKeyGenerator = buildKeyGenerator(({
baseURL,
url,
method,
params,
data
}) => {
// Remove trailing slashes to avoid generating different keys for the "same" final url.
if (baseURL !== undefined) {
baseURL = baseURL.replace(SLASHES_REGEX, '');
} else {
// just to have a consistent hash
baseURL = '';
}
if (url !== undefined) {
url = url.replace(SLASHES_REGEX, '');
} else {
// just to have a consistent hash
url = '';
}
if (method !== undefined) {
method = method.toLowerCase();
} else {
// just to have a consistent hash
method = 'get';
}
return {
url: baseURL + (baseURL && url ? '/' : '') + url,
params: params,
method: method,
data: data
};
});
/**
* Apply the caching interceptors for a already created axios instance.
*
* ```ts
* const axios = setupCache(axios, OPTIONS);
* ```
*
* The `setupCache` function receives global options and all [request
* specifics](https://axios-cache-interceptor.js.org/config/request-specifics) ones too.
* This way, you can customize the defaults for all requests.
*
* @param axios The already created axios instance
* @param config The config for the caching interceptors
* @returns The same instance with extended typescript types.
* @see https://axios-cache-interceptor.js.org/config
*/
function setupCache(axios, options = {}) {
var _options$ttl, _options$etag, _options$modifiedSinc, _options$interpretHea, _options$cacheTakeove, _options$staleIfError, _options$override, _options$hydrate;
const axiosCache = axios;
if (axiosCache.defaults.cache) {
throw new Error('setupCache() should be called only once');
}
axiosCache.storage = options.storage || buildMemoryStorage();
if (!isStorage(axiosCache.storage)) {
throw new Error('Use buildStorage() function');
}
axiosCache.waiting = options.waiting || {};
axiosCache.generateKey = options.generateKey || defaultKeyGenerator;
axiosCache.headerInterpreter = options.headerInterpreter || defaultHeaderInterpreter;
axiosCache.requestInterceptor = options.requestInterceptor || defaultRequestInterceptor(axiosCache);
axiosCache.responseInterceptor = options.responseInterceptor || defaultResponseInterceptor(axiosCache);
axiosCache.debug = options.debug || function noop() {};
// CacheRequestConfig values
axiosCache.defaults.cache = {
update: options.update || {},
ttl: (_options$ttl = options.ttl) != null ? _options$ttl : 1000 * 60 * 5,
// Although RFC 7231 also marks POST as cacheable, most users don't know that
// and may have problems about why their "create X" route not working.
methods: options.methods || ['get', 'head'],
cachePredicate: options.cachePredicate || {
// All cacheable status codes defined in RFC 7231
statusCheck: status => [200, 203, 300, 301, 302, 404, 405, 410, 414, 501].includes(status)
},
etag: (_options$etag = options.etag) != null ? _options$etag : true,
// This option is going to be ignored by servers when ETag is enabled
// Checks strict equality to false to avoid undefined-ish values
modifiedSince: (_options$modifiedSinc = options.modifiedSince) != null ? _options$modifiedSinc : options.etag === false,
interpretHeader: (_options$interpretHea = options.interpretHeader) != null ? _options$interpretHea : true,
cacheTakeover: (_options$cacheTakeove = options.cacheTakeover) != null ? _options$cacheTakeove : true,
staleIfError: (_options$staleIfError = options.staleIfError) != null ? _options$staleIfError : true,
override: (_options$override = options.override) != null ? _options$override : false,
hydrate: (_options$hydrate = options.hydrate) != null ? _options$hydrate : undefined
};
// Apply interceptors
axiosCache.requestInterceptor.apply();
axiosCache.responseInterceptor.apply();
return axiosCache;
}
/**
* Creates a simple storage. You can persist his data by using `sessionStorage` or
* `localStorage` with it.
*
* **ImplNote**: Without polyfill, this storage only works on browser environments.
*
* @example
*
* ```js
* const fromLocalStorage = buildWebStorage(localStorage);
* const fromSessionStorage = buildWebStorage(sessionStorage);
*
* const myStorage = new Storage();
* const fromMyStorage = buildWebStorage(myStorage);
* ```
*
* @param storage The type of web storage to use. localStorage or sessionStorage.
* @param prefix The prefix to index the storage. Useful to prevent collision between
* multiple places using the same storage.
*/
function buildWebStorage(storage, prefix = 'axios-cache-') {
return buildStorage({
find: key => {
const json = storage.getItem(prefix + key);
return json ? JSON.parse(json) : undefined;
},
remove: key => {
storage.removeItem(prefix + key);
},
set: (key, value) => {
const save = () => storage.setItem(prefix + key, JSON.stringify(value));
try {
return save();
} catch (error) {
const allValues = Object.entries(storage).filter(item => item[0].startsWith(prefix)).map(item => [item[0], JSON.parse(item[1])]);
// Remove all expired values
for (const _value of allValues) {
if (_value[1].state === 'cached' && isExpired(_value[1]) && !canStale(_value[1])) {
storage.removeItem(_value[0]);
}
}
// Try save again after removing expired values
try {
return save();
} catch (_unused) {
// Storage still full, try removing the oldest value until it can be saved
// Descending sort by createdAt
const sortedItems = allValues.sort((a, b) => (a[1].createdAt || 0) - (b[1].createdAt || 0));
for (const item of sortedItems) {
storage.removeItem(item[0]);
try {
return save();
} catch (_unused2) {
// This key didn't free all the required space
}
}
}
// Clear the cache for the specified key
storage.removeItem(prefix + key);
}
}
});
}
export { Header, buildKeyGenerator, buildMemoryStorage, buildStorage, buildWebStorage, canStale, createCacheResponse, createValidateStatus, defaultHeaderInterpreter, defaultKeyGenerator, defaultRequestInterceptor, defaultResponseInterceptor, isExpired, isMethodIn, isStorage, setupCache, testCachePredicate, updateCache, updateStaleRequest };
//# sourceMappingURL=index.modern.js.map
/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

/*!
* Axios Cache Interceptor 1.5.0
* Axios Cache Interceptor 1.5.1
* (c) 2021-present Arthur Fiorette & Contributors

@@ -4,0 +4,0 @@ * Released under the MIT License.

{
"name": "axios-cache-interceptor",
"version": "1.5.0",
"version": "1.5.1",
"description": "Cache interceptor for axios",

@@ -5,0 +5,0 @@ "keywords": [

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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