Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ See the [Getting Started guide](https://chat-sdk.dev/docs/getting-started) for a
| `@chat-adapter/whatsapp` | [WhatsApp adapter](https://chat-sdk.dev/adapters/whatsapp) |
| `@chat-adapter/state-redis` | [Redis state adapter](https://chat-sdk.dev/docs/state/redis) (production) |
| `@chat-adapter/state-ioredis` | [ioredis state adapter](https://chat-sdk.dev/docs/state/ioredis) (alternative) |
| `@chat-adapter/state-unstorage` | [Unstorage state adapter](https://chat-sdk.dev/docs/state/unstorage) (bring your own backend) |
| `@chat-adapter/state-pg` | [PostgreSQL state adapter](https://chat-sdk.dev/docs/state/postgres) (production) |
| `@chat-adapter/state-memory` | [In-memory state adapter](https://chat-sdk.dev/docs/state/memory) (development) |

Expand Down
10 changes: 10 additions & 0 deletions apps/docs/adapters.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@
"beta": true,
"readme": "https://github.com/vercel/chat/tree/main/packages/state-ioredis"
},
{
"name": "Unstorage",
"slug": "unstorage",
"type": "state",
"description": "State adapter powered by unstorage so you can choose any unstorage-compatible backend.",
"packageName": "@chat-adapter/state-unstorage",
"icon": "memory",
"beta": true,
"readme": "https://github.com/vercel/chat/tree/main/packages/state-unstorage"
},
{
"name": "PostgreSQL",
"slug": "postgres",
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/concurrency.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ The `queue` and `debounce` strategies require three additional methods on your s
| `dequeue(threadId)` | Pop the next (oldest) message from the queue. Returns `null` if empty. |
| `queueDepth(threadId)` | Return the current number of queued messages. |

All built-in state adapters (`@chat-adapter/state-memory`, `@chat-adapter/state-redis`, `@chat-adapter/state-ioredis`) implement these methods. The Redis adapters use Lua scripts for atomicity.
All built-in state adapters (`@chat-adapter/state-memory`, `@chat-adapter/state-redis`, `@chat-adapter/state-ioredis`, `@chat-adapter/state-unstorage`) implement these methods. The Redis adapters use Lua scripts for atomicity. The unstorage adapter behavior depends on the underlying driver consistency guarantees.

## Observability

Expand Down
1 change: 1 addition & 0 deletions apps/docs/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,6 @@ The SDK is distributed as a set of packages you install based on your needs:
| `@chat-adapter/whatsapp` | WhatsApp Business adapter |
| `@chat-adapter/state-redis` | Redis state adapter (production) |
| `@chat-adapter/state-ioredis` | ioredis state adapter (alternative) |
| `@chat-adapter/state-unstorage` | Unstorage state adapter (bring your own backend) |
| `@chat-adapter/state-pg` | PostgreSQL state adapter (production) |
| `@chat-adapter/state-memory` | In-memory state adapter (development) |
25 changes: 25 additions & 0 deletions apps/docs/content/docs/state.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,28 @@ Note that force-releasing a lock does not cancel the previous handler — it con
### Caching

State adapters provide key-value storage with TTL for thread state (`thread.setState()`), message deduplication, and other internal caching.

## Unstorage adapter

Use `@chat-adapter/state-unstorage` when you want one state adapter API with a pluggable backend via [unstorage](https://unstorage.unjs.io/):

```typescript
import { createUnstorageState } from "@chat-adapter/state-unstorage";
import { createStorage } from "unstorage";
import redisDriver from "unstorage/drivers/redis";

const storage = createStorage({
driver: redisDriver({
url: process.env.REDIS_URL,
base: "chat-sdk",
}),
});

const chat = new Chat({
userName: "my-bot",
adapters: { slack },
state: createUnstorageState({ storage }),
});
```

If you run multiple instances and need strict distributed atomicity, choose a backend with strong consistency guarantees.
11 changes: 11 additions & 0 deletions packages/integration-tests/src/documentation-test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ export const VALID_PACKAGE_README_IMPORTS = [
"@chat-adapter/whatsapp",
"@chat-adapter/state-redis",
"@chat-adapter/state-ioredis",
"@chat-adapter/state-unstorage",
"@chat-adapter/state-pg",
"@chat-adapter/state-memory",
"next/server",
"redis",
"ioredis",
"pg",
"postgres",
"unstorage",
"unstorage/drivers/memory",
"unstorage/drivers/redis",
];

export const VALID_DOC_PACKAGES = [
Expand All @@ -42,6 +46,7 @@ export const VALID_DOC_PACKAGES = [
"@chat-adapter/whatsapp",
"@chat-adapter/state-redis",
"@chat-adapter/state-ioredis",
"@chat-adapter/state-unstorage",
"@chat-adapter/state-pg",
"@chat-adapter/state-memory",
"@chat-adapter/shared",
Expand All @@ -61,6 +66,9 @@ export const VALID_DOC_PACKAGES = [
"ioredis",
"pg",
"postgres",
"unstorage",
"unstorage/drivers/memory",
"unstorage/drivers/redis",
"tsup",
"vitest",
"vitest/config",
Expand Down Expand Up @@ -147,6 +155,9 @@ export function createTempProject(codeBlocks: string[]): string {
"@chat-adapter/state-pg": [
join(import.meta.dirname, "../../state-pg/src/index.ts"),
],
"@chat-adapter/state-unstorage": [
join(import.meta.dirname, "../../state-unstorage/src/index.ts"),
],
"@chat-adapter/state-memory": [
join(import.meta.dirname, "../../state-memory/src/index.ts"),
],
Expand Down
5 changes: 5 additions & 0 deletions packages/state-unstorage/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# @chat-adapter/state-unstorage

## 4.24.0

- Initial release.
66 changes: 66 additions & 0 deletions packages/state-unstorage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# @chat-adapter/state-unstorage

[![npm version](https://img.shields.io/npm/v/@chat-adapter/state-unstorage)](https://www.npmjs.com/package/@chat-adapter/state-unstorage)
[![npm downloads](https://img.shields.io/npm/dm/@chat-adapter/state-unstorage)](https://www.npmjs.com/package/@chat-adapter/state-unstorage)

State adapter for [Chat SDK](https://chat-sdk.dev) built on top of [unstorage](https://unstorage.unjs.io/). It lets you use any unstorage-compatible backend behind the same Chat SDK state API.

## Installation

```bash
pnpm add @chat-adapter/state-unstorage unstorage
```

## Usage

```typescript
import { Chat } from "chat";
import { createUnstorageState } from "@chat-adapter/state-unstorage";
import { createStorage } from "unstorage";
import redisDriver from "unstorage/drivers/redis";

const storage = createStorage({
driver: redisDriver({
base: "chat-sdk",
url: process.env.REDIS_URL,
}),
});

const bot = new Chat({
userName: "mybot",
adapters: { /* ... */ },
state: createUnstorageState({ storage }),
});
```

You can also pass a driver directly:

```typescript
import memoryDriver from "unstorage/drivers/memory";

const state = createUnstorageState({
driver: memoryDriver(),
keyPrefix: "my-bot",
});
```

## Configuration

| Option | Required | Description |
|--------|----------|-------------|
| `storage` | No | Existing unstorage instance |
| `driver` | No | unstorage driver used to create an internal storage instance |
| `keyPrefix` | No | Prefix for all keys (default: `"chat-sdk"`) |
| `logger` | No | Logger instance (defaults to `ConsoleLogger("info")`) |

`storage` and `driver` are mutually exclusive.

## Compatibility

- Implements the full Chat SDK `StateAdapter` surface (`locks`, `subscriptions`, `cache`, `lists`, and `queues`)
- Works with any unstorage backend
- Preserves key prefix namespacing across multiple adapters sharing the same backend

## License

MIT
55 changes: 55 additions & 0 deletions packages/state-unstorage/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "@chat-adapter/state-unstorage",
"version": "4.24.0",
"description": "Unstorage-based state adapter for chat",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"test": "vitest run --coverage",
"test:watch": "vitest",
"typecheck": "tsc --noEmit",
"clean": "rm -rf dist"
},
"dependencies": {
"chat": "workspace:*",
"unstorage": "^1.17.5"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vercel/chat.git",
"directory": "packages/state-unstorage"
},
"homepage": "https://github.com/vercel/chat#readme",
"bugs": {
"url": "https://github.com/vercel/chat/issues"
},
"publishConfig": {
"access": "public"
},
"keywords": [
"chat",
"state",
"unstorage",
"production"
],
"license": "MIT",
"devDependencies": {
"@types/node": "^25.3.2",
"tsup": "^8.5.1",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
}
}
Loading
Loading