From a77cbf4a3c3ac0905b25cb78f046b32ca7c79f84 Mon Sep 17 00:00:00 2001 From: Jared Stowell Date: Sat, 7 Mar 2026 20:23:23 -0600 Subject: [PATCH] Fix unstable_cache undefined cache --- packages/vinext/src/shims/cache.ts | 19 +++++++++++++++++-- tests/shims.test.ts | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/vinext/src/shims/cache.ts b/packages/vinext/src/shims/cache.ts index 34e48ead..5b7f73e4 100644 --- a/packages/vinext/src/shims/cache.ts +++ b/packages/vinext/src/shims/cache.ts @@ -581,6 +581,21 @@ const _UNSTABLE_CACHE_ALS_KEY = Symbol.for("vinext.unstableCache.als"); const _unstableCacheAls = ( (_g[_UNSTABLE_CACHE_ALS_KEY] ??= new AsyncLocalStorage()) as AsyncLocalStorage ); +const UNSTABLE_CACHE_UNDEFINED_SENTINEL = "__vinext_unstable_cache_undefined__"; + +function serializeUnstableCacheResult(value: unknown): string { + return value === undefined + ? UNSTABLE_CACHE_UNDEFINED_SENTINEL + : JSON.stringify(value); +} + +function deserializeUnstableCacheResult(body: string): unknown { + if (body === UNSTABLE_CACHE_UNDEFINED_SENTINEL) { + return undefined; + } + + return JSON.parse(body); +} /** * Check if the current execution context is inside an unstable_cache() callback. @@ -625,7 +640,7 @@ export function unstable_cache Promise>( }); if (existing?.value && existing.value.kind === "FETCH" && existing.cacheState !== "stale") { try { - return JSON.parse(existing.value.data.body); + return deserializeUnstableCacheResult(existing.value.data.body); } catch { // Corrupted entry, fall through to re-fetch } @@ -641,7 +656,7 @@ export function unstable_cache Promise>( kind: "FETCH", data: { headers: {}, - body: JSON.stringify(result), + body: serializeUnstableCacheResult(result), url: cacheKey, }, tags, diff --git a/tests/shims.test.ts b/tests/shims.test.ts index eb6368e5..7584e8a4 100644 --- a/tests/shims.test.ts +++ b/tests/shims.test.ts @@ -733,6 +733,27 @@ describe("next/cache shim", () => { setCacheHandler(new MemoryCacheHandler()); }); + it("unstable_cache caches undefined results", async () => { + const { unstable_cache, setCacheHandler, MemoryCacheHandler } = + await import("../packages/vinext/src/shims/cache.js"); + + setCacheHandler(new MemoryCacheHandler()); + + let callCount = 0; + const cached = unstable_cache(async () => { + callCount++; + return undefined; + }, ["undefined-result-test"]); + + await expect(cached()).resolves.toBeUndefined(); + expect(callCount).toBe(1); + + await expect(cached()).resolves.toBeUndefined(); + expect(callCount).toBe(1); + + setCacheHandler(new MemoryCacheHandler()); + }); + it("revalidateTag invalidates cached entries", async () => { const { unstable_cache,