From dccc763b9e12caab11e88296b82f39af29d6d8e3 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Mon, 27 Apr 2026 18:07:44 -0700 Subject: [PATCH] fix(cache): allow streamed entries at maxEntrySize limit Assisted-by: openai:gpt-5.5 Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> --- lib/cache/memory-cache-store.js | 2 +- lib/cache/sqlite-cache-store.js | 2 +- .../cache-store-test-utils.js | 39 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/lib/cache/memory-cache-store.js b/lib/cache/memory-cache-store.js index dba29ae4de9..ea9c6cae7e9 100644 --- a/lib/cache/memory-cache-store.js +++ b/lib/cache/memory-cache-store.js @@ -138,7 +138,7 @@ class MemoryCacheStore extends EventEmitter { entry.size += chunk.byteLength - if (entry.size >= store.#maxEntrySize) { + if (entry.size > store.#maxEntrySize) { this.destroy() } else { entry.body.push(chunk) diff --git a/lib/cache/sqlite-cache-store.js b/lib/cache/sqlite-cache-store.js index 9ddbefcac4b..9503bf10287 100644 --- a/lib/cache/sqlite-cache-store.js +++ b/lib/cache/sqlite-cache-store.js @@ -325,7 +325,7 @@ module.exports = class SqliteCacheStore { write (chunk, encoding, callback) { size += chunk.byteLength - if (size < store.#maxEntrySize) { + if (size <= store.#maxEntrySize) { body.push(chunk) } else { this.destroy() diff --git a/test/cache-interceptor/cache-store-test-utils.js b/test/cache-interceptor/cache-store-test-utils.js index 54616b89744..4d5d44fe329 100644 --- a/test/cache-interceptor/cache-store-test-utils.js +++ b/test/cache-interceptor/cache-store-test-utils.js @@ -108,6 +108,45 @@ function cacheStoreTests (CacheStore, options) { } }) + test('caches streamed body at maxEntrySize boundary', options, async () => { + /** + * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey} + */ + const key = { + origin: 'localhost', + path: '/', + method: 'GET', + headers: {} + } + + /** + * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue} + */ + const value = { + statusCode: 200, + statusMessage: '', + headers: { foo: 'bar' }, + cacheControlDirectives: {}, + cachedAt: Date.now(), + staleAt: Date.now() + 10000, + deleteAt: Date.now() + 20000 + } + + const body = [Buffer.from('asd'), Buffer.from('123')] + const store = new CacheStore({ maxEntrySize: 6 }) + const writable = store.createWriteStream(key, value) + + notEqual(writable, undefined) + + const finished = once(writable, 'finish') + writeBody(writable, body) + await finished + + const result = await store.get(structuredClone(key)) + notEqual(result, undefined) + await compareGetResults(result, value, body) + }) + test('returns stale response before deleteAt', options, async () => { const clock = FakeTimers.install({ shouldClearNativeTimers: true