diff --git a/src/axios/cache.ts b/src/axios/cache.ts index 13a40eb..6d95be1 100644 --- a/src/axios/cache.ts +++ b/src/axios/cache.ts @@ -2,8 +2,9 @@ import { AxiosInstance } from 'axios'; import { applyRequestInterceptor } from '../interceptors/request'; import { applyResponseInterceptor } from '../interceptors/response'; import { MemoryStorage } from '../storage/memory'; +import { defaultHeaderInterpreter } from '../header'; import { defaultKeyGenerator } from '../util/key-generator'; -import { AxiosCacheInstance, CacheInstance, CacheProperties } from './types'; +import CacheInstance, { AxiosCacheInstance, CacheProperties } from './types'; export function createCache( axios: AxiosInstance, @@ -14,7 +15,8 @@ export function createCache( axiosCache.storage = options.storage || new MemoryStorage(); axiosCache.generateKey = options.generateKey || defaultKeyGenerator; axiosCache.waiting = options.waiting || {}; - + axiosCache.interpretHeader = options.interpretHeader || defaultHeaderInterpreter; + // CacheRequestConfig values axiosCache.defaults = { ...axios.defaults, diff --git a/src/axios/types.ts b/src/axios/types.ts index e5a4c23..53fe473 100644 --- a/src/axios/types.ts +++ b/src/axios/types.ts @@ -7,6 +7,7 @@ import type { Method } from 'axios'; import { Deferred } from 'src/util/deferred'; +import { HeaderInterpreter } from '../header'; import { CachedResponse, CacheStorage } from '../storage/types'; export type DefaultCacheRequestConfig = AxiosRequestConfig & { @@ -78,7 +79,7 @@ export type CacheRequestConfig = AxiosRequestConfig & { cache?: Partial; }; -export interface CacheInstance { +export default interface CacheInstance { /** * The storage to save the cache data. * @@ -97,6 +98,12 @@ export interface CacheInstance { * A simple object that holds all deferred objects until it is resolved. */ waiting: Record>; + + /** + * The function to parse and interpret response headers. + * Only used if cache.interpretHeader is true. + */ + interpretHeader: HeaderInterpreter; } /** diff --git a/src/header/index.ts b/src/header/index.ts new file mode 100644 index 0000000..f719e45 --- /dev/null +++ b/src/header/index.ts @@ -0,0 +1,2 @@ +export * from './interpreter'; +export * from './types'; diff --git a/src/header/interpreter.ts b/src/header/interpreter.ts new file mode 100644 index 0000000..8e2fd22 --- /dev/null +++ b/src/header/interpreter.ts @@ -0,0 +1,23 @@ +import { parse } from '@tusbar/cache-control'; +import { HeaderInterpreter } from './types'; + +export const defaultHeaderInterpreter: HeaderInterpreter = (headers) => { + const cacheControl = headers?.['cache-control']; + + if (!cacheControl) { + return undefined; + } + + const { noCache, noStore, maxAge } = parse(cacheControl); + + // Header told that this response should not be cached. + if (noCache || noStore) { + return false; + } + + if (!maxAge) { + return undefined; + } + + return Date.now() + maxAge * 1000; +}; diff --git a/src/header/types.ts b/src/header/types.ts new file mode 100644 index 0000000..8f57777 --- /dev/null +++ b/src/header/types.ts @@ -0,0 +1,12 @@ +/** + * Interpret the cache control header, if present. + * + * @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 **seconds** to cache the response. + */ +export type HeaderInterpreter = ( + headers: Record | undefined +) => false | undefined | number; diff --git a/src/interceptors/response.ts b/src/interceptors/response.ts index 122f0cb..ed6c203 100644 --- a/src/interceptors/response.ts +++ b/src/interceptors/response.ts @@ -1,5 +1,4 @@ import { AxiosCacheInstance } from '../axios/types'; -import { interpretCacheHeader } from './util/interpret-header'; import { updateCache } from '../util/update-cache'; export function applyResponseInterceptor(axios: AxiosCacheInstance) { @@ -28,7 +27,7 @@ export function applyResponseInterceptor(axios: AxiosCacheInstance) { let shouldCache = true; if (response.config.cache?.interpretHeader) { - const expirationTime = interpretCacheHeader(response.headers['cache-control']); + const expirationTime = axios.interpretHeader(response.headers['cache-control']); // Header told that this response should not be cached. if (expirationTime === false) {