@cg-devcenter/rtc-fpnn-webjs-sdk 1.0.0-cg.3 → 1.0.0-cg.4

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.
@@ -4,6 +4,7 @@ declare const sdk: typeof rtm;
4
4
 
5
5
  export default sdk;
6
6
  export const RTMClient: typeof rtm.RTMClient;
7
+ export const createClient: typeof rtm.createClient;
7
8
  export const RTMSharedWorkerClient: typeof rtm.RTMSharedWorkerClient;
8
9
  export const RTMConfig: typeof rtm.RTMConfig;
9
10
  export const RTMProcessor: typeof rtm.RTMProcessor;
package/dist/rtm.vite.mjs CHANGED
@@ -1,4 +1,7 @@
1
- import rtmModule from './rtm.min.js';
1
+ // Use the package export for the UMD bundle so Vite's dependency optimizer
2
+ // provides CommonJS default interop. A relative import from this ESM wrapper
3
+ // is served as native ESM in dev and has no default export.
4
+ import rtmModule from '@cg-devcenter/rtc-fpnn-webjs-sdk/dist/rtm.min.js';
2
5
  import workerUrl from './rtm.shared-worker.js?url';
3
6
 
4
7
  const rtm = rtmModule.default || rtmModule;
@@ -12,9 +15,17 @@ class RTMSharedWorkerClient extends rtm.RTMSharedWorkerClient {
12
15
  }
13
16
  }
14
17
 
15
- const sdk = Object.assign({}, rtm, { RTMSharedWorkerClient });
18
+ function createClient(options) {
19
+ const config = options || {};
20
+ return rtm.createClient(Object.assign({}, config, {
21
+ workerUrl: config.workerUrl || workerUrl
22
+ }));
23
+ }
24
+
25
+ const sdk = Object.assign({}, rtm, { RTMSharedWorkerClient, createClient });
16
26
 
17
27
  export const RTMClient = sdk.RTMClient;
28
+ export { createClient };
18
29
  export { RTMSharedWorkerClient };
19
30
  export const RTMConfig = sdk.RTMConfig;
20
31
  export const RTMProcessor = sdk.RTMProcessor;
@@ -2,14 +2,18 @@
2
2
 
3
3
  本页按接入方的业务目标整理 SDK 能做什么、每项能力对应哪些 API,以及 TLV v1 与 FPNN 的使用入口。具体参数、回调字段和 wire body 以 [SDK API](USAGE.md) 与 [TLV v1 Client API 契约](TLV_CONTRACT.md) 为准;逐 API 的 TLV 支持状态以 [TLV v1 API 支持矩阵](USAGE.md#6-tlv-v1-api-支持矩阵) 为准。
4
4
 
5
+ 如果要从旧 FPNN 接入切换协议,参见 [FPNN 与 TLV v1 迁移对照](PROTOCOL_MIGRATION.md),其中按业务 API 列出 FPNN method 和 TLV URI 的对应关系。
6
+
5
7
  下面的能力地图覆盖 SDK 整体 API;选择具体协议后,结合“协议与能力入口”表确定对应的调用入口和业务范围。
6
8
 
7
9
  ## 能力地图
8
10
 
11
+ 当前源码推荐通过 `createClient()` 默认共享接入:扁平配置,UID 从登录参数取得,SDK 按 PID + 协议 + UID 自动命名并复用连接。`sharing: false` 显式选择独立连接;旧构造器保留原行为。已发布 `.3` 尚无此新入口,配置、错误和生命周期见 [默认共享接入](USAGE.md#默认共享接入当前源码)。
12
+
9
13
  | 业务目标 | SDK 能力 | 常用 API |
10
14
  | --- | --- | --- |
11
15
  | 建立实时连接 | 登录、协议级关闭、主动关闭、自动重连、服务端时间、连接属性、调试日志 | `login`、`bye`、`close`、`startAutoReconnect`、`getServerTime`、`addAttrs`、`getAttrs`、`addDebugLog` |
12
- | 同浏览器多窗口 | 同一 UID 共享一条 RTM WebSocket、共享登录态、按会话路由 Push | `RTMSharedWorkerClient`、`whenReady`、`loginIfNeeded`、`SharedSessionState`、`setConversation` |
16
+ | 同浏览器多窗口 | 同一 UID 共享一条 RTM WebSocket、共享登录态、按会话路由 Push | `createClient`、`RTMSharedWorkerClient`、`whenReady`、`loginIfNeeded`、`SharedSessionState`、`setConversation` |
13
17
  | 维护用户状态 | 查询在线用户、设置/读取个人资料、读取公开资料、设置翻译语言 | `getOnlineUsers`、`setUserInfo`、`getUserInfo`、`getUserOpenInfo`、`setTranslationLanguage` |
14
18
  | 保存用户数据 | 按 key 写入、读取和删除用户存储 | `dataSet`、`dataGet`、`dataDelete` |
15
19
  | 用户关系控制 | 添加/解除黑名单并读取黑名单列表 | `addBlacks`、`deleteBlacks`、`getBlacks` |
@@ -39,10 +43,10 @@
39
43
  | 能力 | `RTMClient` + TLV v1 | `RTMClient` + FPNN | `RTMSharedWorkerClient` |
40
44
  | --- | --- | --- | --- |
41
45
  | 连接环境 | 浏览器;Node.js 需传入 `webSocketFactory` | 浏览器 | 支持 SharedWorker 的浏览器,同源页面 |
42
- | 用户/群组/房间/广播消息 | 支持;按 TLV v1 契约字段校验 | 支持兼容 API | 支持 TLV 已开放 API |
46
+ | 用户/群组/房间/广播消息 | 支持;按 TLV v1 契约字段校验 | 支持兼容 API | 沿用所选协议已开放 API(文件发送除外) |
43
47
  | Push ACK 与去重 | SDK 自动 ACK 可靠 Push,并按 `message_ref` 去重 | 沿用兼容实现 | 单条共享连接只 ACK 一次,再按页面会话路由给各端口 |
44
- | 文件、设备、好友、多媒体审核 | TLV v1 当前未开放的传统 API 不可用 | 兼容 API 可用,依赖对应服务端能力 | 文件发送明确不支持;其余能力以 TLV v1 支持范围为准 |
45
- | 多页面连接共享 | 每个 `RTMClient` 实例各自管理连接 | 每个 `RTMClient` 实例各自管理连接 | 相同 Worker identity 与配置下,同一 UID 共用一条连接 |
48
+ | 文件、设备、好友、多媒体审核 | TLV v1 当前未开放的传统 API 不可用 | 兼容 API 可用,依赖对应服务端能力 | 两种模式都不支持文件发送;FPNN 设备/好友/审核可转发,TLV 按其支持范围 |
49
+ | 多页面连接共享 | 每个 `RTMClient` 实例各自管理连接 | 每个 `RTMClient` 实例各自管理连接 | 相同协议、Worker identity 与配置下,同一 UID 共用一条连接 |
46
50
  | 业务 UI 与消息存储 | 应用负责展示、缓存和历史补拉 | 应用负责展示、缓存和历史补拉 | 只路由实时 Push;不跨页同步草稿、未读 UI 状态或本地消息列表,也不持久化消息 |
47
51
 
48
52
  TLV v1 的能力以 [Client API 契约](TLV_CONTRACT.md) 的 URI 表为准;旧 API 名称存在不代表 TLV 服务端开放了该 wire。保留但未开放的 wire 返回 `200020`(`RTM_EC_FORBIDDEN_METHOD`);未知且未登记的 wire 返回 `20004`(`RTM_EC_UNKNOWN_METHOD`)。`RTMSharedWorkerClient` 的文件发送方法在本地拒绝:callback 收到 `{mid, error}`,Promise 版本 reject 同一对象,`error.code` 为 `200999`(`RTM_EC_UNKNOWN_ERROR`),不发送 wire 请求。以上情况均不会退回 FPNN。SharedWorker 不是另一套 RTC 协议,也不替业务服务端签发 credential。
@@ -51,8 +55,9 @@ TLV v1 的能力以 [Client API 契约](TLV_CONTRACT.md) 的 URI 表为准;旧
51
55
 
52
56
  - SDK 会默认从加载它的页面脚本同目录定位 `rtm.shared-worker.js`;Vite 项目可从 `@cg-devcenter/rtc-fpnn-webjs-sdk/vite` 导入 ESM 入口,由 SDK 自动将 Worker 作为构建资源配置给客户端。部署时 Worker 必须在页面同源下可访问;非 Vite 自定义部署路径时可显式提供 `workerUrl`。
53
57
  - 共享键由浏览器 origin、Worker URL、Worker 名称和完全相同的 `clientOptions` 共同决定;这些值不同的页面不会共用连接。
58
+ - 支持 `clientOptions.protocol: 'tlv-v1'` 或 `'fpnn'`,默认仍为 TLV;FPNN 扩展目前为源码/本地构建能力,已发布 `.3` 尚不包含它。同 UID 使用不同协议会建立独立连接。
54
59
  - 一个 Worker 只登录一个 UID。不同 UID 同时在线必须分别建立 Worker/RTC 连接;单 UID 的多页面共享登录态。
55
- - 每个页面有独立的 Push 监听端口。配置 `conversation` 后,SDK 按 direct sender、group ref 或 room ref 过滤;未配置时该页面接收全部会话 Push。
60
+ - 每个页面有独立的 Push 监听端口。配置 `conversation` 后,SDK 按所选协议提取 direct sender、group ref 或 room ref 后过滤,保留原协议事件名和数据;未配置时该页面接收全部会话 Push。
56
61
  - SDK 负责连接、重连、Push ACK/去重和端口分发;业务仍负责消息展示、历史补拉、业务未读规则、草稿与页面状态。
57
62
  - 登录态保存在 Worker 内存。Worker 被浏览器回收或空闲回收完成后,需要业务重新创建 client 并登录;SDK 不持久化 credential,也不承诺把断线期间未投递的 Push 自动补齐。
58
63
  - `destroy()` 只 detach 当前页面;最后一个页面 detach 后默认等待 5 秒再销毁共享 RTMClient,期间有新页面接入会取消回收。可通过 `idleShutdownTimeout` 调整,设为 0 表示立即关闭;同一个 Worker 的页面须使用相同值。`bye()` / `close()` 会结束所有共用页面的 RTC 会话。共享 Worker 初始化失败时不会静默降级为每页单独连接。
@@ -119,6 +124,7 @@ const result = await client.promises.sendChat(peerUid, 'hello', '{}', 0, 12000);
119
124
 
120
125
  - [快速开始与引用](../README.md)
121
126
  - [公开 API 参数](USAGE.md)
127
+ - [FPNN 与 TLV v1 迁移对照](PROTOCOL_MIGRATION.md)
122
128
  - [TLV v1 URI、字段和返回值](TLV_CONTRACT.md)
123
129
  - [TLV v1 frame 与编解码](TLV.md)
124
130
  - [在线验证与业务套件](TLV_LIVE_E2E_TEST.md)
@@ -0,0 +1,146 @@
1
+ # FPNN 与 TLV v1 协议迁移对照
2
+
3
+ 本文说明如何在现有 `RTMClient` 接入中选择 FPNN 或 TLV v1,并对照 SDK 业务 API、旧 FPNN wire method 与 TLV URI。
4
+
5
+ ## 先看结论
6
+
7
+ - `RTMClient` 是两种协议共用的业务 API 门面。TLV v1 已实现的接口由 SDK 在内部把旧 method 和字段转换成 TLV URI 与字段;业务代码不需要自己发送 TLV frame,也不应直接调用这些 wire 名称。
8
+ - 协议在创建客户端时选择:`protocol: 'fpnn'` 或 `protocol: 'tlv-v1'`。一个客户端实例不会按请求混用两种协议。
9
+ - 业务 API 名称和调用方式在支持范围内保持一致,但这不保证每个返回对象、错误对象、服务端权限或业务语义都完全相同。切换前请对照本页的差异说明和 [TLV v1 Client API 契约](TLV_CONTRACT.md)。
10
+ - TLV v1 未支持的 FPNN API 不会自动回退到 FPNN。完整支持边界见 [接入指南的支持矩阵](USAGE.md#6-tlv-v1-api-支持矩阵)。
11
+
12
+ ## 选择协议
13
+
14
+ 现有 FPNN 客户端通常使用 `endpoint`(FPNN Gate 地址)创建;保留这套配置时显式指定 `protocol: 'fpnn'`,或依赖兼容默认值:
15
+
16
+ ```js
17
+ const client = new rtm.RTMClient({
18
+ protocol: 'fpnn',
19
+ pid,
20
+ endpoint: fpnnEndpoint,
21
+ });
22
+ ```
23
+
24
+ 迁移到 TLV v1 时,将连接地址换成部署方提供的完整 WebSocket URL,并在创建时选择 TLV:
25
+
26
+ ```js
27
+ const client = new rtm.RTMClient({
28
+ protocol: 'tlv-v1',
29
+ pid,
30
+ tlvEndpoint: 'wss://<gate-host>/service/websocket',
31
+ autoReconnect: true,
32
+ });
33
+
34
+ await client.promises.login(uid, credential);
35
+ await client.promises.sendChat(peerUid, 'hello', '{}');
36
+ ```
37
+
38
+ `credential` 仍须由业务服务端或受控签发工具为当前 UID 签发。不要把项目 `SecretKey` 放入浏览器。其余连接、登录、发送、监听 Push 的业务调用可以继续使用同一套 `RTMClient` API;若使用 `RTMSharedWorkerClient`,默认仍为 TLV v1,当前源码也支持显式选择 FPNN;已发布 `.3` 仅支持 TLV。SharedWorker 配置和能力边界见 [FPNN 多窗口接入](USAGE.md#fpnn-多窗口接入)。
39
+
40
+ ## API 与 wire 名称对照
41
+
42
+ “FPNN method”列是 SDK 内部兼容接口使用的旧请求 method 名称,用于帮助查旧 FPNN 文档和服务端日志。它不是 TLV 请求名,也不是建议业务直接调用的 API。TLV 连接实际发送右侧的 URI。
43
+
44
+ ### 连接、用户与存储
45
+
46
+ | RTMClient 业务 API | FPNN method | TLV v1 URI |
47
+ | --- | --- | --- |
48
+ | `login` | `auth` | `auth.login` |
49
+ | `bye` | `bye` | `connection.close` |
50
+ | `getServerTime` | `getservertime` | `system.time` |
51
+ | `addAttrs` / `getAttrs` | `addattrs` / `getattrs` | `connection.set_props` / `connection.get_props` |
52
+ | `addDebugLog` | `adddebuglog` | `system.debug` |
53
+ | `getOnlineUsers` | `getonlineusers` | `presence.online` |
54
+ | `setUserInfo` / `getUserInfo` | `setuserinfo` / `getuserinfo` | `user.set_profile` / `user.get_profile` |
55
+ | `getUserOpenInfo` | `getuseropeninfo` | `user.get_public` |
56
+ | `setTranslationLanguage` | `setlang` | `user.set_locale` |
57
+ | `dataGet` / `dataSet` / `dataDelete` | `dataget` / `dataset` / `datadel` | `storage.get` / `storage.set` / `storage.delete` |
58
+ | `addBlacks` / `deleteBlacks` / `getBlacks` | `addblacks` / `delblacks` / `getblacks` | `blacklist.add` / `blacklist.remove` / `blacklist.list` |
59
+
60
+ ### 群组与房间
61
+
62
+ | RTMClient 业务 API | FPNN method | TLV v1 URI |
63
+ | --- | --- | --- |
64
+ | `addGroupMembers` / `deleteGroupMembers` | `addgroupmembers` / `delgroupmembers` | `group.add` / `group.remove` |
65
+ | `getGroupMembers` / `getGroupCount` | `getgroupmembers` / `getgroupcount` | `group.members` / `group.count` |
66
+ | `getUserGroups` | `getusergroups` | `group.joined` |
67
+ | `setGroupInfo` / `getGroupInfo` | `setgroupinfo` / `getgroupinfo` | `group.set_profile` / `group.get_profile` |
68
+ | `getGroupOpenInfo` / `getGroupsOpenInfo` | `getgroupopeninfo` / `getgroupsopeninfo` | `group.get_public` / `group.get_public_batch` |
69
+ | `enterRoom` / `leaveRoom` / `enterRooms` | `enterroom` / `leaveroom` / `enterrooms` | `room.join` / `room.leave` / `room.join_batch` |
70
+ | `getUserRooms` / `getRoomMembers` / `getRoomCount` | `getuserrooms` / `getroommembers` / `getroomcount` | `room.joined` / `room.members` / `room.counts` |
71
+ | `setRoomInfo` / `setRoomProfile` | `setroominfo` | `room.set_profile` |
72
+ | `getRoomInfo` / `getRoomOpenInfo` / `getRoomsOpenInfo` | `getroominfo` / `getroomopeninfo` / `getroomsopeninfo` | `room.get_profile` / `room.get_public` / `room.get_public_batch` |
73
+ | `getUserRoomsAndLastMessage` | `getuserroomsandlastmsg` | `room.last_messages` |
74
+
75
+ ### 消息、审核、翻译与历史
76
+
77
+ | RTMClient 业务 API | FPNN method | TLV v1 URI |
78
+ | --- | --- | --- |
79
+ | `sendMessage`、`sendChat`、`sendAudio`、`sendCmd` | `sendmsg` | `message.send` |
80
+ | `sendMessages` | `sendmsgs` | `message.send_batch` |
81
+ | `sendGroupMessage`、`sendGroupChat`、`sendGroupAudio`、`sendGroupCmd` | `sendgroupmsg` | `message.send_group` |
82
+ | `sendRoomMessage`、`sendRoomChat`、`sendRoomAudio`、`sendRoomCmd` | `sendroommsg` | `message.send_room` |
83
+ | `textCheck` | `tcheck` | `moderation.check` |
84
+ | `translate` | `translate` | `translation.run` |
85
+ | `getP2PMessage` / `getP2PChat` | `getp2pmsg` | `history.direct` |
86
+ | `getGroupMessage` / `getGroupChat` | `getgroupmsg` | `history.group` |
87
+ | `getRoomMessage` / `getRoomChat` | `getroommsg` | `history.room` |
88
+ | `getBroadcastMessage` / `getBroadcastChat` | `getbroadcastmsg` | `history.broadcast` |
89
+ | `getP2PMessageByMessageId` / `getGroupMessageByMessageId` | `getp2pmsgbymessageid` / `getgroupmsgbymessageid` | `history.direct_anchor` / `history.group_anchor` |
90
+ | `getRoomMessageByMessageId` / `getBroadcastMessageByMessageId` | `getroommsgbymessageid` / `getbroadcastmsgbymessageid` | `history.room_anchor` / `history.broadcast_anchor` |
91
+ | `getP2PMessageCount` / `getGroupMessageCount` | `getp2pmsgcount` / `getgroupmsgcount` | `history.direct_count` / `history.group_count` |
92
+ | `getRoomMessageCount` / `getBroadcastMessageCount` | `getroommsgcount` / `getbroadcastmsgcount` | `history.room_count` / `history.broadcast_count` |
93
+ | `getMessage` / `getChat` | `getmsg` | `message.get` |
94
+ | `deleteMessage` / `deleteChat` | `delmsg` | `message.revoke` |
95
+
96
+ ### 未读、会话与会话列表
97
+
98
+ | RTMClient 业务 API | FPNN method | TLV v1 URI |
99
+ | --- | --- | --- |
100
+ | `getUnreadMessage` | `getunread` | `unread.summary` |
101
+ | `getSession` | `getsession` | `session.list` |
102
+ | `cleanUnreadMessage` | `cleanunread` | `unread.clear` |
103
+ | `setSessionRead` | `setsessionread` | `session.read` |
104
+ | `getP2PUnreadMessageNum` / `getGroupUnreadMessageNum` | `getp2punread` / `getgroupunread` | `unread.direct` / `unread.group` |
105
+ | `removeSession` | `removesession` | `session.remove` |
106
+ | `getP2PConversationList` / `getP2PUnreadConversationList` | `getp2pconversationlist` / `getp2punreadconversationlist` | `conversation.direct` / `conversation.direct_unread` |
107
+ | `getGroupConversationList` / `getGroupUnreadConversationList` | `getgroupconversationlist` / `getgroupunreadconversationlist` | `conversation.group` / `conversation.group_unread` |
108
+ | `getUnreadConversationList` | `getunreadconversationlist` | `conversation.unread` |
109
+
110
+ ### 服务端 Push 事件
111
+
112
+ 两种协议的 Push 名称和字段也不同。按所选协议使用对应的 `RTMConfig` 常量订阅;不要把常量的字符串值写死在业务代码里。
113
+
114
+ | Push 场景 | FPNN 事件名 | TLV v1 事件名 | TLV 常量 |
115
+ | --- | --- | --- | --- |
116
+ | 一对一消息 | `pushmsg` | `event.direct` | `RTMConfig.TLV_SERVER_PUSH.recvMessage` |
117
+ | 群组消息 | `pushgroupmsg` | `event.group` | `RTMConfig.TLV_SERVER_PUSH.recvGroupMessage` |
118
+ | 房间消息 | `pushroommsg` | `event.room` | `RTMConfig.TLV_SERVER_PUSH.recvRoomMessage` |
119
+ | 广播消息 | `pushbroadcastmsg` | `event.broadcast` | `RTMConfig.TLV_SERVER_PUSH.recvBroadcastMessage` |
120
+ | 当前账号被踢下线 | `kickout` | `event.kicked` | `RTMConfig.TLV_SERVER_PUSH.kickOut` |
121
+ | 从房间移除 | `kickoutroom` | `event.room_removed` | `RTMConfig.TLV_SERVER_PUSH.kickOutRoom` |
122
+
123
+ ## 切换时要核对的差异
124
+
125
+ ### 参数与返回字段
126
+
127
+ TLV URI 使用自己的字段名和 schema。例如 FPNN `sendmsg` 的 `to / mid / mtype / msg / attrs` 在 TLV `message.send` 中映射为 `recipient / message_ref / kind / payload / extra`;历史查询的 `begin / end / lastid / num / desc` 映射为 `start_ms / end_ms / cursor / limit / reverse`。SDK 执行映射,业务代码仍传 `RTMClient` 方法参数。各 URI 的字段、类型、默认值和 Gate Answer 见 [TLV v1 Client API 契约](TLV_CONTRACT.md)。
128
+
129
+ Push 事件也有协议差异:名称见上表,事件字段与 FPNN wire payload 不完全相同。业务侧应依照当前协议订阅并按契约处理事件;不要将 FPNN Push 字段表直接套用到 TLV。FPNN 订阅名见 [事件处理说明](EventProcess.md),TLV Push 字段见 [TLV v1 Client API 契约](TLV_CONTRACT.md#控制-uri-与-push)。
130
+
131
+ ### API 支持范围
132
+
133
+ `protocol: 'tlv-v1'` 只覆盖 [TLV v1 支持矩阵](USAGE.md#6-tlv-v1-api-支持矩阵)中标为支持的 API。常见未开放项包括文件(`filetoken`、`sendfile` 等)、好友(`addfriends`、`getfriends`)、设备 Push 设置、多媒体审核,以及传统 RTM 房间控制类接口。调用未开放 wire 不会切换到 FPNN:保留但未开放的 wire 会返回 `200020`,未知 wire 会返回 `20004`;SharedWorker 文件发送在本地拒绝。错误细节见 [能力总览](CAPABILITIES.md#协议与能力入口) 和支持矩阵。
134
+
135
+ ### 登录与错误处理
136
+
137
+ `login` callback 仍按 `(accepted, errorCode)` 判断是否成功;Promise 登录失败会 reject。TLV 的具体鉴权错误码和仅收到拒绝应答时的默认错误码见 [接入指南错误约定](USAGE.md#callbackpromise-与错误)。TLV 消息发送失败的错误对象结构也有专门约定,不要假设所有协议的业务错误都有同一种形状。
138
+
139
+ **发布前应按业务实际使用的 API 做回归验证**:登录/重连、消息收发与 Push、历史分页、未读/会话查询、业务错误处理及服务端权限。成功建立连接不代表 Gate 已开放所有 URI。
140
+
141
+ ## 相关文档
142
+
143
+ - [TLV v1 接入指南与 API 参数](USAGE.md)
144
+ - [TLV v1 API、字段与返回值契约](TLV_CONTRACT.md)
145
+ - [SDK 能力与协议边界](CAPABILITIES.md)
146
+ - [FPNN 兼容 API 旧说明](Users.md)、[消息](Messages.md)、[聊天](Chat.md)、[群组](Groups.md)、[房间](Rooms.md)
package/docs/USAGE.md CHANGED
@@ -8,10 +8,12 @@
8
8
  | --- | --- | --- |
9
9
  | `RTMClient` + `protocol: 'tlv-v1'` | 浏览器;Node.js 需注入 `webSocketFactory` | 本文 TLV v1 支持矩阵列出的 API。新接入推荐此入口。 |
10
10
  | `RTMClient` + `protocol: 'fpnn'` | 浏览器 | 旧版兼容入口;兼容 API 仍受 FPNN 服务端能力和部署配置限制。 |
11
- | `RTMSharedWorkerClient` | 支持 SharedWorker 的浏览器、同源页面 | TLV v1;多个同源页面共享一个 UID 的 RTC 会话,并由 SDK 路由实时 Push。 |
11
+ | `RTMSharedWorkerClient` | 支持 SharedWorker 的浏览器、同源页面 | TLV v1 / FPNN;多个同源页面共享一个 UID 的 RTC 会话,并由 SDK 路由实时 Push。 |
12
12
 
13
13
  本文给出 TLV v1 方法参数和常见返回约定,不重复 TLV wire 字段的完整 schema。TLV Gate Answer 字段、数据类型和状态码以[TLV v1 Client 契约](TLV_CONTRACT.md)为准;TypeScript 完整声明见 [`dist/rtm.d.ts`](../dist/rtm.d.ts)。`fpnn` 的具体兼容范围以旧服务端为准;旧接口参数说明见[用户](Users.md)、[群组](Groups.md)、[房间](Rooms.md)、[消息](Messages.md)、[聊天](Chat.md)、[数据](Data.md)、[系统](System.md)、[文件](Files.md)、[好友](Friends.md)、[增值服务](ValueAdded.md)和[事件处理](EventProcess.md)文档。
14
14
 
15
+ 需要从 FPNN 迁移到 TLV v1 时,先看 [协议迁移对照](PROTOCOL_MIGRATION.md):它列出了相同 `RTMClient` 业务 API 对应的旧 FPNN method 与 TLV URI,并标明支持边界和兼容差异。
16
+
15
17
  ### 交给测试人员或 AI 接入前
16
18
 
17
19
  请随文提供以下环境信息,避免接入者猜测项目配置或尝试从浏览器获取项目密钥:
@@ -26,6 +28,69 @@
26
28
 
27
29
  ## 2. 快速开始:登录、收 Push、发消息、关闭
28
30
 
31
+ ### 默认共享接入(当前源码)
32
+
33
+ 新入口 `createClient(options)` 默认启用 SharedWorker,默认使用 TLV;显式 `protocol: 'fpnn'` 也可共享。**已发布的 `1.0.0-cg.3` 尚不包含此入口和 FPNN 共享扩展**,请使用本地构建/yalc 或后续版本。旧 `RTMClient`、`RTMSharedWorkerClient` 的构造参数和行为保留。
34
+
35
+ Vite 项目从 `/vite` 导入,普通 HTML 把 SDK 与 Worker bundle 部署到同源目录;业务不需要填写 Worker 名称。下面可作为 Vite 页面脚本使用,替换项目配置和 credential 接口:
36
+
37
+ ```js
38
+ import rtm from '@cg-devcenter/rtc-fpnn-webjs-sdk/vite';
39
+
40
+ const uid = '10001';
41
+ const peerUid = '10002';
42
+ const client = rtm.createClient({
43
+ pid: 12345,
44
+ tlvEndpoint: 'wss://<gate-host>/service/websocket',
45
+ autoReconnect: true,
46
+ });
47
+
48
+ client.processor.on(rtm.RTMConfig.TLV_SERVER_PUSH.recvMessage, (event) => {
49
+ console.log('Received', event);
50
+ });
51
+ client.on('SharedSessionState', (session) => console.log('Session', session));
52
+ client.on('SharedWorkerError', (error) => console.error('Shared connection', error));
53
+
54
+ async function start() {
55
+ // 按 UID 挂接共享会话,尚未登录时不会建立 Gate WebSocket。
56
+ const session = await client.whenReady(uid);
57
+ let credential;
58
+ if (session.status === 'idle') {
59
+ const response = await fetch('/api/rtc/credential?uid=' + encodeURIComponent(uid), {
60
+ credentials: 'same-origin',
61
+ });
62
+ if (!response.ok) throw new Error('Credential request failed: ' + response.status);
63
+ credential = (await response.json()).credential;
64
+ if (!credential) throw new Error('Credential is missing');
65
+ }
66
+ await client.promises.login(uid, credential);
67
+ await client.promises.sendChat(peerUid, 'hello', '{}');
68
+ }
69
+
70
+ start().catch(console.error);
71
+ // 页面离开由 SDK 自动 detach;组件卸载时也可 client.destroy()。
72
+ ```
73
+
74
+ 已持有 credential 时直接 `client.promises.login(uid, credential)` 即可,SDK 从登录参数取 UID,无需先调用 `whenReady()`。第一次登录需要服务端 credential;后续同 UID 页面可省略。`on()` / `processor.on()` 可在登录之前订阅,不需要业务缓存监听器。
75
+
76
+ UID 使用正整数或十进制字符串;超过 JavaScript 安全整数范围时必须用字符串,最大为 uint64。SDK 规范化前导零并转为协议整数,不会把 UID 字符串直接发给 FPNN。无效或超范围的 UID 返回 `RTM_SHARED_UID_INVALID`,缺少 UID 返回 `RTM_SHARED_UID_REQUIRED`。
77
+
78
+ | 配置 / API | 默认与边界 |
79
+ | --- | --- |
80
+ | `createClient(options)` | 使用扁平客户端配置,默认 `sharing: true`、`protocol: 'tlv-v1'`;浏览器没有 SharedWorker 时明确抛出 `RTM_SHARED_WORKER_UNSUPPORTED`。 |
81
+ | `sharing: false` | 返回独立的 `RTMClient`,新入口仍默认 TLV;Node.js 或需要文件上传/函数型配置时显式选择,FPNN 仍仅支持浏览器。不会因共享初始化失败自动降级。 |
82
+ | `uid` | 可选;提供时立即挂接共享 Worker,适合收件箱自动观察其他页面登录。省略时在 `login()` / `loginIfNeeded()` / `whenReady(uid)` 时挂接。一个实例固定一个 UID;切换账号需 detach 后创建新实例。 |
83
+ | `workerName` / `workerUrl` | 可选高级覆盖项;默认名字由 PID、协议、UID 生成,不包含 endpoint。默认路径沿用脚本同目录规则,Vite `/vite` 自动打包。`workerNamespace` 仍可隔离应用。 |
84
+ | `whenReady(uid?)` | 返回本页挂接后的共享会话快照。尚未绑定 UID 且未传 UID 时立即 reject `RTM_SHARED_UID_REQUIRED`,不会无限等待。 |
85
+ | `promises.login(uid, credential?, timeout?)` | 成功 resolve `{accepted: true, errorCode}`,RTC 拒绝或本地/Worker 错误 reject;并发同 UID 登录只发一次鉴权。`loginIfNeeded()` 保留 `{accepted, errorCode, reused, uid}` 的结果约定。 |
86
+ | `login(uid, credential?, callback?, timeout?)` | RTC 拒绝与本地/Worker 失败均回调 `callback(false, numericCode)`;本地/Worker 失败的详细错误通过 `SharedWorkerError` 事件提供。仅 `accepted === true` 表示登录成功;`promises.login()` 会 reject 详细错误。 |
87
+ | `destroy()` | 只 detach 当前页面;最后一页离开后按 `idleShutdownTimeout` 回收共享连接。`bye()` / `close()` 才结束所有共享页面的会话。 |
88
+ | 会话路由与业务 API | 沿用 `setConversation()`、`processor`、重连和原协议的业务返回;共享模式不支持文件发送或函数型配置。绑定 UID 前的业务请求明确报 `RTM_SHARED_UID_REQUIRED`。 |
89
+
90
+ 共享还要求同一浏览器配置文件、origin 和 Worker URL。**同 PID、协议、UID 的页面必须使用相同服务端地址和 `clientOptions`**;地址、鉴权配置、重连策略或其他参数不同会初始化失败,不会覆盖已有连接。不同 PID、协议或 UID 自动分开。新入口的默认名称与旧 `RTMSharedWorkerClient` 的 endpoint 参与命名规则不同:同 UID 的各页应统一使用新入口,或显式传相同 Worker 名称和配置,不要在部分页面切换后假定已经共享。
91
+
92
+ ### 原有单连接接入
93
+
29
94
  先通过业务服务端为当前 UID 签发 credential。浏览器不能持有项目 SecretKey。下面的 `/api/rtc/credential` 是业务服务端接口占位,请替换成你们自己的鉴权接口;它应返回 `{ "credential": "..." }`。
30
95
 
31
96
  将 SDK 浏览器 bundle 放入页面后,这段 HTML 可直接作为最小接入模板。替换 `pid`、`tlvEndpoint` 和用户 ID,并让 credential 接口能签发发送 UID 的 credential,即可连到真实 Gate:
@@ -185,17 +250,17 @@ Promise 成功表示 Gate 已应答关闭请求。若要直接断开,可调用
185
250
 
186
251
  ## 4. 同 UID 多窗口:SharedWorker
187
252
 
188
- `RTMSharedWorkerClient` 是 SDK 提供的同源多页面连接复用能力。每个页面创建一个客户端端口,SDK 在 SharedWorker 内维护一个 `RTMClient` 和一条 WebSocket。普通浏览器脚本加载时,SDK 默认从主 SDK 脚本地址推导同目录下的 `rtm.shared-worker.js`;部署在其他位置时,需显式传入同源 `workerUrl`。Vite 项目推荐从 `@cg-devcenter/rtc-fpnn-webjs-sdk/vite` 导入,SDK 会自动把 Worker 作为构建资源并配置地址。其他构建工具可在其资源管线支持时导入 `@cg-devcenter/rtc-fpnn-webjs-sdk/shared-worker?url`,再将解析出的 URL 传给 `workerUrl`。共享身份需要相同浏览器配置文件、origin、解析后的 Worker URL、`workerName` 和结构一致的 `clientOptions`。一个 Worker 只登录一个 UID;传入 `uid` 后 SDK 会根据项目、UID、endpoint 和可选 `workerNamespace` 生成稳定名称。
253
+ `RTMSharedWorkerClient` 是 SDK 提供的同源多页面连接复用能力。每个页面创建一个客户端端口,SDK 在 SharedWorker 内维护一个 `RTMClient` 和一条 WebSocket。普通浏览器脚本加载时,SDK 默认从主 SDK 脚本地址推导同目录下的 `rtm.shared-worker.js`;部署在其他位置时,需显式传入同源 `workerUrl`。Vite 项目推荐从 `@cg-devcenter/rtc-fpnn-webjs-sdk/vite` 导入,SDK 会自动把 Worker 作为构建资源并配置地址。其他构建工具可在其资源管线支持时导入 `@cg-devcenter/rtc-fpnn-webjs-sdk/shared-worker?url`,再将解析出的 URL 传给 `workerUrl`。共享身份需要相同浏览器配置文件、origin、解析后的 Worker URL、`workerName` 和结构一致的 `clientOptions`。一个 Worker 只登录一个 UID、使用一种协议;传入 `uid` 后 SDK 会根据协议、项目、UID、endpoint 和可选 `workerNamespace` 生成稳定名称。TLV 自动名称沿用此前规则,FPNN 名称单独隔离。
189
254
 
190
255
  | API / 选项 | 参数与结果 |
191
256
  | --- | --- |
192
- | `new RTMSharedWorkerClient(options)` | `clientOptions` 必填,至少提供 `pid` 和完整 `tlvEndpoint`;SharedWorker 固定使用 TLV v1。`workerUrl` 可选,同源 Worker 脚本 URL;普通浏览器脚本省略时 SDK 推导加载 SDK 的脚本同目录下的 `rtm.shared-worker.js`,Vite 项目应使用 `/vite` 入口自动打包 Worker。提供 `uid` 且省略 `workerName` 时,SDK 根据 `clientOptions.pid`、`uid`、endpoint 和 `workerNamespace` 生成稳定名称;也可调用 `createSharedWorkerName({pid, uid, endpoint, namespace})` 自行取得名称。若 `uid` 和 `workerName` 都省略,会使用通用默认名称;多账号应用应传其中一个,避免不同 UID 页面加入同一个 Worker。`readyTimeout` 默认 10000 毫秒;`idleShutdownTimeout` 默认 5000 毫秒,最后一个页面 detach 后等待这段时间再关闭共享连接,设为 `0` 表示立即关闭;同一 Worker 的所有页面必须使用相同值。`conversation` 可选,默认 `null` 接收全部会话 Push。 |
257
+ | `new RTMSharedWorkerClient(options)` | `clientOptions` 必填;`protocol` 省略时仍为 TLV v1,提供 `pid` 和完整 `tlvEndpoint`。选择 FPNN 时显式设置 `protocol: 'fpnn'`,并提供 `endpoint` 或 `ssl_endpoint`(`host[:port]`,不含 URL scheme 和路径)。`workerUrl` 可选,同源 Worker 脚本 URL;普通浏览器脚本省略时 SDK 推导加载 SDK 的脚本同目录下的 `rtm.shared-worker.js`,Vite 项目应使用 `/vite` 入口自动打包 Worker。提供 `uid` 且省略 `workerName` 时,SDK 根据协议、`clientOptions.pid`、`uid`、实际连接地址和 `workerNamespace` 生成稳定名称;也可调用 `createSharedWorkerName({protocol, pid, uid, endpoint, namespace})` 自行取得名称。若 `uid` 和 `workerName` 都省略,会使用通用默认名称;多账号应用应传其中一个,避免不同 UID 页面加入同一个 Worker。`readyTimeout` 默认 10000 毫秒;`idleShutdownTimeout` 默认 5000 毫秒,最后一个页面 detach 后等待这段时间再关闭共享连接,设为 `0` 表示立即关闭;同一 Worker 的所有页面必须使用相同值。`conversation` 可选,默认 `null` 接收全部会话 Push。 |
193
258
  | `whenReady()` | 等 Worker 初始化并返回 `{status, uid}` 快照。初始化失败时 Promise reject,SDK 会自动 detach 并关闭当前页面的端口。 |
194
259
  | `getSharedSession()` | 同步返回本页面最近观察到的 `{status, uid}`。 |
195
260
  | `loginIfNeeded(uid, credential?, timeout?)` | 同 UID 会话已登录则复用;登录中则加入该登录;会话空闲时须提供服务端签发的 credential。resolve `{accepted, errorCode, reused, uid}`。 |
196
261
  | `setConversation(conversation, callback?)` | 修改当前页面 Push 路由;传 `null` 接收全部会话。出错时通过可选 callback 返回。 |
197
262
  | `destroy()` | detach 当前页面端口;Worker 仍有其他页面时不关闭 RTC 连接。它会完成端口与监听器清理后再传播待处理业务回调抛出的异常。 |
198
- | `shared.promises.*` / `shared.*` | 可调用 TLV 支持矩阵中的业务 API,参数、callback 和返回约定与 `RTMClient` 相同。`bye()` / `close()` 影响所有共享页面。 |
263
+ | `shared.promises.*` / `shared.*` | 可调用所选协议的 `RTMClient` 业务 API,参数、callback、返回对象和 Push 事件均沿用该协议;两种协议都不支持 `sendFile` / `sendGroupFile` / `sendRoomFile`。`bye()` / `close()` 影响所有共享页面。 |
199
264
 
200
265
  普通浏览器脚本部署时,把 `dist/rtm.min.js` 与 `dist/rtm.shared-worker.js` 放在同源静态目录的同一目录,SDK 会自动定位 Worker 文件。Vite 项目从包的 `/vite` 入口导入;SDK 会将 Worker 作为构建资源输出并自动配置地址,无需手动设置 `workerUrl`。其他构建工具需保证 Worker 最终同源可访问,并在自动推导不适用时显式传入 `workerUrl`。以下示例展示页面接入、首次登录、后续页面复用、Push 监听和当前页 detach。与快速开始相同,替换项目参数和 credential 接口:
201
266
 
@@ -257,6 +322,40 @@ connectInbox().catch(console.error);
257
322
  - SharedWorker 仅共享实时连接和 Push,不同步页面草稿、未读 UI 或消息列表,也不持久化登录态。它不跨 origin、浏览器配置文件或 InPrivate 窗口共享;不支持时 SDK 不静默降级为每页一条连接。
258
323
  - `clientOptions` 必须可结构化克隆;不能包含 `requestMetadataProvider`、元数据函数或 `webSocketFactory`。SharedWorker 只接受静态 `requestMetadata` 对象。
259
324
 
325
+ ### FPNN 多窗口接入
326
+
327
+ 此扩展目前在源码和本地构建中提供;已发布的 `1.0.0-cg.3` SharedWorker 仍仅支持 TLV,使用 FPNN 共享模式需要新版本或本地打包产物。Node.js 仍只支持 TLV。
328
+
329
+ 首次登录、后续窗口 `loginIfNeeded()`、`SharedSessionState`、路由和 detach 的使用方式与上面的 TLV 示例相同。FPNN 只改变连接配置和 Push 订阅;以下片段假定已加载 `rtm` 并取得 `pid`、`uid`、`peerUid`、FPNN Gate 地址及首次登录所需的 credential:
330
+
331
+ ```js
332
+ const shared = new rtm.RTMSharedWorkerClient({
333
+ uid,
334
+ conversation: { type: 'direct', id: peerUid },
335
+ clientOptions: {
336
+ protocol: 'fpnn', // 显式选择;省略时仍是 TLV。
337
+ pid,
338
+ ssl_endpoint: 'gate.example:13321', // wss;ws 使用 endpoint: 'gate.example:13321'
339
+ autoReconnect: true,
340
+ },
341
+ });
342
+
343
+ // FPNN 文本聊天使用 recvChat,通用消息使用 recvMessage。
344
+ shared.processor.on(rtm.RTMConfig.SERVER_PUSH.recvChat, (event) => {
345
+ console.log('FPNN chat', event.from.toString(), event.msg);
346
+ });
347
+ shared.on('SharedSessionState', (state) => console.log('Shared session', state));
348
+ await shared.whenReady();
349
+ const result = await shared.loginIfNeeded(uid, credential);
350
+ if (!result.accepted) throw new Error('RTC login failed: ' + result.errorCode);
351
+ await shared.promises.sendChat(peerUid, 'hello', '{}');
352
+ // 页面离开时 SDK 自动 detach;也可显式 shared.destroy()。
353
+ ```
354
+
355
+ FPNN 的 `endpoint` / `ssl_endpoint` 接收 `host[:port]`,SDK 补上 `ws://` / `wss://` 和 `/service/websocket`;它与 TLV 的完整 URL 配置不同。自动 Worker identity 使用实际连接地址,同 UID 的 FPNN / TLV 页面不共享一条连接。若显式指定相同 `workerName` 却传入不同协议或配置,后加入页面初始化失败,不会覆盖已有会话。
356
+
357
+ FPNN Push 保留 `SERVER_PUSH` 事件和 `from / gid / rid / mid / msg` 等字段;TLV 保留 `TLV_SERVER_PUSH` 事件及其字段。共享层只提取内部会话路由,不把 FPNN 业务数据包装成 TLV,也不执行额外的 ACK 或去重。FPNN 的聊天、命令、通用消息、文件接收事件均按会话过滤;广播和被踢下线事件发给所有页面。`sendFile` / `sendGroupFile` / `sendRoomFile` 在两种模式下都本地拒绝,文件接收和 FPNN `fileToken` 查询不在此限制内。传统参数与 Push 细节见 [协议迁移对照](PROTOCOL_MIGRATION.md) 和 [事件处理说明](EventProcess.md)。
358
+
260
359
  ## 5. 参数与返回值约定
261
360
 
262
361
  下表解释多个 API 复用的参数;详细 wire 字段、返回字段和 TLV 类型请参照[契约表](TLV_CONTRACT.md)。
@@ -413,7 +512,7 @@ Push 用 `client.processor.on(name, callback)` 订阅:`RTMConfig.TLV_SERVER_PU
413
512
 
414
513
  | 功能 | TLV v1 API | TLV v1 | SharedWorker | 边界 / 替代方式 |
415
514
  | --- | --- | --- | --- | --- |
416
- | 登录、连接与系统 | `login`、`bye`、`getServerTime`、`addAttrs`、`getAttrs`、`addDebugLog`、重连与 Push 事件 | 支持 | 支持 | FPNN 仍是浏览器兼容入口;SharedWorker 只能用 TLV。 |
515
+ | 登录、连接与系统 | `login`、`bye`、`getServerTime`、`addAttrs`、`getAttrs`、`addDebugLog`、重连与 Push 事件 | 支持 | 支持 | SharedWorker 支持 TLV 与显式选择的 FPNN;本表 SharedWorker 列描述 TLV 模式。 |
417
516
  | 用户、资料与在线状态 | `getOnlineUsers`、`setUserInfo`、`getUserInfo`、`getUserOpenInfo`、`setTranslationLanguage` | 支持 | 支持 | 用户可见资料的权限由 Gate 项目控制。 |
418
517
  | 用户存储与黑名单 | `dataSet`、`dataGet`、`dataDelete`、`addBlacks`、`deleteBlacks`、`getBlacks` | 支持 | 支持 | TLV 黑名单批量每次最多 100 个 UID。 |
419
518
  | 群组 | `addGroupMembers`、`deleteGroupMembers`、`getGroupMembers`、`getGroupCount`、`getUserGroups`、`setGroupInfo`、`getGroupInfo`、`getGroupOpenInfo`、`getGroupsOpenInfo` | 支持 | 支持 | 服务端权限和群组存在状态仍由 Gate 校验。 |
@@ -433,13 +532,14 @@ TLV 不支持的调用按实现分为三类:
433
532
  - 未登记且非保留的 wire 返回 `20004`(`RTM_EC_UNKNOWN_METHOD`)。
434
533
  - `RTMSharedWorkerClient` 的 `sendFile`、`sendGroupFile`、`sendRoomFile` 在本地拒绝;callback 收到 `{mid, error}`,其中 `error.code` 为 `200999`(`RTM_EC_UNKNOWN_ERROR`);Promise 版本 reject 同一个错误对象,不会发送 wire 请求。
435
534
 
436
- 这些错误都不会自动切换到 FPNN。SharedWorker 复用 TLV 支持范围,但另外不接受函数型配置;`clientOptions` 必须可结构化克隆。FPNN 兼容能力列表见[能力总览](CAPABILITIES.md),服务端支持仍需单独确认。
535
+ 这些错误都不会自动切换到 FPNN。SharedWorker 的 TLV 模式复用 TLV 支持范围;FPNN 模式可转发其兼容业务 API(文件发送除外)。两种模式都不接受函数型配置;`clientOptions` 必须可结构化克隆。FPNN 兼容能力列表见[能力总览](CAPABILITIES.md),服务端支持仍需单独确认。
437
536
 
438
537
  ## 7. 调试与参考
439
538
 
440
539
  - `requestMetadata` 可为请求附加 `trace`、`sent_ms` 以关联服务端日志,不要放 credential、项目 SecretKey 或个人敏感数据。
441
540
  - `ErrorRecorder` 观察连接、协议、重连和登录错误;普通 API 错误通过该 API 的 callback / Promise 返回。
442
541
  - [能力总览与业务流程](CAPABILITIES.md)
542
+ - [FPNN 与 TLV v1 迁移对照](PROTOCOL_MIGRATION.md)
443
543
  - [TLV v1 URI、参数类型与返回字段](TLV_CONTRACT.md)
444
544
  - [TLV v1 frame 与传输规则](TLV.md)
445
545
  - [在线 Gate 验证说明](TLV_LIVE_E2E_TEST.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cg-devcenter/rtc-fpnn-webjs-sdk",
3
- "version": "1.0.0-cg.3",
3
+ "version": "1.0.0-cg.4",
4
4
  "description": "Browser RTM SDK with FPNN and TLV v1 MessagePack WebSocket transports",
5
5
  "main": "./dist/rtm.min.js",
6
6
  "browser": "./dist/rtm.min.js",
@@ -13,7 +13,8 @@
13
13
  "docs/TLV_CONTRACT.md",
14
14
  "docs/CAPABILITIES.md",
15
15
  "docs/TLV_LIVE_E2E_TEST.md",
16
- "docs/USAGE.md"
16
+ "docs/USAGE.md",
17
+ "docs/PROTOCOL_MIGRATION.md"
17
18
  ],
18
19
  "exports": {
19
20
  ".": {