chat 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.
Files changed (53) hide show
  1. package/README.md +19 -31
  2. package/dist/{chunk-WKJEG4FE.js → chunk-THM4ACIE.js} +12 -4
  3. package/dist/chunk-THM4ACIE.js.map +1 -0
  4. package/dist/index.d.ts +409 -415
  5. package/dist/index.js +63 -29
  6. package/dist/index.js.map +1 -1
  7. package/dist/{jsx-runtime-COVsDskT.d.ts → jsx-runtime-Bdt1Dwzf.d.ts} +71 -71
  8. package/dist/jsx-runtime.d.ts +1 -1
  9. package/dist/jsx-runtime.js +1 -1
  10. package/docs/actions.mdx +98 -0
  11. package/docs/adapters/discord.mdx +217 -0
  12. package/docs/adapters/gchat.mdx +232 -0
  13. package/docs/adapters/github.mdx +225 -0
  14. package/docs/adapters/index.mdx +110 -0
  15. package/docs/adapters/linear.mdx +207 -0
  16. package/docs/adapters/meta.json +12 -0
  17. package/docs/adapters/slack.mdx +293 -0
  18. package/docs/adapters/teams.mdx +225 -0
  19. package/docs/api/cards.mdx +217 -0
  20. package/docs/api/channel.mdx +176 -0
  21. package/docs/api/chat.mdx +469 -0
  22. package/docs/api/index.mdx +29 -0
  23. package/docs/api/markdown.mdx +235 -0
  24. package/docs/api/message.mdx +163 -0
  25. package/docs/api/meta.json +14 -0
  26. package/docs/api/modals.mdx +222 -0
  27. package/docs/api/postable-message.mdx +166 -0
  28. package/docs/api/thread.mdx +186 -0
  29. package/docs/cards.mdx +213 -0
  30. package/docs/direct-messages.mdx +56 -0
  31. package/docs/emoji.mdx +77 -0
  32. package/docs/ephemeral-messages.mdx +77 -0
  33. package/docs/error-handling.mdx +147 -0
  34. package/docs/files.mdx +77 -0
  35. package/docs/getting-started.mdx +12 -0
  36. package/docs/guides/code-review-hono.mdx +248 -0
  37. package/docs/guides/discord-nuxt.mdx +237 -0
  38. package/docs/guides/meta.json +4 -0
  39. package/docs/guides/slack-nextjs.mdx +245 -0
  40. package/docs/index.mdx +92 -0
  41. package/docs/meta.json +20 -0
  42. package/docs/modals.mdx +208 -0
  43. package/docs/posting-messages.mdx +177 -0
  44. package/docs/slash-commands.mdx +110 -0
  45. package/docs/state/index.mdx +31 -0
  46. package/docs/state/ioredis.mdx +81 -0
  47. package/docs/state/memory.mdx +52 -0
  48. package/docs/state/meta.json +9 -0
  49. package/docs/state/redis.mdx +93 -0
  50. package/docs/streaming.mdx +99 -0
  51. package/docs/usage.mdx +338 -0
  52. package/package.json +10 -10
  53. package/dist/chunk-WKJEG4FE.js.map +0 -1
@@ -0,0 +1,208 @@
1
+ ---
2
+ title: Modals
3
+ description: Collect structured user input through modal dialogs with text fields, dropdowns, and validation.
4
+ type: guide
5
+ prerequisites:
6
+ - /docs/actions
7
+ ---
8
+
9
+ Modals open form dialogs in response to button clicks or [slash commands](/docs/slash-commands). They support text inputs, dropdowns, radio buttons, and server-side validation. Currently supported on Slack.
10
+
11
+ ## Open a modal
12
+
13
+ Modals are opened from [action handlers](/docs/actions) or [slash command handlers](/docs/slash-commands) using `event.openModal()`:
14
+
15
+ ```tsx title="lib/bot.tsx" lineNumbers
16
+ import { Modal, TextInput, Select, SelectOption } from "chat";
17
+
18
+ bot.onAction("feedback", async (event) => {
19
+ await event.openModal(
20
+ <Modal
21
+ callbackId="feedback_form"
22
+ title="Send Feedback"
23
+ submitLabel="Send"
24
+ closeLabel="Cancel"
25
+ notifyOnClose
26
+ >
27
+ <TextInput
28
+ id="message"
29
+ label="Your Feedback"
30
+ placeholder="Tell us what you think..."
31
+ multiline
32
+ />
33
+ <Select id="category" label="Category" placeholder="Select a category">
34
+ <SelectOption label="Bug Report" value="bug" />
35
+ <SelectOption label="Feature Request" value="feature" />
36
+ <SelectOption label="General" value="general" />
37
+ </Select>
38
+ <TextInput
39
+ id="email"
40
+ label="Email (optional)"
41
+ placeholder="your@email.com"
42
+ optional
43
+ />
44
+ </Modal>
45
+ );
46
+ });
47
+ ```
48
+
49
+ ## Components
50
+
51
+ ### Modal
52
+
53
+ The top-level container for the form.
54
+
55
+ | Prop | Type | Description |
56
+ |------|------|-------------|
57
+ | `callbackId` | `string` | Identifier for matching submit/close handlers |
58
+ | `title` | `string` | Modal title |
59
+ | `submitLabel` | `string` (optional) | Submit button text (defaults to "Submit") |
60
+ | `closeLabel` | `string` (optional) | Cancel button text (defaults to "Cancel") |
61
+ | `notifyOnClose` | `boolean` (optional) | Fire `onModalClose` when user cancels |
62
+ | `privateMetadata` | `string` (optional) | Custom context passed through to handlers |
63
+
64
+ ### TextInput
65
+
66
+ A text field for user input.
67
+
68
+ | Prop | Type | Description |
69
+ |------|------|-------------|
70
+ | `id` | `string` | Field identifier (key in `event.values`) |
71
+ | `label` | `string` | Field label |
72
+ | `placeholder` | `string` (optional) | Placeholder text |
73
+ | `initialValue` | `string` (optional) | Pre-filled value |
74
+ | `multiline` | `boolean` (optional) | Render as textarea |
75
+ | `optional` | `boolean` (optional) | Allow empty submission |
76
+ | `maxLength` | `number` (optional) | Maximum character count |
77
+
78
+ ### Select
79
+
80
+ A dropdown for selecting a single option.
81
+
82
+ | Prop | Type | Description |
83
+ |------|------|-------------|
84
+ | `id` | `string` | Field identifier |
85
+ | `label` | `string` | Field label |
86
+ | `placeholder` | `string` (optional) | Placeholder text |
87
+ | `initialOption` | `string` (optional) | Pre-selected value |
88
+ | `optional` | `boolean` (optional) | Allow empty submission |
89
+
90
+ ### RadioSelect
91
+
92
+ A radio button group for mutually exclusive options.
93
+
94
+ | Prop | Type | Description |
95
+ |------|------|-------------|
96
+ | `id` | `string` | Field identifier |
97
+ | `label` | `string` | Field label |
98
+ | `initialOption` | `string` (optional) | Pre-selected value |
99
+ | `optional` | `boolean` (optional) | Allow empty submission |
100
+
101
+ ### SelectOption
102
+
103
+ An option for `Select` or `RadioSelect`.
104
+
105
+ | Prop | Type | Description |
106
+ |------|------|-------------|
107
+ | `label` | `string` | Display text |
108
+ | `value` | `string` | Value passed to handler |
109
+ | `description` | `string` (optional) | Help text below the label |
110
+
111
+ ## Handle submissions
112
+
113
+ Register a handler with `onModalSubmit` using the same `callbackId`:
114
+
115
+ ```typescript title="lib/bot.ts" lineNumbers
116
+ bot.onModalSubmit("feedback_form", async (event) => {
117
+ const { message, category, email } = event.values;
118
+
119
+ // Validate input — return errors to show in the modal
120
+ if (!message || message.length < 5) {
121
+ return {
122
+ action: "errors",
123
+ errors: { message: "Feedback must be at least 5 characters" },
124
+ };
125
+ }
126
+
127
+ // Post confirmation to the original thread
128
+ if (event.relatedThread) {
129
+ await event.relatedThread.post(`Feedback received! Category: ${category}`);
130
+ }
131
+
132
+ // Update the message that triggered the modal
133
+ if (event.relatedMessage) {
134
+ await event.relatedMessage.edit("Feedback submitted!");
135
+ }
136
+
137
+ // Return nothing (or { action: "close" }) to close the modal
138
+ });
139
+ ```
140
+
141
+ ### Response types
142
+
143
+ | Response | Description |
144
+ |----------|-------------|
145
+ | `undefined` or `{ action: "close" }` | Close the modal |
146
+ | `{ action: "errors", errors: { fieldId: "message" } }` | Show validation errors on specific fields |
147
+ | `{ action: "update", modal: ModalElement }` | Replace the modal content |
148
+ | `{ action: "push", modal: ModalElement }` | Push a new modal view onto the stack |
149
+
150
+ ### ModalSubmitEvent
151
+
152
+ | Property | Type | Description |
153
+ |----------|------|-------------|
154
+ | `callbackId` | `string` | Modal identifier |
155
+ | `viewId` | `string` | Platform view ID |
156
+ | `values` | `Record<string, string>` | Form field values keyed by input `id` |
157
+ | `user` | `Author` | The user who submitted |
158
+ | `privateMetadata` | `string` (optional) | Custom context from the Modal component |
159
+ | `relatedThread` | `Thread` (optional) | Thread where the modal was triggered |
160
+ | `relatedMessage` | `SentMessage` (optional) | Message with the button that opened the modal |
161
+ | `relatedChannel` | `Channel` (optional) | Channel where the modal was triggered (from [slash commands](/docs/slash-commands)) |
162
+ | `adapter` | `Adapter` | The platform adapter |
163
+ | `raw` | `unknown` | Platform-specific payload |
164
+
165
+ ## Handle cancellation
166
+
167
+ Optionally handle when users cancel a modal. Requires `notifyOnClose` on the `Modal` component:
168
+
169
+ ```typescript title="lib/bot.ts" lineNumbers
170
+ bot.onModalClose("feedback_form", async (event) => {
171
+ console.log(`${event.user.userName} cancelled the feedback form`);
172
+
173
+ if (event.relatedThread) {
174
+ await event.relatedThread.post("No worries, let us know if you change your mind!");
175
+ }
176
+ });
177
+ ```
178
+
179
+ ## Pass context with privateMetadata
180
+
181
+ Use `privateMetadata` to carry context from the button click through to the submit handler:
182
+
183
+ ```tsx title="lib/bot.tsx" lineNumbers
184
+ bot.onAction("report", async (event) => {
185
+ await event.openModal(
186
+ <Modal
187
+ callbackId="report_form"
188
+ title="Report Bug"
189
+ submitLabel="Submit"
190
+ privateMetadata={JSON.stringify({
191
+ reportType: event.value,
192
+ threadId: event.threadId,
193
+ })}
194
+ >
195
+ <TextInput id="title" label="Bug Title" />
196
+ <TextInput id="steps" label="Steps to Reproduce" multiline />
197
+ </Modal>
198
+ );
199
+ });
200
+
201
+ bot.onModalSubmit("report_form", async (event) => {
202
+ const metadata = event.privateMetadata
203
+ ? JSON.parse(event.privateMetadata)
204
+ : {};
205
+
206
+ console.log(metadata.reportType); // "bug"
207
+ });
208
+ ```
@@ -0,0 +1,177 @@
1
+ ---
2
+ title: Posting Messages
3
+ description: Different ways to render and send messages with thread.post().
4
+ type: guide
5
+ prerequisites:
6
+ - /docs/usage
7
+ related:
8
+ - /docs/cards
9
+ - /docs/streaming
10
+ - /docs/files
11
+ ---
12
+
13
+ `thread.post()` accepts several message formats, each suited to different use cases. Choose the format that best fits your content — from plain strings to structured AST to rich interactive cards.
14
+
15
+ ## Plain text
16
+
17
+ The simplest option. Pass a string and it goes through as-is to the platform.
18
+
19
+ ```typescript title="lib/bot.ts"
20
+ await thread.post("Hello world");
21
+ ```
22
+
23
+ This sends the string directly without any formatting conversion.
24
+
25
+ ## Markdown
26
+
27
+ Pass a `{ markdown }` object to have the SDK convert standard markdown to each platform's native format — mrkdwn for Slack, HTML for Teams, and so on.
28
+
29
+ ```typescript title="lib/bot.ts"
30
+ await thread.post({
31
+ markdown: "**Bold**, _italic_, and `code`",
32
+ });
33
+ ```
34
+
35
+ Under the hood, the SDK parses the markdown into an mdast AST, then each adapter converts it to the platform's format.
36
+
37
+ ## AST builders
38
+
39
+ For programmatic control over message formatting, use the mdast AST builder functions exported from `chat`. This is the recommended approach for most use cases — it gives you fine-grained control without the overhead of card rendering.
40
+
41
+ ```typescript title="lib/bot.ts"
42
+ import { root, paragraph, text, strong, link } from "chat";
43
+
44
+ await thread.post({
45
+ ast: root([
46
+ paragraph([
47
+ strong([text("Deployment complete")]),
48
+ text(" — "),
49
+ link("https://example.com", [text("View site")]),
50
+ ]),
51
+ ]),
52
+ });
53
+ ```
54
+
55
+ ### Available builders
56
+
57
+ | Builder | Description | Example |
58
+ |---------|-------------|---------|
59
+ | `root(children)` | Root node (required wrapper) | `root([paragraph([...])])` |
60
+ | `paragraph(children)` | Paragraph block | `paragraph([text("Hello")])` |
61
+ | `text(value)` | Plain text | `text("Hello")` |
62
+ | `strong(children)` | **Bold** text | `strong([text("bold")])` |
63
+ | `emphasis(children)` | _Italic_ text | `emphasis([text("italic")])` |
64
+ | `strikethrough(children)` | ~~Strikethrough~~ text | `strikethrough([text("done")])` |
65
+ | `inlineCode(value)` | `Inline code` | `inlineCode("const x = 1")` |
66
+ | `codeBlock(value, lang?)` | Fenced code block | `codeBlock("const x = 1", "ts")` |
67
+ | `link(url, children, title?)` | Hyperlink | `link("https://...", [text("click")])` |
68
+ | `blockquote(children)` | Block quote | `blockquote([paragraph([text("...")])])` |
69
+
70
+ ### Parsing markdown to AST
71
+
72
+ You can also parse a markdown string into an AST, manipulate it, then send it:
73
+
74
+ ```typescript title="lib/bot.ts"
75
+ import { parseMarkdown, stringifyMarkdown } from "chat";
76
+
77
+ const ast = parseMarkdown("**Hello** world");
78
+ // Manipulate the AST...
79
+ await thread.post({ ast });
80
+ ```
81
+
82
+ ## Cards
83
+
84
+ When you need interactive elements like buttons, dropdowns, or structured layouts, use cards. Cards render natively on each platform — Block Kit on Slack, Adaptive Cards on Teams, and Google Chat Cards.
85
+
86
+ ### Function syntax
87
+
88
+ Use the function-call API for type-safe card construction:
89
+
90
+ ```typescript title="lib/bot.ts"
91
+ import { Card, Text, Actions, Button } from "chat";
92
+
93
+ await thread.post(
94
+ Card({
95
+ title: "Order #1234",
96
+ children: [
97
+ Text("Your order has been received!"),
98
+ Actions([
99
+ Button({ id: "approve", label: "Approve", style: "primary" }),
100
+ Button({ id: "reject", label: "Reject", style: "danger" }),
101
+ ]),
102
+ ],
103
+ })
104
+ );
105
+ ```
106
+
107
+ ### JSX syntax
108
+
109
+ You can also use JSX if you configure the `chat` JSX runtime:
110
+
111
+ ```json title="tsconfig.json"
112
+ {
113
+ "compilerOptions": {
114
+ "jsx": "react-jsx",
115
+ "jsxImportSource": "chat"
116
+ }
117
+ }
118
+ ```
119
+
120
+ ```tsx title="lib/bot.tsx"
121
+ import { Card, CardText, Actions, Button } from "chat";
122
+
123
+ await thread.post(
124
+ <Card title="Order #1234">
125
+ <CardText>Your order has been received!</CardText>
126
+ <Actions>
127
+ <Button id="approve" style="primary">Approve</Button>
128
+ <Button id="reject" style="danger">Reject</Button>
129
+ </Actions>
130
+ </Card>
131
+ );
132
+ ```
133
+
134
+ <Callout type="warn">
135
+ The JSX syntax requires `jsxImportSource: "chat"` in your `tsconfig.json` (or a per-file `/** @jsxImportSource chat */` pragma). Without this, TypeScript won't recognize the card JSX types. If you run into type issues with JSX, use the function-call syntax instead — it produces the same output with better type inference.
136
+ </Callout>
137
+
138
+ See the [Cards](/docs/cards) page for the full list of card components.
139
+
140
+ ## Streaming
141
+
142
+ Pass an `AsyncIterable<string>` (like the AI SDK's `textStream`) to stream a message in real time. The SDK uses platform-native streaming where available and falls back to post-then-edit on other platforms.
143
+
144
+ ```typescript title="lib/bot.ts"
145
+ import { streamText } from "ai";
146
+
147
+ const result = streamText({ model, prompt: message.text });
148
+ await thread.post(result.textStream);
149
+ ```
150
+
151
+ See the [Streaming](/docs/streaming) page for details on platform behavior and configuration.
152
+
153
+ ## Attachments and files
154
+
155
+ Any structured message format (`markdown`, `ast`, or `card`) supports `files` for uploading attachments alongside the message:
156
+
157
+ ```typescript title="lib/bot.ts"
158
+ await thread.post({
159
+ markdown: "Here's the report:",
160
+ files: [{ data: buffer, filename: "report.pdf" }],
161
+ });
162
+ ```
163
+
164
+ See the [Files](/docs/files) page for more on attachments.
165
+
166
+ ## Choosing a format
167
+
168
+ | Format | Use when | Example |
169
+ |--------|----------|---------|
170
+ | Plain string | Simple, unformatted text | Status updates, acknowledgements |
171
+ | `{ markdown }` | You have a markdown string (e.g. from a template) | Notifications with links and formatting |
172
+ | `{ ast }` | You need programmatic formatting control | Dynamic messages built from data |
173
+ | Card (function) | You need buttons, fields, or structured layouts | Approval flows, dashboards |
174
+ | Card (JSX) | Same as above, with JSX syntax preference | Same use cases as function cards |
175
+ | `AsyncIterable` | Streaming AI responses | Chat with LLMs |
176
+
177
+ For most cases, **AST builders** give the best balance of control and simplicity. Reach for **cards** when you need interactive elements like buttons or dropdowns.
@@ -0,0 +1,110 @@
1
+ ---
2
+ title: Slash Commands
3
+ description: Handle slash command invocations and respond with messages or modals.
4
+ type: guide
5
+ prerequisites:
6
+ - /docs/getting-started
7
+ related:
8
+ - /docs/modals
9
+ - /docs/adapters/slack
10
+ ---
11
+
12
+ Slash commands let users invoke your bot with `/command` syntax. Register handlers with `onSlashCommand` to respond.
13
+
14
+ ## Handle a specific command
15
+
16
+ ```typescript title="lib/bot.ts" lineNumbers
17
+ bot.onSlashCommand("/status", async (event) => {
18
+ await event.channel.post("All systems operational!");
19
+ });
20
+ ```
21
+
22
+ ## Handle multiple commands
23
+
24
+ ```typescript title="lib/bot.ts" lineNumbers
25
+ bot.onSlashCommand(["/help", "/info"], async (event) => {
26
+ await event.channel.post(`You invoked ${event.command}`);
27
+ });
28
+ ```
29
+
30
+ ## Catch-all handler
31
+
32
+ Register a handler without a command to catch all slash commands:
33
+
34
+ ```typescript title="lib/bot.ts" lineNumbers
35
+ bot.onSlashCommand(async (event) => {
36
+ console.log(`Command: ${event.command}, Args: ${event.text}`);
37
+ });
38
+ ```
39
+
40
+ ## SlashCommandEvent
41
+
42
+ The `event` object passed to slash command handlers:
43
+
44
+ | Property | Type | Description |
45
+ |----------|------|-------------|
46
+ | `command` | `string` | The command name (e.g., `/status`) |
47
+ | `text` | `string` | Arguments after the command |
48
+ | `user` | `Author` | The user who invoked the command |
49
+ | `channel` | `Channel` | The channel where the command was invoked |
50
+ | `adapter` | `Adapter` | The platform adapter |
51
+ | `triggerId` | `string` (optional) | Platform trigger ID for opening modals |
52
+ | `openModal` | `(modal) => Promise<{ viewId: string } \| undefined>` | Open a modal dialog |
53
+ | `raw` | `unknown` | Platform-specific payload |
54
+
55
+ ## Respond to a command
56
+
57
+ Use `event.channel` to post messages:
58
+
59
+ ```typescript title="lib/bot.ts" lineNumbers
60
+ bot.onSlashCommand("/greet", async (event) => {
61
+ // Public message to the channel
62
+ await event.channel.post(`Hello, ${event.user.fullName}!`);
63
+
64
+ // Ephemeral message (only visible to the user)
65
+ await event.channel.postEphemeral(
66
+ event.user,
67
+ "This message is just for you!",
68
+ { fallbackToDM: false }
69
+ );
70
+ });
71
+ ```
72
+
73
+ ## Open a modal
74
+
75
+ Use `event.openModal()` to open a [modal](/docs/modals) in response to a slash command:
76
+
77
+ ```tsx title="lib/bot.tsx" lineNumbers
78
+ import { Modal, TextInput, Select, SelectOption } from "chat";
79
+
80
+ bot.onSlashCommand("/feedback", async (event) => {
81
+ const result = await event.openModal(
82
+ <Modal callbackId="feedback_form" title="Send Feedback" submitLabel="Send">
83
+ <TextInput id="message" label="Your Feedback" multiline />
84
+ <Select id="category" label="Category">
85
+ <SelectOption label="Bug" value="bug" />
86
+ <SelectOption label="Feature" value="feature" />
87
+ </Select>
88
+ </Modal>
89
+ );
90
+
91
+ if (!result) {
92
+ await event.channel.post("Couldn't open the feedback form. Please try again.");
93
+ }
94
+ });
95
+ ```
96
+
97
+ <Callout type="info">
98
+ When a modal is opened from a slash command, the submit handler receives `relatedChannel` instead of `relatedThread`. Use this to post back to the channel where the command was invoked.
99
+ </Callout>
100
+
101
+ ```typescript title="lib/bot.ts" lineNumbers
102
+ bot.onModalSubmit("feedback_form", async (event) => {
103
+ const { message, category } = event.values;
104
+
105
+ // Post to the channel where the slash command was invoked
106
+ if (event.relatedChannel) {
107
+ await event.relatedChannel.post(`Feedback received! Category: ${category}`);
108
+ }
109
+ });
110
+ ```
@@ -0,0 +1,31 @@
1
+ ---
2
+ title: Overview
3
+ description: Pluggable state adapters for thread subscriptions, distributed locking, and caching.
4
+ type: overview
5
+ prerequisites:
6
+ - /docs/getting-started
7
+ ---
8
+
9
+ State adapters handle persistent storage for thread subscriptions, distributed locks (to prevent duplicate processing), and caching. You must provide a state adapter when creating a `Chat` instance.
10
+
11
+ ## Comparison
12
+
13
+ | Adapter | Package | Persistence | Multi-instance | Use case |
14
+ |---------|---------|-------------|----------------|----------|
15
+ | [Redis](/docs/state/redis) | `@chat-adapter/state-redis` | Yes | Yes | Production (recommended) |
16
+ | [ioredis](/docs/state/ioredis) | `@chat-adapter/state-ioredis` | Yes | Yes | Production (Cluster/Sentinel) |
17
+ | [Memory](/docs/state/memory) | `@chat-adapter/state-memory` | No | No | Development and testing |
18
+
19
+ ## What state adapters manage
20
+
21
+ ### Thread subscriptions
22
+
23
+ When your bot calls `thread.subscribe()`, the state adapter persists that subscription. On subsequent webhooks, the SDK checks subscriptions to route messages to `onSubscribedMessage` handlers. With Redis, subscriptions survive restarts and work across multiple instances.
24
+
25
+ ### Distributed locking
26
+
27
+ When a webhook arrives, the SDK acquires a lock on the thread to prevent duplicate processing. This is critical for serverless deployments where multiple instances may receive the same event.
28
+
29
+ ### Caching
30
+
31
+ State adapters provide key-value storage with TTL for thread state (`thread.setState()`), message deduplication, and other internal caching.
@@ -0,0 +1,81 @@
1
+ ---
2
+ title: ioredis
3
+ description: Alternative Redis state adapter using ioredis with Cluster and Sentinel support.
4
+ type: reference
5
+ prerequisites:
6
+ - /docs/getting-started
7
+ ---
8
+
9
+ An alternative Redis state adapter using [ioredis](https://www.npmjs.com/package/ioredis). Use this if you already have ioredis in your project or need Redis Cluster/Sentinel support.
10
+
11
+ ## Installation
12
+
13
+ ```sh title="Terminal"
14
+ pnpm add @chat-adapter/state-ioredis
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```typescript title="lib/bot.ts" lineNumbers
20
+ import { Chat } from "chat";
21
+ import { createIORedisState } from "@chat-adapter/state-ioredis";
22
+
23
+ const bot = new Chat({
24
+ userName: "mybot",
25
+ adapters: { /* ... */ },
26
+ state: createIORedisState({
27
+ url: process.env.REDIS_URL!,
28
+ }),
29
+ });
30
+ ```
31
+
32
+ ### Using an existing client
33
+
34
+ ```typescript title="lib/bot.ts" lineNumbers
35
+ import Redis from "ioredis";
36
+
37
+ const client = new Redis("redis://localhost:6379");
38
+
39
+ const state = createIORedisState({ client });
40
+ ```
41
+
42
+ ## Configuration
43
+
44
+ | Option | Required | Description |
45
+ |--------|----------|-------------|
46
+ | `url` | Yes* | Redis connection URL |
47
+ | `client` | No | Existing `ioredis` client instance |
48
+ | `keyPrefix` | No | Prefix for all keys (default: `"chat-sdk"`) |
49
+
50
+ *Either `url` or `client` is required.
51
+
52
+ ## When to use ioredis vs redis
53
+
54
+ **Use `@chat-adapter/state-ioredis` when:**
55
+
56
+ - You already use ioredis in your project
57
+ - You need Redis Cluster support
58
+ - You need Redis Sentinel support
59
+ - You prefer the ioredis API
60
+
61
+ **Use `@chat-adapter/state-redis` when:**
62
+
63
+ - You want the official Redis client
64
+ - You're starting a new project
65
+ - You don't need Cluster or Sentinel
66
+
67
+ ## Key structure
68
+
69
+ ```
70
+ {keyPrefix}:subscriptions - SET of subscribed thread IDs
71
+ {keyPrefix}:lock:{threadId} - Lock key with TTL
72
+ ```
73
+
74
+ ## Features
75
+
76
+ - Persistent subscriptions across restarts
77
+ - Distributed locking across multiple instances
78
+ - Automatic reconnection
79
+ - Redis Cluster support
80
+ - Redis Sentinel support
81
+ - Key prefix namespacing
@@ -0,0 +1,52 @@
1
+ ---
2
+ title: Memory
3
+ description: In-memory state adapter for local development and testing.
4
+ type: reference
5
+ prerequisites:
6
+ - /docs/getting-started
7
+ ---
8
+
9
+ An in-memory state adapter for development and testing. Zero configuration required.
10
+
11
+ <Callout type="warn">
12
+ Only use the memory adapter for local development and testing. State is lost on restart and locks don't work across multiple instances. For production, use [Redis](/docs/state/redis) or [ioredis](/docs/state/ioredis).
13
+ </Callout>
14
+
15
+ ## Installation
16
+
17
+ ```sh title="Terminal"
18
+ pnpm add @chat-adapter/state-memory
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```typescript title="lib/bot.ts" lineNumbers
24
+ import { Chat } from "chat";
25
+ import { createMemoryState } from "@chat-adapter/state-memory";
26
+
27
+ const bot = new Chat({
28
+ userName: "mybot",
29
+ adapters: { /* ... */ },
30
+ state: createMemoryState(),
31
+ });
32
+ ```
33
+
34
+ No configuration options are needed.
35
+
36
+ ## Features
37
+
38
+ - Thread subscriptions (in-memory)
39
+ - Locking (single-process only)
40
+ - Zero configuration
41
+
42
+ ## Limitations
43
+
44
+ - **Not suitable for production** — state is lost on restart
45
+ - **Single process only** — locks don't work across multiple instances
46
+ - **No persistence** — subscriptions reset when the process restarts
47
+
48
+ ## When to use
49
+
50
+ - Local development
51
+ - Unit testing
52
+ - Quick prototyping
@@ -0,0 +1,9 @@
1
+ {
2
+ "title": "State",
3
+ "pages": [
4
+ "index",
5
+ "redis",
6
+ "ioredis",
7
+ "memory"
8
+ ]
9
+ }