From aec8e39f786599397a3a8459f5b8208c267bc0f9 Mon Sep 17 00:00:00 2001 From: Merii Date: Fri, 8 Aug 2025 14:47:57 -0700 Subject: [PATCH] Pass args on to validate method in cache options Fixes #3525 --- src/runtime/internal/cache.ts | 4 +++- test/fixture/api/cachedFunction.ts | 18 ++++++++++++++++++ test/presets/vercel.test.ts | 4 ++++ test/tests.ts | 20 ++++++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 test/fixture/api/cachedFunction.ts diff --git a/src/runtime/internal/cache.ts b/src/runtime/internal/cache.ts index f848b82c79..af623943a7 100644 --- a/src/runtime/internal/cache.ts +++ b/src/runtime/internal/cache.ts @@ -45,11 +45,11 @@ export function defineCachedFunction( const group = opts.group || "nitro/functions"; const name = opts.name || fn.name || "_"; const integrity = opts.integrity || hash([fn, opts]); - const validate = opts.validate || ((entry) => entry.value !== undefined); async function get( key: string, resolver: () => T | Promise, + validate: (entry: CacheEntry) => boolean, shouldInvalidateCache?: boolean, event?: H3Event ): Promise> { @@ -162,10 +162,12 @@ export function defineCachedFunction( return fn(...args); } const key = await (opts.getKey || getKey)(...args); + const validate = opts.validate || ((entry) => entry.value !== undefined); const shouldInvalidateCache = await opts.shouldInvalidateCache?.(...args); const entry = await get( key, () => fn(...args), + (entry) => validate(entry, ...args), shouldInvalidateCache, args[0] && isEvent(args[0]) ? args[0] : undefined ); diff --git a/test/fixture/api/cachedFunction.ts b/test/fixture/api/cachedFunction.ts new file mode 100644 index 0000000000..96befd5781 --- /dev/null +++ b/test/fixture/api/cachedFunction.ts @@ -0,0 +1,18 @@ +export default defineEventHandler(async (event) => { + return { value: await func(Math.random()) }; +}); + +const func = defineCachedFunction( + (testValue: number): number => { + return testValue; + }, + { + validate(entry, testValue) { + return entry.value !== undefined && testValue !== undefined; + }, + maxAge: 10, + getKey() { + return "testCachedFunction"; + }, + } +); diff --git a/test/presets/vercel.test.ts b/test/presets/vercel.test.ts index cedb232806..8bd6239535 100644 --- a/test/presets/vercel.test.ts +++ b/test/presets/vercel.test.ts @@ -352,6 +352,10 @@ describe("nitro:preset:vercel", async () => { "dest": "/api/db", "src": "/api/db", }, + { + "dest": "/api/cachedFunction", + "src": "/api/cachedFunction", + }, { "dest": "/api/cached", "src": "/api/cached", diff --git a/test/tests.ts b/test/tests.ts index 72e7602a83..004700e8a7 100644 --- a/test/tests.ts +++ b/test/tests.ts @@ -738,6 +738,26 @@ export function testNitro( } } ); + describe("cache", () => { + it.skipIf(ctx.isIsolated)( + "should handle args in the validate function", + async () => { + const { + data: { value }, + } = await callHandler({ url: "/api/cachedFunction" }); + + const calls = await Promise.all([ + callHandler({ url: "/api/cachedFunction" }), + callHandler({ url: "/api/cachedFunction" }), + callHandler({ url: "/api/cachedFunction" }), + ]); + + for (const call of calls) { + expect(call.data.value).toBe(value); + } + } + ); + }); }); describe("scanned files", () => {