mirror of
https://github.com/arthurfiorette/axios-cache-interceptor.git
synced 2025-12-08 17:36:16 +00:00
refactor: removed classes to decrease bundle size
This commit is contained in:
parent
4f938a4793
commit
cd2f14c446
4
src/cache/axios.ts
vendored
4
src/cache/axios.ts
vendored
@ -76,8 +76,8 @@ export interface AxiosCacheInstance extends CacheInstance, AxiosInstance {
|
||||
};
|
||||
|
||||
interceptors: {
|
||||
request: AxiosInterceptorManager<CacheRequestConfig<any, any>>;
|
||||
response: AxiosInterceptorManager<CacheAxiosResponse<any, any>>;
|
||||
request: AxiosInterceptorManager<CacheRequestConfig<unknown, unknown>>;
|
||||
response: AxiosInterceptorManager<CacheAxiosResponse<unknown, unknown>>;
|
||||
};
|
||||
|
||||
/** @template D The type that the request body use */
|
||||
|
||||
4
src/cache/cache.ts
vendored
4
src/cache/cache.ts
vendored
@ -1,7 +1,7 @@
|
||||
import type { Method } from 'axios';
|
||||
import type { Deferred } from 'fast-defer';
|
||||
import type { HeadersInterpreter } from '../header/types';
|
||||
import type { AxiosInterceptor } from '../interceptors/types';
|
||||
import type { AxiosInterceptor } from '../interceptors/build';
|
||||
import type { AxiosStorage, CachedResponse } from '../storage/types';
|
||||
import type { CachePredicate, CacheUpdater, KeyGenerator } from '../util/types';
|
||||
import type { CacheAxiosResponse, CacheRequestConfig } from './axios';
|
||||
@ -89,7 +89,7 @@ export interface CacheInstance {
|
||||
/**
|
||||
* 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
|
||||
* baseURL, params, and url
|
||||
*/
|
||||
generateKey: KeyGenerator;
|
||||
|
||||
|
||||
14
src/cache/create.ts
vendored
14
src/cache/create.ts
vendored
@ -1,7 +1,7 @@
|
||||
import type { AxiosInstance } from 'axios';
|
||||
import { defaultHeaderInterpreter } from '../header/interpreter';
|
||||
import { CacheRequestInterceptor } from '../interceptors/request';
|
||||
import { CacheResponseInterceptor } from '../interceptors/response';
|
||||
import { defaultRequestInterceptor } from '../interceptors/request';
|
||||
import { defaultResponseInterceptor } from '../interceptors/response';
|
||||
import { isStorage } from '../storage/build';
|
||||
import { buildMemoryStorage } from '../storage/memory';
|
||||
import { defaultKeyGenerator } from '../util/key-generator';
|
||||
@ -65,16 +65,16 @@ export function setupCache(
|
||||
axiosCache.storage = storage || buildMemoryStorage();
|
||||
|
||||
if (!isStorage(axiosCache.storage)) {
|
||||
throw new Error('Use buildStorage()');
|
||||
throw new Error('Use buildStorage() function');
|
||||
}
|
||||
|
||||
axiosCache.generateKey = generateKey || defaultKeyGenerator;
|
||||
axiosCache.waiting = waiting || {};
|
||||
axiosCache.headerInterpreter = headerInterpreter || defaultHeaderInterpreter;
|
||||
axiosCache.requestInterceptor =
|
||||
requestInterceptor || new CacheRequestInterceptor(axiosCache);
|
||||
requestInterceptor || defaultRequestInterceptor(axiosCache);
|
||||
axiosCache.responseInterceptor =
|
||||
responseInterceptor || new CacheResponseInterceptor(axiosCache);
|
||||
responseInterceptor || defaultResponseInterceptor(axiosCache);
|
||||
|
||||
// CacheRequestConfig values
|
||||
axiosCache.defaults = {
|
||||
@ -92,8 +92,8 @@ export function setupCache(
|
||||
};
|
||||
|
||||
// Apply interceptors
|
||||
axiosCache.requestInterceptor.use();
|
||||
axiosCache.responseInterceptor.use();
|
||||
axiosCache.requestInterceptor.apply(axiosCache);
|
||||
axiosCache.responseInterceptor.apply(axiosCache);
|
||||
|
||||
// @ts-expect-error - internal only
|
||||
axiosCache[symbolKey] = 1;
|
||||
|
||||
@ -1,11 +1,4 @@
|
||||
/**
|
||||
* `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.
|
||||
*/
|
||||
type MaybeTtl = 'dont cache' | 'not enough headers' | number;
|
||||
export type InterpreterResult = 'dont cache' | 'not enough headers' | number;
|
||||
|
||||
/**
|
||||
* Interpret all http headers to determina a time to live.
|
||||
@ -15,7 +8,7 @@ type MaybeTtl = 'dont cache' | 'not enough headers' | number;
|
||||
* enough to determine a valid value. Or a `number` containing the number of
|
||||
* **milliseconds** to cache the response.
|
||||
*/
|
||||
export type HeadersInterpreter = (headers?: Record<string, string>) => MaybeTtl;
|
||||
export type HeadersInterpreter = (headers?: Record<string, string>) => InterpreterResult;
|
||||
|
||||
/**
|
||||
* Interpret a single string header
|
||||
@ -28,4 +21,4 @@ export type HeadersInterpreter = (headers?: Record<string, string>) => MaybeTtl;
|
||||
export type HeaderInterpreter = (
|
||||
header: string,
|
||||
headers: Record<string, string>
|
||||
) => MaybeTtl;
|
||||
) => InterpreterResult;
|
||||
|
||||
@ -3,9 +3,9 @@ export * from './cache/cache';
|
||||
export * from './cache/create';
|
||||
export * from './header/interpreter';
|
||||
export * from './header/types';
|
||||
export * from './interceptors/build';
|
||||
export * from './interceptors/request';
|
||||
export * from './interceptors/response';
|
||||
export * from './interceptors/types';
|
||||
export * as InterceptorUtil from './interceptors/util';
|
||||
export * from './storage/build';
|
||||
export * from './storage/memory';
|
||||
|
||||
35
src/interceptors/build.ts
Normal file
35
src/interceptors/build.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import type {
|
||||
AxiosCacheInstance,
|
||||
CacheAxiosResponse,
|
||||
CacheRequestConfig
|
||||
} from '../cache/axios';
|
||||
|
||||
export interface AxiosInterceptor<T> {
|
||||
onFulfilled?(value: T): T | Promise<T>;
|
||||
onRejected?(error: any): any;
|
||||
apply: (axios: AxiosCacheInstance) => void;
|
||||
}
|
||||
|
||||
export type RequestInterceptor = AxiosInterceptor<CacheRequestConfig<unknown, unknown>>;
|
||||
export type ResponseInterceptor = AxiosInterceptor<CacheAxiosResponse<unknown, unknown>>;
|
||||
|
||||
export function buildInterceptor(
|
||||
type: 'request',
|
||||
interceptor: Omit<RequestInterceptor, 'apply'>
|
||||
): RequestInterceptor;
|
||||
|
||||
export function buildInterceptor(
|
||||
type: 'response',
|
||||
interceptor: Omit<ResponseInterceptor, 'apply'>
|
||||
): ResponseInterceptor;
|
||||
|
||||
export function buildInterceptor(
|
||||
type: 'request' | 'response',
|
||||
{ onFulfilled, onRejected }: Omit<AxiosInterceptor<unknown>, 'apply'>
|
||||
): AxiosInterceptor<unknown> {
|
||||
return {
|
||||
onFulfilled,
|
||||
onRejected,
|
||||
apply: (axios) => axios.interceptors[type].use(onFulfilled, onRejected)
|
||||
};
|
||||
}
|
||||
@ -1,15 +1,11 @@
|
||||
import { deferred } from 'fast-defer';
|
||||
import type {
|
||||
AxiosCacheInstance,
|
||||
CacheAxiosResponse,
|
||||
CacheRequestConfig
|
||||
} from '../cache/axios';
|
||||
import { buildInterceptor } from '..';
|
||||
import type { AxiosCacheInstance, CacheAxiosResponse } from '../cache/axios';
|
||||
import type {
|
||||
CachedResponse,
|
||||
CachedStorageValue,
|
||||
LoadingStorageValue
|
||||
} from '../storage/types';
|
||||
import type { AxiosInterceptor } from './types';
|
||||
import {
|
||||
ConfigWithCache,
|
||||
createValidateStatus,
|
||||
@ -17,111 +13,100 @@ import {
|
||||
setRevalidationHeaders
|
||||
} from './util';
|
||||
|
||||
export class CacheRequestInterceptor
|
||||
implements AxiosInterceptor<CacheRequestConfig<unknown, unknown>>
|
||||
{
|
||||
constructor(readonly axios: AxiosCacheInstance) {}
|
||||
|
||||
readonly use = (): void => {
|
||||
this.axios.interceptors.request.use(this.onFulfilled);
|
||||
};
|
||||
|
||||
readonly onFulfilled = async (
|
||||
config: CacheRequestConfig<unknown>
|
||||
): Promise<CacheRequestConfig<unknown>> => {
|
||||
if (config.cache === false) {
|
||||
return config;
|
||||
}
|
||||
|
||||
// merge defaults with per request configuration
|
||||
config.cache = { ...this.axios.defaults.cache, ...config.cache };
|
||||
|
||||
if (
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
!isMethodIn(config.method!, config.cache.methods)
|
||||
) {
|
||||
return config;
|
||||
}
|
||||
|
||||
const key = this.axios.generateKey(config);
|
||||
|
||||
// Assumes that the storage handled staled responses
|
||||
let cache = await 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]) {
|
||||
cache = (await this.axios.storage.get(key)) as
|
||||
| CachedStorageValue
|
||||
| LoadingStorageValue;
|
||||
break emptyOrStale;
|
||||
}
|
||||
|
||||
// Create a deferred to resolve other requests for the same key when it's completed
|
||||
this.axios.waiting[key] = deferred();
|
||||
|
||||
/**
|
||||
* Add a default reject handler to catch when the request is aborted without others
|
||||
* waiting for it.
|
||||
*/
|
||||
this.axios.waiting[key]?.catch(() => undefined);
|
||||
|
||||
await this.axios.storage.set(key, {
|
||||
state: 'loading',
|
||||
data: cache.data
|
||||
});
|
||||
|
||||
if (cache.state === 'stale') {
|
||||
setRevalidationHeaders(cache, config as ConfigWithCache<unknown>);
|
||||
}
|
||||
|
||||
config.validateStatus = createValidateStatus(config.validateStatus);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
let cachedResponse: CachedResponse;
|
||||
|
||||
if (cache.state === 'loading') {
|
||||
const deferred = this.axios.waiting[key];
|
||||
|
||||
// Just in case, the deferred doesn't exists.
|
||||
/* istanbul ignore if 'really hard to test' */
|
||||
if (!deferred) {
|
||||
await this.axios.storage.remove(key);
|
||||
export function defaultRequestInterceptor(axios: AxiosCacheInstance) {
|
||||
return buildInterceptor('request', {
|
||||
onFulfilled: async (config) => {
|
||||
if (config.cache === false) {
|
||||
return config;
|
||||
}
|
||||
|
||||
try {
|
||||
cachedResponse = await deferred;
|
||||
} catch {
|
||||
// The deferred is rejected when the request that we are waiting rejected cache.
|
||||
// merge defaults with per request configuration
|
||||
config.cache = { ...axios.defaults.cache, ...config.cache };
|
||||
|
||||
if (!isMethodIn(config.method, config.cache.methods)) {
|
||||
return config;
|
||||
}
|
||||
} else {
|
||||
cachedResponse = cache.data;
|
||||
|
||||
const key = axios.generateKey(config);
|
||||
|
||||
// Assumes that the storage handled staled responses
|
||||
let cache = await 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 (axios.waiting[key]) {
|
||||
cache = (await axios.storage.get(key)) as
|
||||
| CachedStorageValue
|
||||
| LoadingStorageValue;
|
||||
break emptyOrStale;
|
||||
}
|
||||
|
||||
// Create a deferred to resolve other requests for the same key when it's completed
|
||||
axios.waiting[key] = deferred();
|
||||
|
||||
/**
|
||||
* Add a default reject handler to catch when the request is aborted without
|
||||
* others waiting for it.
|
||||
*/
|
||||
axios.waiting[key]?.catch(() => undefined);
|
||||
|
||||
await axios.storage.set(key, {
|
||||
state: 'loading',
|
||||
data: cache.data
|
||||
});
|
||||
|
||||
if (cache.state === 'stale') {
|
||||
setRevalidationHeaders(cache, config as ConfigWithCache<unknown>);
|
||||
}
|
||||
|
||||
config.validateStatus = createValidateStatus(config.validateStatus);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
let cachedResponse: CachedResponse;
|
||||
|
||||
if (cache.state === 'loading') {
|
||||
const deferred = axios.waiting[key];
|
||||
|
||||
// Just in case, the deferred doesn't exists.
|
||||
/* istanbul ignore if 'really hard to test' */
|
||||
if (!deferred) {
|
||||
await axios.storage.remove(key);
|
||||
return config;
|
||||
}
|
||||
|
||||
try {
|
||||
cachedResponse = await deferred;
|
||||
} catch {
|
||||
// The deferred is rejected when the request that we are waiting rejected cache.
|
||||
return config;
|
||||
}
|
||||
} else {
|
||||
cachedResponse = cache.data;
|
||||
}
|
||||
|
||||
config.adapter = () =>
|
||||
/**
|
||||
* Even though the response interceptor receives this one from here, it has been
|
||||
* configured to ignore cached responses: true
|
||||
*/
|
||||
Promise.resolve<CacheAxiosResponse<unknown, unknown>>({
|
||||
config,
|
||||
data: cachedResponse.data,
|
||||
headers: cachedResponse.headers,
|
||||
status: cachedResponse.status,
|
||||
statusText: cachedResponse.statusText,
|
||||
cached: true,
|
||||
id: key
|
||||
});
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
config.adapter = () =>
|
||||
/**
|
||||
* Even though the response interceptor receives this one from here, it has been
|
||||
* configured to ignore cached responses: true
|
||||
*/
|
||||
Promise.resolve<CacheAxiosResponse<unknown, unknown>>({
|
||||
config,
|
||||
data: cachedResponse.data,
|
||||
headers: cachedResponse.headers,
|
||||
status: cachedResponse.status,
|
||||
statusText: cachedResponse.statusText,
|
||||
cached: true,
|
||||
id: key
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,140 +1,111 @@
|
||||
import type { AxiosResponse } from 'axios';
|
||||
import type { AxiosCacheInstance, CacheAxiosResponse } from '../cache/axios';
|
||||
import { buildInterceptor } from '..';
|
||||
import type { AxiosCacheInstance } from '../cache/axios';
|
||||
import type { CacheProperties } from '../cache/cache';
|
||||
import type { CachedStorageValue } from '../storage/types';
|
||||
import { shouldCacheResponse } from '../util/cache-predicate';
|
||||
import { Header } from '../util/headers';
|
||||
import { updateCache } from '../util/update-cache';
|
||||
import type { AxiosInterceptor } from './types';
|
||||
import { setupCacheData } from './util';
|
||||
import { rejectResponse, setupCacheData } from './util';
|
||||
|
||||
export class CacheResponseInterceptor
|
||||
implements AxiosInterceptor<CacheAxiosResponse<unknown, unknown>>
|
||||
{
|
||||
constructor(readonly axios: AxiosCacheInstance) {}
|
||||
export function defaultResponseInterceptor(axios: AxiosCacheInstance) {
|
||||
return buildInterceptor('response', {
|
||||
onFulfilled: async (response) => {
|
||||
response.id ??= axios.generateKey(response.config);
|
||||
response.cached ??= false;
|
||||
|
||||
readonly use = (): void => {
|
||||
this.axios.interceptors.response.use(this.onFulfilled);
|
||||
};
|
||||
|
||||
readonly onFulfilled = async (
|
||||
axiosResponse: AxiosResponse<unknown, unknown>
|
||||
): Promise<CacheAxiosResponse<unknown, unknown>> => {
|
||||
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) {
|
||||
return { ...response, cached: false };
|
||||
}
|
||||
|
||||
const cacheConfig = response.config.cache as CacheProperties;
|
||||
|
||||
const cache = await this.axios.storage.get(response.id);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Config told that this response should be cached.
|
||||
if (
|
||||
// For 'loading' values (post stale), this check was already run in the past.
|
||||
!cache.data &&
|
||||
!shouldCacheResponse(response, cacheConfig)
|
||||
) {
|
||||
await this.rejectResponse(response.id);
|
||||
return response;
|
||||
}
|
||||
|
||||
// avoid remnant headers from remote server to break implementation
|
||||
delete response.headers[Header.XAxiosCacheEtag];
|
||||
delete response.headers[Header.XAxiosCacheLastModified];
|
||||
|
||||
if (cacheConfig.etag && cacheConfig.etag !== true) {
|
||||
response.headers[Header.XAxiosCacheEtag] = cacheConfig.etag;
|
||||
}
|
||||
|
||||
if (cacheConfig.modifiedSince) {
|
||||
response.headers[Header.XAxiosCacheLastModified] =
|
||||
cacheConfig.modifiedSince === true
|
||||
? 'use-cache-timestamp'
|
||||
: cacheConfig.modifiedSince.toUTCString();
|
||||
}
|
||||
|
||||
let ttl = cacheConfig.ttl || -1; // always set from global config
|
||||
|
||||
if (cacheConfig?.interpretHeader) {
|
||||
const expirationTime = this.axios.headerInterpreter(response.headers);
|
||||
|
||||
// Cache should not be used
|
||||
if (expirationTime === 'dont cache') {
|
||||
await this.rejectResponse(response.id);
|
||||
// Response is already cached
|
||||
if (response.cached) {
|
||||
return response;
|
||||
}
|
||||
|
||||
ttl = expirationTime === 'not enough headers' ? ttl : expirationTime;
|
||||
// Skip cache: either false or weird behavior
|
||||
// config.cache should always exists, at least from global config merge.
|
||||
if (!response.config.cache) {
|
||||
return { ...response, cached: false };
|
||||
}
|
||||
|
||||
const cacheConfig = response.config.cache as CacheProperties;
|
||||
|
||||
const cache = await axios.storage.get(response.id);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Config told that this response should be cached.
|
||||
if (
|
||||
// For 'loading' values (post stale), this check was already run in the past.
|
||||
!cache.data &&
|
||||
!shouldCacheResponse(response, cacheConfig)
|
||||
) {
|
||||
await rejectResponse(axios, response.id);
|
||||
return response;
|
||||
}
|
||||
|
||||
// avoid remnant headers from remote server to break implementation
|
||||
delete response.headers[Header.XAxiosCacheEtag];
|
||||
delete response.headers[Header.XAxiosCacheLastModified];
|
||||
|
||||
if (cacheConfig.etag && cacheConfig.etag !== true) {
|
||||
response.headers[Header.XAxiosCacheEtag] = cacheConfig.etag;
|
||||
}
|
||||
|
||||
if (cacheConfig.modifiedSince) {
|
||||
response.headers[Header.XAxiosCacheLastModified] =
|
||||
cacheConfig.modifiedSince === true
|
||||
? 'use-cache-timestamp'
|
||||
: cacheConfig.modifiedSince.toUTCString();
|
||||
}
|
||||
|
||||
let ttl = cacheConfig.ttl || -1; // always set from global config
|
||||
|
||||
if (cacheConfig?.interpretHeader) {
|
||||
const expirationTime = axios.headerInterpreter(response.headers);
|
||||
|
||||
// Cache should not be used
|
||||
if (expirationTime === 'dont cache') {
|
||||
await rejectResponse(axios, response.id);
|
||||
return response;
|
||||
}
|
||||
|
||||
ttl = expirationTime === 'not enough headers' ? ttl : expirationTime;
|
||||
}
|
||||
|
||||
const data = setupCacheData(response, cache.data);
|
||||
|
||||
if (typeof ttl === 'function') {
|
||||
ttl = await ttl(response);
|
||||
}
|
||||
|
||||
const newCache: CachedStorageValue = {
|
||||
state: 'cached',
|
||||
ttl,
|
||||
createdAt: Date.now(),
|
||||
data
|
||||
};
|
||||
|
||||
// Update other entries before updating himself
|
||||
if (cacheConfig?.update) {
|
||||
updateCache(axios.storage, response, cacheConfig.update);
|
||||
}
|
||||
|
||||
const deferred = axios.waiting[response.id];
|
||||
|
||||
// Resolve all other requests waiting for this response
|
||||
await deferred?.resolve(newCache.data);
|
||||
delete axios.waiting[response.id];
|
||||
|
||||
// Define this key as cache on the storage
|
||||
await axios.storage.set(response.id, newCache);
|
||||
|
||||
// Return the response with cached as false, because it was not cached at all
|
||||
return response;
|
||||
}
|
||||
|
||||
const data = setupCacheData(response, cache.data);
|
||||
|
||||
if (typeof ttl === 'function') {
|
||||
ttl = await ttl(response);
|
||||
}
|
||||
|
||||
const newCache: CachedStorageValue = {
|
||||
state: 'cached',
|
||||
ttl,
|
||||
createdAt: Date.now(),
|
||||
data
|
||||
};
|
||||
|
||||
// Update other entries before updating himself
|
||||
if (cacheConfig?.update) {
|
||||
updateCache(this.axios.storage, response, cacheConfig.update);
|
||||
}
|
||||
|
||||
const deferred = this.axios.waiting[response.id];
|
||||
|
||||
// Resolve all other requests waiting for this response
|
||||
await deferred?.resolve(newCache.data);
|
||||
delete this.axios.waiting[response.id];
|
||||
|
||||
// Define this key as cache on the storage
|
||||
await 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. */
|
||||
readonly rejectResponse = async (key: string) => {
|
||||
// Update the cache to empty to prevent infinite loading state
|
||||
await this.axios.storage.remove(key);
|
||||
// Reject the deferred if present
|
||||
this.axios.waiting[key]?.reject(null);
|
||||
delete this.axios.waiting[key];
|
||||
};
|
||||
|
||||
readonly cachedResponse = (
|
||||
response: AxiosResponse<unknown, unknown>
|
||||
): CacheAxiosResponse<unknown, unknown> => {
|
||||
return {
|
||||
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 as CacheAxiosResponse<unknown, unknown>).cached || false,
|
||||
...response
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@ -1,5 +1,9 @@
|
||||
import type { Method } from 'axios';
|
||||
import type { CacheAxiosResponse, CacheRequestConfig } from '../cache/axios';
|
||||
import type {
|
||||
AxiosCacheInstance,
|
||||
CacheAxiosResponse,
|
||||
CacheRequestConfig
|
||||
} from '../cache/axios';
|
||||
import type { CacheProperties } from '../cache/cache';
|
||||
import type { CachedResponse, StaleStorageValue } from '../storage/types';
|
||||
import { Header } from '../util/headers';
|
||||
@ -17,7 +21,11 @@ export function createValidateStatus(
|
||||
}
|
||||
|
||||
/** Checks if the given method is in the methods array */
|
||||
export function isMethodIn(requestMethod: Method, methodList: Method[] = []): boolean {
|
||||
export function isMethodIn(requestMethod?: Method, methodList?: Method[]): boolean {
|
||||
if (!requestMethod || !methodList) {
|
||||
return false;
|
||||
}
|
||||
|
||||
requestMethod = requestMethod.toLowerCase() as Lowercase<Method>;
|
||||
|
||||
for (const method of methodList) {
|
||||
@ -91,3 +99,19 @@ export function setupCacheData<R, D>(
|
||||
headers: response.headers
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Rejects cache for an response response.
|
||||
*
|
||||
* Also update the waiting list for this key by rejecting it.
|
||||
*/
|
||||
export async function rejectResponse(
|
||||
{ storage, waiting }: AxiosCacheInstance,
|
||||
responseId: string
|
||||
) {
|
||||
// Update the cache to empty to prevent infinite loading state
|
||||
await storage.remove(responseId);
|
||||
// Reject the deferred if present
|
||||
waiting[responseId]?.reject(null);
|
||||
delete waiting[responseId];
|
||||
}
|
||||
|
||||
@ -36,8 +36,7 @@ export function isCachePredicateValid<R, D>(
|
||||
|
||||
if (containsHeaders) {
|
||||
for (const headerName in containsHeaders) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const value = containsHeaders[headerName]!;
|
||||
const value = containsHeaders[headerName as keyof typeof containsHeaders];
|
||||
const header = response.headers[headerName];
|
||||
|
||||
// At any case, if the header is not found, the predicate fails.
|
||||
|
||||
@ -25,7 +25,7 @@ describe('test util functions', () => {
|
||||
expect(isMethodIn('get', ['get', 'post', 'put'])).toBe(true);
|
||||
expect(isMethodIn('post', ['get', 'post', 'put'])).toBe(true);
|
||||
|
||||
expect(isMethodIn('get')).toBe(false);
|
||||
expect(isMethodIn()).toBe(false);
|
||||
expect(isMethodIn('get', [])).toBe(false);
|
||||
expect(isMethodIn('post', ['get', 'put', 'delete'])).toBe(false);
|
||||
expect(isMethodIn('get', ['post', 'put', 'delete'])).toBe(false);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user