diff --git a/src/axios/cache.ts b/src/axios/cache.ts index 6d95be1b..3387ab50 100644 --- a/src/axios/cache.ts +++ b/src/axios/cache.ts @@ -1,8 +1,8 @@ import { AxiosInstance } from 'axios'; +import { defaultHeaderInterpreter } from '../header'; 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 CacheInstance, { AxiosCacheInstance, CacheProperties } from './types'; @@ -16,7 +16,7 @@ export function createCache( 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 53fe4732..62e64218 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 { KeyGenerator } from 'src/util/key-generator'; import { HeaderInterpreter } from '../header'; import { CachedResponse, CacheStorage } from '../storage/types'; @@ -92,7 +93,7 @@ export default interface CacheInstance { * Defaults to a function that priorizes the id, and if not specified, * a string is generated using the method, baseUrl, params, and url */ - generateKey: (options: CacheRequestConfig) => string; + generateKey: KeyGenerator; /** * A simple object that holds all deferred objects until it is resolved. diff --git a/src/index.ts b/src/index.ts index 673a4ce3..7a39de05 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ export * from './axios'; -export * as Constants from './constants'; export * from './storage'; +export * as StatusCodes from './util/status-codes'; diff --git a/src/interceptors/request.ts b/src/interceptors/request.ts index cb2e1303..1df321ac 100644 --- a/src/interceptors/request.ts +++ b/src/interceptors/request.ts @@ -1,9 +1,9 @@ import { CachedResponse } from 'src/storage/types'; import { AxiosCacheInstance } from '../axios/types'; -import { CACHED_RESPONSE_STATUS, CACHED_RESPONSE_STATUS_TEXT } from '../constants'; import { Deferred } from '../util/deferred'; +import { CACHED_RESPONSE_STATUS, CACHED_RESPONSE_STATUS_TEXT } from '../util/status-codes'; -export function applyRequestInterceptor(axios: AxiosCacheInstance) { +export function applyRequestInterceptor(axios: AxiosCacheInstance): void { axios.interceptors.request.use(async (config) => { // Only cache specified methods if (config.cache?.methods?.some((method) => (config.method || 'get').toLowerCase() == method)) { diff --git a/src/interceptors/response.ts b/src/interceptors/response.ts index ed6c2031..54d9b325 100644 --- a/src/interceptors/response.ts +++ b/src/interceptors/response.ts @@ -1,15 +1,17 @@ import { AxiosCacheInstance } from '../axios/types'; import { updateCache } from '../util/update-cache'; -export function applyResponseInterceptor(axios: AxiosCacheInstance) { +export function applyResponseInterceptor(axios: AxiosCacheInstance): void { axios.interceptors.response.use(async (response) => { // Update other entries before updating himself if (response.config.cache?.update) { updateCache(axios, response.data, response.config.cache.update); } + const shouldCache = response.config.cache?.shouldCache || axios.defaults.cache.shouldCache; + // Config told that this response should be cached. - if (!response.config.cache?.shouldCache!(response)) { + if (shouldCache(response)) { return response; } @@ -24,14 +26,14 @@ export function applyResponseInterceptor(axios: AxiosCacheInstance) { const defaultMaxAge = response.config.cache?.maxAge || axios.defaults.cache.maxAge; cache.expiration = cache.expiration || defaultMaxAge; - let shouldCache = true; + let saveCache = true; if (response.config.cache?.interpretHeader) { const expirationTime = axios.interpretHeader(response.headers['cache-control']); // Header told that this response should not be cached. if (expirationTime === false) { - shouldCache = false; + saveCache = false; } else { cache.expiration = expirationTime ? expirationTime : defaultMaxAge; } @@ -45,7 +47,7 @@ export function applyResponseInterceptor(axios: AxiosCacheInstance) { deferred.resolve(data); } - if (shouldCache) { + if (saveCache) { await axios.storage.set(key, { data, expiration: cache.expiration, diff --git a/src/util/deferred.ts b/src/util/deferred.ts index 7040b4d4..c8c3063b 100644 --- a/src/util/deferred.ts +++ b/src/util/deferred.ts @@ -1,9 +1,11 @@ +export type MaybePromise = T | PromiseLike; + /** * Represents the completion of an asynchronous operation that can be completed later. */ export class Deferred { readonly promise: Promise; - private _resolve: (value: T | PromiseLike) => void = () => {}; + private _resolve: (value: MaybePromise) => void = () => {}; private _reject: (reason?: any) => void = () => {}; constructor() { @@ -17,7 +19,7 @@ export class Deferred { * Resolve this deferred promise with the given value. * @param the value to resolve */ - public readonly resolve = (value: T | PromiseLike): void => { + public readonly resolve = (value: MaybePromise): void => { this._resolve(value); }; @@ -36,8 +38,8 @@ export class Deferred { * @returns A Promise for the completion of which ever callback is executed. */ public readonly then = ( - onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, - onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null + onfulfilled?: ((value: T) => MaybePromise) | undefined | null, + onrejected?: ((reason: any) => MaybePromise) | undefined | null ): Promise => { return this.promise.then(onfulfilled, onrejected); }; @@ -48,7 +50,7 @@ export class Deferred { * @returns A Promise for the completion of the callback. */ public readonly catch = ( - onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null + onrejected?: ((reason: any) => MaybePromise) | undefined | null ): Promise => { return this.promise.catch(onrejected); }; diff --git a/src/util/interpret-header.ts b/src/util/interpret-header.ts deleted file mode 100644 index 2873df60..00000000 --- a/src/util/interpret-header.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { parse } from '@tusbar/cache-control'; -import { AxiosResponse } from 'axios'; - -// response.config.cache?.maxAge || axios.defaults.cache.maxAge - -/** - * Interpret the cache control header, if present. - * - * @param header the header to interpret. - * @returns false if header is not valid, undefined if the maxAge was not specified or a number in seconds from now. - */ -export function interpretCacheHeader( - headers: AxiosResponse['headers'] -): false | undefined | number { - const cacheControl = headers['cache-control'] || ''; - 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/util/key-generator.ts b/src/util/key-generator.ts index 853f69cb..0e6ff0c3 100644 --- a/src/util/key-generator.ts +++ b/src/util/key-generator.ts @@ -1,13 +1,20 @@ import { CacheRequestConfig } from '../axios/types'; -export function defaultKeyGenerator({ +export type KeyGenerator = (options: CacheRequestConfig) => string; + +export const defaultKeyGenerator: KeyGenerator = ({ baseURL, url, - method, + method: nullableMethod, params, id -}: CacheRequestConfig): string { - return id - ? `id::${String(id)}` - : `${method?.toLowerCase() || 'get'}::${baseURL}::${url}::${JSON.stringify(params || '{}')}`; -} +}) => { + if (id) { + return `id::${String(id)}`; + } + + const method = nullableMethod?.toLowerCase() || 'get'; + const jsonParams = params ? JSON.stringify(params) : '{}'; + + return `${method}::${baseURL}::${url}::${jsonParams}`; +}; diff --git a/src/constants.ts b/src/util/status-codes.ts similarity index 100% rename from src/constants.ts rename to src/util/status-codes.ts diff --git a/src/util/update-cache.ts b/src/util/update-cache.ts index c89435f2..f404fd86 100644 --- a/src/util/update-cache.ts +++ b/src/util/update-cache.ts @@ -4,7 +4,7 @@ export async function updateCache( axios: AxiosCacheInstance, data: any, entries: CacheProperties['update'] -) { +): Promise { for (const [cacheKey, value] of Object.entries(entries)) { if (value == 'delete') { await axios.storage.remove(cacheKey);