# @chatto/client 中文文档

规范地址：https://npmcn.com/package/%40chatto/client/

版本：0\.5\.0
npm 元数据更新时间：2026-11-09T12:05:34.732Z
中文译文更新时间：2026-11-09T13:36:33.689Z
软件包许可：Apache-2\.0

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40chatto/client
原文对照：https://npmcn.com/package/%40chatto/client/?lang=original
项目主页：https://github.com/chattocorp/chatto#readme
源码仓库：https://github.com/chattocorp/chatto

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：3.5 MB，431 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：7 个
- peer 依赖：svelte ^5\.7\.0
- 首次发布：2026-10-23T18:46:40.660Z
- 最新版本发布：2026-11-09T12:05:34.358Z
- 近 30 天下载量：2514（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：17（近 12 个月 17 个）

## 中文简介

框架无关的Chatto客户端：连接、实时投影和响应式存储

## 安装

```sh
npm install @chatto/client
```

也可以使用：`pnpm add @chatto/client`、`yarn add @chatto/client`、`bun add @chatto/client`

## README

# Chatto 客户端

`@chatto/client` 是查托0.5服务器的客户端. 它连接到一个或 更多服务器,通过实时API保持数据流,并曝光 数据是反应状态 它也有请求和消息循环 和整合用。 它只保存服务器数据:主机状态 UI属于主机. 它不依赖UI框架。 捆绑 Chatto前端,ChattoBot,和"跑步机器人"的例子使用.

安装方式 `npm install @chatto/client`npm 软件包包含 ES 模块 和 TypeScript 声明,包括生成的协议类型。 使用节点.js 22岁或更晚的婴儿。 Svelte 可选; 使用时安装 Svelte 5 `@chatto/client/svelte`。模块路径可以在1.0之前改变.

## 客户端和服务器

一个客户端是一组孤立的服务器 具有实时传输和 工作背景。 为每个独立主机创建一个客户端. 一个进程可以 拥有多个客户端,每个客户端可以连接多个服务器:

```ts
import { createClient } from '@chatto/client';

const client = createClient();
process.on('SIGTERM', () => client.close());
try {
  const eu = client.connect({ serverUrl: 'https://eu.example', apiKey: euKey });
  const us = client.connect({ serverUrl: 'https://us.example', apiKey: usKey });
  await Promise.all([
    eu.run((ctx) => ctx.reply(`Hello from EU, you said: ${ctx.message.body}`)),
    us.run((ctx) => ctx.reply('Hello from US'))
  ]);
} finally {
  // Close every server and stop all background work.
  client.close();
}
```

`connect()` 添加一个带有固定无记名符号的服务器, 如 bot API 密钥, 并归还它。 该标志只保存在记忆中: 它永远不会被更新或 写入设备存储 . `client.server(id)` 返回服务器 客户有。 都一样 `Server` 类型:服务器的数据、事件和 请求是一个对象.

`server.reportPresence()` 在服务器运行时将账户显示为在线 打开:一旦服务器接受密钥,客户端就会发送出存在心跳 每30秒钟一次。 之后 `close()`,账户显示在60之内的离线 秒数。 它永远不会改变账户 保存的存在选择.

### 答复信件

`server.run(handler)` 等待服务器接受密钥, 同时 服务器无法访问,然后调用 `handler` 发送到 取景者(密钥的帐户)。 默认这些是直接消息, 并回复了观众的留言。 作用, `@here`,以及 `@all` 提及是单独的原因(`role_mention`, (中文(简体) ), `here_mention`, (中文(简体) ), `all_mention`) 使您能够 `reasons`。消息为 处理有序。 处理器收到上下文 :

```ts
await server.run(
  async (ctx) => {
    ctx.message; // the addressed message: id, roomId, threadRootId, authorId, body, reasons
    const thread = await ctx.readThread(); // messages with `fromViewer`
    await ctx.withTyping(async () => {
      await ctx.reply(await answer(thread.messages));
    });
    await ctx.addReaction('eyes');
  },
  {
    signal, // stops the loop; without it, the loop stops when the server closes
    reasons: ['mention', 'direct_message'], // default: also 'reply'
    onStatus: (status) => console.log(status.state),
    onError: (error) => console.error(error) // keep running after a failure
  }
);
```

`ctx.signal` 当循环因任何理由停止时中止; 传递给 你自己的可以工作. `ctx.conversationKey` 将对话范围到 观众、房间、线条和发送者. `ctx.refreshTyping()` 刷新输入 指标一次.

`run` 何时决定 `signal` 中止或服务器关闭 。 它拒绝当 服务器拒绝密钥或不支持此客户端,并且,不 `onError`,与处理器的第一个失败.

### 事件、状况和状态

-   `server.consumeEvents({ signal, onEvent, onStatus })` 处理 按顺序排列实时事件. `run` 用起来. `onEvent(event, { signal })` 接收到一个信号,当循环因任何原因停止时,会中止 当服务器结束会话时的例子。 多达1 000起事件等待 对于一个慢处理器;除此之外,它们被放弃,并报告有缺口.
-   `onStatus` 接收 `connecting`, (中文(简体) ), `ready`,以及 `reconnecting`A级 `ready` 状态为 `gap: true` 意味着事件可能缺失,例如 因为服务器在重新连接后无法恢复流 。 虽然 `run` 等待服务器, a `connecting` 具有 `error` 报告 每一次尝试都失败,例如无法到达的服务器.
-   一个服务器 `connect` 添加接收创建时的事件, 也 在此之前 `ready()` 决断,并保存在第一个 `consumeEvents` 或者说 `run` 电话 后一召报先有漏.
-   `onEvent(listener)` 财务报告和审定财务报表 `onSnapshot(listener)` 接收事件和新 直接快照,无回环.
-   服务器的数据是被动的:例如 `roomList.rooms`, (中文(简体) ), `notifications.occurrences`, (中文(简体) ), `projection.users`,以及 `rooms.messages(id)` {\\fn黑体\\fs22\\bord1\\shad0\\3ahbe\\4ah00\\fscx67\\fscy66\\2chffffff\\3ch808080}一个呼号 直播室时间表.

### 直播时间表和房间商店

`rooms.messages(roomId)` 财务报告和审定财务报表 `rooms.thread(roomId, rootId)` 返回一个活 时间表。 时间线读取它的最新一页 当你第一次得到它。 实时 发送后添加新信件并应用编辑和删除,所以主机 不为每次更改再次读取线程.

-   `isCurrent` 当装入最新页面而未读取或重新连接时为真 正在等待重新装入 . `events` 然后有最新消息.
-   `loadError` 错误的读取失败, 或失去房间。 打电话 `restoreLatestWindow()` 在读取失败后再次读取.
-   默认情况下,客户端会保留最近使用的10个房间的商店 (时间线、文件列表、针头列表和成员列表)和最近20个 使用线程. 一个呼叫 `rooms.messages`, (中文(简体) ), `rooms.files`, (中文(简体) ), `rooms.pins`,或 `rooms.members` 使用房间;呼叫 `rooms.thread` 只使用线程。 客户 处理旧的仓库; 下一个调用返回新商店, 它 又读了 不要同时使用更多的房间或线程. 留着 设置客户端选项 `keepRecent`,例如 `createClient({ keepRecent: { threads: 50 } })`。 。 。 .
-   已处理的时间表不再更新, 也永远不会更新 。 如果你坚持 跨越漫长的等待时间,例如直到 `isCurrent`时停止 `isDisposed` 成为真实的, 并再次获得时间线.

使用 `readThread` 而不是当你需要一个快照 而没有实时更新.

`rooms.members(roomId)` 在不启动目录的情况下返回成员存储 扫描 打电话 `store.loadRoomMembers(roomId)` 消费者需要完整名单时; 服务器商店将推迟此请求, 直至得知房间访问 。 使用 `members.searchMembers(query)` 不装满 列表中选择一个选项。 空查询返回绑定的建议页。 回收刷新器 只有明确要求的目录.

对于引用,呼叫 `members.retainMentions(handles)` 并释放 当内容离开视图时返回订阅 。 读取 `mentionMembers` 用于当前配置。 这些共享的, 房间扫描的搜索只匹配 页面,不要求完整目录。 缺少的手柄保持纯文本 . 访问和成员更改了丢弃的缓存匹配和旧的栅栏读取.

### 准备和失败

-   `ready()` 当服务器拒绝密钥, 当服务器是 无法到达或失败, 或当服务器发布时不支持此功能 客户端。 背景服务器重试; 调用 `ready()` 再来一次 等待下一次尝试. `run()` 等待通过这些重复.
-   `sessionEnded` 当服务器拒绝或撤销密钥时,则变为真实. `realtimeUnsupported` 当服务器不支持时变为真实 客户端的实时协议. 这两起事件都没有到来,而且事件 循环拒绝; 关闭服务器.
-   一个服务器 `connect` 添加后保留一个持久的 WebSocket, 也在 客户端,有一个选定的活服务器.
-   `server.close()` 从客户端中删除服务器并停止其实时 交货。 飞行中的请求失败, 即使服务器应用了它们, 没有 发出新的请求。 当最后一个服务器 `connect` 增加了关闭, 客户端停止计时器,所以一个节点主机可以退出.

### 请求

服务器和无国籍者 `Api` 拥有同样的请求助手。 每个 请求有十秒的超时, 不自动重试; 失败的写入可以 仍然到达服务器 .

| 帮助 | 对 |
| --- | --- |
| `getMessage({ roomId, messageId })` | 读取一个信件, 包括没有下载地址的附件元数据 |
| `createMessage(destination, body, { inReplyTo })` | 发送一个消息; 返回其 `id` |
| `prepareMessage(destination, body, { inReplyTo })` | 为明确重试准备一个逻辑发送 |
| `postMessage(destination, body)` | 发送任意长度的文本, 以 8000 个代码点为部分 |
| `reply(message, body)` | 在信件线索中标注 |
| `readThread(location, { after, limit })` | 读取线程; `after` 仅读取新信件 |
| `readAttachment({ roomId, attachmentId }, opts)` | 读取文件; `maxImageSize` 更改服务器上的图像大小 |
| `refreshTyping(destination)` | 刷新打字指标一次 |
| `withTyping(destination, work)` | 显示打字指标,同时 `work` 运行 |
| `addReaction({ roomId, messageId }, emoji)` | 对消息的反应 |
| `addressedMessage(event, { reasons })` | 承认一个事件是给观众的讯息 |

请求帮助者接受 `{ signal }` 作为他们最后的论点。 准备的行动 收进去 `send({ signal })`。 。 。 . `postMessage`, (中文(简体) ), `reply`, (中文(简体) ), 财务报告和审定财务报表 `ctx.reply` 返回 `ids` 新信件内容。 其他帮助者包括: `conversationKey`, (中文(简体) ), `replyDestination`, (中文(简体) ), `withTyping` 财务报告和审定财务报表 `startTyping` 页:1 自定义输入更新,以及 `createDeliveryTracker` 用于重放过滤器.

等待最后答复 `withTyping(destination, work)` 发布前 留言。 帮助者停止计时器 等待任何等待更新,与 2秒请求超时。 无法成功刷新 在消息后恢复打字指示符.

信件机构可以包含emoji的短码,例如: `Hello :wave:`编辑 服务器保存此文本。 客户端选择如何显示. 对于定制渲染器, `@chatto/client/messaging/emojiShortcodes` 出口 `resolveEmoji(name)` 页:1 对大小写敏感的内置名称和 `emojiShortcodes(text)` 源区域 有 Unicode 或未知结果。 在Unicode 字母、数字和组合旁的标记 标记或下划线. `emojiShortcodeQuery(textBeforeCursor)` 读取一个部分名称,其自动完成的开放边界相同。 解析 先标出并只扫描出品;留下代码和URL不变. 自定义emoji 未支持资产.

Skin-tone 短码使用 Slack 语法 : `:thumbsup::skin-tone-4:` 显示为 👍🏽. 通音从2 (光)到6 (暗). 支持的混合通配对使用两个 数字在一个后缀中,例如 `:couple::skin-tone-2-6:`不支持的组合 保持文字状态。 反应API没有周围的结肠取同名. 旧服务器可以拒绝这些反应名称;没有连接底部增加值 所需经费。 现有名称和Unicode消息内容保持不变.

`emojiNameFromUnicode(emoji)` 返回选定的稳定反应名称 Unicode emoji,包括其语气. 未知序列返回 `undefined`。 。 。 . 对新选择使用此帮助; 保持现有反应名称不变 .

`mise codegen-emoji` 构建客户端语气映射、服务器映射和转换器映射 (单位:千美元)`tools/emoji-map.json`)来自gemoji和被捆绑的Unicode Emoji 15.0数据. 转换器应该查找此地图中的完整名称, 而不是附加 a 修饰为基格. 生成的地图在运行时不需要网络呼叫 .

对于需要重试安全性的发送,保留准备操作:

```ts
const send = server.prepareMessage(destination, 'Hello');
const { id } = await send.send({ signal });
// After a lost response, call send.send() again with the same operation.
```

每个准备的发送分配一个UUID. 精确重试使用此密钥和相同 参数。 UI 消息 API 也曝光 `prepareMessage(input)` 并保留 上传附件ID. 准备的寄件留在记忆中。 他们不再重复了 距离第一次投球还有30分钟 `MessageSendExpiredError`。 。 。 . 在启动新发送前检查原始结果 。 需要重试安全性 所有用于支持信息创建的复制件; 旧服务器可以 忽略密钥. `postMessage` 财务报告和审定财务报表 `reply` 能够发送多个部件; 重试 整个帮手开始给这些零件发新信.

对于具有可更新证书的服务器,隐私重置无效保留 发送。 他们的下一个 `send()` 掷出 `StaleResponseError` 不再张贴. 在启动新发送前检查原始结果 。 固定托肯寄出还剩 跨隐私重置有效,但服务器关闭后无法发送.

对于任何其他请求, `service(Service)` 创建输入的连接客户端 服务器的认证. `@chatto/client/types` 有协议的 信息和服务,以及 `@chatto/client/types/admin` 行政部门 API : (英语)

```ts
import { UserService } from '@chatto/client/types';

const { user } = await server.service(UserService).getUser({
  target: { case: 'userId', value: authorId }
});
```

### 无国籍请求

`createApi` 提出没有实时连接或保留状态的请求, 例如,在webhook处理器中,或用于在工作结束后可以继续的工作 服务器关闭 :

```ts
import { createApi } from '@chatto/client';

const api = createApi({ serverUrl, apiKey });
const { viewerId } = await api.ready({ signal }); // read once, then reused
await api.reply(message, 'Done', { signal });
```

它使用Connect JSON,拒绝重定向,并装入没有商店. 过 `viewerId` 当主机已经知道密钥的账户时, 例如 从 `server.ready()`· ; `ready()` 然后不发出请求.

### 除错输出

除错输出关闭了外部浏览器. 打电话 `setDebugLogging(true)` 写下来 到操纵台.

## 隐私

只请求联系所配置的服务器 。 每个服务器都收到主机的 IP地址,其证书,以及请求数据. 客户端不登录 符号或信件内容。 客户端移除其私人数据的复制件 认证、授权和隐私界限,并报告这些 与主机的边框; 参见 [东道国国](https://npmcn.com/package/%40chatto/client/#readme-state-of-the-host)。 。 。 .

## 带有UI的应用程序

一个应用程序创建了一个拥有设备存储和源服务器的客户端, 启动运行时间,并报告用户查看的服务器。 只有这个 服务器保留一个持久的 WebSocket; 其它的则通过投票赶上:

```ts
import { createClient } from '@chatto/client';

export const client = createClient({
  storage: 'device', // restore and keep the server catalogue and sessions
  originServer: true, // the page's own server uses its cookie session
  liveServers: 'selected'
});

client.start(); // pair each start() with a stop(), for example on unmount
client.setActiveServer(serverId);
const server = client.server(serverId);
```

一个进程可以有一个客户端和设备存储, 以及一个来源 服务器.

如果主机之前获取发现 `registry.addServer`,将答复作为 `serverInfo` 在第三个参数中避免另一个启动请求. `server.serverInfo.init()` 共享飞行中发现请求。 继续 和解, 读取 `server.serverInfo.discovery` 用于答复,或 `null` 打开 失败。 后一通电话提出了新的要求.

### 本地存储和浏览器登录

本地主机可以通过 `persistence: ServerRegistryPersistence` 改为 `createClient` 改为 `storage: 'device'`此适配器恢复服务器注册和 单独存储认证。 最新的认证记录覆盖 综合目录快照; `null` 是清场记录。 写作 在返回之前必须持续完成。 失败的写入必须丢出或返回 `false` 从 `writeAuthentication`。读取返回必须返回承诺值. 主机拥有加密,访问控制,以及专属流程访问. 别 让两个进程在不进行共同协调的情况下延长同一个保存的会话.

SDK在发送更新前会保存刷新请求ID. 重新启动时 重新使用该标识,然后在替换证书之前保存和核实 返回新访问符 。 更新请求省略 cookie并拒绝 调头 保留所有认证字段,包括 OAuth 客户端ID 和 在持久记录中刷新请求ID .

`@chatto/client/auth/oauth` 提供 `createOAuthFlow`, (中文(简体) ), `isOAuthCallback`,以及 `exchangeOAuthCode` 为本地主机。 供应发现的授权 URL,the 主机的公开 OAuth 客户端 ID, 和一个字面回回回回 HTTP 调用 URL 或内置 `chatto://desktop/servers/callback?mode=popup` 调用。 这个 主机在系统浏览器中打开返回的授权 URL并接收 召回电话。 帮助者使用 S256 PKCE, 交易状态, 精确回调 验证,可选的发行人检查,以及有界限的活字交换. 留着 流动和回调记忆。 永远不要登录他们的代码或核查人。 主机拥有 环回听器、取消、同意UI和客户端元数据文档.

通过交回的证书 `persistedBearerSession`,将其与 `emptyServerSession()`,并向 `registry.addServer`。 。 。 . SDK随后拥有更新和实时状态.

### 本地密码登录

`passwordLogin(origin, { login, password }, { signal })` 返回可再生人类 允许直接密码登录的服务器上的证书。 本地主机供应 他们自己的安全密码域。 请求有15秒的超时和 64 KiB响应限制. 它省略了饼干,拒绝方向调整,并接受HTTPS 或回回回 HTTP 源代码,没有路径、查询或片段.

通过证书 `persistedBearerSession` 从 `@chatto/client/auth/bearerSession`,将其与 `emptyServerSession()` 从 `@chatto/client/server/sessions`,并向 `client.registry.addServer(registration, session)`。然后SDK 拥有符号 更新和实时状态。 此函数不保存密码或会话 . 出错会暴露一个状态和一个固定消息,从不显示服务器响应机构. 远程 `signOutServer` 请求还省略了 cookie 并拒绝重定向 .

### 东道国国

客户端保存服务器数据. 主机保持自己的状态, 如侧边栏 分组、 搜索结果、 调用媒体或查询缓存, 每个服务器旁边 :

-   尽可能从服务器获取衍生状态 。 随 服务器,不需要清理.
-   复制服务器数据的主机必须在服务器上清除副本 边界事件 : `onReset`, (中文(简体) ), `onRoomAccessLost`, (中文(简体) ), `onRoomAccessRestored`, (中文(简体) ), `onUserDeleted`, (中文(简体) ), `onAuthorityChanged`, (中文(简体) ), `onPermissionsChanged`, (中文(简体) ), `onSessionEnded`,以及 `onDispose`。 。 。 . `onUpdate` 报告每个应用事件 资源。 服务器在移除后同步发布每个事件 它的拷贝.
-   向隐私边界投放的收听器失败私有数据 清除:然后服务器不报告其投影为当前 。 这个 服务器等待承诺 `onPermissionsChanged` 听众返回 .
-   `registry.watchStores(setup)` 电话 `setup` 每个服务器,当 注册在服务器收到实时数据之前创建它.

### 丝绒

导入 `@chatto/client/svelte` 一次,在组件读取服务器之前. 斯韦尔特然后用组件读取音轨, `$derived`,以及 `$effect`数字 :

```svelte
<script lang="ts">
  import '@chatto/client/svelte';
  import { client } from '$lib/client';

  let { serverId } = $props();
  const server = $derived(client.server(serverId));
</script>

{#each server?.roomList.rooms ?? [] as room (room.id)}
  <p>{room.name}</p>
{/each}
```

其他框架可以使用 `setReadHook` 财务报告和审定财务报表 `subscribe` 从 `@chatto/client/reactivity` 同样的.

### 语音通话

服务器跟踪其当前呼叫 `projection.activeCalls`客户端 包含没有媒体执行。 连接呼叫的主机执行 并留一个电话到 `onRoomAccessLost` 财务报告和审定财务报表 `onDispose`。 。 。 .

## 反应

软件包有一个小的信号库 `@chatto/client/reactivity`数字 : `signal`, (中文(简体) ), `computed`, (中文(简体) ), `effect`, (中文(简体) ), `effectRoot`, (中文(简体) ), `batch`, (中文(简体) ), `untrack`, (中文(简体) ), `subscribe`, (中文(简体) ), `ReactiveMap`,以及 `ReactiveSet`。计算值是懒惰的. 当最外层写入结束时,效果会同步运行.

## 发展

```sh
mise test-chatto-client   # type checks, lint, and tests
```

`src/types.ts` 财务报告和审定财务报表 `src/types/admin.ts` 从 `@chatto/api-types`· ; `mise codegen-proto` 更新它们和软件包 检查过期时失败 .

见 [维持状态](https://github.com/chattocorp/chatto/blob/main/packages/chatto-client/MAINTAINING.md) 用于包检查和放行指令.

客户端和捆绑生成的协议代码使用Apache-2.0; npm 包包括许可证和 a `NOTICE` 文档.

## 常见问题

### @chatto/client 是什么？

框架无关的Chatto客户端：连接、实时投影和响应式存储

### 如何安装 @chatto/client？

在项目目录运行 npm install @chatto/client，或使用 pnpm add / yarn add / bun add。

### @chatto/client 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @chatto/client 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @chatto/client 可以商用吗？

@chatto/client 使用 Apache-2\.0 许可。宽松许可：允许商业使用、修改和再分发，并附带明确的专利授权；分发时需保留许可与 NOTICE 文件，修改过的文件要注明改动。以上为概括，不构成法律意见，请以许可原文为准。

### @chatto/client 的下载量有多少？

根据 npm 官方下载统计，@chatto/client 在 2026/10/10 – 2026/11/08 这 30 天内被下载 2,514 次，日均约 84 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @chatto/client 还在维护吗？

最新版本 0\.5\.0 发布于 2026/11/09（今天）；至今共发布 17 个版本，其中过去 12 个月发布了 17 个；首次发布于 2026/10/24。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

