From 47a8bc5fde40db650e6765e23540a229f6f40755 Mon Sep 17 00:00:00 2001 From: Ciro Lo Sapio Date: Wed, 11 Nov 2026 20:56:14 +0100 Subject: [PATCH 1/2] fix: register cache mount under `kv` on nitro v3 Co-Authored-By: Claude Opus 5.5 --- packages/script/src/module.ts | 3 ++- .../src/runtime/server/utils/cache-config.ts | 16 +++++++++--- test/unit/cached-upstream-lifecycle.test.ts | 25 +++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/packages/script/src/module.ts b/packages/script/src/module.ts index 0deff72b..a29a7c69 100644 --- a/packages/script/src/module.ts +++ b/packages/script/src/module.ts @@ -47,6 +47,7 @@ import { addTemplate, createResolver, defineNuxtModule, + getNitroVersion, hasNuxtModule, } from '@nuxt/kit' import { defu } from 'defu' @@ -1021,7 +1022,7 @@ export default defineNuxtModule({ // Nitro's default memory storage has no eviction policy. Every proxy and // embed cache shares this bounded mount, including proxy-only registries // without dedicated server handlers. Preserve application-supplied mounts. - ensureNuxtScriptsCacheStorage(nuxt.options.nitro as any, await resolvePath('./runtime/server/utils/lru-cache-driver')) + ensureNuxtScriptsCacheStorage(nuxt.options.nitro as any, await resolvePath('./runtime/server/utils/lru-cache-driver'), getNitroVersion(nuxt)) // Publish enabled endpoints to client for component opt-in checks nuxt.options.runtimeConfig.public['nuxt-scripts'] = defu( diff --git a/packages/script/src/runtime/server/utils/cache-config.ts b/packages/script/src/runtime/server/utils/cache-config.ts index f546762f..7b1d9703 100644 --- a/packages/script/src/runtime/server/utils/cache-config.ts +++ b/packages/script/src/runtime/server/utils/cache-config.ts @@ -11,11 +11,21 @@ export const NUXT_SCRIPTS_CACHE_MAX_ENTRY_SIZE = 8 * 1024 * 1024 interface NitroOptionsWithStorage { storage?: Record + kv?: Record } -export function ensureNuxtScriptsCacheStorage(nitroOptions: NitroOptionsWithStorage, driver: string): void { - nitroOptions.storage ||= {} - nitroOptions.storage[NUXT_SCRIPTS_CACHE_BASE] ||= { +/** + * Nitro v3 renamed the `storage` option to `kv` (`storage` still works but logs a + * deprecation warning), so the mount goes to `kv` there. A mount the application + * configured under either key is preserved. + */ +export function ensureNuxtScriptsCacheStorage(nitroOptions: NitroOptionsWithStorage, driver: string, nitroVersion: number = 2): void { + if (nitroVersion >= 3 && nitroOptions.storage?.[NUXT_SCRIPTS_CACHE_BASE]) + return + const mounts = nitroVersion >= 3 + ? (nitroOptions.kv ||= {}) + : (nitroOptions.storage ||= {}) + mounts[NUXT_SCRIPTS_CACHE_BASE] ||= { driver, max: NUXT_SCRIPTS_CACHE_MAX_ENTRIES, maxSize: NUXT_SCRIPTS_CACHE_MAX_SIZE, diff --git a/test/unit/cached-upstream-lifecycle.test.ts b/test/unit/cached-upstream-lifecycle.test.ts index 5cbfcea1..78557975 100644 --- a/test/unit/cached-upstream-lifecycle.test.ts +++ b/test/unit/cached-upstream-lifecycle.test.ts @@ -28,6 +28,31 @@ describe('upstream cache storage ownership', () => { })) }) + it('installs the cache mount under `kv` on Nitro v3', () => { + const nitroOptions: { storage?: Record, kv?: Record } = {} + + ensureNuxtScriptsCacheStorage(nitroOptions, '/nuxt-scripts/lru-cache-driver', 3) + + expect(nitroOptions.storage).toBeUndefined() + expect(nitroOptions.kv?.[NUXT_SCRIPTS_CACHE_BASE]).toEqual(expect.objectContaining({ + driver: '/nuxt-scripts/lru-cache-driver', + max: NUXT_SCRIPTS_CACHE_MAX_ENTRIES, + })) + }) + + it('preserves an application mount under either key on Nitro v3', () => { + const userMount = { driver: 'redis' } + const legacy: { storage?: Record, kv?: Record } = { storage: { [NUXT_SCRIPTS_CACHE_BASE]: userMount } } + const current: { storage?: Record, kv?: Record } = { kv: { [NUXT_SCRIPTS_CACHE_BASE]: userMount } } + + ensureNuxtScriptsCacheStorage(legacy, '/nuxt-scripts/lru-cache-driver', 3) + ensureNuxtScriptsCacheStorage(current, '/nuxt-scripts/lru-cache-driver', 3) + + expect(legacy.kv).toBeUndefined() + expect(legacy.storage?.[NUXT_SCRIPTS_CACHE_BASE]).toBe(userMount) + expect(current.kv?.[NUXT_SCRIPTS_CACHE_BASE]).toBe(userMount) + }) + it('puts binary proxy payloads in the bounded Nuxt Scripts cache mount', () => { createCachedBinaryFetch('binary-test', 60) From 6770c4b995342a55f41afb9563a02af95d4798c3 Mon Sep 17 00:00:00 2001 From: Ciro Lo Sapio Date: Wed, 11 Nov 2026 22:57:33 +0100 Subject: [PATCH 2/2] fix: add nitro v3 cache mount after `nitro.config` is resolved Co-Authored-By: Claude Opus 5.5 --- packages/script/src/module.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/script/src/module.ts b/packages/script/src/module.ts index a29a7c69..68d69f8a 100644 --- a/packages/script/src/module.ts +++ b/packages/script/src/module.ts @@ -1022,7 +1022,18 @@ export default defineNuxtModule({ // Nitro's default memory storage has no eviction policy. Every proxy and // embed cache shares this bounded mount, including proxy-only registries // without dedicated server handlers. Preserve application-supplied mounts. - ensureNuxtScriptsCacheStorage(nuxt.options.nitro as any, await resolvePath('./runtime/server/utils/lru-cache-driver'), getNitroVersion(nuxt)) + const cacheDriver = await resolvePath('./runtime/server/utils/lru-cache-driver') + if (getNitroVersion(nuxt) === 3) { + // Nitro v3 merges `kv` from `nitro.config.ts` under the inline Nuxt config, so a + // default set here would replace an application mount declared there. Add it + // once the options are resolved instead; mounts are read again at build time. + nuxt.hook('nitro:init', (nitro) => { + ensureNuxtScriptsCacheStorage(nitro.options as any, cacheDriver, 3) + }) + } + else { + ensureNuxtScriptsCacheStorage(nuxt.options.nitro as any, cacheDriver) + } // Publish enabled endpoints to client for component opt-in checks nuxt.options.runtimeConfig.public['nuxt-scripts'] = defu(