chat 4.13.0 → 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,77 @@
1
+ ---
2
+ title: Ephemeral messages
3
+ description: Send messages visible only to a specific user.
4
+ type: guide
5
+ prerequisites:
6
+ - /docs/usage
7
+ related:
8
+ - /docs/direct-messages
9
+ ---
10
+
11
+ Ephemeral messages are visible only to a specific user within a thread. They're useful for confirmations, hints, and private notifications.
12
+
13
+ ## Send an ephemeral message
14
+
15
+ ```typescript title="lib/bot.ts" lineNumbers
16
+ await thread.postEphemeral(user, "Only you can see this!", {
17
+ fallbackToDM: true,
18
+ });
19
+ ```
20
+
21
+ The `fallbackToDM` option is required and controls behavior on platforms without native ephemeral support:
22
+
23
+ - `fallbackToDM: true` — send as a DM if native ephemeral is not supported
24
+ - `fallbackToDM: false` — return `null` if native ephemeral is not supported
25
+
26
+ ## Platform behavior
27
+
28
+ | Platform | Native support | Behavior | Persistence |
29
+ |----------|---------------|----------|-------------|
30
+ | Slack | Yes | Ephemeral in channel | Session-only (disappears on reload) |
31
+ | Google Chat | Yes | Private message in space | Persists until deleted |
32
+ | Discord | No | DM fallback | Persists in DM |
33
+ | Teams | No | DM fallback | Persists in DM |
34
+
35
+ ## Check for fallback
36
+
37
+ ```typescript title="lib/bot.ts" lineNumbers
38
+ const result = await thread.postEphemeral(user, "Private notification", {
39
+ fallbackToDM: true,
40
+ });
41
+
42
+ if (result?.usedFallback) {
43
+ console.log("Sent as DM instead of ephemeral");
44
+ }
45
+ ```
46
+
47
+ ## Graceful degradation
48
+
49
+ Only send if the platform supports native ephemeral:
50
+
51
+ ```typescript title="lib/bot.ts" lineNumbers
52
+ const result = await thread.postEphemeral(user, "Contextual hint", {
53
+ fallbackToDM: false,
54
+ });
55
+
56
+ if (!result) {
57
+ // Platform doesn't support native ephemeral
58
+ // Message was not sent
59
+ }
60
+ ```
61
+
62
+ ## Ephemeral cards
63
+
64
+ Cards work with ephemeral messages too:
65
+
66
+ ```tsx title="lib/bot.tsx" lineNumbers
67
+ await thread.postEphemeral(
68
+ event.user,
69
+ <Card title="Ephemeral Card">
70
+ <CardText>Only you can see this card.</CardText>
71
+ <Actions>
72
+ <Button id="open_modal" style="primary">Open Modal</Button>
73
+ </Actions>
74
+ </Card>,
75
+ { fallbackToDM: true }
76
+ );
77
+ ```
@@ -0,0 +1,147 @@
1
+ ---
2
+ title: Error handling
3
+ description: Handle rate limits, unsupported features, and other errors from adapters.
4
+ type: guide
5
+ prerequisites:
6
+ - /docs/usage
7
+ ---
8
+
9
+ The SDK provides typed error classes for common failure scenarios. All errors are importable from the `chat` package.
10
+
11
+ ```typescript
12
+ import { ChatError, RateLimitError, NotImplementedError, LockError } from "chat";
13
+ ```
14
+
15
+ ## Error types
16
+
17
+ ### ChatError
18
+
19
+ Base error class for all SDK errors. Every error below extends `ChatError`.
20
+
21
+ <TypeTable
22
+ type={{
23
+ message: {
24
+ description: 'Human-readable error message.',
25
+ type: 'string',
26
+ },
27
+ code: {
28
+ description: 'Machine-readable error code.',
29
+ type: 'string',
30
+ },
31
+ cause: {
32
+ description: 'Original error that caused this one.',
33
+ type: 'unknown | undefined',
34
+ },
35
+ }}
36
+ />
37
+
38
+ ### RateLimitError
39
+
40
+ Thrown when a platform API returns a 429 response. The `retryAfterMs` property tells you how long to wait before retrying.
41
+
42
+ ```typescript title="lib/bot.ts" lineNumbers
43
+ import { RateLimitError } from "chat";
44
+
45
+ try {
46
+ await thread.post("Hello!");
47
+ } catch (error) {
48
+ if (error instanceof RateLimitError) {
49
+ console.log(`Rate limited, retry after ${error.retryAfterMs}ms`);
50
+ }
51
+ }
52
+ ```
53
+
54
+ <TypeTable
55
+ type={{
56
+ code: {
57
+ description: 'Always "RATE_LIMITED".',
58
+ type: 'string',
59
+ },
60
+ retryAfterMs: {
61
+ description: 'Milliseconds to wait before retrying.',
62
+ type: 'number | undefined',
63
+ },
64
+ }}
65
+ />
66
+
67
+ ### NotImplementedError
68
+
69
+ Thrown when you call a feature that a platform doesn't support. For example, calling `addReaction()` on Teams or `startTyping()` on Slack.
70
+
71
+ ```typescript title="lib/bot.ts" lineNumbers
72
+ import { NotImplementedError } from "chat";
73
+
74
+ try {
75
+ await thread.addReaction(emoji.thumbs_up);
76
+ } catch (error) {
77
+ if (error instanceof NotImplementedError) {
78
+ console.log(`Feature not supported: ${error.feature}`);
79
+ }
80
+ }
81
+ ```
82
+
83
+ <TypeTable
84
+ type={{
85
+ code: {
86
+ description: 'Always "NOT_IMPLEMENTED".',
87
+ type: 'string',
88
+ },
89
+ feature: {
90
+ description: 'Name of the unsupported feature.',
91
+ type: 'string | undefined',
92
+ },
93
+ }}
94
+ />
95
+
96
+ See the [feature matrix](/docs/adapters) for which features are supported on each platform.
97
+
98
+ ### LockError
99
+
100
+ Thrown when the SDK fails to acquire a distributed lock on a thread (used to prevent concurrent processing of messages in the same thread).
101
+
102
+ <TypeTable
103
+ type={{
104
+ code: {
105
+ description: 'Always "LOCK_FAILED".',
106
+ type: 'string',
107
+ },
108
+ }}
109
+ />
110
+
111
+ ## Adapter errors
112
+
113
+ Adapters also throw specialized errors from the `@chat-adapter/shared` package:
114
+
115
+ | Error | Code | Description |
116
+ |-------|------|-------------|
117
+ | `AdapterRateLimitError` | `RATE_LIMITED` | Platform rate limit hit, includes `retryAfter` in seconds |
118
+ | `AuthenticationError` | `AUTH_FAILED` | Invalid or expired credentials |
119
+ | `ResourceNotFoundError` | `NOT_FOUND` | Requested resource (channel, message) doesn't exist |
120
+ | `PermissionError` | `PERMISSION_DENIED` | Bot lacks required permissions/scopes |
121
+ | `ValidationError` | `VALIDATION_ERROR` | Invalid input data (e.g. message too long) |
122
+ | `NetworkError` | `NETWORK_ERROR` | Connectivity issue with platform API |
123
+
124
+ ## Catching errors
125
+
126
+ Use `instanceof` to handle specific error types:
127
+
128
+ ```typescript title="lib/bot.ts" lineNumbers
129
+ import { RateLimitError, NotImplementedError } from "chat";
130
+
131
+ bot.onNewMention(async (thread, message) => {
132
+ try {
133
+ await thread.post("Processing...");
134
+ await thread.addReaction(emoji.eyes);
135
+ } catch (error) {
136
+ if (error instanceof RateLimitError) {
137
+ // Wait and retry
138
+ await new Promise((r) => setTimeout(r, error.retryAfterMs ?? 5000));
139
+ await thread.post("Processing...");
140
+ } else if (error instanceof NotImplementedError) {
141
+ // Skip unsupported features gracefully
142
+ } else {
143
+ throw error;
144
+ }
145
+ }
146
+ });
147
+ ```
package/docs/files.mdx ADDED
@@ -0,0 +1,77 @@
1
+ ---
2
+ title: File uploads
3
+ description: Send and receive files across chat platforms.
4
+ type: guide
5
+ prerequisites:
6
+ - /docs/usage
7
+ ---
8
+
9
+ ## Send files
10
+
11
+ Attach files to messages using the `files` property:
12
+
13
+ ```typescript title="lib/bot.ts" lineNumbers
14
+ const reportBuffer = Buffer.from("PDF content");
15
+
16
+ await thread.post({
17
+ markdown: "Here's the report you requested:",
18
+ files: [
19
+ {
20
+ data: reportBuffer,
21
+ filename: "report.pdf",
22
+ mimeType: "application/pdf",
23
+ },
24
+ ],
25
+ });
26
+ ```
27
+
28
+ ### Multiple files
29
+
30
+ ```typescript title="lib/bot.ts" lineNumbers
31
+ await thread.post({
32
+ markdown: "Attached are the images:",
33
+ files: [
34
+ { data: image1, filename: "screenshot1.png" },
35
+ { data: image2, filename: "screenshot2.png" },
36
+ ],
37
+ });
38
+ ```
39
+
40
+ ### Files without text
41
+
42
+ ```typescript title="lib/bot.ts" lineNumbers
43
+ await thread.post({
44
+ markdown: "",
45
+ files: [{ data: buffer, filename: "document.xlsx" }],
46
+ });
47
+ ```
48
+
49
+ ## Receive files
50
+
51
+ Access attachments from incoming messages:
52
+
53
+ ```typescript title="lib/bot.ts" lineNumbers
54
+ bot.onSubscribedMessage(async (thread, message) => {
55
+ for (const attachment of message.attachments ?? []) {
56
+ console.log(`File: ${attachment.name}, Type: ${attachment.mimeType}`);
57
+
58
+ if (attachment.fetchData) {
59
+ const data = await attachment.fetchData();
60
+ console.log(`Downloaded ${data.length} bytes`);
61
+ }
62
+ }
63
+ });
64
+ ```
65
+
66
+ ### Attachment properties
67
+
68
+ | Property | Type | Description |
69
+ |----------|------|-------------|
70
+ | `type` | `string` | Attachment type (e.g., "image", "file") |
71
+ | `url` | `string` (optional) | Public URL |
72
+ | `name` | `string` (optional) | Filename |
73
+ | `mimeType` | `string` (optional) | MIME type |
74
+ | `size` | `number` (optional) | File size in bytes |
75
+ | `width` | `number` (optional) | Image width |
76
+ | `height` | `number` (optional) | Image height |
77
+ | `fetchData` | `() => Promise<Buffer>` (optional) | Download the file data |
@@ -0,0 +1,12 @@
1
+ ---
2
+ title: Getting Started
3
+ description: Pick a guide to start building with Chat SDK.
4
+ ---
5
+
6
+ Choose a guide below to get up and running with Chat SDK on your platform of choice.
7
+
8
+ <Cards>
9
+ <Card title="Slack bot with Next.js and Redis" description="Build a Slack bot from scratch using Chat SDK, Next.js, and Redis." href="/docs/guides/slack-nextjs" />
10
+ <Card title="Code review GitHub bot with Hono and Redis" description="Build a GitHub bot that reviews pull requests using AI SDK, Vercel Sandbox, and Chat SDK." href="/docs/guides/code-review-hono" />
11
+ <Card title="Discord support bot with Nuxt and Redis" description="Build a Discord support bot using Chat SDK, Nuxt, and AI SDK." href="/docs/guides/discord-nuxt" />
12
+ </Cards>
@@ -0,0 +1,248 @@
1
+ ---
2
+ title: Code review GitHub bot with Hono and Redis
3
+ description: This guide walks through building a GitHub bot that reviews pull requests on demand. When a user @mentions the bot on a PR, Chat SDK picks up the mention, spins up a Vercel Sandbox with the repo cloned, and uses AI SDK to analyze the diff.
4
+ type: guide
5
+ prerequisites: []
6
+ related:
7
+ - /docs/adapters/github
8
+ - /docs/state/redis
9
+ ---
10
+
11
+ ## Prerequisites
12
+
13
+ - Node.js 18+
14
+ - [pnpm](https://pnpm.io) (or npm/yarn)
15
+ - A GitHub repository where you have admin access
16
+ - A Redis instance for state management
17
+ - A [Vercel](https://vercel.com) account
18
+
19
+ ## Create a Hono app
20
+
21
+ Scaffold a new Hono project and install dependencies:
22
+
23
+ ```sh title="Terminal"
24
+ pnpm create hono my-review-bot
25
+ cd my-review-bot
26
+ pnpm add @octokit/rest @vercel/sandbox ai bash-tool chat @chat-adapter/github @chat-adapter/state-redis
27
+ ```
28
+
29
+ <Callout type="info">
30
+ Select the `vercel` template when prompted by `create-hono`. This sets up the project for Vercel deployment with the correct entry point.
31
+ </Callout>
32
+
33
+ ## Configure a GitHub webhook
34
+
35
+ 1. Go to your repository **Settings** then **Webhooks** then **Add webhook**
36
+ 2. Set **Payload URL** to `https://your-domain.com/api/webhooks/github`
37
+ 3. Set **Content type** to `application/json`
38
+ 4. Set a **Secret** and save it — you'll need this as `GITHUB_WEBHOOK_SECRET`
39
+ 5. Under **Which events would you like to trigger this webhook?**, select **Let me select individual events** and check:
40
+ - **Issue comments** (for @mention on the PR conversation tab)
41
+ - **Pull request review comments** (for @mention on inline review threads)
42
+
43
+ ### Get credentials
44
+
45
+ 1. Go to [Settings > Developer settings > Personal access tokens](https://github.com/settings/tokens) and create a token with `repo` scope — you'll need this as `GITHUB_TOKEN`
46
+ 2. Copy the **Webhook secret** you set above — you'll need this as `GITHUB_WEBHOOK_SECRET`
47
+
48
+ ## Configure environment variables
49
+
50
+ Create a `.env` file in your project root:
51
+
52
+ ```bash title=".env"
53
+ GITHUB_TOKEN=ghp_your_personal_access_token
54
+ GITHUB_WEBHOOK_SECRET=your_webhook_secret
55
+ REDIS_URL=redis://localhost:6379
56
+ BOT_USERNAME=my-review-bot
57
+ ```
58
+
59
+ The model (`anthropic/claude-sonnet-4.6`) uses AI Gateway. Develop locally by linking to your Vercel project with `vc link` then pulling your OIDC token with `vc pull --environment development`.
60
+
61
+ ## Define the review function
62
+
63
+ Create the core review logic. This clones the repo into a Vercel Sandbox, then uses AI SDK with a bash tool to let Claude analyze the diff and read files directly.
64
+
65
+ ```typescript title="src/review.ts" lineNumbers
66
+ import { Sandbox } from "@vercel/sandbox";
67
+ import { ToolLoopAgent, stepCountIs } from "ai";
68
+ import { createBashTool } from "bash-tool";
69
+
70
+ interface ReviewInput {
71
+ owner: string;
72
+ repo: string;
73
+ prBranch: string;
74
+ baseBranch: string;
75
+ }
76
+
77
+ export async function reviewPullRequest(input: ReviewInput): Promise<string> {
78
+ const { owner, repo, prBranch, baseBranch } = input;
79
+
80
+ const sandbox = await Sandbox.create({
81
+ source: {
82
+ type: "git",
83
+ url: `https://github.com/${owner}/${repo}`,
84
+ username: "x-access-token",
85
+ password: process.env.GITHUB_TOKEN,
86
+ depth: 50,
87
+ },
88
+ timeout: 5 * 60 * 1000,
89
+ });
90
+
91
+ try {
92
+ await sandbox.runCommand("git", ["fetch", "origin", prBranch, baseBranch]);
93
+ await sandbox.runCommand("git", ["checkout", prBranch]);
94
+
95
+ const diffResult = await sandbox.runCommand("git", [
96
+ "diff",
97
+ `origin/${baseBranch}...HEAD`,
98
+ ]);
99
+ const diff = await diffResult.output("stdout");
100
+
101
+ const { tools } = await createBashTool({ sandbox });
102
+
103
+ const agent = new ToolLoopAgent({
104
+ model: "anthropic/claude-sonnet-4.6",
105
+ tools,
106
+ stopWhen: stepCountIs(20),
107
+ });
108
+
109
+ const result = await agent.generate({
110
+ prompt: `You are reviewing a pull request for bugs and issues.
111
+
112
+ Here is the diff for this PR:
113
+
114
+ \`\`\`diff
115
+ ${diff}
116
+ \`\`\`
117
+
118
+ Use the bash and readFile tools to inspect any files you need more context on.
119
+
120
+ Look for bugs, security issues, performance problems, and missing error handling.
121
+ Organize findings by severity (critical, warning, suggestion).
122
+ If the code looks good, say so.`,
123
+ });
124
+
125
+ return result.text;
126
+ } finally {
127
+ await sandbox.stop();
128
+ }
129
+ }
130
+ ```
131
+
132
+ The `createBashTool` gives the agent `bash`, `readFile`, and `writeFile` tools — all scoped to the sandbox. The agent can run `git diff`, read source files, and explore the repo freely without any code escaping the sandbox.
133
+
134
+ The function returns the review text instead of posting it directly. This lets the Chat SDK handler post it as a threaded reply.
135
+
136
+ ## Create the bot
137
+
138
+ Create a `Chat` instance with the GitHub adapter. When someone @mentions the bot on a PR, it fetches the PR metadata, runs the review, and posts the result back to the thread.
139
+
140
+ ```typescript title="src/bot.ts" lineNumbers
141
+ import { Chat } from "chat";
142
+ import { createGitHubAdapter } from "@chat-adapter/github";
143
+ import { createRedisState } from "@chat-adapter/state-redis";
144
+ import { Octokit } from "@octokit/rest";
145
+ import { reviewPullRequest } from "./review";
146
+ import type { GitHubRawMessage } from "@chat-adapter/github";
147
+
148
+ const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
149
+
150
+ export const bot = new Chat({
151
+ userName: process.env.BOT_USERNAME!,
152
+ adapters: {
153
+ github: createGitHubAdapter({
154
+ token: process.env.GITHUB_TOKEN!,
155
+ webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,
156
+ userName: process.env.BOT_USERNAME!,
157
+ }),
158
+ },
159
+ state: createRedisState({
160
+ url: process.env.REDIS_URL!,
161
+ }),
162
+ });
163
+
164
+ bot.onNewMention(async (thread, message) => {
165
+ const raw = message.raw as GitHubRawMessage;
166
+ const { owner, repo, prNumber } = {
167
+ owner: raw.repository.owner.login,
168
+ repo: raw.repository.name,
169
+ prNumber: raw.prNumber,
170
+ };
171
+
172
+ // Fetch PR branch info
173
+ const { data: pr } = await octokit.pulls.get({
174
+ owner,
175
+ repo,
176
+ pull_number: prNumber,
177
+ });
178
+
179
+ await thread.post("Starting code review...");
180
+ await thread.subscribe();
181
+
182
+ const review = await reviewPullRequest({
183
+ owner,
184
+ repo,
185
+ prBranch: pr.head.ref,
186
+ baseBranch: pr.base.ref,
187
+ });
188
+
189
+ await thread.post(review);
190
+ });
191
+
192
+ bot.onSubscribedMessage(async (thread, message) => {
193
+ await thread.post(
194
+ "I've already reviewed this PR. @mention me on a new PR to start another review."
195
+ );
196
+ });
197
+ ```
198
+
199
+ `onNewMention` fires when a user @mentions the bot — for example, `@codereview can you review this?`. The handler extracts the PR details from the message's raw payload, runs the sandboxed review, and posts the result. Calling `thread.subscribe()` lets the bot respond to follow-up messages in the same thread.
200
+
201
+ ## Handle the webhook
202
+
203
+ Create the Hono app with a single webhook route that delegates to Chat SDK:
204
+
205
+ ```typescript title="src/index.ts" lineNumbers
206
+ import { Hono } from "hono";
207
+ import { bot } from "./bot";
208
+
209
+ const app = new Hono();
210
+
211
+ app.post("/api/webhooks/github", async (c) => {
212
+ const handler = bot.webhooks.github;
213
+ if (!handler) {
214
+ return c.text("GitHub adapter not configured", 404);
215
+ }
216
+
217
+ return handler(c.req.raw, {
218
+ waitUntil: (task) => c.executionCtx.waitUntil(task),
219
+ });
220
+ });
221
+
222
+ export default app;
223
+ ```
224
+
225
+ Chat SDK's GitHub adapter handles signature verification, event parsing, and routing internally. The `waitUntil` option ensures the review completes after the HTTP response is sent.
226
+
227
+ ## Test locally
228
+
229
+ 1. Start your development server (`pnpm dev`)
230
+ 2. Expose it with a tunnel (e.g. `ngrok http 3000`)
231
+ 3. Update the webhook URL in your GitHub repository settings to your tunnel URL
232
+ 4. Open a pull request
233
+ 5. Comment `@my-review-bot can you review this?` — the bot should respond with "Starting code review..." followed by the full review
234
+
235
+ ## Deploy to Vercel
236
+
237
+ Deploy your bot to Vercel:
238
+
239
+ ```sh title="Terminal"
240
+ vercel deploy
241
+ ```
242
+
243
+ After deployment, set your environment variables in the Vercel dashboard (`GITHUB_TOKEN`, `GITHUB_WEBHOOK_SECRET`, `REDIS_URL`, `BOT_USERNAME`). Update the webhook URL in your GitHub repository settings to your production URL.
244
+
245
+ ## Next steps
246
+
247
+ - [GitHub adapter](/docs/adapters/github) — Authentication options, thread model, and full configuration reference
248
+ - [Redis state](/docs/state/redis) — Production state adapter for subscriptions and distributed locking