diff --git a/packages/cache-handler/src/data-cache/factory.ts b/packages/cache-handler/src/data-cache/factory.ts index 991354f..9627a01 100644 --- a/packages/cache-handler/src/data-cache/factory.ts +++ b/packages/cache-handler/src/data-cache/factory.ts @@ -24,14 +24,17 @@ function loadIoredis(type: string): typeof import("ioredis").default { } /** - * Create adapter for ioredis (lowercase methods) to match RedisClient interface (camelCase) + * Create adapter for ioredis (lowercase methods) to match RedisClient interface (camelCase). + * Translates node-redis-style SET options `{ EX: seconds }` to ioredis positional args. */ function createRedisAdapter(redis: import("ioredis").default): RedisClient { return { get: (key) => redis.get(key), - set: (key, value, exFlag?, ttl?) => { - if (exFlag === "EX" && typeof ttl === "number") { - return redis.set(key, value, "EX", ttl) as Promise; + set: (key, value, ...args) => { + // node-redis style: set(key, value, { EX: seconds }) + const opts = args[0] as Record | undefined; + if (opts && typeof opts === "object" && typeof opts.EX === "number") { + return redis.set(key, value, "EX", opts.EX) as Promise; } return redis.set(key, value) as Promise; }, diff --git a/packages/cache-handler/src/data-cache/redis.test.ts b/packages/cache-handler/src/data-cache/redis.test.ts index ef0c56e..442211e 100644 --- a/packages/cache-handler/src/data-cache/redis.test.ts +++ b/packages/cache-handler/src/data-cache/redis.test.ts @@ -27,9 +27,10 @@ class FakeRedis { this.setCalls.push({ key, args }); let expireAt: number | undefined; - // ioredis style: set(key, value, "EX", seconds) - if (args[0] === "EX" && typeof args[1] === "number") { - expireAt = Date.now() + args[1] * 1000; + // node-redis style: set(key, value, { EX: seconds }) + const opts = args[0] as Record | undefined; + if (opts && typeof opts === "object" && typeof opts.EX === "number") { + expireAt = Date.now() + opts.EX * 1000; } this.store.set(key, { value, expireAt }); @@ -147,7 +148,7 @@ describe("RedisDataCacheHandler", () => { expect(redis.setCalls).toHaveLength(1); expect(redis.setCalls[0]).toMatchObject({ key: "nextjs:data-cache:cache-key", - args: ["EX", 120], + args: [{ EX: 120 }], }); const result = await handler.get("cache-key", []); @@ -231,7 +232,7 @@ describe("RedisDataCacheHandler", () => { expect(redis.delCalls).toContainEqual(["nextjs:data-cache:invalidate-key"]); }); - test("sets TTL correctly with ioredis style args (fixes #16)", async () => { + test("sets TTL correctly with node-redis style options (fixes #16)", async () => { vi.useFakeTimers(); vi.setSystemTime(BASE_TIME); @@ -241,11 +242,11 @@ describe("RedisDataCacheHandler", () => { const entry = createEntry("ttl-test", { expire: 60, revalidate: 30 }); await handler.set("ttl-key", Promise.resolve(entry)); - // Verify the set call used ioredis style: "EX", seconds + // Verify the set call used node-redis style: { EX: seconds } expect(redis.setCalls).toHaveLength(1); const setCall = redis.setCalls[0]; expect(setCall.key).toBe("nextjs:data-cache:ttl-key"); - expect(setCall.args).toEqual(["EX", 60]); + expect(setCall.args).toEqual([{ EX: 60 }]); }); test("TTL causes entry to expire after specified time", async () => { @@ -286,6 +287,6 @@ describe("RedisDataCacheHandler", () => { await handler.set("default-ttl-key", Promise.resolve(entry)); expect(redis.setCalls).toHaveLength(1); - expect(redis.setCalls[0].args).toEqual(["EX", 3600]); + expect(redis.setCalls[0].args).toEqual([{ EX: 3600 }]); }); }); diff --git a/packages/cache-handler/src/data-cache/redis.ts b/packages/cache-handler/src/data-cache/redis.ts index ea8531e..cbb1742 100644 --- a/packages/cache-handler/src/data-cache/redis.ts +++ b/packages/cache-handler/src/data-cache/redis.ts @@ -10,7 +10,7 @@ import type { DataCacheEntry, DataCacheHandler } from "./types.js"; export interface RedisDataCacheHandlerOptions { /** - * Redis client instance (ioredis compatible) + * Redis client instance (node-redis compatible) */ redis: RedisClient; @@ -41,8 +41,8 @@ export interface RedisDataCacheHandlerOptions { } /** - * Redis client interface (ioredis compatible) - * Uses ioredis-style SET with "EX" positional args: set(key, value, "EX", seconds) + * Redis client interface (node-redis compatible) + * Uses node-redis-style SET with options object: set(key, value, { EX: seconds }) */ export interface RedisClient { get(key: string): Promise; @@ -275,7 +275,7 @@ export function createRedisDataCacheHandler( const ttl = entry.expire < 4294967294 ? entry.expire : defaultTTL; // Store in Redis with TTL - await redis.set(key, JSON.stringify(serialized), "EX", Math.ceil(ttl)); + await redis.set(key, JSON.stringify(serialized), { EX: Math.ceil(ttl) }); log?.("set", cacheKey, "done", { ttl }); } catch (error) {