From 8e877e0a6a2291fa79e855e01fe22e11e2caa377 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 8 Dec 2025 13:29:17 +0000 Subject: [PATCH] Add enabled flag to CacheProperties with full implementation and tests Co-authored-by: arthurfiorette <47537704+arthurfiorette@users.noreply.github.com> --- src/cache/axios.ts | 3 + src/cache/cache.ts | 14 +++ src/cache/create.ts | 2 + src/interceptors/request.ts | 15 ++- test/interceptors/enabled.test.ts | 191 ++++++++++++++++++++++++++++++ 5 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 test/interceptors/enabled.test.ts diff --git a/src/cache/axios.ts b/src/cache/axios.ts index 13a8a61..38567fb 100644 --- a/src/cache/axios.ts +++ b/src/cache/axios.ts @@ -81,10 +81,13 @@ export interface CacheRequestConfig extends AxiosRequestConfig * * Setting the `cache` property to `false` will disable the cache for this request. * + * **Note:** Setting `cache: false` is deprecated. Please use `cache: { enabled: false }` instead. + * * This does not mean that the current cache will be excluded from the storage. * * @default 'inherits from global configuration' * @see https://axios-cache-interceptor.js.org/config/response-object#cache + * @deprecated Setting `cache: false` is deprecated, use `cache: { enabled: false }` instead */ cache?: false | Partial>; } diff --git a/src/cache/cache.ts b/src/cache/cache.ts index aef4cf6..62c9a34 100644 --- a/src/cache/cache.ts +++ b/src/cache/cache.ts @@ -22,6 +22,20 @@ import type { CacheAxiosResponse, InternalCacheRequestConfig } from './axios.js' * @template D The type for the request body */ export interface CacheProperties { + /** + * Whether the cache is enabled for this request. + * + * When set to `false`, the cache will be completely disabled for this request, + * similar to setting `cache: false` in the request config. + * + * This is useful for opt-in cache scenarios where you want to disable cache globally + * but enable it for specific requests by setting `cache: { enabled: true }`. + * + * @default true + * @see https://axios-cache-interceptor.js.org/config/request-specifics#cache-enabled + */ + enabled: boolean; + /** * The time until the cached value is expired in milliseconds. * diff --git a/src/cache/create.ts b/src/cache/create.ts index 6719c9e..b9c41fa 100644 --- a/src/cache/create.ts +++ b/src/cache/create.ts @@ -57,6 +57,8 @@ export function setupCache(axios: AxiosInstance, options: CacheOptions = {}): Ax // CacheRequestConfig values axiosCache.defaults.cache = { + enabled: options.enabled ?? true, + update: options.update || {}, ttl: options.ttl ?? 1000 * 60 * 5, diff --git a/src/interceptors/request.ts b/src/interceptors/request.ts index 92a1a33..00483c1 100644 --- a/src/interceptors/request.ts +++ b/src/interceptors/request.ts @@ -19,7 +19,7 @@ export function defaultRequestInterceptor(axios: AxiosCacheInstance): RequestInt if (__ACI_DEV__) { axios.debug({ id: config.id, - msg: 'Ignoring cache because config.cache === false', + msg: 'Ignoring cache because config.cache === false (deprecated, use cache.enabled = false)', data: config }); } @@ -30,6 +30,19 @@ export function defaultRequestInterceptor(axios: AxiosCacheInstance): RequestInt // merge defaults with per request configuration config.cache = { ...axios.defaults.cache, ...config.cache }; + // Check if cache is disabled via enabled flag + if (config.cache.enabled === false) { + if (__ACI_DEV__) { + axios.debug({ + id: config.id, + msg: 'Ignoring cache because config.cache.enabled === false', + data: config + }); + } + + return config; + } + // ignoreUrls (blacklist) if ( typeof config.cache.cachePredicate === 'object' && diff --git a/test/interceptors/enabled.test.ts b/test/interceptors/enabled.test.ts new file mode 100644 index 0000000..23c2066 --- /dev/null +++ b/test/interceptors/enabled.test.ts @@ -0,0 +1,191 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { mockAxios } from '../mocks/axios.js'; + +describe('Cache Enabled Flag', () => { + it('Cache enabled by default (enabled: true)', async () => { + const axios = mockAxios(); + + const response1 = await axios.get('http://test.com'); + assert.equal(response1.cached, false); + assert.equal(response1.stale, undefined); + + const response2 = await axios.get('http://test.com'); + assert.ok(response2.cached); + assert.equal(response2.stale, false); + }); + + it('Global cache disabled (enabled: false)', async () => { + const axios = mockAxios({ enabled: false }); + + const response1 = await axios.get('http://test.com'); + const response2 = await axios.get('http://test.com'); + + assert.equal(response1.cached, false); + assert.equal(response1.stale, undefined); + assert.equal(response2.cached, false); + assert.equal(response2.stale, undefined); + + // Verify cache is empty + const cacheKey = axios.generateKey(response1.config); + const cache = await axios.storage.get(cacheKey); + assert.equal(cache.state, 'empty'); + }); + + it('Global cache disabled, per-request enabled', async () => { + const axios = mockAxios({ enabled: false }); + + // First request with cache enabled + const response1 = await axios.get('http://test.com', { + cache: { enabled: true } + }); + assert.equal(response1.cached, false); + assert.equal(response1.stale, undefined); + + // Second request with cache enabled - should be cached + const response2 = await axios.get('http://test.com', { + cache: { enabled: true } + }); + assert.ok(response2.cached); + assert.equal(response2.stale, false); + + // Third request without cache config - should not be cached (global default) + const response3 = await axios.get('http://test.com/other'); + const response4 = await axios.get('http://test.com/other'); + + assert.equal(response3.cached, false); + assert.equal(response3.stale, undefined); + assert.equal(response4.cached, false); + assert.equal(response4.stale, undefined); + }); + + it('Global cache enabled, per-request disabled', async () => { + const axios = mockAxios({ enabled: true }); + + // First request with cache disabled + const response1 = await axios.get('http://test.com', { + cache: { enabled: false } + }); + assert.equal(response1.cached, false); + assert.equal(response1.stale, undefined); + + // Second request with cache disabled - should not be cached + const response2 = await axios.get('http://test.com', { + cache: { enabled: false } + }); + assert.equal(response2.cached, false); + assert.equal(response2.stale, undefined); + + // Third request without cache config - should be cached (global default) + const response3 = await axios.get('http://test.com/other'); + const response4 = await axios.get('http://test.com/other'); + + assert.equal(response3.cached, false); + assert.equal(response3.stale, undefined); + assert.ok(response4.cached); + assert.equal(response4.stale, false); + }); + + it('Backward compatibility: cache: false still works', async () => { + const axios = mockAxios(); + + const response1 = await axios.get('http://test.com', { cache: false }); + const response2 = await axios.get('http://test.com', { cache: false }); + + assert.equal(response1.cached, false); + assert.equal(response1.stale, undefined); + assert.equal(response2.cached, false); + assert.equal(response2.stale, undefined); + + // Verify cache is empty + const cacheKey = axios.generateKey(response1.config); + const cache = await axios.storage.get(cacheKey); + assert.equal(cache.state, 'empty'); + }); + + it('Enabled flag works with other cache options', async () => { + const axios = mockAxios({ enabled: false }); + + const response1 = await axios.get('http://test.com', { + cache: { + enabled: true, + ttl: 1000 * 60 * 10 // 10 minutes + } + }); + assert.equal(response1.cached, false); + assert.equal(response1.stale, undefined); + + const response2 = await axios.get('http://test.com', { + cache: { + enabled: true, + ttl: 1000 * 60 * 10 + } + }); + assert.ok(response2.cached); + assert.equal(response2.stale, false); + + // Verify cache config is applied + assert.equal(response2.config.cache?.ttl, 1000 * 60 * 10); + }); + + it('Enabled flag overrides in request after global enabled', async () => { + const axios = mockAxios({ enabled: true }); + + // Request 1 with enabled: true (explicit) + const response1 = await axios.get('http://test.com', { + cache: { enabled: true } + }); + assert.equal(response1.cached, false); + assert.equal(response1.stale, undefined); + + // Request 2 - should be cached + const response2 = await axios.get('http://test.com'); + assert.ok(response2.cached); + assert.equal(response2.stale, false); + + // Request 3 with enabled: false - should not be cached even though cache exists + const response3 = await axios.get('http://test.com', { + cache: { enabled: false } + }); + assert.equal(response3.cached, false); + assert.equal(response3.stale, undefined); + }); + + it('Concurrent requests respect enabled flag', async () => { + const axios = mockAxios({ enabled: false }); + + const [resp1, resp2] = await Promise.all([ + axios.get('http://test.com', { cache: { enabled: true } }), + axios.get('http://test.com', { cache: { enabled: true } }) + ]); + + assert.equal(resp1.cached, false); + assert.equal(resp1.stale, undefined); + assert.ok(resp2.cached); + assert.equal(resp2.stale, false); + }); + + it('Mixed enabled and disabled requests do not interfere', async () => { + const axios = mockAxios({ enabled: false }); + + // Disabled request + const resp1 = await axios.get('http://test.com'); + assert.equal(resp1.cached, false); + + // Enabled request - creates cache + const resp2 = await axios.get('http://test.com', { + cache: { enabled: true } + }); + assert.equal(resp2.cached, false); + + // Disabled request - does not use cache + const resp3 = await axios.get('http://test.com'); + assert.equal(resp3.cached, false); + + // Enabled request - uses cache + const resp4 = await axios.get('http://test.com', { + cache: { enabled: true } + }); + assert.ok(resp4.cached); + }); +});