axios-cache-interceptor
Advanced tools
Comparing version 0.1.3 to 0.2.0
@@ -1,4 +0,19 @@ | ||
import { AxiosInstance } from 'axios'; | ||
import { AxiosInstance, AxiosRequestConfig } from 'axios'; | ||
import CacheInstance, { AxiosCacheInstance, CacheProperties } from './types'; | ||
export declare function createCache(axios: AxiosInstance, options?: Partial<CacheInstance> & Partial<CacheProperties>): AxiosCacheInstance; | ||
/** | ||
* Apply the caching interceptors for a already created axios instance. | ||
* | ||
* @param axios the already created axios instance | ||
* @param config the config for the caching interceptors | ||
* @returns the same instance but with caching enabled | ||
*/ | ||
export declare function applyCache(axios: AxiosInstance, { storage, generateKey, waiting, headerInterpreter, requestInterceptor, responseInterceptor, ...cacheOptions }?: Partial<CacheInstance> & Partial<CacheProperties>): AxiosCacheInstance; | ||
/** | ||
* Returns a new axios instance with caching enabled. | ||
* | ||
* @param config the config for the caching interceptors | ||
* @param axiosConfig the config for the created axios instance | ||
* @returns the same instance but with caching enabled | ||
*/ | ||
export declare function createCache(config?: Partial<CacheInstance> & Partial<CacheProperties>, axiosConfig?: AxiosRequestConfig): AxiosCacheInstance; | ||
//# sourceMappingURL=cache.d.ts.map |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createCache = void 0; | ||
exports.createCache = exports.applyCache = void 0; | ||
const axios_1 = __importDefault(require("axios")); | ||
const header_1 = require("../header"); | ||
@@ -9,8 +13,17 @@ const request_1 = require("../interceptors/request"); | ||
const key_generator_1 = require("../util/key-generator"); | ||
function createCache(axios, options = {}) { | ||
/** | ||
* Apply the caching interceptors for a already created axios instance. | ||
* | ||
* @param axios the already created axios instance | ||
* @param config the config for the caching interceptors | ||
* @returns the same instance but with caching enabled | ||
*/ | ||
function applyCache(axios, { storage, generateKey, waiting, headerInterpreter, requestInterceptor, responseInterceptor, ...cacheOptions } = {}) { | ||
const axiosCache = axios; | ||
axiosCache.storage = options.storage || new memory_1.MemoryStorage(); | ||
axiosCache.generateKey = options.generateKey || key_generator_1.defaultKeyGenerator; | ||
axiosCache.waiting = options.waiting || {}; | ||
axiosCache.headerInterpreter = options.headerInterpreter || header_1.defaultHeaderInterpreter; | ||
axiosCache.storage = storage || new memory_1.MemoryStorage(); | ||
axiosCache.generateKey = generateKey || key_generator_1.defaultKeyGenerator; | ||
axiosCache.waiting = waiting || {}; | ||
axiosCache.headerInterpreter = headerInterpreter || header_1.defaultHeaderInterpreter; | ||
axiosCache.requestInterceptor = requestInterceptor || new request_1.CacheRequestInterceptor(axiosCache); | ||
axiosCache.responseInterceptor = responseInterceptor || new response_1.CacheResponseInterceptor(axiosCache); | ||
// CacheRequestConfig values | ||
@@ -23,13 +36,26 @@ axiosCache.defaults = { | ||
methods: ['get'], | ||
cachePredicate: { statusCheck: [200, 399] }, | ||
cachePredicate: { | ||
statusCheck: [200, 399] | ||
}, | ||
update: {}, | ||
...options | ||
...cacheOptions | ||
} | ||
}; | ||
// Apply interceptors | ||
(0, request_1.applyRequestInterceptor)(axiosCache); | ||
(0, response_1.applyResponseInterceptor)(axiosCache); | ||
axiosCache.requestInterceptor.apply(); | ||
axiosCache.responseInterceptor.apply(); | ||
return axiosCache; | ||
} | ||
exports.applyCache = applyCache; | ||
/** | ||
* Returns a new axios instance with caching enabled. | ||
* | ||
* @param config the config for the caching interceptors | ||
* @param axiosConfig the config for the created axios instance | ||
* @returns the same instance but with caching enabled | ||
*/ | ||
function createCache(config = {}, axiosConfig = {}) { | ||
return applyCache(axios_1.default.create(axiosConfig), config); | ||
} | ||
exports.createCache = createCache; | ||
//# sourceMappingURL=cache.js.map |
import type { AxiosInstance, AxiosInterceptorManager, AxiosPromise, AxiosRequestConfig, AxiosResponse, Method } from 'axios'; | ||
import { HeaderInterpreter } from '../header'; | ||
import { AxiosInterceptor } from '../interceptors/types'; | ||
import { CachedResponse, CachedStorageValue, CacheStorage, EmptyStorageValue } from '../storage/types'; | ||
@@ -7,2 +8,3 @@ import { CachePredicate } from '../util/cache-predicate'; | ||
import { KeyGenerator } from '../util/key-generator'; | ||
export declare type CacheUpdater = ((cached: EmptyStorageValue | CachedStorageValue, newData: any) => CachedStorageValue | void) | 'delete'; | ||
export declare type DefaultCacheRequestConfig = AxiosRequestConfig & { | ||
@@ -43,3 +45,3 @@ cache: CacheProperties; | ||
* | ||
* If the function returns void, the entry is deleted | ||
* If the function returns nothing, the entry is deleted | ||
* | ||
@@ -52,6 +54,7 @@ * This is independent if the request made was cached or not. | ||
*/ | ||
update: { | ||
[id: string]: 'delete' | ((cached: EmptyStorageValue | CachedStorageValue, newData: any) => CachedStorageValue | undefined); | ||
}; | ||
update: Record<string, CacheUpdater>; | ||
}; | ||
export declare type CacheAxiosResponse = AxiosResponse & { | ||
config: CacheRequestConfig; | ||
}; | ||
/** | ||
@@ -67,3 +70,3 @@ * Options that can be overridden per request | ||
*/ | ||
id?: string | number; | ||
id?: string | symbol; | ||
/** | ||
@@ -98,2 +101,10 @@ * All cache options for the request. | ||
headerInterpreter: HeaderInterpreter; | ||
/** | ||
* The request interceptor that will be used to handle the cache. | ||
*/ | ||
requestInterceptor: AxiosInterceptor<CacheRequestConfig>; | ||
/** | ||
* The response interceptor that will be used to handle the cache. | ||
*/ | ||
responseInterceptor: AxiosInterceptor<CacheAxiosResponse>; | ||
} | ||
@@ -113,5 +124,3 @@ /** | ||
request: AxiosInterceptorManager<CacheRequestConfig>; | ||
response: AxiosInterceptorManager<AxiosResponse & { | ||
config: CacheRequestConfig; | ||
}>; | ||
response: AxiosInterceptorManager<CacheAxiosResponse>; | ||
}; | ||
@@ -118,0 +127,0 @@ getUri(config?: CacheRequestConfig): string; |
@@ -6,7 +6,7 @@ "use strict"; | ||
const defaultHeaderInterpreter = (headers) => { | ||
const cacheControl = headers?.['Cache-Control']; | ||
const cacheControl = headers?.['cache-control']; | ||
if (!cacheControl) { | ||
// Checks if Expires header is present | ||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires | ||
const expires = headers?.['Expires']; | ||
const expires = headers?.['expires']; | ||
if (expires) { | ||
@@ -13,0 +13,0 @@ const milliseconds = Date.parse(expires) - Date.now(); |
@@ -10,3 +10,3 @@ /** | ||
*/ | ||
export declare type HeaderInterpreter = (headers?: Record<string, string>) => false | undefined | number; | ||
export declare type HeaderInterpreter = (headers?: Record<Lowercase<string>, string>) => false | undefined | number; | ||
//# sourceMappingURL=types.d.ts.map |
@@ -1,3 +0,9 @@ | ||
import { AxiosCacheInstance } from '../axios/types'; | ||
export declare function applyRequestInterceptor(axios: AxiosCacheInstance): void; | ||
import { AxiosCacheInstance, CacheRequestConfig } from '../axios/types'; | ||
import { AxiosInterceptor } from './types'; | ||
export declare class CacheRequestInterceptor implements AxiosInterceptor<CacheRequestConfig> { | ||
readonly axios: AxiosCacheInstance; | ||
constructor(axios: AxiosCacheInstance); | ||
apply: () => void; | ||
onFulfilled: (config: CacheRequestConfig) => Promise<CacheRequestConfig>; | ||
} | ||
//# sourceMappingURL=request.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.applyRequestInterceptor = void 0; | ||
exports.CacheRequestInterceptor = void 0; | ||
const deferred_1 = require("../util/deferred"); | ||
const status_codes_1 = require("../util/status-codes"); | ||
function applyRequestInterceptor(axios) { | ||
axios.interceptors.request.use(async (config) => { | ||
class CacheRequestInterceptor { | ||
axios; | ||
constructor(axios) { | ||
this.axios = axios; | ||
} | ||
apply = () => { | ||
this.axios.interceptors.request.use(this.onFulfilled); | ||
}; | ||
onFulfilled = async (config) => { | ||
// Ignore caching | ||
@@ -13,14 +20,22 @@ if (config.cache === false) { | ||
// Only cache specified methods | ||
const allowedMethods = config.cache?.methods || axios.defaults.cache.methods; | ||
const allowedMethods = config.cache?.methods || this.axios.defaults.cache.methods; | ||
if (!allowedMethods.some((method) => (config.method || 'get').toLowerCase() == method)) { | ||
return config; | ||
} | ||
const key = axios.generateKey(config); | ||
const key = this.axios.generateKey(config); | ||
// Assumes that the storage handled staled responses | ||
const cache = await axios.storage.get(key); | ||
let cache = await this.axios.storage.get(key); | ||
// Not cached, continue the request, and mark it as fetching | ||
if (cache.state == 'empty') { | ||
emptyState: if (cache.state == 'empty') { | ||
// This if catches concurrent access to a new key. | ||
// The js event loop skips in the first await statement, | ||
// so the next code block will be executed both if called | ||
// from two places asynchronously. | ||
if (this.axios.waiting[key]) { | ||
cache = (await this.axios.storage.get(key)); | ||
break emptyState; | ||
} | ||
// Create a deferred to resolve other requests for the same key when it's completed | ||
axios.waiting[key] = new deferred_1.Deferred(); | ||
await axios.storage.set(key, { | ||
this.axios.waiting[key] = new deferred_1.Deferred(); | ||
await this.axios.storage.set(key, { | ||
state: 'loading', | ||
@@ -33,7 +48,7 @@ ttl: config.cache?.ttl | ||
if (cache.state === 'loading') { | ||
const deferred = axios.waiting[key]; | ||
const deferred = this.axios.waiting[key]; | ||
// If the deferred is undefined, means that the | ||
// outside has removed that key from the waiting list | ||
if (!deferred) { | ||
await axios.storage.remove(key); | ||
await this.axios.storage.remove(key); | ||
return config; | ||
@@ -50,9 +65,9 @@ } | ||
headers: data.headers, | ||
status: status_codes_1.CACHED_RESPONSE_STATUS, | ||
statusText: status_codes_1.CACHED_RESPONSE_STATUS_TEXT | ||
status: status_codes_1.CACHED_STATUS_CODE, | ||
statusText: status_codes_1.CACHED_STATUS_TEXT | ||
}); | ||
return config; | ||
}); | ||
}; | ||
} | ||
exports.applyRequestInterceptor = applyRequestInterceptor; | ||
exports.CacheRequestInterceptor = CacheRequestInterceptor; | ||
//# sourceMappingURL=request.js.map |
@@ -1,3 +0,15 @@ | ||
import { AxiosCacheInstance } from '../axios/types'; | ||
export declare function applyResponseInterceptor(axios: AxiosCacheInstance): void; | ||
import { AxiosResponse } from 'axios'; | ||
import { AxiosCacheInstance, CacheAxiosResponse, CacheProperties, CacheRequestConfig } from '../axios/types'; | ||
import { AxiosInterceptor } from './types'; | ||
declare type CacheConfig = CacheRequestConfig & { | ||
cache?: Partial<CacheProperties>; | ||
}; | ||
export declare class CacheResponseInterceptor implements AxiosInterceptor<CacheAxiosResponse> { | ||
readonly axios: AxiosCacheInstance; | ||
constructor(axios: AxiosCacheInstance); | ||
apply: () => void; | ||
testCachePredicate: (response: AxiosResponse, { cache }: CacheConfig) => boolean; | ||
onFulfilled: (response: CacheAxiosResponse) => Promise<CacheAxiosResponse>; | ||
} | ||
export {}; | ||
//# sourceMappingURL=response.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.applyResponseInterceptor = void 0; | ||
exports.CacheResponseInterceptor = void 0; | ||
const cache_predicate_1 = require("../util/cache-predicate"); | ||
const update_cache_1 = require("../util/update-cache"); | ||
function applyResponseInterceptor(axios) { | ||
const testCachePredicate = (response, config) => { | ||
const cachePredicate = config.cache?.cachePredicate || axios.defaults.cache.cachePredicate; | ||
class CacheResponseInterceptor { | ||
axios; | ||
constructor(axios) { | ||
this.axios = axios; | ||
} | ||
apply = () => { | ||
this.axios.interceptors.response.use(this.onFulfilled); | ||
}; | ||
testCachePredicate = (response, { cache }) => { | ||
const cachePredicate = cache?.cachePredicate || this.axios.defaults.cache.cachePredicate; | ||
return ((typeof cachePredicate === 'function' && cachePredicate(response)) || | ||
(typeof cachePredicate === 'object' && (0, cache_predicate_1.checkPredicateObject)(response, cachePredicate))); | ||
}; | ||
axios.interceptors.response.use(async (response) => { | ||
onFulfilled = async (response) => { | ||
// Ignore caching | ||
@@ -17,4 +24,4 @@ if (response.config.cache === false) { | ||
} | ||
const key = axios.generateKey(response.config); | ||
const cache = await axios.storage.get(key); | ||
const key = this.axios.generateKey(response.config); | ||
const cache = await this.axios.storage.get(key); | ||
// Response shouldn't be cached or was already cached | ||
@@ -25,14 +32,14 @@ if (cache.state !== 'loading') { | ||
// Config told that this response should be cached. | ||
if (!testCachePredicate(response, response.config)) { | ||
if (!this.testCachePredicate(response, response.config)) { | ||
// Update the cache to empty to prevent infinite loading state | ||
await axios.storage.remove(key); | ||
await this.axios.storage.remove(key); | ||
return response; | ||
} | ||
let ttl = response.config.cache?.ttl || axios.defaults.cache.ttl; | ||
let ttl = response.config.cache?.ttl || this.axios.defaults.cache.ttl; | ||
if (response.config.cache?.interpretHeader) { | ||
const expirationTime = axios.headerInterpreter(response.headers['cache-control']); | ||
const expirationTime = this.axios.headerInterpreter(response.headers); | ||
// Cache should not be used | ||
if (expirationTime === false) { | ||
// Update the cache to empty to prevent infinite loading state | ||
await axios.storage.remove(key); | ||
await this.axios.storage.remove(key); | ||
return response; | ||
@@ -50,5 +57,5 @@ } | ||
if (response.config.cache?.update) { | ||
(0, update_cache_1.updateCache)(axios, response.data, response.config.cache.update); | ||
(0, update_cache_1.updateCache)(this.axios, response.data, response.config.cache.update); | ||
} | ||
const deferred = axios.waiting[key]; | ||
const deferred = this.axios.waiting[key]; | ||
// Resolve all other requests waiting for this response | ||
@@ -58,7 +65,7 @@ if (deferred) { | ||
} | ||
await axios.storage.set(key, newCache); | ||
await this.axios.storage.set(key, newCache); | ||
return response; | ||
}); | ||
}; | ||
} | ||
exports.applyResponseInterceptor = applyResponseInterceptor; | ||
exports.CacheResponseInterceptor = CacheResponseInterceptor; | ||
//# sourceMappingURL=response.js.map |
@@ -1,3 +0,3 @@ | ||
export declare const CACHED_RESPONSE_STATUS = 304; | ||
export declare const CACHED_RESPONSE_STATUS_TEXT = "304 Cached by axios-cache-interceptor"; | ||
export declare const CACHED_STATUS_CODE = 304; | ||
export declare const CACHED_STATUS_TEXT = "304 Cached by axios-cache-interceptor"; | ||
//# sourceMappingURL=status-codes.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.CACHED_RESPONSE_STATUS_TEXT = exports.CACHED_RESPONSE_STATUS = void 0; | ||
exports.CACHED_RESPONSE_STATUS = 304; | ||
exports.CACHED_RESPONSE_STATUS_TEXT = '304 Cached by axios-cache-interceptor'; | ||
exports.CACHED_STATUS_TEXT = exports.CACHED_STATUS_CODE = void 0; | ||
exports.CACHED_STATUS_CODE = 304; | ||
exports.CACHED_STATUS_TEXT = '304 Cached by axios-cache-interceptor'; | ||
//# sourceMappingURL=status-codes.js.map |
@@ -1,3 +0,3 @@ | ||
import { AxiosCacheInstance, CacheProperties } from '../axios'; | ||
export declare function updateCache(axios: AxiosCacheInstance, data: any, entries: CacheProperties['update']): Promise<void>; | ||
import { AxiosCacheInstance, CacheUpdater } from '../axios'; | ||
export declare function updateCache(axios: AxiosCacheInstance, data: any, entries: Record<string, CacheUpdater>): Promise<void>; | ||
//# sourceMappingURL=update-cache.d.ts.map |
{ | ||
"name": "axios-cache-interceptor", | ||
"version": "0.1.3", | ||
"version": "0.2.0", | ||
"description": "Cache interceptor for axios", | ||
@@ -13,3 +13,4 @@ "main": "dist/index.js", | ||
"lint": "tsc --noEmit && eslint . --ext .ts", | ||
"lint:fix": "eslint . --ext .ts --fix" | ||
"lint:fix": "eslint . --ext .ts --fix", | ||
"version": "auto-changelog -p && git add CHANGELOG.md" | ||
}, | ||
@@ -48,2 +49,3 @@ "repository": { | ||
"@typescript-eslint/parser": "^4.30.0", | ||
"auto-changelog": "^2.3.0", | ||
"axios": "^0.21.1", | ||
@@ -50,0 +52,0 @@ "eslint": "^7.32.0", |
@@ -1,5 +0,5 @@ | ||
import { AxiosInstance } from 'axios'; | ||
import Axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; | ||
import { defaultHeaderInterpreter } from '../header'; | ||
import { applyRequestInterceptor } from '../interceptors/request'; | ||
import { applyResponseInterceptor } from '../interceptors/response'; | ||
import { CacheRequestInterceptor } from '../interceptors/request'; | ||
import { CacheResponseInterceptor } from '../interceptors/response'; | ||
import { MemoryStorage } from '../storage/memory'; | ||
@@ -9,12 +9,29 @@ import { defaultKeyGenerator } from '../util/key-generator'; | ||
export function createCache( | ||
/** | ||
* Apply the caching interceptors for a already created axios instance. | ||
* | ||
* @param axios the already created axios instance | ||
* @param config the config for the caching interceptors | ||
* @returns the same instance but with caching enabled | ||
*/ | ||
export function applyCache( | ||
axios: AxiosInstance, | ||
options: Partial<CacheInstance> & Partial<CacheProperties> = {} | ||
{ | ||
storage, | ||
generateKey, | ||
waiting, | ||
headerInterpreter, | ||
requestInterceptor, | ||
responseInterceptor, | ||
...cacheOptions | ||
}: Partial<CacheInstance> & Partial<CacheProperties> = {} | ||
): AxiosCacheInstance { | ||
const axiosCache = axios as AxiosCacheInstance; | ||
axiosCache.storage = options.storage || new MemoryStorage(); | ||
axiosCache.generateKey = options.generateKey || defaultKeyGenerator; | ||
axiosCache.waiting = options.waiting || {}; | ||
axiosCache.headerInterpreter = options.headerInterpreter || defaultHeaderInterpreter; | ||
axiosCache.storage = storage || new MemoryStorage(); | ||
axiosCache.generateKey = generateKey || defaultKeyGenerator; | ||
axiosCache.waiting = waiting || {}; | ||
axiosCache.headerInterpreter = headerInterpreter || defaultHeaderInterpreter; | ||
axiosCache.requestInterceptor = requestInterceptor || new CacheRequestInterceptor(axiosCache); | ||
axiosCache.responseInterceptor = responseInterceptor || new CacheResponseInterceptor(axiosCache); | ||
@@ -28,5 +45,7 @@ // CacheRequestConfig values | ||
methods: ['get'], | ||
cachePredicate: { statusCheck: [200, 399] }, | ||
cachePredicate: { | ||
statusCheck: [200, 399] | ||
}, | ||
update: {}, | ||
...options | ||
...cacheOptions | ||
} | ||
@@ -36,6 +55,20 @@ }; | ||
// Apply interceptors | ||
applyRequestInterceptor(axiosCache); | ||
applyResponseInterceptor(axiosCache); | ||
axiosCache.requestInterceptor.apply(); | ||
axiosCache.responseInterceptor.apply(); | ||
return axiosCache; | ||
} | ||
/** | ||
* Returns a new axios instance with caching enabled. | ||
* | ||
* @param config the config for the caching interceptors | ||
* @param axiosConfig the config for the created axios instance | ||
* @returns the same instance but with caching enabled | ||
*/ | ||
export function createCache( | ||
config: Partial<CacheInstance> & Partial<CacheProperties> = {}, | ||
axiosConfig: AxiosRequestConfig = {} | ||
): AxiosCacheInstance { | ||
return applyCache(Axios.create(axiosConfig), config); | ||
} |
@@ -10,2 +10,3 @@ import type { | ||
import { HeaderInterpreter } from '../header'; | ||
import { AxiosInterceptor } from '../interceptors/types'; | ||
import { | ||
@@ -21,2 +22,6 @@ CachedResponse, | ||
export type CacheUpdater = | ||
| ((cached: EmptyStorageValue | CachedStorageValue, newData: any) => CachedStorageValue | void) | ||
| 'delete'; | ||
export type DefaultCacheRequestConfig = AxiosRequestConfig & { | ||
@@ -62,3 +67,3 @@ cache: CacheProperties; | ||
* | ||
* If the function returns void, the entry is deleted | ||
* If the function returns nothing, the entry is deleted | ||
* | ||
@@ -71,12 +76,9 @@ * This is independent if the request made was cached or not. | ||
*/ | ||
update: { | ||
[id: string]: | ||
| 'delete' | ||
| (( | ||
cached: EmptyStorageValue | CachedStorageValue, | ||
newData: any | ||
) => CachedStorageValue | undefined); | ||
}; | ||
update: Record<string, CacheUpdater>; | ||
}; | ||
export type CacheAxiosResponse = AxiosResponse & { | ||
config: CacheRequestConfig; | ||
}; | ||
/** | ||
@@ -92,3 +94,3 @@ * Options that can be overridden per request | ||
*/ | ||
id?: string | number; | ||
id?: string | symbol; | ||
@@ -128,2 +130,12 @@ /** | ||
headerInterpreter: HeaderInterpreter; | ||
/** | ||
* The request interceptor that will be used to handle the cache. | ||
*/ | ||
requestInterceptor: AxiosInterceptor<CacheRequestConfig>; | ||
/** | ||
* The response interceptor that will be used to handle the cache. | ||
*/ | ||
responseInterceptor: AxiosInterceptor<CacheAxiosResponse>; | ||
} | ||
@@ -146,3 +158,3 @@ | ||
request: AxiosInterceptorManager<CacheRequestConfig>; | ||
response: AxiosInterceptorManager<AxiosResponse & { config: CacheRequestConfig }>; | ||
response: AxiosInterceptorManager<CacheAxiosResponse>; | ||
}; | ||
@@ -149,0 +161,0 @@ |
@@ -5,3 +5,3 @@ import { parse } from '@tusbar/cache-control'; | ||
export const defaultHeaderInterpreter: HeaderInterpreter = (headers) => { | ||
const cacheControl = headers?.['Cache-Control']; | ||
const cacheControl = headers?.['cache-control']; | ||
@@ -11,3 +11,3 @@ if (!cacheControl) { | ||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires | ||
const expires = headers?.['Expires']; | ||
const expires = headers?.['expires']; | ||
@@ -14,0 +14,0 @@ if (expires) { |
@@ -10,2 +10,4 @@ /** | ||
*/ | ||
export type HeaderInterpreter = (headers?: Record<string, string>) => false | undefined | number; | ||
export type HeaderInterpreter = ( | ||
headers?: Record<Lowercase<string>, string> | ||
) => false | undefined | number; |
@@ -1,8 +0,15 @@ | ||
import { AxiosCacheInstance } from '../axios/types'; | ||
import { CachedResponse } from '../storage/types'; | ||
import { AxiosCacheInstance, CacheRequestConfig } from '../axios/types'; | ||
import { CachedResponse, CachedStorageValue, LoadingStorageValue } from '../storage/types'; | ||
import { Deferred } from '../util/deferred'; | ||
import { CACHED_RESPONSE_STATUS, CACHED_RESPONSE_STATUS_TEXT } from '../util/status-codes'; | ||
import { CACHED_STATUS_CODE, CACHED_STATUS_TEXT } from '../util/status-codes'; | ||
import { AxiosInterceptor } from './types'; | ||
export function applyRequestInterceptor(axios: AxiosCacheInstance): void { | ||
axios.interceptors.request.use(async (config) => { | ||
export class CacheRequestInterceptor implements AxiosInterceptor<CacheRequestConfig> { | ||
constructor(readonly axios: AxiosCacheInstance) {} | ||
apply = (): void => { | ||
this.axios.interceptors.request.use(this.onFulfilled); | ||
}; | ||
onFulfilled = async (config: CacheRequestConfig): Promise<CacheRequestConfig> => { | ||
// Ignore caching | ||
@@ -14,3 +21,3 @@ if (config.cache === false) { | ||
// Only cache specified methods | ||
const allowedMethods = config.cache?.methods || axios.defaults.cache.methods; | ||
const allowedMethods = config.cache?.methods || this.axios.defaults.cache.methods; | ||
@@ -21,13 +28,22 @@ if (!allowedMethods.some((method) => (config.method || 'get').toLowerCase() == method)) { | ||
const key = axios.generateKey(config); | ||
const key = this.axios.generateKey(config); | ||
// Assumes that the storage handled staled responses | ||
const cache = await axios.storage.get(key); | ||
let cache = await this.axios.storage.get(key); | ||
// Not cached, continue the request, and mark it as fetching | ||
if (cache.state == 'empty') { | ||
emptyState: if (cache.state == 'empty') { | ||
// This if catches concurrent access to a new key. | ||
// The js event loop skips in the first await statement, | ||
// so the next code block will be executed both if called | ||
// from two places asynchronously. | ||
if (this.axios.waiting[key]) { | ||
cache = (await this.axios.storage.get(key)) as CachedStorageValue | LoadingStorageValue; | ||
break emptyState; | ||
} | ||
// Create a deferred to resolve other requests for the same key when it's completed | ||
axios.waiting[key] = new Deferred(); | ||
this.axios.waiting[key] = new Deferred(); | ||
await axios.storage.set(key, { | ||
await this.axios.storage.set(key, { | ||
state: 'loading', | ||
@@ -43,3 +59,3 @@ ttl: config.cache?.ttl | ||
if (cache.state === 'loading') { | ||
const deferred = axios.waiting[key]; | ||
const deferred = this.axios.waiting[key]; | ||
@@ -49,3 +65,3 @@ // If the deferred is undefined, means that the | ||
if (!deferred) { | ||
await axios.storage.remove(key); | ||
await this.axios.storage.remove(key); | ||
return config; | ||
@@ -64,8 +80,8 @@ } | ||
headers: data.headers, | ||
status: CACHED_RESPONSE_STATUS, | ||
statusText: CACHED_RESPONSE_STATUS_TEXT | ||
status: CACHED_STATUS_CODE, | ||
statusText: CACHED_STATUS_TEXT | ||
}); | ||
return config; | ||
}); | ||
}; | ||
} |
import { AxiosResponse } from 'axios'; | ||
import { AxiosCacheInstance, CacheProperties, CacheRequestConfig } from '../axios/types'; | ||
import { | ||
AxiosCacheInstance, | ||
CacheAxiosResponse, | ||
CacheProperties, | ||
CacheRequestConfig | ||
} from '../axios/types'; | ||
import { CachedStorageValue } from '../storage/types'; | ||
import { checkPredicateObject } from '../util/cache-predicate'; | ||
import { updateCache } from '../util/update-cache'; | ||
import { AxiosInterceptor } from './types'; | ||
type CacheConfig = CacheRequestConfig & { cache?: Partial<CacheProperties> }; | ||
export function applyResponseInterceptor(axios: AxiosCacheInstance): void { | ||
const testCachePredicate = (response: AxiosResponse, config: CacheConfig): boolean => { | ||
const cachePredicate = config.cache?.cachePredicate || axios.defaults.cache.cachePredicate; | ||
export class CacheResponseInterceptor implements AxiosInterceptor<CacheAxiosResponse> { | ||
constructor(readonly axios: AxiosCacheInstance) {} | ||
apply = (): void => { | ||
this.axios.interceptors.response.use(this.onFulfilled); | ||
}; | ||
testCachePredicate = (response: AxiosResponse, { cache }: CacheConfig): boolean => { | ||
const cachePredicate = cache?.cachePredicate || this.axios.defaults.cache.cachePredicate; | ||
return ( | ||
@@ -19,3 +31,3 @@ (typeof cachePredicate === 'function' && cachePredicate(response)) || | ||
axios.interceptors.response.use(async (response) => { | ||
onFulfilled = async (response: CacheAxiosResponse): Promise<CacheAxiosResponse> => { | ||
// Ignore caching | ||
@@ -26,4 +38,4 @@ if (response.config.cache === false) { | ||
const key = axios.generateKey(response.config); | ||
const cache = await axios.storage.get(key); | ||
const key = this.axios.generateKey(response.config); | ||
const cache = await this.axios.storage.get(key); | ||
@@ -36,12 +48,12 @@ // Response shouldn't be cached or was already cached | ||
// Config told that this response should be cached. | ||
if (!testCachePredicate(response, response.config as CacheConfig)) { | ||
if (!this.testCachePredicate(response, response.config as CacheConfig)) { | ||
// Update the cache to empty to prevent infinite loading state | ||
await axios.storage.remove(key); | ||
await this.axios.storage.remove(key); | ||
return response; | ||
} | ||
let ttl = response.config.cache?.ttl || axios.defaults.cache.ttl; | ||
let ttl = response.config.cache?.ttl || this.axios.defaults.cache.ttl; | ||
if (response.config.cache?.interpretHeader) { | ||
const expirationTime = axios.headerInterpreter(response.headers['cache-control']); | ||
const expirationTime = this.axios.headerInterpreter(response.headers); | ||
@@ -51,3 +63,3 @@ // Cache should not be used | ||
// Update the cache to empty to prevent infinite loading state | ||
await axios.storage.remove(key); | ||
await this.axios.storage.remove(key); | ||
return response; | ||
@@ -68,6 +80,6 @@ } | ||
if (response.config.cache?.update) { | ||
updateCache(axios, response.data, response.config.cache.update); | ||
updateCache(this.axios, response.data, response.config.cache.update); | ||
} | ||
const deferred = axios.waiting[key]; | ||
const deferred = this.axios.waiting[key]; | ||
@@ -79,6 +91,6 @@ // Resolve all other requests waiting for this response | ||
await axios.storage.set(key, newCache); | ||
await this.axios.storage.set(key, newCache); | ||
return response; | ||
}); | ||
}; | ||
} |
@@ -1,2 +0,2 @@ | ||
export const CACHED_RESPONSE_STATUS = 304; | ||
export const CACHED_RESPONSE_STATUS_TEXT = '304 Cached by axios-cache-interceptor'; | ||
export const CACHED_STATUS_CODE = 304; | ||
export const CACHED_STATUS_TEXT = '304 Cached by axios-cache-interceptor'; |
@@ -1,2 +0,2 @@ | ||
import { AxiosCacheInstance, CacheProperties } from '../axios'; | ||
import { AxiosCacheInstance, CacheUpdater } from '../axios'; | ||
@@ -6,3 +6,3 @@ export async function updateCache( | ||
data: any, | ||
entries: CacheProperties['update'] | ||
entries: Record<string, CacheUpdater> | ||
): Promise<void> { | ||
@@ -9,0 +9,0 @@ for (const [cacheKey, value] of Object.entries(entries)) { |
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
119390
104
1625
16