From e83d8067cb4c2ab804944953fbb5aaa2ee8328d8 Mon Sep 17 00:00:00 2001 From: Vasil Chomakov Date: Tue, 27 Jan 2026 22:13:46 +0200 Subject: [PATCH 1/5] feat(redis): add clientInfoTag support with version detection --- docs/2.drivers/redis.md | 1 + src/drivers/redis.ts | 37 ++++++++++++++++++++++++++++++++----- test/drivers/redis.test.ts | 18 ++++++++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/docs/2.drivers/redis.md b/docs/2.drivers/redis.md index bb4b9bd8e..d8d57fdb5 100644 --- a/docs/2.drivers/redis.md +++ b/docs/2.drivers/redis.md @@ -72,6 +72,7 @@ const storage = createStorage({ - `ttl`: Default TTL for all items in **seconds**. - `scanCount`: How many keys to scan at once ([redis documentation](https://redis.io/docs/latest/commands/scan/#the-count-option)). - `preConnect`: Whether to initialize the redis instance immediately. Otherwise, it will be initialized on the first read/write call. Default: `false`. +- `clientInfoTag`: Tag to append to the library name in `CLIENT SETINFO` (e.g., `ioredis(unstorage_v2.0.0)`). This helps identify the higher-level library using ioredis. Default: `"unstorage_vX.X.X"` (with version) or `"unstorage"` (fallback). See [ioredis](https://github.com/redis/ioredis/blob/master/API.md#new-redisport-host-options) for all available options. diff --git a/src/drivers/redis.ts b/src/drivers/redis.ts index 3127bcf5b..069601d67 100644 --- a/src/drivers/redis.ts +++ b/src/drivers/redis.ts @@ -1,5 +1,6 @@ import { defineDriver, joinKeys } from "./utils/index.ts"; import { Cluster, Redis } from "ioredis"; +import pkg from "../../package.json" with { type: "json" }; import type { ClusterOptions, @@ -46,22 +47,48 @@ export interface RedisOptions extends _RedisOptions { * @default false */ preConnect?: boolean; + + /** + * Tag to append to the library name in CLIENT SETINFO (ioredis(tag)). + * This helps identify the higher-level library using ioredis. + * @link https://redis.io/docs/latest/commands/client-setinfo/ + * @default "unstorage_vX.X.X" (with version) or "unstorage" (fallback) + */ + clientInfoTag?: string; } const DRIVER_NAME = "redis"; +function getDefaultClientInfoTag(): string { + return `unstorage_v${pkg.version}`; +} + export default defineDriver((opts: RedisOptions) => { let redisClient: Redis | Cluster; const getRedisClient = () => { if (redisClient) { return redisClient; } - if (opts.cluster) { - redisClient = new Redis.Cluster(opts.cluster, opts.clusterOptions); - } else if (opts.url) { - redisClient = new Redis(opts.url, opts); + + // Set default clientInfoTag to "unstorage_vX.X.X" if not explicitly set + const options = { + ...opts, + clientInfoTag: opts.clientInfoTag ?? getDefaultClientInfoTag(), + }; + + if (options.cluster) { + const clusterOptions = { + ...options.clusterOptions, + redisOptions: { + clientInfoTag: options.clientInfoTag, + ...options.clusterOptions?.redisOptions, + }, + }; + redisClient = new Redis.Cluster(options.cluster, clusterOptions); + } else if (options.url) { + redisClient = new Redis(options.url, options); } else { - redisClient = new Redis(opts); + redisClient = new Redis(options); } return redisClient; }; diff --git a/test/drivers/redis.test.ts b/test/drivers/redis.test.ts index 103800770..a9f3619ca 100644 --- a/test/drivers/redis.test.ts +++ b/test/drivers/redis.test.ts @@ -39,6 +39,24 @@ describe("drivers: redis", () => { (ioredisMock as any).default ); }); + + it("sets default clientInfoTag with version", () => { + const instance = driver.getInstance?.(); + const tag = (instance?.options as any)?.clientInfoTag; + // Should be either "unstorage_vX.X.X" or "unstorage" (fallback) + expect(tag).toMatch(/^unstorage(_v[\d.]+.*)?$/); + }); + + it("allows custom clientInfoTag", () => { + const customDriver = redisDriver({ + base: "test:", + url: "ioredis://localhost:6379/0", + lazyConnect: false, + clientInfoTag: "my-custom-app", + }); + const instance = customDriver.getInstance?.(); + expect((instance?.options as any)?.clientInfoTag).toBe("my-custom-app"); + }); }, }); }); From 234ccac0c4e6b6eb26a3fe89e55596d4cd44bef5 Mon Sep 17 00:00:00 2001 From: Vasil Chomakov Date: Fri, 6 Mar 2026 18:39:52 +0200 Subject: [PATCH 2/5] fix(redis): add fallback for clientInfoTag when version is missing --- src/drivers/redis.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/drivers/redis.ts b/src/drivers/redis.ts index 9eff5576f..2b479126a 100644 --- a/src/drivers/redis.ts +++ b/src/drivers/redis.ts @@ -56,7 +56,10 @@ export interface RedisOptions extends _RedisOptions { const DRIVER_NAME = "redis"; function getDefaultClientInfoTag(): string { - return `unstorage_v${pkg.version}`; + if (pkg.version) { + return `unstorage_v${pkg.version}`; + } + return "unstorage"; } export default defineDriver((opts: RedisOptions) => { From c9a4f14f148f749a38dcc80328ccc94a855b8c06 Mon Sep 17 00:00:00 2001 From: Vasil Chomakov Date: Fri, 6 Mar 2026 19:30:29 +0200 Subject: [PATCH 3/5] fix(redis): resolve merge conflict and fix test reference --- src/drivers/redis.ts | 3 +-- test/drivers/redis.test.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/drivers/redis.ts b/src/drivers/redis.ts index 2b479126a..9d0d0aee0 100644 --- a/src/drivers/redis.ts +++ b/src/drivers/redis.ts @@ -62,8 +62,7 @@ function getDefaultClientInfoTag(): string { return "unstorage"; } -export default defineDriver((opts: RedisOptions) => { -const driver: DriverFactory = (opts) => { +const driver: DriverFactory = (opts: RedisOptions) => { let redisClient: Redis | Cluster; const getRedisClient = () => { if (redisClient) { diff --git a/test/drivers/redis.test.ts b/test/drivers/redis.test.ts index 2e815d506..7fde92655 100644 --- a/test/drivers/redis.test.ts +++ b/test/drivers/redis.test.ts @@ -38,7 +38,7 @@ describe("drivers: redis", () => { }); it("sets default clientInfoTag with version", () => { - const instance = driver.getInstance?.(); + const instance = ctx.driver.getInstance?.(); const tag = (instance?.options as any)?.clientInfoTag; // Should be either "unstorage_vX.X.X" or "unstorage" (fallback) expect(tag).toMatch(/^unstorage(_v[\d.]+.*)?$/); From d1c1d27bf6df026af63a0b072a8ba5742c26d233 Mon Sep 17 00:00:00 2001 From: Vasil Chomakov Date: Fri, 6 Mar 2026 19:42:12 +0200 Subject: [PATCH 4/5] fix(redis): make top-level clientInfoTag authoritative in cluster mode --- src/drivers/redis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/redis.ts b/src/drivers/redis.ts index 9d0d0aee0..1de38a14d 100644 --- a/src/drivers/redis.ts +++ b/src/drivers/redis.ts @@ -79,8 +79,8 @@ const driver: DriverFactory = (opts: RedisOptions const clusterOptions = { ...options.clusterOptions, redisOptions: { - clientInfoTag: options.clientInfoTag, ...options.clusterOptions?.redisOptions, + clientInfoTag: options.clientInfoTag, }, }; redisClient = new Redis.Cluster(options.cluster, clusterOptions); From ceb9c78b6620a5d2dd5f6540ace47fbcf730d863 Mon Sep 17 00:00:00 2001 From: Vasil Chomakov Date: Thu, 12 Mar 2026 13:20:53 +0200 Subject: [PATCH 5/5] docs(redis): add JSDoc to getDefaultClientInfoTag function --- src/drivers/redis.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/drivers/redis.ts b/src/drivers/redis.ts index 1de38a14d..c90eb94e8 100644 --- a/src/drivers/redis.ts +++ b/src/drivers/redis.ts @@ -55,6 +55,10 @@ export interface RedisOptions extends _RedisOptions { const DRIVER_NAME = "redis"; +/** + * Returns the default client info tag for Redis CLIENT SETINFO. + * Uses the package version if available, otherwise falls back to "unstorage". + */ function getDefaultClientInfoTag(): string { if (pkg.version) { return `unstorage_v${pkg.version}`;