axios-cache-interceptor
Advanced tools
Comparing version 0.7.8 to 0.7.9
import type { AxiosDefaults, AxiosInstance, AxiosInterceptorManager, AxiosRequestConfig, AxiosResponse } from 'axios'; | ||
import type { CacheInstance, CacheProperties } from './cache'; | ||
/** | ||
* @template R The type returned by this response | ||
* @template D The type that the request body was | ||
*/ | ||
export declare type CacheAxiosResponse<R = any, D = any> = AxiosResponse<R, D> & { | ||
config: CacheRequestConfig<D>; | ||
/** The id used for this request. if config specified an id, the id will be returned */ | ||
id: string; | ||
/** A simple boolean to check whether this request was cached or not */ | ||
cached: boolean; | ||
}; | ||
/** | ||
* Options that can be overridden per request | ||
* | ||
* @template D The type for the request body | ||
*/ | ||
export declare type CacheRequestConfig<D = any> = AxiosRequestConfig<D> & { | ||
/** | ||
* An id for this request, if this request is used in cache, only the last request made | ||
* with this id will be returned. | ||
* | ||
* @default undefined | ||
*/ | ||
id?: string; | ||
/** | ||
* All cache options for the request. | ||
* | ||
* False means ignore everything about cache, for this request. | ||
*/ | ||
cache?: false | Partial<CacheProperties>; | ||
}; | ||
/** | ||
* Same as the AxiosInstance but with CacheRequestConfig as a config type and | ||
* CacheAxiosResponse as response type. | ||
* | ||
* @see AxiosInstance | ||
* @see CacheRequestConfig | ||
* @see CacheInstance | ||
*/ | ||
export interface AxiosCacheInstance extends CacheInstance, AxiosInstance { | ||
/** | ||
* @template T The type returned by this response | ||
* @template R The custom response type that the request can return | ||
* @template D The type that the request body use | ||
*/ | ||
<T = any, D = any, R = CacheAxiosResponse<T, D>>(config: CacheRequestConfig<D>): Promise<R>; | ||
/** | ||
* @template T The type returned by this response | ||
* @template R The custom response type that the request can return | ||
* @template D The type that the request body use | ||
*/ | ||
<T = any, D = any, R = CacheAxiosResponse<T, D>>(url: string, config?: CacheRequestConfig<D>): Promise<R>; | ||
@@ -22,12 +62,53 @@ defaults: AxiosDefaults<any> & { | ||
}; | ||
/** @template D The type that the request body use */ | ||
getUri<D>(config?: CacheRequestConfig<D>): string; | ||
/** | ||
* @template T The type returned by this response | ||
* @template R The custom response type that the request can return | ||
* @template D The type that the request body use | ||
*/ | ||
request<T = any, D = any, R = CacheAxiosResponse<T, D>>(config?: CacheRequestConfig<D>): Promise<R>; | ||
/** | ||
* @template T The type returned by this response | ||
* @template R The custom response type that the request can return | ||
* @template D The type that the request body use | ||
*/ | ||
get<T = any, D = any, R = CacheAxiosResponse<T, D>>(url: string, config?: CacheRequestConfig<D>): Promise<R>; | ||
/** | ||
* @template T The type returned by this response | ||
* @template R The custom response type that the request can return | ||
* @template D The type that the request body use | ||
*/ | ||
delete<T = any, D = any, R = CacheAxiosResponse<T, D>>(url: string, config?: CacheRequestConfig<D>): Promise<R>; | ||
/** | ||
* @template T The type returned by this response | ||
* @template R The custom response type that the request can return | ||
* @template D The type that the request body use | ||
*/ | ||
head<T = any, D = any, R = CacheAxiosResponse<T, D>>(url: string, config?: CacheRequestConfig<D>): Promise<R>; | ||
/** | ||
* @template T The type returned by this response | ||
* @template R The custom response type that the request can return | ||
* @template D The type that the request body use | ||
*/ | ||
options<T = any, D = any, R = CacheAxiosResponse<T, D>>(url: string, config?: CacheRequestConfig<D>): Promise<R>; | ||
/** | ||
* @template T The type returned by this response | ||
* @template R The custom response type that the request can return | ||
* @template D The type that the request body use | ||
*/ | ||
post<T = any, D = any, R = CacheAxiosResponse<T, D>>(url: string, data?: D, config?: CacheRequestConfig<D>): Promise<R>; | ||
/** | ||
* @template T The type returned by this response | ||
* @template R The custom response type that the request can return | ||
* @template D The type that the request body use | ||
*/ | ||
put<T = any, D = any, R = CacheAxiosResponse<T, D>>(url: string, data?: D, config?: CacheRequestConfig<D>): Promise<R>; | ||
/** | ||
* @template T The type returned by this response | ||
* @template R The custom response type that the request can return | ||
* @template D The type that the request body use | ||
*/ | ||
patch<T = any, D = any, R = CacheAxiosResponse<T, D>>(url: string, data?: D, config?: CacheRequestConfig<D>): Promise<R>; | ||
} | ||
//# sourceMappingURL=axios.d.ts.map |
@@ -11,18 +11,91 @@ import type { Method } from 'axios'; | ||
export declare type CacheProperties = { | ||
/** | ||
* The time until the cached value is expired in milliseconds. | ||
* | ||
* When using `interpretHeader: true`, this value will only be used if the interpreter | ||
* can't determine their TTL value to override this | ||
* | ||
* **Note**: a custom storage implementation may not respect this. | ||
* | ||
* @default 1000 * 60 * 5 // 5 Minutes | ||
*/ | ||
ttl: number; | ||
/** | ||
* If this interceptor should configure the cache from the request cache header When | ||
* used, the ttl property is ignored | ||
* | ||
* @default false | ||
*/ | ||
interpretHeader: boolean; | ||
/** | ||
* All methods that should be cached. | ||
* | ||
* @default ['get'] | ||
*/ | ||
methods: Lowercase<Method>[]; | ||
/** | ||
* The function to check if the response code permit being cached. | ||
* | ||
* @default {statusCheck: [200, 399]} | ||
*/ | ||
cachePredicate: CachePredicate; | ||
/** | ||
* Once the request is resolved, this specifies what requests should we change the | ||
* cache. Can be used to update the request or delete other caches. | ||
* | ||
* If the function returns nothing, the entry is deleted | ||
* | ||
* This is independent if the request made was cached or not. | ||
* | ||
* The id used is the same as the id on `CacheRequestConfig['id']`, auto-generated or not. | ||
* | ||
* @default {{}} | ||
*/ | ||
update: Record<string, CacheUpdater>; | ||
/** | ||
* If the request should handle ETag and If-None-Match support. Use a string to force a | ||
* custom value or true to use the response ETag | ||
* | ||
* @default false | ||
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag | ||
*/ | ||
etag: string | boolean; | ||
/** | ||
* Use If-Modified-Since header in this request. Use a date to force a custom value or | ||
* true to use the last cached timestamp. If never cached before, the header is not set. | ||
* | ||
* @default false | ||
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since | ||
*/ | ||
modifiedSince: Date | boolean; | ||
}; | ||
export interface CacheInstance { | ||
/** | ||
* The storage to save the cache data. | ||
* | ||
* @default new MemoryAxiosStorage() | ||
*/ | ||
storage: AxiosStorage; | ||
/** | ||
* The function used to create different keys for each request. Defaults to a function | ||
* that priorizes the id, and if not specified, a string is generated using the method, | ||
* baseUrl, params, and url | ||
*/ | ||
generateKey: KeyGenerator; | ||
/** | ||
* A simple object that holds all deferred objects until it is resolved or rejected. | ||
* | ||
* Can be used to listen when a request is cached or not. | ||
*/ | ||
waiting: Record<string, Deferred<CachedResponse>>; | ||
/** | ||
* The function to parse and interpret response headers. Only used if | ||
* cache.interpretHeader is true. | ||
*/ | ||
headerInterpreter: HeadersInterpreter; | ||
/** The request interceptor that will be used to handle the cache. */ | ||
requestInterceptor: AxiosInterceptor<CacheRequestConfig<any>>; | ||
/** The response interceptor that will be used to handle the cache. */ | ||
responseInterceptor: AxiosInterceptor<CacheAxiosResponse<any, any>>; | ||
} | ||
//# sourceMappingURL=cache.d.ts.map |
@@ -5,4 +5,43 @@ import type { AxiosInstance } from 'axios'; | ||
export declare type CacheOptions = Partial<CacheInstance> & Partial<CacheProperties>; | ||
export declare function createCache(axios: AxiosInstance, { storage, generateKey, waiting, headerInterpreter, requestInterceptor, responseInterceptor, ...cacheOptions }?: CacheOptions): AxiosCacheInstance; | ||
export declare const useCache: typeof createCache; | ||
/** | ||
* Apply the caching interceptors for a already created axios instance. | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* import Axios from 'axios'; | ||
* import { setupCache, AxiosCacheInstance } from 'axios-cache-interceptor'; | ||
* | ||
* // instance will have our custom typings from the return of this function | ||
* const instance = setupCache( | ||
* Axios.create({ | ||
* // Axios options | ||
* }), | ||
* { | ||
* // Axios-cache-interceptor options | ||
* } | ||
* ); | ||
* | ||
* // OR | ||
* | ||
* const instance = axios.create({ | ||
* // Axios options | ||
* }) as AxiosCacheInstance; | ||
* | ||
* // As this functions returns the same axios instance but only with | ||
* // different typings, you can ignore the function return. | ||
* setupCache(instance, { | ||
* // Axios-cache-interceptor options | ||
* }); | ||
* ``` | ||
* | ||
* @param axios The already created axios instance | ||
* @param config The config for the caching interceptors | ||
* @returns The same instance with better typescript types. | ||
*/ | ||
export declare function setupCache(axios: AxiosInstance, { storage, generateKey, waiting, headerInterpreter, requestInterceptor, responseInterceptor, ...cacheOptions }?: CacheOptions): AxiosCacheInstance; | ||
/** @deprecated */ | ||
export declare const useCache: "use setupCache instead"; | ||
/** @deprecated */ | ||
export declare const createCache: "use setupCache instead"; | ||
//# sourceMappingURL=create.d.ts.map |
@@ -14,3 +14,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.useCache = exports.createCache = void 0; | ||
exports.createCache = exports.useCache = exports.setupCache = void 0; | ||
const interpreter_1 = require("../header/interpreter"); | ||
@@ -21,3 +21,39 @@ const request_1 = require("../interceptors/request"); | ||
const key_generator_1 = require("../util/key-generator"); | ||
function createCache(axios, _a = {}) { | ||
/** | ||
* Apply the caching interceptors for a already created axios instance. | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* import Axios from 'axios'; | ||
* import { setupCache, AxiosCacheInstance } from 'axios-cache-interceptor'; | ||
* | ||
* // instance will have our custom typings from the return of this function | ||
* const instance = setupCache( | ||
* Axios.create({ | ||
* // Axios options | ||
* }), | ||
* { | ||
* // Axios-cache-interceptor options | ||
* } | ||
* ); | ||
* | ||
* // OR | ||
* | ||
* const instance = axios.create({ | ||
* // Axios options | ||
* }) as AxiosCacheInstance; | ||
* | ||
* // As this functions returns the same axios instance but only with | ||
* // different typings, you can ignore the function return. | ||
* setupCache(instance, { | ||
* // Axios-cache-interceptor options | ||
* }); | ||
* ``` | ||
* | ||
* @param axios The already created axios instance | ||
* @param config The config for the caching interceptors | ||
* @returns The same instance with better typescript types. | ||
*/ | ||
function setupCache(axios, _a = {}) { | ||
var { storage, generateKey, waiting, headerInterpreter, requestInterceptor, responseInterceptor } = _a, cacheOptions = __rest(_a, ["storage", "generateKey", "waiting", "headerInterpreter", "requestInterceptor", "responseInterceptor"]); | ||
@@ -33,3 +69,5 @@ const axiosCache = axios; | ||
responseInterceptor || new response_1.CacheResponseInterceptor(axiosCache); | ||
// CacheRequestConfig values | ||
axiosCache.defaults = Object.assign(Object.assign({}, axios.defaults), { cache: Object.assign({ ttl: 1000 * 60 * 5, interpretHeader: false, methods: ['get'], cachePredicate: { statusCheck: [200, 399] }, etag: false, modifiedSince: false, update: {} }, cacheOptions) }); | ||
// Apply interceptors | ||
axiosCache.requestInterceptor.use(); | ||
@@ -39,3 +77,6 @@ axiosCache.responseInterceptor.use(); | ||
} | ||
exports.createCache = createCache; | ||
exports.useCache = createCache; | ||
exports.setupCache = setupCache; | ||
/** @deprecated */ | ||
exports.useCache = setupCache; | ||
/** @deprecated */ | ||
exports.createCache = setupCache; |
@@ -8,5 +8,7 @@ "use strict"; | ||
if (headers_1.Header.CacheControl in headers) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
return interpretCacheControl(headers[headers_1.Header.CacheControl], headers); | ||
} | ||
if (headers_1.Header.Expires in headers) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
return interpretExpires(headers[headers_1.Header.Expires], headers); | ||
@@ -23,2 +25,3 @@ } | ||
const { noCache, noStore, mustRevalidate, maxAge, immutable } = (0, cache_parser_1.parse)(cacheControl); | ||
// Header told that this response should not be cached. | ||
if (noCache || noStore) { | ||
@@ -28,4 +31,7 @@ return false; | ||
if (immutable) { | ||
// 1 year is sufficient, as Infinity may cause more problems. | ||
// It might not be the best way, but a year is better than none. | ||
return 1000 * 60 * 60 * 24 * 365; | ||
} | ||
// Already out of date, for cache can be saved, but must be requested again | ||
if (mustRevalidate) { | ||
@@ -32,0 +38,0 @@ return 0; |
@@ -0,5 +1,28 @@ | ||
/** | ||
* `false` if cache should not be used. | ||
* | ||
* `undefined` when provided headers was not enough to determine a valid value. | ||
* | ||
* `number` containing the number of **milliseconds** to cache the response. | ||
*/ | ||
declare type MaybeTtl = false | undefined | number; | ||
/** | ||
* Interpret all http headers to determina a time to live. | ||
* | ||
* @param header The header object to interpret. | ||
* @returns `false` if cache should not be used. `undefined` when provided headers was not | ||
* enough to determine a valid value. Or a `number` containing the number of | ||
* **milliseconds** to cache the response. | ||
*/ | ||
export declare type HeadersInterpreter = (headers?: Record<string, string>) => MaybeTtl; | ||
/** | ||
* Interpret a single string header | ||
* | ||
* @param header The header string to interpret. | ||
* @returns `false` if cache should not be used. `undefined` when provided headers was not | ||
* enough to determine a valid value. Or a `number` containing the number of | ||
* **milliseconds** to cache the response. | ||
*/ | ||
export declare type HeaderInterpreter = (header: string, headers: Record<string, string>) => MaybeTtl; | ||
export {}; | ||
//# sourceMappingURL=types.d.ts.map |
@@ -1,2 +0,2 @@ | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AxiosCacheInterceptor=t():e.AxiosCacheInterceptor=t()}("undefined"==typeof self?this:self,(function(){return(()=>{var e={86:(e,t)=>{!function(e){var t=Symbol("cache-parser"),a=Number;function i(e){return("string"==typeof e||"number"==typeof e)&&(e=a(e))>=0&&e<1/0}function r(e){return"number"==typeof e||!0===e||"string"==typeof e&&"false"!==e}e.__internal={isDuration:i,toBoolean:r},e.isCacheControl=function(e){return!!e&&!!e[t]},e.tokenize=function(e){if(!e||"object"!=typeof e)return[];var t=[];return r(e.immutable)&&t.push("immutable"),i(e.maxAge)&&t.push("max-age="+a(e.maxAge)),i(e.maxStale)&&t.push("max-stale="+a(e.maxStale)),i(e.minFresh)&&t.push("min-fresh="+a(e.minFresh)),r(e.mustRevalidate)&&t.push("must-revalidate"),r(e.mustUnderstand)&&t.push("must-understand"),r(e.noCache)&&t.push("no-cache"),r(e.noStore)&&t.push("no-store"),r(e.noTransform)&&t.push("no-transform"),r(e.onlyIfCached)&&t.push("only-if-cached"),r(e.private)&&t.push("private"),r(e.proxyRevalidate)&&t.push("proxy-revalidate"),r(e.public)&&t.push("public"),i(e.sMaxAge)&&t.push("s-maxage="+a(e.sMaxAge)),i(e.staleIfError)&&t.push("stale-if-error="+a(e.staleIfError)),i(e.staleWhileRevalidate)&&t.push("stale-while-revalidate="+a(e.staleWhileRevalidate)),t},e.parse=function(e){var n=Object.defineProperty({},t,{enumerable:!1,value:1});if(!e||"string"!=typeof e)return n;var s={},o=e.toLowerCase().replace(/\s+/g,"").split(",");for(var c in o){var d=o[c].split("=",2);s[d[0]]=1===d.length||d[1]}return r(s.immutable)&&(n.immutable=!0),i(s["max-age"])&&(n.maxAge=a(s["max-age"])),i(s["max-stale"])&&(n.maxStale=a(s["max-stale"])),i(s["min-fresh"])&&(n.minFresh=a(s["min-fresh"])),r(s["must-revalidate"])&&(n.mustRevalidate=!0),r(s["must-understand"])&&(n.mustUnderstand=!0),r(s["no-cache"])&&(n.noCache=!0),r(s["no-store"])&&(n.noStore=!0),r(s["no-transform"])&&(n.noTransform=!0),r(s["only-if-cached"])&&(n.onlyIfCached=!0),r(s.private)&&(n.private=!0),r(s["proxy-revalidate"])&&(n.proxyRevalidate=!0),r(s.public)&&(n.public=!0),i(s["s-maxage"])&&(n.sMaxAge=a(s["s-maxage"])),i(s["stale-if-error"])&&(n.staleIfError=a(s["stale-if-error"])),i(s["stale-while-revalidate"])&&(n.staleWhileRevalidate=a(s["stale-while-revalidate"])),n}}(t)},549:(e,t)=>{var a,i;a=t,i=Symbol("fast-defer"),a.deferred=function(){var e,t,a=new Promise(((a,i)=>{e=a,t=i}));return a.resolve=e,a.reject=t,a[i]=1,a},a.isDeferred=function(e){return!!e&&!!e[i]}}},t={};function a(i){var r=t[i];if(void 0!==r)return r.exports;var n=t[i]={exports:{}};return e[i](n,n.exports,a),n.exports}a.d=(e,t)=>{for(var i in t)a.o(t,i)&&!a.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i={};return(()=>{"use strict";a.r(i),a.d(i,{AxiosStorage:()=>p,BrowserAxiosStorage:()=>C,MemoryAxiosStorage:()=>g,createCache:()=>w,useCache:()=>b});var e,t=a(86);!function(e){e.IfModifiedSince="if-modified-since",e.LastModified="last-modified",e.IfNoneMatch="if-none-match",e.CacheControl="cache-control",e.ETag="etag",e.Expires="expires",e.Age="age",e.ContentType="content-type",e.XAxiosCacheEtag="x-axios-cache-etag",e.XAxiosCacheLastModified="x-axios-cache-last-modified"}(e||(e={}));const r=(t={})=>e.CacheControl in t?s(t[e.CacheControl],t):e.Expires in t?n(t[e.Expires],t):void 0,n=e=>{const t=Date.parse(e)-Date.now();return t>=0&&t},s=(a,i)=>{const{noCache:r,noStore:n,mustRevalidate:s,maxAge:o,immutable:c}=(0,t.parse)(a);if(r||n)return!1;if(c)return 31536e6;if(s)return 0;if(o){const t=i[e.Age];return t?1e3*(o-Number(t)):1e3*o}};var o=a(549),c=function(e,t,a,i){return new(a||(a=Promise))((function(r,n){function s(e){try{c(i.next(e))}catch(e){n(e)}}function o(e){try{c(i.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};class d{constructor(e){this.axios=e,this.use=()=>{this.axios.interceptors.request.use(this.onFulfilled)},this.onFulfilled=e=>c(this,void 0,void 0,(function*(){var t;if(!1===e.cache)return e;if(e.cache=Object.assign(Object.assign({},this.axios.defaults.cache),e.cache),!d.isMethodAllowed(e.method,e.cache))return e;const a=this.axios.generateKey(e);let i,r=yield this.axios.storage.get(a);e:if("empty"==r.state||"stale"===r.state){if(this.axios.waiting[a]){r=yield this.axios.storage.get(a);break e}return this.axios.waiting[a]=(0,o.deferred)(),null===(t=this.axios.waiting[a])||void 0===t||t.catch((()=>{})),yield this.axios.storage.set(a,{state:"loading",data:r.data}),"stale"===r.state&&d.setRevalidationHeaders(r,e),e.validateStatus=d.createValidateStatus(e.validateStatus),e}if("loading"===r.state){const t=this.axios.waiting[a];if(!t)return yield this.axios.storage.remove(a),e;try{i=yield t}catch(t){return e}}else i=r.data;return e.adapter=()=>Promise.resolve({config:e,data:i.data,headers:i.headers,status:i.status,statusText:i.statusText,cached:!0,id:a}),e}))}}d.isMethodAllowed=(e,t)=>{const a=e.toLowerCase();for(const e of t.methods||[])if(e.toLowerCase()===a)return!0;return!1},d.setRevalidationHeaders=(t,a)=>{var i;a.headers||(a.headers={});const{etag:r,modifiedSince:n}=a.cache;if(r){const n=!0===r?null===(i=t.data)||void 0===i?void 0:i.headers[e.ETag]:r;n&&(a.headers[e.IfNoneMatch]=n)}n&&(a.headers[e.IfModifiedSince]=!0===n?t.data.headers[e.LastModified]||new Date(t.createdAt).toUTCString():n.toUTCString())},d.createValidateStatus=e=>t=>e?e(t)||304===t:t>=200&&t<300||304===t;var u=function(e,t,a,i){return new(a||(a=Promise))((function(r,n){function s(e){try{c(i.next(e))}catch(e){n(e)}}function o(e){try{c(i.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};var h=function(e,t,a,i){return new(a||(a=Promise))((function(r,n){function s(e){try{c(i.next(e))}catch(e){n(e)}}function o(e){try{c(i.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};class f{constructor(t){this.axios=t,this.use=()=>{this.axios.interceptors.response.use(this.onFulfilled)},this.onFulfilled=t=>h(this,void 0,void 0,(function*(){const a=this.cachedResponse(t);if(a.cached)return a;if(!a.config.cache)return Object.assign(Object.assign({},a),{cached:!1});const i=a.config.cache,r=yield this.axios.storage.get(a.id);if("stale"===r.state||"empty"===r.state||"cached"===r.state)return a;if(!r.data&&!function(e,{cachePredicate:t}){return"function"==typeof t?t(e):function(e,{statusCheck:t,containsHeaders:a,responseMatch:i}){if(t)if("function"==typeof t){if(!t(e.status))return!1}else{const[a,i]=t;if(e.status<a||e.status>i)return!1}if(a)for(const t in a){const i=a[t],r=e.headers[t];if(!r)return!1;switch(typeof i){case"string":if(r!=i)return!1;break;case"function":if(!i(r))return!1}}return!(i&&!i(e.data))}(e,t)}(a,i))return yield this.rejectResponse(a.id),a;delete a.headers[e.XAxiosCacheEtag],delete a.headers[e.XAxiosCacheLastModified],i.etag&&!0!==i.etag&&(a.headers[e.XAxiosCacheEtag]=i.etag),i.modifiedSince&&(a.headers[e.XAxiosCacheLastModified]=!0===i.modifiedSince?"use-cache-timestamp":i.modifiedSince.toUTCString());let n=i.ttl||-1;if(null==i?void 0:i.interpretHeader){const e=this.axios.headerInterpreter(a.headers);if(!1===e)return yield this.rejectResponse(a.id),a;n=e||0===e?e:n}const s=f.createCacheData(a,r.data),o={state:"cached",ttl:n,createdAt:Date.now(),data:s};(null==i?void 0:i.update)&&function(e,t,a){u(this,void 0,void 0,(function*(){for(const i in a){const r=a[i];if("delete"===r){yield e.remove(i);continue}const n=yield e.get(i);if("loading"===n.state)throw new Error("cannot update the cache while loading");const s=r(n,t);void 0!==s?yield e.set(i,s):yield e.remove(i)}}))}(this.axios.storage,a.data,i.update);const c=this.axios.waiting[a.id];return yield null==c?void 0:c.resolve(o.data),delete this.axios.waiting[a.id],yield this.axios.storage.set(a.id,o),a})),this.rejectResponse=e=>h(this,void 0,void 0,(function*(){var t;yield this.axios.storage.remove(e),null===(t=this.axios.waiting[e])||void 0===t||t.reject(null),delete this.axios.waiting[e]})),this.cachedResponse=e=>Object.assign({id:this.axios.generateKey(e.config),cached:e.cached||!1},e)}}f.createCacheData=(e,t)=>304===e.status&&t?(e.cached=!0,e.data=t.data,e.status=t.status,e.statusText=t.statusText,e.headers=Object.assign(Object.assign({},t.headers),e.headers),t):{data:e.data,status:e.status,statusText:e.statusText,headers:e.headers};var l=function(e,t,a,i){return new(a||(a=Promise))((function(r,n){function s(e){try{c(i.next(e))}catch(e){n(e)}}function o(e){try{c(i.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};class p{constructor(){this.get=e=>l(this,void 0,void 0,(function*(){const t=yield this.find(e);if("cached"!==t.state||t.createdAt+t.ttl>Date.now())return t;if(p.keepIfStale(t)){const a={data:t.data,state:"stale",createdAt:t.createdAt};return yield this.set(e,a),a}return yield this.remove(e),{state:"empty"}}))}}p.keepIfStale=({data:t})=>!!(null==t?void 0:t.headers)&&(e.ETag in t.headers||e.LastModified in t.headers||e.XAxiosCacheEtag in t.headers||e.XAxiosCacheLastModified in t.headers);var v=function(e,t,a,i){return new(a||(a=Promise))((function(r,n){function s(e){try{c(i.next(e))}catch(e){n(e)}}function o(e){try{c(i.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};class g extends p{constructor(e={}){super(),this.storage=e,this.find=e=>v(this,void 0,void 0,(function*(){return this.storage[e]||{state:"empty"}})),this.set=(e,t)=>v(this,void 0,void 0,(function*(){this.storage[e]=t})),this.remove=e=>v(this,void 0,void 0,(function*(){delete this.storage[e]}))}}const m=/^\/|\/+$/g,x=({baseURL:e="",url:t="",method:a,params:i,id:r})=>{if(r)return String(r);e=e.replace(m,""),t=t.replace(m,"");return`${(null==a?void 0:a.toLowerCase())||"get"}::${e+(t&&e?"/":"")+t}::${i?JSON.stringify(i,Object.keys(i).sort()):"{}"}`};var y=function(e,t){var a={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&t.indexOf(i)<0&&(a[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var r=0;for(i=Object.getOwnPropertySymbols(e);r<i.length;r++)t.indexOf(i[r])<0&&Object.prototype.propertyIsEnumerable.call(e,i[r])&&(a[i[r]]=e[i[r]])}return a};function w(e,t={}){var{storage:a,generateKey:i,waiting:n,headerInterpreter:s,requestInterceptor:o,responseInterceptor:c}=t,u=y(t,["storage","generateKey","waiting","headerInterpreter","requestInterceptor","responseInterceptor"]);const h=e;return h.storage=a||new g,h.generateKey=i||x,h.waiting=n||{},h.headerInterpreter=s||r,h.requestInterceptor=o||new d(h),h.responseInterceptor=c||new f(h),h.defaults=Object.assign(Object.assign({},e.defaults),{cache:Object.assign({ttl:3e5,interpretHeader:!1,methods:["get"],cachePredicate:{statusCheck:[200,399]},etag:!1,modifiedSince:!1,update:{}},u)}),h.requestInterceptor.use(),h.responseInterceptor.use(),h}const b=w;var S=function(e,t,a,i){return new(a||(a=Promise))((function(r,n){function s(e){try{c(i.next(e))}catch(e){n(e)}}function o(e){try{c(i.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};class C extends p{constructor(e,t=C.DEFAULT_KEY_PREFIX){super(),this.storage=e,this.prefix=t,this.find=e=>S(this,void 0,void 0,(function*(){const t=this.storage.getItem(`${this.prefix}:${e}`);return t?JSON.parse(t):{state:"empty"}})),this.set=(e,t)=>S(this,void 0,void 0,(function*(){return this.storage.setItem(`${this.prefix}:${e}`,JSON.stringify(t))})),this.remove=e=>S(this,void 0,void 0,(function*(){return this.storage.removeItem(`${this.prefix}:${e}`)}))}}C.DEFAULT_KEY_PREFIX="a-c-i"})(),i})()})); | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AxiosCacheInterceptor=t():e.AxiosCacheInterceptor=t()}("undefined"==typeof self?this:self,(function(){return(()=>{var e={86:(e,t)=>{!function(e){var t=Symbol("cache-parser"),a=Number;function i(e){return("string"==typeof e||"number"==typeof e)&&(e=a(e))>=0&&e<1/0}function n(e){return"number"==typeof e||!0===e||"string"==typeof e&&"false"!==e}e.__internal={isDuration:i,toBoolean:n},e.isCacheControl=function(e){return!!e&&!!e[t]},e.tokenize=function(e){if(!e||"object"!=typeof e)return[];var t=[];return n(e.immutable)&&t.push("immutable"),i(e.maxAge)&&t.push("max-age="+a(e.maxAge)),i(e.maxStale)&&t.push("max-stale="+a(e.maxStale)),i(e.minFresh)&&t.push("min-fresh="+a(e.minFresh)),n(e.mustRevalidate)&&t.push("must-revalidate"),n(e.mustUnderstand)&&t.push("must-understand"),n(e.noCache)&&t.push("no-cache"),n(e.noStore)&&t.push("no-store"),n(e.noTransform)&&t.push("no-transform"),n(e.onlyIfCached)&&t.push("only-if-cached"),n(e.private)&&t.push("private"),n(e.proxyRevalidate)&&t.push("proxy-revalidate"),n(e.public)&&t.push("public"),i(e.sMaxAge)&&t.push("s-maxage="+a(e.sMaxAge)),i(e.staleIfError)&&t.push("stale-if-error="+a(e.staleIfError)),i(e.staleWhileRevalidate)&&t.push("stale-while-revalidate="+a(e.staleWhileRevalidate)),t},e.parse=function(e){var r=Object.defineProperty({},t,{enumerable:!1,value:1});if(!e||"string"!=typeof e)return r;var s={},o=e.toLowerCase().replace(/\s+/g,"").split(",");for(var c in o){var d=o[c].split("=",2);s[d[0]]=1===d.length||d[1]}return n(s.immutable)&&(r.immutable=!0),i(s["max-age"])&&(r.maxAge=a(s["max-age"])),i(s["max-stale"])&&(r.maxStale=a(s["max-stale"])),i(s["min-fresh"])&&(r.minFresh=a(s["min-fresh"])),n(s["must-revalidate"])&&(r.mustRevalidate=!0),n(s["must-understand"])&&(r.mustUnderstand=!0),n(s["no-cache"])&&(r.noCache=!0),n(s["no-store"])&&(r.noStore=!0),n(s["no-transform"])&&(r.noTransform=!0),n(s["only-if-cached"])&&(r.onlyIfCached=!0),n(s.private)&&(r.private=!0),n(s["proxy-revalidate"])&&(r.proxyRevalidate=!0),n(s.public)&&(r.public=!0),i(s["s-maxage"])&&(r.sMaxAge=a(s["s-maxage"])),i(s["stale-if-error"])&&(r.staleIfError=a(s["stale-if-error"])),i(s["stale-while-revalidate"])&&(r.staleWhileRevalidate=a(s["stale-while-revalidate"])),r}}(t)},549:(e,t)=>{var a,i;a=t,i=Symbol("fast-defer"),a.deferred=function(){var e,t,a=new Promise((function(a,i){e=a,t=i}));return a.resolve=e,a.reject=t,a[i]=1,a},a.isDeferred=function(e){return!!e&&!!e[i]}}},t={};function a(i){var n=t[i];if(void 0!==n)return n.exports;var r=t[i]={exports:{}};return e[i](r,r.exports,a),r.exports}a.d=(e,t)=>{for(var i in t)a.o(t,i)&&!a.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i={};return(()=>{"use strict";a.r(i),a.d(i,{AxiosStorage:()=>p,BrowserAxiosStorage:()=>A,MemoryAxiosStorage:()=>g,createCache:()=>C,setupCache:()=>w,useCache:()=>b});var e,t=a(86);!function(e){e.IfModifiedSince="if-modified-since",e.LastModified="last-modified",e.IfNoneMatch="if-none-match",e.CacheControl="cache-control",e.ETag="etag",e.Expires="expires",e.Age="age",e.ContentType="content-type",e.XAxiosCacheEtag="x-axios-cache-etag",e.XAxiosCacheLastModified="x-axios-cache-last-modified"}(e||(e={}));const n=(t={})=>e.CacheControl in t?s(t[e.CacheControl],t):e.Expires in t?r(t[e.Expires],t):void 0,r=e=>{const t=Date.parse(e)-Date.now();return t>=0&&t},s=(a,i)=>{const{noCache:n,noStore:r,mustRevalidate:s,maxAge:o,immutable:c}=(0,t.parse)(a);if(n||r)return!1;if(c)return 31536e6;if(s)return 0;if(o){const t=i[e.Age];return t?1e3*(o-Number(t)):1e3*o}};var o=a(549),c=function(e,t,a,i){return new(a||(a=Promise))((function(n,r){function s(e){try{c(i.next(e))}catch(e){r(e)}}function o(e){try{c(i.throw(e))}catch(e){r(e)}}function c(e){var t;e.done?n(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};class d{constructor(e){this.axios=e,this.use=()=>{this.axios.interceptors.request.use(this.onFulfilled)},this.onFulfilled=e=>c(this,void 0,void 0,(function*(){var t;if(!1===e.cache)return e;if(e.cache=Object.assign(Object.assign({},this.axios.defaults.cache),e.cache),!d.isMethodAllowed(e.method,e.cache))return e;const a=this.axios.generateKey(e);let i,n=yield this.axios.storage.get(a);e:if("empty"==n.state||"stale"===n.state){if(this.axios.waiting[a]){n=yield this.axios.storage.get(a);break e}return this.axios.waiting[a]=(0,o.deferred)(),null===(t=this.axios.waiting[a])||void 0===t||t.catch((()=>{})),yield this.axios.storage.set(a,{state:"loading",data:n.data}),"stale"===n.state&&d.setRevalidationHeaders(n,e),e.validateStatus=d.createValidateStatus(e.validateStatus),e}if("loading"===n.state){const t=this.axios.waiting[a];if(!t)return yield this.axios.storage.remove(a),e;try{i=yield t}catch(t){return e}}else i=n.data;return e.adapter=()=>Promise.resolve({config:e,data:i.data,headers:i.headers,status:i.status,statusText:i.statusText,cached:!0,id:a}),e}))}}d.isMethodAllowed=(e,t)=>{const a=e.toLowerCase();for(const e of t.methods||[])if(e.toLowerCase()===a)return!0;return!1},d.setRevalidationHeaders=(t,a)=>{var i;a.headers||(a.headers={});const{etag:n,modifiedSince:r}=a.cache;if(n){const r=!0===n?null===(i=t.data)||void 0===i?void 0:i.headers[e.ETag]:n;r&&(a.headers[e.IfNoneMatch]=r)}r&&(a.headers[e.IfModifiedSince]=!0===r?t.data.headers[e.LastModified]||new Date(t.createdAt).toUTCString():r.toUTCString())},d.createValidateStatus=e=>t=>e?e(t)||304===t:t>=200&&t<300||304===t;var u=function(e,t,a,i){return new(a||(a=Promise))((function(n,r){function s(e){try{c(i.next(e))}catch(e){r(e)}}function o(e){try{c(i.throw(e))}catch(e){r(e)}}function c(e){var t;e.done?n(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};var h=function(e,t,a,i){return new(a||(a=Promise))((function(n,r){function s(e){try{c(i.next(e))}catch(e){r(e)}}function o(e){try{c(i.throw(e))}catch(e){r(e)}}function c(e){var t;e.done?n(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};class f{constructor(t){this.axios=t,this.use=()=>{this.axios.interceptors.response.use(this.onFulfilled)},this.onFulfilled=t=>h(this,void 0,void 0,(function*(){const a=this.cachedResponse(t);if(a.cached)return a;if(!a.config.cache)return Object.assign(Object.assign({},a),{cached:!1});const i=a.config.cache,n=yield this.axios.storage.get(a.id);if("stale"===n.state||"empty"===n.state||"cached"===n.state)return a;if(!n.data&&!function(e,{cachePredicate:t}){return"function"==typeof t?t(e):function(e,{statusCheck:t,containsHeaders:a,responseMatch:i}){if(t)if("function"==typeof t){if(!t(e.status))return!1}else{const[a,i]=t;if(e.status<a||e.status>i)return!1}if(a)for(const t in a){const i=a[t],n=e.headers[t];if(!n)return!1;switch(typeof i){case"string":if(n!=i)return!1;break;case"function":if(!i(n))return!1}}return!(i&&!i(e.data))}(e,t)}(a,i))return yield this.rejectResponse(a.id),a;delete a.headers[e.XAxiosCacheEtag],delete a.headers[e.XAxiosCacheLastModified],i.etag&&!0!==i.etag&&(a.headers[e.XAxiosCacheEtag]=i.etag),i.modifiedSince&&(a.headers[e.XAxiosCacheLastModified]=!0===i.modifiedSince?"use-cache-timestamp":i.modifiedSince.toUTCString());let r=i.ttl||-1;if(null==i?void 0:i.interpretHeader){const e=this.axios.headerInterpreter(a.headers);if(!1===e)return yield this.rejectResponse(a.id),a;r=e||0===e?e:r}const s=f.setupCacheData(a,n.data),o={state:"cached",ttl:r,createdAt:Date.now(),data:s};(null==i?void 0:i.update)&&function(e,t,a){u(this,void 0,void 0,(function*(){for(const i in a){const n=a[i];if("delete"===n){yield e.remove(i);continue}const r=yield e.get(i);if("loading"===r.state)throw new Error("cannot update the cache while loading");const s=n(r,t);void 0!==s?yield e.set(i,s):yield e.remove(i)}}))}(this.axios.storage,a.data,i.update);const c=this.axios.waiting[a.id];return yield null==c?void 0:c.resolve(o.data),delete this.axios.waiting[a.id],yield this.axios.storage.set(a.id,o),a})),this.rejectResponse=e=>h(this,void 0,void 0,(function*(){var t;yield this.axios.storage.remove(e),null===(t=this.axios.waiting[e])||void 0===t||t.reject(null),delete this.axios.waiting[e]})),this.cachedResponse=e=>Object.assign({id:this.axios.generateKey(e.config),cached:e.cached||!1},e)}}f.setupCacheData=(e,t)=>304===e.status&&t?(e.cached=!0,e.data=t.data,e.status=t.status,e.statusText=t.statusText,e.headers=Object.assign(Object.assign({},t.headers),e.headers),t):{data:e.data,status:e.status,statusText:e.statusText,headers:e.headers};var l=function(e,t,a,i){return new(a||(a=Promise))((function(n,r){function s(e){try{c(i.next(e))}catch(e){r(e)}}function o(e){try{c(i.throw(e))}catch(e){r(e)}}function c(e){var t;e.done?n(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};class p{constructor(){this.get=e=>l(this,void 0,void 0,(function*(){const t=yield this.find(e);if("cached"!==t.state||t.createdAt+t.ttl>Date.now())return t;if(p.keepIfStale(t)){const a={data:t.data,state:"stale",createdAt:t.createdAt};return yield this.set(e,a),a}return yield this.remove(e),{state:"empty"}}))}}p.keepIfStale=({data:t})=>!!(null==t?void 0:t.headers)&&(e.ETag in t.headers||e.LastModified in t.headers||e.XAxiosCacheEtag in t.headers||e.XAxiosCacheLastModified in t.headers);var v=function(e,t,a,i){return new(a||(a=Promise))((function(n,r){function s(e){try{c(i.next(e))}catch(e){r(e)}}function o(e){try{c(i.throw(e))}catch(e){r(e)}}function c(e){var t;e.done?n(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};class g extends p{constructor(e={}){super(),this.storage=e,this.find=e=>v(this,void 0,void 0,(function*(){return this.storage[e]||{state:"empty"}})),this.set=(e,t)=>v(this,void 0,void 0,(function*(){this.storage[e]=t})),this.remove=e=>v(this,void 0,void 0,(function*(){delete this.storage[e]}))}}const m=/^\/|\/+$/g,x=({baseURL:e="",url:t="",method:a,params:i,id:n})=>{if(n)return String(n);e=e.replace(m,""),t=t.replace(m,"");return`${(null==a?void 0:a.toLowerCase())||"get"}::${e+(t&&e?"/":"")+t}::${i?JSON.stringify(i,Object.keys(i).sort()):"{}"}`};var y=function(e,t){var a={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&t.indexOf(i)<0&&(a[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var n=0;for(i=Object.getOwnPropertySymbols(e);n<i.length;n++)t.indexOf(i[n])<0&&Object.prototype.propertyIsEnumerable.call(e,i[n])&&(a[i[n]]=e[i[n]])}return a};function w(e,t={}){var{storage:a,generateKey:i,waiting:r,headerInterpreter:s,requestInterceptor:o,responseInterceptor:c}=t,u=y(t,["storage","generateKey","waiting","headerInterpreter","requestInterceptor","responseInterceptor"]);const h=e;return h.storage=a||new g,h.generateKey=i||x,h.waiting=r||{},h.headerInterpreter=s||n,h.requestInterceptor=o||new d(h),h.responseInterceptor=c||new f(h),h.defaults=Object.assign(Object.assign({},e.defaults),{cache:Object.assign({ttl:3e5,interpretHeader:!1,methods:["get"],cachePredicate:{statusCheck:[200,399]},etag:!1,modifiedSince:!1,update:{}},u)}),h.requestInterceptor.use(),h.responseInterceptor.use(),h}const b=w,C=w;var S=function(e,t,a,i){return new(a||(a=Promise))((function(n,r){function s(e){try{c(i.next(e))}catch(e){r(e)}}function o(e){try{c(i.throw(e))}catch(e){r(e)}}function c(e){var t;e.done?n(e.value):(t=e.value,t instanceof a?t:new a((function(e){e(t)}))).then(s,o)}c((i=i.apply(e,t||[])).next())}))};class A extends p{constructor(e,t=A.DEFAULT_KEY_PREFIX){super(),this.storage=e,this.prefix=t,this.find=e=>S(this,void 0,void 0,(function*(){const t=this.storage.getItem(`${this.prefix}:${e}`);return t?JSON.parse(t):{state:"empty"}})),this.set=(e,t)=>S(this,void 0,void 0,(function*(){return this.storage.setItem(`${this.prefix}:${e}`,JSON.stringify(t))})),this.remove=e=>S(this,void 0,void 0,(function*(){return this.storage.removeItem(`${this.prefix}:${e}`)}))}}A.DEFAULT_KEY_PREFIX="a-c-i"})(),i})()})); | ||
//# sourceMappingURL=index.min.js.map |
@@ -18,4 +18,8 @@ import type { AxiosRequestConfig, Method } from 'axios'; | ||
}) => void; | ||
/** | ||
* Creates a new validateStatus function that will use the one already used and also | ||
* accept status code 304. | ||
*/ | ||
static readonly createValidateStatus: (oldValidate?: AxiosRequestConfig['validateStatus']) => (status: number) => boolean; | ||
} | ||
//# sourceMappingURL=request.d.ts.map |
@@ -26,9 +26,19 @@ "use strict"; | ||
} | ||
// merge defaults with per request configuration | ||
config.cache = Object.assign(Object.assign({}, this.axios.defaults.cache), config.cache); | ||
if (!CacheRequestInterceptor.isMethodAllowed(config.method, config.cache)) { | ||
if ( | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
!CacheRequestInterceptor.isMethodAllowed(config.method, config.cache)) { | ||
return config; | ||
} | ||
const key = this.axios.generateKey(config); | ||
// Assumes that the storage handled staled responses | ||
let cache = yield this.axios.storage.get(key); | ||
// Not cached, continue the request, and mark it as fetching | ||
emptyOrStale: if (cache.state == 'empty' || cache.state === 'stale') { | ||
/** | ||
* 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 (this.axios.waiting[key]) { | ||
@@ -38,3 +48,8 @@ cache = (yield this.axios.storage.get(key)); | ||
} | ||
// Create a deferred to resolve other requests for the same key when it's completed | ||
this.axios.waiting[key] = (0, fast_defer_1.deferred)(); | ||
/** | ||
* Add a default reject handler to catch when the request is aborted without others | ||
* waiting for it. | ||
*/ | ||
(_a = this.axios.waiting[key]) === null || _a === void 0 ? void 0 : _a.catch(() => undefined); | ||
@@ -46,2 +61,3 @@ yield this.axios.storage.set(key, { | ||
if (cache.state === 'stale') { | ||
//@ts-expect-error type infer couldn't resolve this | ||
CacheRequestInterceptor.setRevalidationHeaders(cache, config); | ||
@@ -55,2 +71,4 @@ } | ||
const deferred = this.axios.waiting[key]; | ||
// Just in case, the deferred doesn't exists. | ||
/* istanbul ignore if 'really hard to test' */ | ||
if (!deferred) { | ||
@@ -64,2 +82,3 @@ yield this.axios.storage.remove(key); | ||
catch (_b) { | ||
// The deferred is rejected when the request that we are waiting rejected cache. | ||
return config; | ||
@@ -71,3 +90,8 @@ } | ||
} | ||
config.adapter = () => Promise.resolve({ | ||
config.adapter = () => | ||
/** | ||
* Even though the response interceptor receives this one from here, it has been | ||
* configured to ignore cached responses: true | ||
*/ | ||
Promise.resolve({ | ||
config: config, | ||
@@ -108,3 +132,3 @@ data: cachedResponse.data, | ||
modifiedSince === true | ||
? | ||
? // If last-modified is not present, use the createdAt timestamp | ||
cache.data.headers[headers_1.Header.LastModified] || | ||
@@ -115,2 +139,6 @@ new Date(cache.createdAt).toUTCString() | ||
}; | ||
/** | ||
* Creates a new validateStatus function that will use the one already used and also | ||
* accept status code 304. | ||
*/ | ||
CacheRequestInterceptor.createValidateStatus = (oldValidate) => { | ||
@@ -117,0 +145,0 @@ return (status) => { |
@@ -10,6 +10,11 @@ import type { AxiosResponse } from 'axios'; | ||
readonly onFulfilled: (axiosResponse: AxiosResponse<R, D>) => Promise<CacheAxiosResponse<R, D>>; | ||
/** Rejects cache for this response. Also update the waiting list for this key by rejecting it. */ | ||
readonly rejectResponse: (key: string) => Promise<void>; | ||
readonly cachedResponse: (response: AxiosResponse<R, D>) => CacheAxiosResponse<R, D>; | ||
static readonly createCacheData: <R_1, D_1>(response: CacheAxiosResponse<R_1, D_1>, cache?: CachedResponse | undefined) => CachedResponse; | ||
/** | ||
* Creates the new date to the cache by the provided response. Also handles possible 304 | ||
* Not Modified by updating response properties. | ||
*/ | ||
static readonly setupCacheData: <R_1, D_1>(response: CacheAxiosResponse<R_1, D_1>, cache?: CachedResponse | undefined) => CachedResponse; | ||
} | ||
//# sourceMappingURL=response.d.ts.map |
@@ -24,5 +24,8 @@ "use strict"; | ||
const response = this.cachedResponse(axiosResponse); | ||
// Response is already cached | ||
if (response.cached) { | ||
return response; | ||
} | ||
// Skip cache | ||
// either false or weird behavior, config.cache should always exists, from global config merge at least | ||
if (!response.config.cache) { | ||
@@ -33,8 +36,14 @@ return Object.assign(Object.assign({}, response), { cached: false }); | ||
const cache = yield this.axios.storage.get(response.id); | ||
if (cache.state === 'stale' || | ||
if ( | ||
// If the request interceptor had a problem | ||
cache.state === 'stale' || | ||
cache.state === 'empty' || | ||
// Should not hit here because of previous response.cached check | ||
cache.state === 'cached') { | ||
return response; | ||
} | ||
if (!cache.data && | ||
// Config told that this response should be cached. | ||
if ( | ||
// For 'loading' values (post stale), this check was already run in the past. | ||
!cache.data && | ||
!(0, cache_predicate_1.shouldCacheResponse)(response, cacheConfig)) { | ||
@@ -44,2 +53,3 @@ yield this.rejectResponse(response.id); | ||
} | ||
// avoid remnant headers from remote server to break implementation | ||
delete response.headers[headers_1.Header.XAxiosCacheEtag]; | ||
@@ -56,5 +66,6 @@ delete response.headers[headers_1.Header.XAxiosCacheLastModified]; | ||
} | ||
let ttl = cacheConfig.ttl || -1; | ||
let ttl = cacheConfig.ttl || -1; // always set from global config | ||
if (cacheConfig === null || cacheConfig === void 0 ? void 0 : cacheConfig.interpretHeader) { | ||
const expirationTime = this.axios.headerInterpreter(response.headers); | ||
// Cache should not be used | ||
if (expirationTime === false) { | ||
@@ -66,3 +77,3 @@ yield this.rejectResponse(response.id); | ||
} | ||
const data = CacheResponseInterceptor.createCacheData(response, cache.data); | ||
const data = CacheResponseInterceptor.setupCacheData(response, cache.data); | ||
const newCache = { | ||
@@ -74,2 +85,3 @@ state: 'cached', | ||
}; | ||
// Update other entries before updating himself | ||
if (cacheConfig === null || cacheConfig === void 0 ? void 0 : cacheConfig.update) { | ||
@@ -79,10 +91,16 @@ (0, update_cache_1.updateCache)(this.axios.storage, response.data, cacheConfig.update); | ||
const deferred = this.axios.waiting[response.id]; | ||
// Resolve all other requests waiting for this response | ||
yield (deferred === null || deferred === void 0 ? void 0 : deferred.resolve(newCache.data)); | ||
delete this.axios.waiting[response.id]; | ||
// Define this key as cache on the storage | ||
yield this.axios.storage.set(response.id, newCache); | ||
// Return the response with cached as false, because it was not cached at all | ||
return response; | ||
}); | ||
/** Rejects cache for this response. Also update the waiting list for this key by rejecting it. */ | ||
this.rejectResponse = (key) => __awaiter(this, void 0, void 0, function* () { | ||
var _a; | ||
// Update the cache to empty to prevent infinite loading state | ||
yield this.axios.storage.remove(key); | ||
// Reject the deferred if present | ||
(_a = this.axios.waiting[key]) === null || _a === void 0 ? void 0 : _a.reject(null); | ||
@@ -92,3 +110,5 @@ delete this.axios.waiting[key]; | ||
this.cachedResponse = (response) => { | ||
return Object.assign({ id: this.axios.generateKey(response.config), cached: response.cached || false }, response); | ||
return Object.assign({ id: this.axios.generateKey(response.config), | ||
// The request interceptor response.cache will return true or undefined. And true only when the response was cached. | ||
cached: response.cached || false }, response); | ||
}; | ||
@@ -98,4 +118,9 @@ } | ||
exports.CacheResponseInterceptor = CacheResponseInterceptor; | ||
CacheResponseInterceptor.createCacheData = (response, cache) => { | ||
/** | ||
* Creates the new date to the cache by the provided response. Also handles possible 304 | ||
* Not Modified by updating response properties. | ||
*/ | ||
CacheResponseInterceptor.setupCacheData = (response, cache) => { | ||
if (response.status === 304 && cache) { | ||
// Set the cache information into the response object | ||
response.cached = true; | ||
@@ -105,5 +130,8 @@ response.data = cache.data; | ||
response.statusText = cache.statusText; | ||
// Update possible new headers | ||
response.headers = Object.assign(Object.assign({}, cache.headers), response.headers); | ||
// return the old cache | ||
return cache; | ||
} | ||
// New Response | ||
return { | ||
@@ -110,0 +138,0 @@ data: response.data, |
export interface AxiosInterceptor<T> { | ||
onFulfilled?(value: T): T | Promise<T>; | ||
onRejected?(error: any): any; | ||
/** | ||
* Should apply this interceptor to an already provided axios instance. Does not call | ||
* this method explicitly. | ||
*/ | ||
use(): void; | ||
} | ||
//# sourceMappingURL=types.d.ts.map |
@@ -8,2 +8,6 @@ import type { NotEmptyStorageValue } from '..'; | ||
static readonly DEFAULT_KEY_PREFIX = "a-c-i"; | ||
/** | ||
* @param storage Any browser storage, like sessionStorage or localStorage | ||
* @param prefix The key prefix to use on all keys. | ||
*/ | ||
constructor(storage: Storage, prefix?: string); | ||
@@ -10,0 +14,0 @@ readonly find: (key: string) => Promise<StorageValue>; |
@@ -15,2 +15,6 @@ "use strict"; | ||
class BrowserAxiosStorage extends storage_1.AxiosStorage { | ||
/** | ||
* @param storage Any browser storage, like sessionStorage or localStorage | ||
* @param prefix The key prefix to use on all keys. | ||
*/ | ||
constructor(storage, prefix = BrowserAxiosStorage.DEFAULT_KEY_PREFIX) { | ||
@@ -17,0 +21,0 @@ super(); |
import type { CachedStorageValue, NotEmptyStorageValue } from '..'; | ||
import type { StorageValue } from './types'; | ||
export declare abstract class AxiosStorage { | ||
/** | ||
* Returns the cached value for the given key. The get method is what takes care to | ||
* invalidate the values. | ||
*/ | ||
protected abstract readonly find: (key: string) => Promise<StorageValue>; | ||
/** | ||
* Sets a new value for the given key | ||
* | ||
* Use CacheStorage.remove(key) to define a key to 'empty' state. | ||
*/ | ||
abstract readonly set: (key: string, value: NotEmptyStorageValue) => Promise<void>; | ||
/** Removes the value for the given key */ | ||
abstract readonly remove: (key: string) => Promise<void>; | ||
readonly get: (key: string) => Promise<StorageValue>; | ||
/** Returns true if a invalid cache should still be kept */ | ||
static readonly keepIfStale: ({ data }: CachedStorageValue) => boolean; | ||
} | ||
//# sourceMappingURL=storage.d.ts.map |
@@ -19,5 +19,7 @@ "use strict"; | ||
if (value.state !== 'cached' || | ||
// Cached and fresh value | ||
value.createdAt + value.ttl > Date.now()) { | ||
return value; | ||
} | ||
// Check if his can stale value. | ||
if (AxiosStorage.keepIfStale(value)) { | ||
@@ -38,2 +40,3 @@ const stale = { | ||
exports.AxiosStorage = AxiosStorage; | ||
/** Returns true if a invalid cache should still be kept */ | ||
AxiosStorage.keepIfStale = ({ data }) => { | ||
@@ -40,0 +43,0 @@ if (data === null || data === void 0 ? void 0 : data.headers) { |
@@ -7,2 +7,3 @@ export declare type CachedResponse = { | ||
}; | ||
/** The value returned for a given key. */ | ||
export declare type StorageValue = StaleStorageValue | CachedStorageValue | LoadingStorageValue | EmptyStorageValue; | ||
@@ -18,2 +19,3 @@ export declare type NotEmptyStorageValue = Exclude<StorageValue, EmptyStorageValue>; | ||
data: CachedResponse; | ||
/** The number in milliseconds to wait after createdAt before the value is considered stale. */ | ||
ttl: number; | ||
@@ -24,4 +26,9 @@ createdAt: number; | ||
export declare type LoadingStorageValue = { | ||
/** | ||
* Only present if the previous state was `stale`. So, in case the new response comes | ||
* without a value, this data is used | ||
*/ | ||
data?: CachedResponse; | ||
ttl?: number; | ||
/** Defined when the state is cached */ | ||
createdAt?: undefined; | ||
@@ -33,2 +40,3 @@ state: 'loading'; | ||
ttl?: undefined; | ||
/** Defined when the state is cached */ | ||
createdAt?: undefined; | ||
@@ -35,0 +43,0 @@ state: 'empty'; |
import type { AxiosResponse } from 'axios'; | ||
import type { CacheProperties } from '..'; | ||
import type { CachePredicateObject } from './types'; | ||
/** Returns true if the response should be cached */ | ||
export declare function shouldCacheResponse<R>(response: AxiosResponse<R>, { cachePredicate }: CacheProperties): boolean; | ||
export declare function isCachePredicateValid<R>(response: AxiosResponse<R>, { statusCheck, containsHeaders, responseMatch }: CachePredicateObject): boolean; | ||
//# sourceMappingURL=cache-predicate.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isCachePredicateValid = exports.shouldCacheResponse = void 0; | ||
/** Returns true if the response should be cached */ | ||
function shouldCacheResponse(response, { cachePredicate }) { | ||
@@ -20,3 +21,3 @@ if (typeof cachePredicate === 'function') { | ||
const [start, end] = statusCheck; | ||
if (response.status < start || | ||
if (response.status < start || // | ||
response.status > end) { | ||
@@ -29,4 +30,6 @@ return false; | ||
for (const headerName in containsHeaders) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const value = containsHeaders[headerName]; | ||
const header = response.headers[headerName]; | ||
// At any case, if the header is not found, the predicate fails. | ||
if (!header) { | ||
@@ -33,0 +36,0 @@ return false; |
export declare enum Header { | ||
/** | ||
* ```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", | ||
/** @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control */ | ||
CacheControl = "cache-control", | ||
/** | ||
* ```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", | ||
/** | ||
* ```txt | ||
* Content-Type: text/html; charset=UTF-8 | ||
* Content-Type: multipart/form-data; boundary=something | ||
* ``` | ||
* | ||
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type | ||
*/ | ||
ContentType = "content-type", | ||
/** | ||
* Used internally to mark the cache item as being 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 to mark the cache item as being 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" | ||
} | ||
//# sourceMappingURL=headers.d.ts.map |
@@ -6,12 +6,87 @@ "use strict"; | ||
(function (Header) { | ||
/** | ||
* ```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 | ||
*/ | ||
Header["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 | ||
*/ | ||
Header["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 | ||
*/ | ||
Header["IfNoneMatch"] = "if-none-match"; | ||
/** @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control */ | ||
Header["CacheControl"] = "cache-control"; | ||
/** | ||
* ```txt | ||
* ETag: W / '<etag_value>'; | ||
* ETag: '<etag_value>'; | ||
* ``` | ||
* | ||
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag | ||
*/ | ||
Header["ETag"] = "etag"; | ||
/** | ||
* ```txt | ||
* Expires: <http-date> | ||
* ``` | ||
* | ||
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires | ||
*/ | ||
Header["Expires"] = "expires"; | ||
/** | ||
* ```txt | ||
* Age: <delta-seconds> | ||
* ``` | ||
* | ||
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Age | ||
*/ | ||
Header["Age"] = "age"; | ||
/** | ||
* ```txt | ||
* Content-Type: text/html; charset=UTF-8 | ||
* Content-Type: multipart/form-data; boundary=something | ||
* ``` | ||
* | ||
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type | ||
*/ | ||
Header["ContentType"] = "content-type"; | ||
/** | ||
* Used internally to mark the cache item as being 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>" | ||
* ``` | ||
*/ | ||
Header["XAxiosCacheEtag"] = "x-axios-cache-etag"; | ||
/** | ||
* Used internally to mark the cache item as being 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 | ||
* ``` | ||
*/ | ||
Header["XAxiosCacheLastModified"] = "x-axios-cache-last-modified"; | ||
})(Header = exports.Header || (exports.Header = {})); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.defaultKeyGenerator = void 0; | ||
// Remove first and last '/' char, if present | ||
// https://regex101.com/r/ENqrFy/1 | ||
const SLASHES_REGEX = /^\/|\/+$/g; | ||
@@ -8,2 +10,3 @@ const defaultKeyGenerator = ({ baseURL = '', url = '', method: nullableMethod, params, id }) => { | ||
return String(id); | ||
// Remove trailing slashes | ||
baseURL = baseURL.replace(SLASHES_REGEX, ''); | ||
@@ -10,0 +13,0 @@ url = url.replace(SLASHES_REGEX, ''); |
@@ -5,7 +5,17 @@ import type { AxiosResponse } from 'axios'; | ||
export declare type CachePredicateObject = { | ||
/** | ||
* The status predicate, if a tuple is returned, the first and seconds value means the | ||
* interval (inclusive) accepted. Can also be a function. | ||
*/ | ||
statusCheck?: [start: number, end: number] | ((status: number) => boolean); | ||
/** | ||
* Matches if the response header container all keys. A tuple also checks for values. | ||
* Can also be a predicate. | ||
*/ | ||
containsHeaders?: Record<string, true | string | ((header: string) => boolean)>; | ||
/** Check if the desired response matches this predicate. */ | ||
responseMatch?: <T = any>(res: T | undefined) => boolean; | ||
}; | ||
/** A simple function that receives a cache request config and should return a string id for it. */ | ||
export declare type KeyGenerator = <R>(options: CacheRequestConfig<R>) => string; | ||
//# sourceMappingURL=types.d.ts.map |
import type { AxiosStorage } from '../storage/storage'; | ||
import type { CachedStorageValue, LoadingStorageValue, StorageValue } from '../storage/types'; | ||
export declare type CacheUpdater = 'delete' | ((cached: Exclude<StorageValue, LoadingStorageValue>, newData: any) => CachedStorageValue | void); | ||
/** Function to update all caches, from CacheProperties.update, with the new data. */ | ||
export declare function updateCache<T = any>(storage: AxiosStorage, data: T, entries: Record<string, CacheUpdater>): Promise<void>; | ||
//# sourceMappingURL=update-cache.d.ts.map |
@@ -13,5 +13,7 @@ "use strict"; | ||
exports.updateCache = void 0; | ||
/** Function to update all caches, from CacheProperties.update, with the new data. */ | ||
function updateCache(storage, data, entries) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
for (const cacheKey in entries) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const value = entries[cacheKey]; | ||
@@ -18,0 +20,0 @@ if (value === 'delete') { |
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
const Axios = require('axios'); | ||
const { createCache } = require('axios-cache-interceptor'); | ||
const { create: createAxios } = require('axios').default; | ||
const { setupCache } = require('../dist'); | ||
async function main() { | ||
const axios = Axios.create({ | ||
baseUrl: 'https://api.github.com' | ||
}); | ||
const axios = setupCache( | ||
// creating axios instance | ||
createAxios({ | ||
baseUrl: 'https://registry.npmjs.org/' | ||
}), | ||
/** | ||
* The same instance of the previous axios, but has custom Typescript types to better intellisense | ||
* | ||
* @example | ||
* | ||
* ```js | ||
* axios === axiosWithCache; | ||
* ``` | ||
*/ | ||
const axiosWithCache = createCache(axios, { | ||
ttl: 99999, | ||
// configuring the cache | ||
{ | ||
ttl: 99999, | ||
// Parse the Cache-Control header to determine the cache strategy | ||
interpretHeader: true | ||
}); | ||
const fetchedResponse = await axiosWithCache.get( | ||
'https://registry.npmjs.org//axios-cache-interceptor' | ||
// Parse the Cache-Control header to determine the cache strategy | ||
interpretHeader: true | ||
} | ||
); | ||
const fetchedResponse = await axios.get('/axios-cache-interceptor'); | ||
// This won't made a network request, because the response is already cached | ||
const cachedResponse = await axiosWithCache.get( | ||
'https://registry.npmjs.org//axios-cache-interceptor' | ||
); | ||
const cachedResponse = await axios.get('/axios-cache-interceptor'); | ||
@@ -48,3 +39,3 @@ console.log('First request was cached?'); | ||
const cacheInformation = await axiosWithCache.storage.get(fetchedResponse.id); | ||
const cacheInformation = await axios.storage.get(fetchedResponse.id); | ||
@@ -63,15 +54,12 @@ console.log( | ||
// Remove the old cache by brute force | ||
await axiosWithCache.storage.remove(fetchedResponse.id); | ||
await axios.storage.remove(fetchedResponse.id); | ||
const refetchedResponse = await axiosWithCache.get( | ||
'https://registry.npmjs.org//axios-cache-interceptor', | ||
{ | ||
cache: { | ||
// This time with interpretHeader disabled | ||
interpretHeader: false | ||
} | ||
const refetchedResponse = await axios.get('/axios-cache-interceptor', { | ||
cache: { | ||
// This time with interpretHeader disabled | ||
interpretHeader: false | ||
} | ||
); | ||
}); | ||
const refetchedInformation = await axiosWithCache.storage.get(refetchedResponse.id); | ||
const refetchedInformation = await axios.storage.get(refetchedResponse.id); | ||
@@ -78,0 +66,0 @@ console.log('Third request TTL:'); |
{ | ||
"name": "axios-cache-interceptor", | ||
"version": "0.7.8", | ||
"version": "0.7.9", | ||
"description": "Cache interceptor for axios", | ||
@@ -17,2 +17,3 @@ "main": "./dist/index.js", | ||
"test:coverage": "jest --coverage", | ||
"escheck": "es-check es5 ./dist/index.es5.min.js && es-check es6 ./dist/index.min.js", | ||
"format": "prettier --write .", | ||
@@ -48,3 +49,3 @@ "lint": "tsc --noEmit && eslint . --ext .ts", | ||
"cache-parser": "^1.1.2", | ||
"fast-defer": "^1.1.2" | ||
"fast-defer": "^1.1.3" | ||
}, | ||
@@ -61,2 +62,3 @@ "devDependencies": { | ||
"concurrently": "^6.4.0", | ||
"es-check": "^6.1.1", | ||
"eslint": "^8.3.0", | ||
@@ -63,0 +65,0 @@ "eslint-config-prettier": "^8.3.0", |
156
README.md
@@ -40,3 +40,3 @@ <br /> | ||
<code | ||
><a href="https://bundlephobia.com/package/axios-cache-interceptor" | ||
><a href="https://bundlephobia.com/package/axios-cache-interceptor@latest" | ||
><img | ||
@@ -48,3 +48,3 @@ src="https://img.shields.io/bundlephobia/minzip/axios-cache-interceptor/latest?style=flat" | ||
<code | ||
><a href="https://bundlephobia.com/package/axios-cache-interceptor" | ||
><a href="https://packagephobia.com/result?p=axios-cache-interceptor@latest" | ||
><img | ||
@@ -84,6 +84,6 @@ src="https://packagephobia.com/badge?p=axios-cache-interceptor@latest" | ||
import axios from 'axios'; | ||
import { createCache, SessionCacheStorage } from 'axios-cache-interceptor'; | ||
import { setupCache, SessionCacheStorage } from 'axios-cache-interceptor'; | ||
// An axios instance with modified types | ||
const api = createCache(axios.create(), { | ||
const api = setupCache(axios.create(), { | ||
/* options */ | ||
@@ -113,2 +113,5 @@ }); | ||
- [Compiled code](#compiled-code) | ||
- [NodeJS](#nodejs) | ||
- [Browsers](#browsers) | ||
- [Typescript users](#typescript-users) | ||
- [Basic Knowledge](#basic-knowledge) | ||
@@ -165,5 +168,5 @@ - [Request id](#request-id) | ||
```js | ||
const { createCache } = require('axios-cache-interceptor'); | ||
const { setupCache } = require('axios-cache-interceptor'); | ||
// or | ||
import { createCache } from 'axios-cache-interceptor'; | ||
import { setupCache } from 'axios-cache-interceptor'; | ||
``` | ||
@@ -176,17 +179,31 @@ | ||
```html | ||
<!-- Replace VERSION with the desired version --> | ||
<!-- Replace latest with the desired version --> | ||
<!-- Development (ES2020) (~30KB) --> | ||
<script | ||
crossorigin | ||
src="https://cdn.jsdelivr.net/npm/axios-cache-interceptor@VERSION/dist/index.min.js" | ||
src="https://cdn.jsdelivr.net/npm/axios-cache-interceptor@latest/dist/index.development.js" | ||
></script> | ||
<!-- or --> | ||
<!-- Production for ES6+ (~12KB) --> | ||
<script | ||
crossorigin | ||
src="https://unpkg.com/axios-cache-interceptor@VERSION/dist/index.min.js" | ||
src="https://cdn.jsdelivr.net/npm/axios-cache-interceptor@latest/dist/index.min.js" | ||
></script> | ||
<!-- Production for ES5+ (~22KB) --> | ||
<script | ||
crossorigin | ||
src="https://cdn.jsdelivr.net/npm/axios-cache-interceptor@latest/dist/index.es5.min.js" | ||
></script> | ||
<!-- Production for ES2020+ (~9KB) --> | ||
<script | ||
crossorigin | ||
src="https://cdn.jsdelivr.net/npm/axios-cache-interceptor@latest/dist/index.es2020.min.js" | ||
></script> | ||
``` | ||
```js | ||
const { createCache } = window.AxiosCacheInterceptor; | ||
const { setupCache } = window.AxiosCacheInterceptor; | ||
``` | ||
@@ -206,5 +223,5 @@ | ||
| ----------------------------------------------------------------------------- | ------------------------------------------------ | | ||
| `~v0.5` | `>= v0.24` | | ||
| `~v0.4` | `>= v0.23` | | ||
| `~v0.3` | `>= v0.22` | | ||
| `>= v0.5` | `>= v0.24` | | ||
| `~ v0.4` | `>= v0.23` | | ||
| `~ v0.3` | `>= v0.22` | | ||
| `<= v0.2` | `v0.21` | | ||
@@ -220,3 +237,3 @@ | ||
```js | ||
import { createCache } from 'axios-cache-interceptor'; | ||
import { setupCache } from 'axios-cache-interceptor'; | ||
@@ -227,3 +244,3 @@ // Your axios instance | ||
// Return the same axios instance, but with a modified Typescript type. | ||
axios = createCache(axios, { | ||
axios = setupCache(axios, { | ||
/* options here */ | ||
@@ -233,4 +250,23 @@ }); | ||
After that, you can made your own requests normally. | ||
After that, you can made your own requests normally, as this library respects axios API. | ||
Afterwards, the only thing you may need to configure is per-request configuration, you can | ||
change them with the `cache` property. | ||
```js | ||
import { setupCache } from 'axios-cache-interceptor'; | ||
// Your axios-cache-interceptor instance | ||
let axios; | ||
axios.get('url', { | ||
cache: { | ||
/** Options here */ | ||
} | ||
}); | ||
``` | ||
You will get syntax highlighting for all options and what they do. But you can also read | ||
here: [Per-request configuration](#per-request-configuration). | ||
<br /> | ||
@@ -240,15 +276,85 @@ | ||
As axios itself requires [ES6 Promises](https://axios-http.com/docs/notes#promises), the | ||
CommonsJS module and the Browser minified version are also `>= ES6` compatibles. | ||
### NodeJS | ||
You can see more here about compiling options: | ||
The code is compiled with `tsc` with support to `>= ES6`. See the | ||
[build config](/tsconfig.build.json). | ||
- [Browser config](/tsconfig.browser.json) & [Webpack config](/webpack.config.js) | ||
- [NodeJS Config](/tsconfig.build.json) | ||
- `axios-cache-interceptor`: Redirects to `/dist/index.js` | ||
- `axios-cache-interceptor/dist/index.js`: The main library file. | ||
- `axios-cache-interceptor/dist/index.d.ts`: The Typescript definition file. | ||
Don't forget, you can always rebuilt this library by up from it's source code or recompile | ||
the dist with lower ecma script versions. | ||
Every browser build is also compatible with CommonsJS because it builds with UMD, so you | ||
can use them too. | ||
### Browsers | ||
> _NOTE_: Axios itself requires [ES6 Promises](https://axios-http.com/docs/notes#promises) | ||
The UMD code is compiled with `webpack` with support to `>= ES5`. See the | ||
[build config](/webpack.config.js). You can import these files anywhere (Browser, | ||
CommonsJS and more) | ||
- `axios-cache-interceptor/dist/index.min.js`: Production file for ES6+ | ||
- `axios-cache-interceptor/dist/index.es5.min.js`: Production file for ES5+ | ||
- `axios-cache-interceptor/dist/index.development.js`: Development file | ||
```html | ||
<!-- You can use the cdn of your choice --> | ||
<!-- UNPKG --> | ||
<script crossorigin src="https://unpkg.com/axios-cache-interceptor@latest"></script> | ||
<!-- JSDELIVR --> | ||
<script | ||
crossorigin | ||
src="https://cdn.jsdelivr.net/npm/axios-cache-interceptor@latest" | ||
></script> | ||
<!-- Etc... --> | ||
``` | ||
<br /> | ||
## Typescript users | ||
This package does not pollute the global axios typings. Instead, the `setupCache` returns | ||
the same axios instance but with **extended** typings. | ||
```ts | ||
const axios = axios.create(); | ||
axios === setupCache(axios, {}); | ||
``` | ||
In this way, we recommend you to not use a global axios instance with typescript, so you | ||
can use all exported types from `axios-cache-interceptor` by creating a new variable. | ||
```ts | ||
import Axios from 'axios'; | ||
import { setupCache, AxiosCacheInstance } from 'axios-cache-interceptor'; | ||
// instance will have our custom typings from the return of this function | ||
const instance = setupCache( | ||
Axios.create({ | ||
// Axios options | ||
}), | ||
{ | ||
// Axios-cache-interceptor options | ||
} | ||
); | ||
// OR | ||
const instance = axios.create({ | ||
// Axios options | ||
}) as AxiosCacheInstance; | ||
// As this functions returns the same axios instance but only with | ||
// different typings, you can ignore the function return. | ||
setupCache(instance, { | ||
// Axios-cache-interceptor options | ||
}); | ||
``` | ||
<br /> | ||
## Basic Knowledge | ||
@@ -310,3 +416,3 @@ | ||
```js | ||
const axios = createCache(axios, { | ||
const axios = setupCache(axios, { | ||
// Properties here | ||
@@ -313,0 +419,0 @@ }); |
@@ -15,7 +15,36 @@ import type { AxiosInstance } from 'axios'; | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* import Axios from 'axios'; | ||
* import { setupCache, AxiosCacheInstance } from 'axios-cache-interceptor'; | ||
* | ||
* // instance will have our custom typings from the return of this function | ||
* const instance = setupCache( | ||
* Axios.create({ | ||
* // Axios options | ||
* }), | ||
* { | ||
* // Axios-cache-interceptor options | ||
* } | ||
* ); | ||
* | ||
* // OR | ||
* | ||
* const instance = axios.create({ | ||
* // Axios options | ||
* }) as AxiosCacheInstance; | ||
* | ||
* // As this functions returns the same axios instance but only with | ||
* // different typings, you can ignore the function return. | ||
* setupCache(instance, { | ||
* // Axios-cache-interceptor options | ||
* }); | ||
* ``` | ||
* | ||
* @param axios The already created axios instance | ||
* @param config The config for the caching interceptors | ||
* @returns The same instance but with caching enabled | ||
* @returns The same instance with better typescript types. | ||
*/ | ||
export function createCache( | ||
export function setupCache( | ||
axios: AxiosInstance, | ||
@@ -65,10 +94,5 @@ { | ||
/** | ||
* Apply the caching interceptors for a already created axios instance. | ||
* | ||
* @deprecated Prefer {@link createCache} | ||
* @param axios The already created axios instance | ||
* @param config The config for the caching interceptors | ||
* @returns The same instance but with caching enabled | ||
*/ | ||
export const useCache = createCache; | ||
/** @deprecated */ | ||
export const useCache = setupCache as unknown as 'use setupCache instead'; | ||
/** @deprecated */ | ||
export const createCache = setupCache as unknown as 'use setupCache instead'; |
/** Index file for webpack and cdn usage */ | ||
export { createCache, useCache } from './cache/create'; | ||
export { createCache, setupCache, useCache } from './cache/create'; | ||
export { BrowserAxiosStorage } from './storage/browser'; | ||
export { MemoryAxiosStorage } from './storage/memory'; | ||
export { AxiosStorage } from './storage/storage'; |
@@ -88,3 +88,3 @@ import type { AxiosResponse } from 'axios'; | ||
const data = CacheResponseInterceptor.createCacheData(response, cache.data); | ||
const data = CacheResponseInterceptor.setupCacheData(response, cache.data); | ||
@@ -139,3 +139,3 @@ const newCache: CachedStorageValue = { | ||
*/ | ||
static readonly createCacheData = <R, D>( | ||
static readonly setupCacheData = <R, D>( | ||
response: CacheAxiosResponse<R, D>, | ||
@@ -142,0 +142,0 @@ cache?: CachedResponse |
@@ -10,3 +10,3 @@ { | ||
}, | ||
"include": ["src/index.browser.ts"] | ||
"include": ["src/index.*.ts"] | ||
} |
@@ -8,3 +8,3 @@ { | ||
"include": ["src"], | ||
"exclude": ["src/index.browser.ts"] | ||
"exclude": ["src/index.*.ts"] | ||
} |
@@ -51,3 +51,3 @@ { | ||
"outDir": "./dist" /* Specify an output folder for all emitted files. */, | ||
"removeComments": true, /* Disable emitting comments. */ | ||
// "removeComments": true, /* Disable emitting comments. */ | ||
"noEmit": true, /* Disable emitting files from a compilation. */ | ||
@@ -54,0 +54,0 @@ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ |
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
461403
90
3435
582
22
Updatedfast-defer@^1.1.3