From 276e016b6b37bed80411ecaa001f8b5bd311f294 Mon Sep 17 00:00:00 2001 From: yan-almeida Date: Tue, 10 Nov 2026 12:27:58 -0300 Subject: [PATCH 1/7] chore(cache-manager): add keyv 6 as a dev dependency Add the keyv6 alias (npm:keyv@^6.1.0). The tests use it to make a Keyv 6 store. The runtime dependency stays at keyv ^5.6.0. --- packages/cache-manager/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cache-manager/package.json b/packages/cache-manager/package.json index ec71d7cb..90d02f8c 100644 --- a/packages/cache-manager/package.json +++ b/packages/cache-manager/package.json @@ -71,6 +71,7 @@ "@keyv/redis": "^5.1.6", "cache-manager-redis-yet": "^5.1.5", "cacheable": "workspace:^", + "keyv6": "npm:keyv@^6.1.0", "tsdown": "^0.23.0", "typescript": "^7.0.2" } From 280067efa1a322b77039c0c34a82b7a0465d7512 Mon Sep 17 00:00:00 2001 From: yan-almeida Date: Tue, 10 Nov 2026 12:27:58 -0300 Subject: [PATCH 2/7] chore(cache-manager): lock keyv 6.1.0 for the keyv6 alias --- pnpm-lock.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e4a6e0b..e7de21d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -268,6 +268,9 @@ importers: cacheable: specifier: workspace:^ version: link:../cacheable + keyv6: + specifier: npm:keyv@^6.1.0 + version: keyv@6.1.0 tsdown: specifier: ^0.23.0 version: 0.23.0(@arethetypeswrong/core@0.18.5)(publint@0.3.24)(tsx@4.23.15)(typescript@7.0.2) @@ -2927,6 +2930,10 @@ packages: keyv@5.6.0: resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + keyv@6.1.0: + resolution: {integrity: sha512-0OO64DxKOITdyQljNCcO+vc+iyaPmm3OV5eKOWRxWCp3V0QnXenpqcKfjGjycNILUJNV+erm1ose6Hsgr1fNFA==} + engines: {node: '>= 22.19.0'} + kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} @@ -6294,6 +6301,10 @@ snapshots: dependencies: '@keyv/serialize': 1.1.1 + keyv@6.1.0: + dependencies: + hookified: 3.0.4 + kleur@4.1.5: {} ky@1.14.3: {} From 6c3ba3e12b0ff36c07dfdfa923ed0df6094a4439 Mon Sep 17 00:00:00 2001 From: yan-almeida Date: Tue, 10 Nov 2026 12:27:58 -0300 Subject: [PATCH 3/7] test(cache-manager): run wrap and ttl with a Keyv 6 store Keyv 6 get() does not have the raw option. It returns the value and not the stored entry. Thus wrap() calls the function each time, and ttl() returns undefined. These tests fail before the fix. --- packages/cache-manager/test/keyv6.test.ts | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 packages/cache-manager/test/keyv6.test.ts diff --git a/packages/cache-manager/test/keyv6.test.ts b/packages/cache-manager/test/keyv6.test.ts new file mode 100644 index 00000000..72496181 --- /dev/null +++ b/packages/cache-manager/test/keyv6.test.ts @@ -0,0 +1,30 @@ +import { faker } from "@faker-js/faker"; +import type { Keyv } from "keyv"; +import { Keyv as Keyv6 } from "keyv6"; +import { describe, expect, it, vi } from "vitest"; +import { createCache } from "../src/index.js"; + +describe("Keyv 6 store", () => { + const ttl = 1000; + // The `stores` option has the Keyv 5 type, so a Keyv 6 instance needs a cast. + const createKeyv6Cache = () => + createCache({ stores: [new Keyv6() as unknown as Keyv] }); + + it("wrap returns the cached value", async () => { + const cache = createKeyv6Cache(); + const key = faker.string.alpha(20); + const getValue = vi.fn(() => ({ id: 1 })); + + await cache.wrap(key, getValue, ttl); + await expect(cache.wrap(key, getValue, ttl)).resolves.toEqual({ id: 1 }); + expect(getValue).toBeCalledTimes(1); + }); + + it("ttl returns the expiration", async () => { + const cache = createKeyv6Cache(); + const key = faker.string.alpha(20); + + await cache.set(key, faker.string.sample(), ttl); + await expect(cache.ttl(key)).resolves.toBeGreaterThan(Date.now()); + }); +}); From 6c1d5e8567fa4661d7a3572173d3acb5cb7a682b Mon Sep 17 00:00:00 2001 From: yan-almeida Date: Tue, 10 Nov 2026 12:27:58 -0300 Subject: [PATCH 4/7] fix(cache-manager): read raw entries with getRaw() ttl() and wrap() read the stored entry with get(key, { raw: true }). Keyv 6 ignores this option, so these calls got the value and not the entry. Use getRaw(key). Keyv 5.6.0 and Keyv 6 both have getRaw(). --- packages/cache-manager/src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cache-manager/src/index.ts b/packages/cache-manager/src/index.ts index 8ebc1a80..697836e9 100644 --- a/packages/cache-manager/src/index.ts +++ b/packages/cache-manager/src/index.ts @@ -185,7 +185,7 @@ export const createCache = (options?: CreateCacheOptions): Cache => { if (nonBlocking) { try { result = await Promise.race( - stores.map(async (store) => store.get(key, { raw: true })), + stores.map(async (store) => store.getRaw(key)), ); if (result === undefined) { return undefined; @@ -196,7 +196,7 @@ export const createCache = (options?: CreateCacheOptions): Cache => { } else { for (const store of stores) { try { - const cacheValue = await store.get(key, { raw: true }); + const cacheValue = await store.getRaw(key); if (cacheValue !== undefined) { result = cacheValue; eventEmitter.emit("ttl", { key, value: result }); @@ -349,7 +349,7 @@ export const createCache = (options?: CreateCacheOptions): Cache => { for (; i < stores.length; i++) { try { - const data = await stores[i].get(key, { raw: true }); + const data = await stores[i].getRaw(key); if (data !== undefined) { value = data.value; rawData = data; From 3e92ffa5d2f6712ede149d2e60f163f430898d23 Mon Sep 17 00:00:00 2001 From: yan-almeida Date: Tue, 10 Nov 2026 12:27:58 -0300 Subject: [PATCH 5/7] test(cache-manager): mock getRaw in the ttl store error tests ttl() reads with getRaw() now. A mock on get() does not reach the error path, and the catch blocks of ttl() lose their coverage. --- packages/cache-manager/test/ttl.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cache-manager/test/ttl.test.ts b/packages/cache-manager/test/ttl.test.ts index 692ddc53..5c763748 100644 --- a/packages/cache-manager/test/ttl.test.ts +++ b/packages/cache-manager/test/ttl.test.ts @@ -31,7 +31,7 @@ describe("get", () => { it("error", async () => { await cache.set(data.key, data.value); - keyv.get = () => { + keyv.getRaw = () => { throw new Error("get error"); }; @@ -39,7 +39,7 @@ describe("get", () => { }); it("error on non-blocking enabled", async () => { const secondKeyv = new Keyv(); - keyv.get = () => { + keyv.getRaw = () => { throw new Error("get error"); }; From 11215f06b854f7ae02a9ce59ad7f04a719a239ce Mon Sep 17 00:00:00 2001 From: yan-almeida Date: Tue, 10 Nov 2026 12:27:58 -0300 Subject: [PATCH 6/7] test(cache-manager): mock getRaw in the wrap store error test wrap() reads with getRaw() now. A mock on get() does not make the store read fail. --- packages/cache-manager/test/wrap.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cache-manager/test/wrap.test.ts b/packages/cache-manager/test/wrap.test.ts index ed5bdea7..ab4b48c8 100644 --- a/packages/cache-manager/test/wrap.test.ts +++ b/packages/cache-manager/test/wrap.test.ts @@ -247,7 +247,7 @@ describe("wrap", () => { it("store get failed", async () => { const getValue = vi.fn(() => data.value); - keyv.get = () => { + keyv.getRaw = () => { throw new Error("get failed"); }; From f593db46f9a4f967a1998e337e72d028027d83b4 Mon Sep 17 00:00:00 2001 From: yan-almeida Date: Tue, 10 Nov 2026 12:27:58 -0300 Subject: [PATCH 7/7] test(cache-manager): mock getRaw in the multiple stores wrap tests wrap() reads with getRaw() now. A mock on get() does not make the read of the first store fail. --- packages/cache-manager/test/multiple-stores.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cache-manager/test/multiple-stores.test.ts b/packages/cache-manager/test/multiple-stores.test.ts index c5c405fb..62db511a 100644 --- a/packages/cache-manager/test/multiple-stores.test.ts +++ b/packages/cache-manager/test/multiple-stores.test.ts @@ -70,7 +70,7 @@ describe("multiple stores", () => { await expect(keyv2.get(data.key)).resolves.toEqual(data.value); // Store 1 get error - keyv1.get = () => { + keyv1.getRaw = () => { throw new Error("store 1 get error"); }; @@ -92,12 +92,12 @@ describe("multiple stores", () => { await expect(keyv2.get(data.key)).resolves.toEqual(data.value); // Store 1 get error - const getOk = keyv1.get; + const getOk = keyv1.getRaw; const getError = () => { throw new Error("store 1 get error"); }; - keyv1.get = getError; + keyv1.getRaw = getError; await sleep(ttl - refreshThreshold + 100); @@ -105,7 +105,7 @@ describe("multiple stores", () => { cache.wrap(data.key, () => "new", ttl, refreshThreshold), ).resolves.toEqual(data.value); - keyv1.get = getOk; + keyv1.getRaw = getOk; // Store 1 has been updated the latest value await expect(keyv1.get(data.key)).resolves.toEqual("new"); await expect(cache.wrap(data.key, () => "latest", ttl)).resolves.toEqual(