@chat-adapter/state-redis 4.13.1 → 4.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,14 @@
1
1
  # @chat-adapter/state-redis
2
2
 
3
- Redis state adapter for the [chat](https://github.com/vercel-labs/chat) SDK using the official [redis](https://www.npmjs.com/package/redis) package.
3
+ [![npm version](https://img.shields.io/npm/v/@chat-adapter/state-redis)](https://www.npmjs.com/package/@chat-adapter/state-redis)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@chat-adapter/state-redis)](https://www.npmjs.com/package/@chat-adapter/state-redis)
5
+
6
+ Redis state adapter for [Chat SDK](https://chat-sdk.dev/docs) using the official [redis](https://www.npmjs.com/package/redis) package. Recommended for production.
4
7
 
5
8
  ## Installation
6
9
 
7
10
  ```bash
8
- npm install chat @chat-adapter/state-redis redis
11
+ npm install chat @chat-adapter/state-redis
9
12
  ```
10
13
 
11
14
  ## Usage
@@ -14,7 +17,7 @@ npm install chat @chat-adapter/state-redis redis
14
17
  import { Chat } from "chat";
15
18
  import { createRedisState } from "@chat-adapter/state-redis";
16
19
 
17
- const chat = new Chat({
20
+ const bot = new Chat({
18
21
  userName: "mybot",
19
22
  adapters: { /* ... */ },
20
23
  state: createRedisState({
@@ -23,55 +26,9 @@ const chat = new Chat({
23
26
  });
24
27
  ```
25
28
 
26
- ## Configuration
27
-
28
- | Option | Required | Description |
29
- |--------|----------|-------------|
30
- | `url` | Yes* | Redis connection URL |
31
- | `client` | No | Existing redis client instance |
32
- | `keyPrefix` | No | Prefix for all keys (default: `"chat-sdk"`) |
33
-
34
- *Either `url` or `client` is required.
35
-
36
- ### Using Connection URL
37
-
38
- ```typescript
39
- const state = createRedisState({
40
- url: "redis://localhost:6379",
41
- });
42
- ```
43
-
44
- ### Using Existing Client
45
-
46
- ```typescript
47
- import { createClient } from "redis";
48
-
49
- const client = createClient({ url: "redis://localhost:6379" });
50
- await client.connect();
51
-
52
- const state = createRedisState({ client });
53
- ```
54
-
55
- ## Features
56
-
57
- - Thread subscriptions (persistent)
58
- - Distributed locking (works across instances)
59
- - Automatic reconnection
60
- - Key prefix namespacing
61
-
62
- ## Key Structure
63
-
64
- ```
65
- {keyPrefix}:subscriptions - SET of subscribed thread IDs
66
- {keyPrefix}:lock:{threadId} - Lock key with TTL
67
- ```
68
-
69
- ## Production Recommendations
29
+ ## Documentation
70
30
 
71
- - Use Redis 6.0+ for best performance
72
- - Enable Redis persistence (RDB or AOF)
73
- - Use Redis Cluster for high availability
74
- - Set appropriate memory limits
31
+ Full documentation at [chat-sdk.dev/docs/state/redis](https://chat-sdk.dev/docs/state/redis).
75
32
 
76
33
  ## License
77
34
 
package/dist/index.d.ts CHANGED
@@ -2,12 +2,12 @@ import { Logger, StateAdapter, Lock } from 'chat';
2
2
  import { RedisClientType } from 'redis';
3
3
 
4
4
  interface RedisStateAdapterOptions {
5
- /** Redis connection URL (e.g., redis://localhost:6379) */
6
- url: string;
7
5
  /** Key prefix for all Redis keys (default: "chat-sdk") */
8
6
  keyPrefix?: string;
9
7
  /** Logger instance for error reporting */
10
8
  logger: Logger;
9
+ /** Redis connection URL (e.g., redis://localhost:6379) */
10
+ url: string;
11
11
  }
12
12
  /**
13
13
  * Redis state adapter for production use.
@@ -16,9 +16,9 @@ interface RedisStateAdapterOptions {
16
16
  * across multiple server instances.
17
17
  */
18
18
  declare class RedisStateAdapter implements StateAdapter {
19
- private client;
20
- private keyPrefix;
21
- private logger;
19
+ private readonly client;
20
+ private readonly keyPrefix;
21
+ private readonly logger;
22
22
  private connected;
23
23
  private connectPromise;
24
24
  constructor(options: RedisStateAdapterOptions);
@@ -29,7 +29,6 @@ declare class RedisStateAdapter implements StateAdapter {
29
29
  subscribe(threadId: string): Promise<void>;
30
30
  unsubscribe(threadId: string): Promise<void>;
31
31
  isSubscribed(threadId: string): Promise<boolean>;
32
- listSubscriptions(adapterName?: string): AsyncIterable<string>;
33
32
  acquireLock(threadId: string, ttlMs: number): Promise<Lock | null>;
34
33
  releaseLock(lock: Lock): Promise<void>;
35
34
  extendLock(lock: Lock, ttlMs: number): Promise<boolean>;
package/dist/index.js CHANGED
@@ -50,29 +50,6 @@ var RedisStateAdapter = class {
50
50
  this.ensureConnected();
51
51
  return this.client.sIsMember(this.subscriptionsSetKey(), threadId);
52
52
  }
53
- async *listSubscriptions(adapterName) {
54
- this.ensureConnected();
55
- let cursor = 0;
56
- do {
57
- const result = await this.client.sScan(
58
- this.subscriptionsSetKey(),
59
- cursor,
60
- {
61
- COUNT: 100
62
- }
63
- );
64
- cursor = result.cursor;
65
- for (const threadId of result.members) {
66
- if (adapterName) {
67
- if (threadId.startsWith(`${adapterName}:`)) {
68
- yield threadId;
69
- }
70
- } else {
71
- yield threadId;
72
- }
73
- }
74
- } while (cursor !== 0);
75
- }
76
53
  async acquireLock(threadId, ttlMs) {
77
54
  this.ensureConnected();
78
55
  const token = generateToken();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Lock, Logger, StateAdapter } from \"chat\";\nimport { createClient, type RedisClientType } from \"redis\";\n\nexport interface RedisStateAdapterOptions {\n /** Redis connection URL (e.g., redis://localhost:6379) */\n url: string;\n /** Key prefix for all Redis keys (default: \"chat-sdk\") */\n keyPrefix?: string;\n /** Logger instance for error reporting */\n logger: Logger;\n}\n\n/**\n * Redis state adapter for production use.\n *\n * Provides persistent subscriptions and distributed locking\n * across multiple server instances.\n */\nexport class RedisStateAdapter implements StateAdapter {\n private client: RedisClientType;\n private keyPrefix: string;\n private logger: Logger;\n private connected = false;\n private connectPromise: Promise<void> | null = null;\n\n constructor(options: RedisStateAdapterOptions) {\n this.client = createClient({ url: options.url });\n this.keyPrefix = options.keyPrefix || \"chat-sdk\";\n this.logger = options.logger;\n\n // Handle connection errors\n this.client.on(\"error\", (err) => {\n this.logger.error(\"Redis client error\", { error: err });\n });\n }\n\n private key(type: \"sub\" | \"lock\" | \"cache\", id: string): string {\n return `${this.keyPrefix}:${type}:${id}`;\n }\n\n private subscriptionsSetKey(): string {\n return `${this.keyPrefix}:subscriptions`;\n }\n\n async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n // Reuse existing connection attempt to avoid race conditions\n if (!this.connectPromise) {\n this.connectPromise = this.client.connect().then(() => {\n this.connected = true;\n });\n }\n\n await this.connectPromise;\n }\n\n async disconnect(): Promise<void> {\n if (this.connected) {\n await this.client.quit();\n this.connected = false;\n this.connectPromise = null;\n }\n }\n\n async subscribe(threadId: string): Promise<void> {\n this.ensureConnected();\n await this.client.sAdd(this.subscriptionsSetKey(), threadId);\n }\n\n async unsubscribe(threadId: string): Promise<void> {\n this.ensureConnected();\n await this.client.sRem(this.subscriptionsSetKey(), threadId);\n }\n\n async isSubscribed(threadId: string): Promise<boolean> {\n this.ensureConnected();\n return this.client.sIsMember(this.subscriptionsSetKey(), threadId);\n }\n\n async *listSubscriptions(adapterName?: string): AsyncIterable<string> {\n this.ensureConnected();\n\n // Use SSCAN for large sets to avoid blocking\n let cursor = 0;\n do {\n const result = await this.client.sScan(\n this.subscriptionsSetKey(),\n cursor,\n {\n COUNT: 100,\n },\n );\n cursor = result.cursor;\n\n for (const threadId of result.members) {\n if (adapterName) {\n if (threadId.startsWith(`${adapterName}:`)) {\n yield threadId;\n }\n } else {\n yield threadId;\n }\n }\n } while (cursor !== 0);\n }\n\n async acquireLock(threadId: string, ttlMs: number): Promise<Lock | null> {\n this.ensureConnected();\n\n const token = generateToken();\n const lockKey = this.key(\"lock\", threadId);\n\n // Use SET NX EX for atomic lock acquisition\n const acquired = await this.client.set(lockKey, token, {\n NX: true,\n PX: ttlMs,\n });\n\n if (acquired) {\n return {\n threadId,\n token,\n expiresAt: Date.now() + ttlMs,\n };\n }\n\n return null;\n }\n\n async releaseLock(lock: Lock): Promise<void> {\n this.ensureConnected();\n\n const lockKey = this.key(\"lock\", lock.threadId);\n\n // Use Lua script for atomic check-and-delete\n const script = `\n if redis.call(\"get\", KEYS[1]) == ARGV[1] then\n return redis.call(\"del\", KEYS[1])\n else\n return 0\n end\n `;\n\n await this.client.eval(script, {\n keys: [lockKey],\n arguments: [lock.token],\n });\n }\n\n async extendLock(lock: Lock, ttlMs: number): Promise<boolean> {\n this.ensureConnected();\n\n const lockKey = this.key(\"lock\", lock.threadId);\n\n // Use Lua script for atomic check-and-extend\n const script = `\n if redis.call(\"get\", KEYS[1]) == ARGV[1] then\n return redis.call(\"pexpire\", KEYS[1], ARGV[2])\n else\n return 0\n end\n `;\n\n const result = await this.client.eval(script, {\n keys: [lockKey],\n arguments: [lock.token, ttlMs.toString()],\n });\n\n return result === 1;\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n this.ensureConnected();\n\n const cacheKey = this.key(\"cache\", key);\n const value = await this.client.get(cacheKey);\n\n if (value === null) {\n return null;\n }\n\n try {\n return JSON.parse(value) as T;\n } catch {\n // If parsing fails, return as string\n return value as unknown as T;\n }\n }\n\n async set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void> {\n this.ensureConnected();\n\n const cacheKey = this.key(\"cache\", key);\n const serialized = JSON.stringify(value);\n\n if (ttlMs) {\n await this.client.set(cacheKey, serialized, { PX: ttlMs });\n } else {\n await this.client.set(cacheKey, serialized);\n }\n }\n\n async delete(key: string): Promise<void> {\n this.ensureConnected();\n\n const cacheKey = this.key(\"cache\", key);\n await this.client.del(cacheKey);\n }\n\n private ensureConnected(): void {\n if (!this.connected) {\n throw new Error(\n \"RedisStateAdapter is not connected. Call connect() first.\",\n );\n }\n }\n\n /**\n * Get the underlying Redis client for advanced usage.\n */\n getClient(): RedisClientType {\n return this.client;\n }\n}\n\nfunction generateToken(): string {\n return `redis_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;\n}\n\nexport function createRedisState(\n options: RedisStateAdapterOptions,\n): RedisStateAdapter {\n return new RedisStateAdapter(options);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Lock, Logger, StateAdapter } from \"chat\";\nimport { createClient, type RedisClientType } from \"redis\";\n\nexport interface RedisStateAdapterOptions {\n /** Key prefix for all Redis keys (default: \"chat-sdk\") */\n keyPrefix?: string;\n /** Logger instance for error reporting */\n logger: Logger;\n /** Redis connection URL (e.g., redis://localhost:6379) */\n url: string;\n}\n\n/**\n * Redis state adapter for production use.\n *\n * Provides persistent subscriptions and distributed locking\n * across multiple server instances.\n */\nexport class RedisStateAdapter implements StateAdapter {\n private readonly client: RedisClientType;\n private readonly keyPrefix: string;\n private readonly logger: Logger;\n private connected = false;\n private connectPromise: Promise<void> | null = null;\n\n constructor(options: RedisStateAdapterOptions) {\n this.client = createClient({ url: options.url });\n this.keyPrefix = options.keyPrefix || \"chat-sdk\";\n this.logger = options.logger;\n\n // Handle connection errors\n this.client.on(\"error\", (err) => {\n this.logger.error(\"Redis client error\", { error: err });\n });\n }\n\n private key(type: \"sub\" | \"lock\" | \"cache\", id: string): string {\n return `${this.keyPrefix}:${type}:${id}`;\n }\n\n private subscriptionsSetKey(): string {\n return `${this.keyPrefix}:subscriptions`;\n }\n\n async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n // Reuse existing connection attempt to avoid race conditions\n if (!this.connectPromise) {\n this.connectPromise = this.client.connect().then(() => {\n this.connected = true;\n });\n }\n\n await this.connectPromise;\n }\n\n async disconnect(): Promise<void> {\n if (this.connected) {\n await this.client.quit();\n this.connected = false;\n this.connectPromise = null;\n }\n }\n\n async subscribe(threadId: string): Promise<void> {\n this.ensureConnected();\n await this.client.sAdd(this.subscriptionsSetKey(), threadId);\n }\n\n async unsubscribe(threadId: string): Promise<void> {\n this.ensureConnected();\n await this.client.sRem(this.subscriptionsSetKey(), threadId);\n }\n\n async isSubscribed(threadId: string): Promise<boolean> {\n this.ensureConnected();\n return this.client.sIsMember(this.subscriptionsSetKey(), threadId);\n }\n\n async acquireLock(threadId: string, ttlMs: number): Promise<Lock | null> {\n this.ensureConnected();\n\n const token = generateToken();\n const lockKey = this.key(\"lock\", threadId);\n\n // Use SET NX EX for atomic lock acquisition\n const acquired = await this.client.set(lockKey, token, {\n NX: true,\n PX: ttlMs,\n });\n\n if (acquired) {\n return {\n threadId,\n token,\n expiresAt: Date.now() + ttlMs,\n };\n }\n\n return null;\n }\n\n async releaseLock(lock: Lock): Promise<void> {\n this.ensureConnected();\n\n const lockKey = this.key(\"lock\", lock.threadId);\n\n // Use Lua script for atomic check-and-delete\n const script = `\n if redis.call(\"get\", KEYS[1]) == ARGV[1] then\n return redis.call(\"del\", KEYS[1])\n else\n return 0\n end\n `;\n\n await this.client.eval(script, {\n keys: [lockKey],\n arguments: [lock.token],\n });\n }\n\n async extendLock(lock: Lock, ttlMs: number): Promise<boolean> {\n this.ensureConnected();\n\n const lockKey = this.key(\"lock\", lock.threadId);\n\n // Use Lua script for atomic check-and-extend\n const script = `\n if redis.call(\"get\", KEYS[1]) == ARGV[1] then\n return redis.call(\"pexpire\", KEYS[1], ARGV[2])\n else\n return 0\n end\n `;\n\n const result = await this.client.eval(script, {\n keys: [lockKey],\n arguments: [lock.token, ttlMs.toString()],\n });\n\n return result === 1;\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n this.ensureConnected();\n\n const cacheKey = this.key(\"cache\", key);\n const value = await this.client.get(cacheKey);\n\n if (value === null) {\n return null;\n }\n\n try {\n return JSON.parse(value) as T;\n } catch {\n // If parsing fails, return as string\n return value as unknown as T;\n }\n }\n\n async set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void> {\n this.ensureConnected();\n\n const cacheKey = this.key(\"cache\", key);\n const serialized = JSON.stringify(value);\n\n if (ttlMs) {\n await this.client.set(cacheKey, serialized, { PX: ttlMs });\n } else {\n await this.client.set(cacheKey, serialized);\n }\n }\n\n async delete(key: string): Promise<void> {\n this.ensureConnected();\n\n const cacheKey = this.key(\"cache\", key);\n await this.client.del(cacheKey);\n }\n\n private ensureConnected(): void {\n if (!this.connected) {\n throw new Error(\n \"RedisStateAdapter is not connected. Call connect() first.\"\n );\n }\n }\n\n /**\n * Get the underlying Redis client for advanced usage.\n */\n getClient(): RedisClientType {\n return this.client;\n }\n}\n\nfunction generateToken(): string {\n return `redis_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;\n}\n\nexport function createRedisState(\n options: RedisStateAdapterOptions\n): RedisStateAdapter {\n return new RedisStateAdapter(options);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chat-adapter/state-redis",
3
- "version": "4.13.1",
3
+ "version": "4.13.2",
4
4
  "description": "Redis state adapter for chat (production)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,16 +17,16 @@
17
17
  ],
18
18
  "dependencies": {
19
19
  "redis": "^4.7.0",
20
- "chat": "4.13.1"
20
+ "chat": "4.13.2"
21
21
  },
22
22
  "repository": {
23
23
  "type": "git",
24
- "url": "git+https://github.com/vercel-labs/chat.git",
24
+ "url": "git+https://github.com/vercel/chat.git",
25
25
  "directory": "packages/state-redis"
26
26
  },
27
- "homepage": "https://github.com/vercel-labs/chat#readme",
27
+ "homepage": "https://github.com/vercel/chat#readme",
28
28
  "bugs": {
29
- "url": "https://github.com/vercel-labs/chat/issues"
29
+ "url": "https://github.com/vercel/chat/issues"
30
30
  },
31
31
  "publishConfig": {
32
32
  "access": "public"
@@ -47,10 +47,9 @@
47
47
  "scripts": {
48
48
  "build": "tsup",
49
49
  "dev": "tsup --watch",
50
- "test": "vitest run",
50
+ "test": "vitest run --coverage",
51
51
  "test:watch": "vitest",
52
52
  "typecheck": "tsc --noEmit",
53
- "lint": "biome check src",
54
53
  "clean": "rm -rf dist"
55
54
  }
56
55
  }