im-rtc-call-engine 1.0.0

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 (218) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bytes.d.ts +11 -0
  3. package/dist/bytes.d.ts.map +1 -0
  4. package/dist/bytes.js +20 -0
  5. package/dist/bytes.js.map +1 -0
  6. package/dist/callOptions.d.ts +27 -0
  7. package/dist/callOptions.d.ts.map +1 -0
  8. package/dist/callOptions.js +19 -0
  9. package/dist/callOptions.js.map +1 -0
  10. package/dist/deviceId.d.ts +10 -0
  11. package/dist/deviceId.d.ts.map +1 -0
  12. package/dist/deviceId.js +55 -0
  13. package/dist/deviceId.js.map +1 -0
  14. package/dist/engine.d.ts +302 -0
  15. package/dist/engine.d.ts.map +1 -0
  16. package/dist/engine.js +489 -0
  17. package/dist/engine.js.map +1 -0
  18. package/dist/engineBus.d.ts +16 -0
  19. package/dist/engineBus.d.ts.map +1 -0
  20. package/dist/engineBus.js +89 -0
  21. package/dist/engineBus.js.map +1 -0
  22. package/dist/engineWiring.d.ts +40 -0
  23. package/dist/engineWiring.d.ts.map +1 -0
  24. package/dist/engineWiring.js +57 -0
  25. package/dist/engineWiring.js.map +1 -0
  26. package/dist/errors.d.ts +185 -0
  27. package/dist/errors.d.ts.map +1 -0
  28. package/dist/errors.js +253 -0
  29. package/dist/errors.js.map +1 -0
  30. package/dist/eventBus.d.ts +24 -0
  31. package/dist/eventBus.d.ts.map +1 -0
  32. package/dist/eventBus.js +50 -0
  33. package/dist/eventBus.js.map +1 -0
  34. package/dist/events.d.ts +220 -0
  35. package/dist/events.d.ts.map +1 -0
  36. package/dist/events.js +35 -0
  37. package/dist/events.js.map +1 -0
  38. package/dist/frameLoop.d.ts +93 -0
  39. package/dist/frameLoop.d.ts.map +1 -0
  40. package/dist/frameLoop.js +346 -0
  41. package/dist/frameLoop.js.map +1 -0
  42. package/dist/index.d.ts +60 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +42 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/logger.d.ts +64 -0
  47. package/dist/logger.d.ts.map +1 -0
  48. package/dist/logger.js +101 -0
  49. package/dist/logger.js.map +1 -0
  50. package/dist/media/engineMediaApi.d.ts +40 -0
  51. package/dist/media/engineMediaApi.d.ts.map +1 -0
  52. package/dist/media/engineMediaApi.js +71 -0
  53. package/dist/media/engineMediaApi.js.map +1 -0
  54. package/dist/media/firstFrameGate.d.ts +47 -0
  55. package/dist/media/firstFrameGate.d.ts.map +1 -0
  56. package/dist/media/firstFrameGate.js +117 -0
  57. package/dist/media/firstFrameGate.js.map +1 -0
  58. package/dist/media/mediaAdapter.d.ts +135 -0
  59. package/dist/media/mediaAdapter.d.ts.map +1 -0
  60. package/dist/media/mediaAdapter.js +2 -0
  61. package/dist/media/mediaAdapter.js.map +1 -0
  62. package/dist/media/mediaBridge.d.ts +99 -0
  63. package/dist/media/mediaBridge.d.ts.map +1 -0
  64. package/dist/media/mediaBridge.js +186 -0
  65. package/dist/media/mediaBridge.js.map +1 -0
  66. package/dist/media/mediaPlane.d.ts +58 -0
  67. package/dist/media/mediaPlane.d.ts.map +1 -0
  68. package/dist/media/mediaPlane.js +119 -0
  69. package/dist/media/mediaPlane.js.map +1 -0
  70. package/dist/media/videoProfile.d.ts +79 -0
  71. package/dist/media/videoProfile.d.ts.map +1 -0
  72. package/dist/media/videoProfile.js +46 -0
  73. package/dist/media/videoProfile.js.map +1 -0
  74. package/dist/media/videoProfile.test.d.ts +2 -0
  75. package/dist/media/videoProfile.test.d.ts.map +1 -0
  76. package/dist/media/videoProfile.test.js +26 -0
  77. package/dist/media/videoProfile.test.js.map +1 -0
  78. package/dist/media/viewRegistry.d.ts +75 -0
  79. package/dist/media/viewRegistry.d.ts.map +1 -0
  80. package/dist/media/viewRegistry.js +130 -0
  81. package/dist/media/viewRegistry.js.map +1 -0
  82. package/dist/media/webrtcAdapter.d.ts +158 -0
  83. package/dist/media/webrtcAdapter.d.ts.map +1 -0
  84. package/dist/media/webrtcAdapter.js +502 -0
  85. package/dist/media/webrtcAdapter.js.map +1 -0
  86. package/dist/protocolId.d.ts +14 -0
  87. package/dist/protocolId.d.ts.map +1 -0
  88. package/dist/protocolId.js +109 -0
  89. package/dist/protocolId.js.map +1 -0
  90. package/dist/reasons.d.ts +63 -0
  91. package/dist/reasons.d.ts.map +1 -0
  92. package/dist/reasons.js +81 -0
  93. package/dist/reasons.js.map +1 -0
  94. package/dist/redact.d.ts +19 -0
  95. package/dist/redact.d.ts.map +1 -0
  96. package/dist/redact.js +50 -0
  97. package/dist/redact.js.map +1 -0
  98. package/dist/signaling/backoff.d.ts +17 -0
  99. package/dist/signaling/backoff.d.ts.map +1 -0
  100. package/dist/signaling/backoff.js +23 -0
  101. package/dist/signaling/backoff.js.map +1 -0
  102. package/dist/signaling/candidate.d.ts +19 -0
  103. package/dist/signaling/candidate.d.ts.map +1 -0
  104. package/dist/signaling/candidate.js +22 -0
  105. package/dist/signaling/candidate.js.map +1 -0
  106. package/dist/signaling/caseMapping.d.ts +19 -0
  107. package/dist/signaling/caseMapping.d.ts.map +1 -0
  108. package/dist/signaling/caseMapping.js +34 -0
  109. package/dist/signaling/caseMapping.js.map +1 -0
  110. package/dist/signaling/connection.d.ts +110 -0
  111. package/dist/signaling/connection.d.ts.map +1 -0
  112. package/dist/signaling/connection.js +365 -0
  113. package/dist/signaling/connection.js.map +1 -0
  114. package/dist/signaling/connectionFactory.d.ts +55 -0
  115. package/dist/signaling/connectionFactory.d.ts.map +1 -0
  116. package/dist/signaling/connectionFactory.js +29 -0
  117. package/dist/signaling/connectionFactory.js.map +1 -0
  118. package/dist/signaling/connectionTypes.d.ts +94 -0
  119. package/dist/signaling/connectionTypes.d.ts.map +1 -0
  120. package/dist/signaling/connectionTypes.js +2 -0
  121. package/dist/signaling/connectionTypes.js.map +1 -0
  122. package/dist/signaling/discipline.d.ts +6 -0
  123. package/dist/signaling/discipline.d.ts.map +1 -0
  124. package/dist/signaling/discipline.js +100 -0
  125. package/dist/signaling/discipline.js.map +1 -0
  126. package/dist/signaling/enums.d.ts +53 -0
  127. package/dist/signaling/enums.d.ts.map +1 -0
  128. package/dist/signaling/enums.js +52 -0
  129. package/dist/signaling/enums.js.map +1 -0
  130. package/dist/signaling/envelope.d.ts +31 -0
  131. package/dist/signaling/envelope.d.ts.map +1 -0
  132. package/dist/signaling/envelope.js +91 -0
  133. package/dist/signaling/envelope.js.map +1 -0
  134. package/dist/signaling/fieldSpec.d.ts +101 -0
  135. package/dist/signaling/fieldSpec.d.ts.map +1 -0
  136. package/dist/signaling/fieldSpec.js +130 -0
  137. package/dist/signaling/fieldSpec.js.map +1 -0
  138. package/dist/signaling/frameSender.d.ts +34 -0
  139. package/dist/signaling/frameSender.d.ts.map +1 -0
  140. package/dist/signaling/frameSender.js +57 -0
  141. package/dist/signaling/frameSender.js.map +1 -0
  142. package/dist/signaling/frames.call.d.ts +299 -0
  143. package/dist/signaling/frames.call.d.ts.map +1 -0
  144. package/dist/signaling/frames.call.js +149 -0
  145. package/dist/signaling/frames.call.js.map +1 -0
  146. package/dist/signaling/frames.room.d.ts +566 -0
  147. package/dist/signaling/frames.room.d.ts.map +1 -0
  148. package/dist/signaling/frames.room.js +165 -0
  149. package/dist/signaling/frames.room.js.map +1 -0
  150. package/dist/signaling/frames.sys.d.ts +145 -0
  151. package/dist/signaling/frames.sys.d.ts.map +1 -0
  152. package/dist/signaling/frames.sys.js +53 -0
  153. package/dist/signaling/frames.sys.js.map +1 -0
  154. package/dist/signaling/handshakeGiveUp.d.ts +36 -0
  155. package/dist/signaling/handshakeGiveUp.d.ts.map +1 -0
  156. package/dist/signaling/handshakeGiveUp.js +48 -0
  157. package/dist/signaling/handshakeGiveUp.js.map +1 -0
  158. package/dist/signaling/heartbeat.d.ts +34 -0
  159. package/dist/signaling/heartbeat.d.ts.map +1 -0
  160. package/dist/signaling/heartbeat.js +52 -0
  161. package/dist/signaling/heartbeat.js.map +1 -0
  162. package/dist/signaling/pendingRequests.d.ts +33 -0
  163. package/dist/signaling/pendingRequests.d.ts.map +1 -0
  164. package/dist/signaling/pendingRequests.js +57 -0
  165. package/dist/signaling/pendingRequests.js.map +1 -0
  166. package/dist/signaling/reconnector.d.ts +43 -0
  167. package/dist/signaling/reconnector.d.ts.map +1 -0
  168. package/dist/signaling/reconnector.js +76 -0
  169. package/dist/signaling/reconnector.js.map +1 -0
  170. package/dist/signaling/registry.d.ts +100 -0
  171. package/dist/signaling/registry.d.ts.map +1 -0
  172. package/dist/signaling/registry.js +217 -0
  173. package/dist/signaling/registry.js.map +1 -0
  174. package/dist/signaling/resumeDeadline.d.ts +48 -0
  175. package/dist/signaling/resumeDeadline.d.ts.map +1 -0
  176. package/dist/signaling/resumeDeadline.js +89 -0
  177. package/dist/signaling/resumeDeadline.js.map +1 -0
  178. package/dist/signaling/tokenExpiry.d.ts +89 -0
  179. package/dist/signaling/tokenExpiry.d.ts.map +1 -0
  180. package/dist/signaling/tokenExpiry.js +106 -0
  181. package/dist/signaling/tokenExpiry.js.map +1 -0
  182. package/dist/signaling/webSocket.d.ts +59 -0
  183. package/dist/signaling/webSocket.d.ts.map +1 -0
  184. package/dist/signaling/webSocket.js +50 -0
  185. package/dist/signaling/webSocket.js.map +1 -0
  186. package/dist/state/callMachine.d.ts +56 -0
  187. package/dist/state/callMachine.d.ts.map +1 -0
  188. package/dist/state/callMachine.js +176 -0
  189. package/dist/state/callMachine.js.map +1 -0
  190. package/dist/state/callRecv.d.ts +12 -0
  191. package/dist/state/callRecv.d.ts.map +1 -0
  192. package/dist/state/callRecv.js +255 -0
  193. package/dist/state/callRecv.js.map +1 -0
  194. package/dist/state/engineMachine.d.ts +41 -0
  195. package/dist/state/engineMachine.d.ts.map +1 -0
  196. package/dist/state/engineMachine.js +210 -0
  197. package/dist/state/engineMachine.js.map +1 -0
  198. package/dist/state/forceEnd.d.ts +44 -0
  199. package/dist/state/forceEnd.d.ts.map +1 -0
  200. package/dist/state/forceEnd.js +96 -0
  201. package/dist/state/forceEnd.js.map +1 -0
  202. package/dist/state/roomMachine.d.ts +92 -0
  203. package/dist/state/roomMachine.d.ts.map +1 -0
  204. package/dist/state/roomMachine.js +319 -0
  205. package/dist/state/roomMachine.js.map +1 -0
  206. package/dist/state/roomRecv.d.ts +7 -0
  207. package/dist/state/roomRecv.d.ts.map +1 -0
  208. package/dist/state/roomRecv.js +209 -0
  209. package/dist/state/roomRecv.js.map +1 -0
  210. package/dist/state/types.d.ts +61 -0
  211. package/dist/state/types.d.ts.map +1 -0
  212. package/dist/state/types.js +30 -0
  213. package/dist/state/types.js.map +1 -0
  214. package/dist/version.d.ts +11 -0
  215. package/dist/version.d.ts.map +1 -0
  216. package/dist/version.js +11 -0
  217. package/dist/version.js.map +1 -0
  218. package/package.json +35 -0
@@ -0,0 +1,365 @@
1
+ import { ErrorCode, RtcError, rtcErrorFromWire } from '../errors.js';
2
+ import { logger, redact } from '../logger.js';
3
+ import { SDK_VERSION } from '../version.js';
4
+ import { decodeEnvelope, encodeEnvelope, okType } from './envelope.js';
5
+ import { decodeFields, encodeFields } from './fieldSpec.js';
6
+ import { HELLO_FIELDS, HELLO_OK_FIELDS } from './frames.sys.js';
7
+ import { TokenExpiryTimer } from './tokenExpiry.js';
8
+ import { handshakeGiveUpReason } from './handshakeGiveUp.js';
9
+ import { Heartbeat } from './heartbeat.js';
10
+ import { PendingRequests } from './pendingRequests.js';
11
+ import { Reconnector } from './reconnector.js';
12
+ import { ResumeDeadline } from './resumeDeadline.js';
13
+ import { FrameType, lookupFrame } from './registry.js';
14
+ import { CloseCode, WS_OPEN, browserWebSocketFactory, shouldReconnect } from './webSocket.js';
15
+ const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;
16
+ /** 宿主不传 sdk 时 `sys.hello` 带的标识。版本号取 SDK_VERSION,别在这里另写一份。 */
17
+ const DEFAULT_SDK = `web/${SDK_VERSION}`;
18
+ /**
19
+ * MAX_AUTH_FAILURES 是连续几次 4401 之后彻底放弃(协议 §1.5 关闭码表)。
20
+ *
21
+ * **一定要有这个上限**:4401 说的是「这枚 token 不好使」,而重连**带的是同一枚
22
+ * token**——没有上限就是拿同一把坏钥匙永远敲同一扇门。实测里服务端重启换了签名密钥,
23
+ * 一个没关的标签页重试到第 19 次还在敲,日志里全是 `token_invalid`,
24
+ * 把真正的问题淹掉了。上限到了就抛 `onKickedOut` 把宿主赶回登录页去换新 token。
25
+ */
26
+ const MAX_AUTH_FAILURES = 3;
27
+ /** Connection 是一条信令连接。断线会自动重连,除非关闭码明说不该重连。 */
28
+ export class Connection {
29
+ options;
30
+ ws = null;
31
+ state = 'idle';
32
+ sessionId = '';
33
+ seq = 0;
34
+ pending;
35
+ heartbeat;
36
+ reconnector;
37
+ token;
38
+ /** 连续鉴权失败次数。握手一成功就清零——只有**连续**失败才说明票是死的。 */
39
+ authFailures = 0;
40
+ tokenExpiry;
41
+ /** 「服务端已经彻底放弃这条会话」的倒计时。见 `ResumeDeadline`。 */
42
+ resumeDeadline;
43
+ constructor(options) {
44
+ this.token = options.token;
45
+ this.heartbeat = new Heartbeat({
46
+ sendPing: () => this.sendPing(),
47
+ onDead: () => this.ws?.close(CloseCode.goingAway, 'heartbeat timeout'),
48
+ });
49
+ this.pending = new PendingRequests(options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
50
+ this.tokenExpiry = new TokenExpiryTimer({
51
+ ...(options.tokenExpiryLeadMs === undefined ? {} : { leadMs: options.tokenExpiryLeadMs }),
52
+ onWillExpire: (info) => this.options.events?.onTokenWillExpire?.(info),
53
+ });
54
+ this.resumeDeadline = new ResumeDeadline(() => {
55
+ this.sessionId = ''; // 服务端已经丢掉它,再拿去要 resume 只会白跑一趟
56
+ this.options.events?.onSessionUnrecoverable?.();
57
+ });
58
+ this.reconnector = new Reconnector(async () => {
59
+ await this.connect();
60
+ }, (err) => this.emitError(err), options.random ?? Math.random);
61
+ this.options = {
62
+ url: options.url,
63
+ token: options.token,
64
+ deviceId: options.deviceId,
65
+ sdk: options.sdk ?? DEFAULT_SDK,
66
+ events: options.events ?? {},
67
+ webSocketFactory: options.webSocketFactory ?? browserWebSocketFactory,
68
+ requestTimeoutMs: options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
69
+ random: options.random ?? Math.random,
70
+ };
71
+ }
72
+ /** currentState 返回连接状态。 */
73
+ get currentState() {
74
+ return this.state;
75
+ }
76
+ /** currentSessionId 返回会话 id;重连时会带上它请求恢复。 */
77
+ get currentSessionId() {
78
+ return this.sessionId;
79
+ }
80
+ /**
81
+ * updateToken 换一枚新的接入票(旧票过期时用)。下次连接生效。
82
+ *
83
+ * **顺带把鉴权失败计数清零**:换票就是「这次不一样了」的唯一信号,
84
+ * 不清的话已经用光重试次数的连接换了新票也再没有机会试。
85
+ */
86
+ updateToken(token, expiresAtMs) {
87
+ this.token = token;
88
+ this.authFailures = 0;
89
+ // 宿主刚从自家后台拿到票,必然知道它的 expires_in。传了就按新票重新武装;
90
+ // 不传就让旧定时器继续跑到下一次握手——那时 sys.hello.ok 会给出权威值。
91
+ if (expiresAtMs !== undefined)
92
+ this.tokenExpiry.arm(expiresAtMs);
93
+ }
94
+ /** connect 建立连接并完成握手。已连上时直接返回。 */
95
+ async connect() {
96
+ if (this.state === 'connected' && this.ws !== null) {
97
+ throw new RtcError(ErrorCode.invalidState, { cause: new Error('已经连上了') });
98
+ }
99
+ this.state = this.sessionId === '' ? 'connecting' : 'reconnecting';
100
+ const socket = this.options.webSocketFactory(this.options.url);
101
+ this.ws = socket;
102
+ await new Promise((resolve, reject) => {
103
+ socket.onopen = () => resolve();
104
+ socket.onerror = () => reject(new RtcError(ErrorCode.networkUnreachable, { cause: new Error('WebSocket 打开失败') }));
105
+ socket.onclose = (event) => this.handleClose(event);
106
+ });
107
+ socket.onmessage = (event) => this.handleMessage(event.data);
108
+ const hello = await this.handshake();
109
+ this.state = 'connected';
110
+ this.authFailures = 0;
111
+ this.reconnector.succeeded();
112
+ this.resumeDeadline.connected(hello.pingIntervalSec);
113
+ this.heartbeat.start(hello.pingIntervalSec);
114
+ this.tokenExpiry.arm(hello.tokenExpiresAtMs);
115
+ this.options.events?.onConnected?.(hello);
116
+ return hello;
117
+ }
118
+ /** close 主动关闭,**不会**触发重连。 */
119
+ close() {
120
+ this.state = 'closed';
121
+ this.heartbeat.stop();
122
+ this.reconnector.stop();
123
+ // **只有 logout 撤这条倒计时**:鉴权连续失败那条路要让它走完(见 ResumeDeadline)。
124
+ this.resumeDeadline.cancel();
125
+ this.tokenExpiry.disarm();
126
+ this.pending.rejectAll(new RtcError(ErrorCode.invalidState, { cause: new Error('连接已关闭') }));
127
+ this.ws?.close(CloseCode.normal, 'client logout');
128
+ this.ws = null;
129
+ }
130
+ /**
131
+ * request 发一个请求并等它的应答。
132
+ *
133
+ * 返回的 data 是**已按帧声明解码**的对象;未注册的应答类型返回原始 data。
134
+ */
135
+ async request(type, fields, value) {
136
+ if (this.state !== 'connected') {
137
+ throw new RtcError(ErrorCode.invalidState, {
138
+ forType: type,
139
+ cause: new Error(`连接不可用(当前 ${this.state})`),
140
+ });
141
+ }
142
+ return this.dispatchRequest(type, fields, value);
143
+ }
144
+ /**
145
+ * dispatchRequest 不检查连接状态。
146
+ *
147
+ * 独立出来是因为 **sys.hello 本身就要在 connecting 状态下发出去**——
148
+ * 让握手走公开的 request() 会被状态检查挡住(踩过一次:所有时序测试都挂在
149
+ * 「一帧都没发出去」)。
150
+ */
151
+ async dispatchRequest(type, fields, value) {
152
+ const socket = this.ws;
153
+ if (socket === null) {
154
+ throw new RtcError(ErrorCode.invalidState, {
155
+ forType: type,
156
+ cause: new Error('还没有连接'),
157
+ });
158
+ }
159
+ const reqId = this.nextReqId();
160
+ const raw = encodeEnvelope(type, reqId, encodeFields(fields, value));
161
+ const waiting = this.pending.track(reqId, type);
162
+ try {
163
+ socket.send(raw);
164
+ }
165
+ catch (cause) {
166
+ this.pending.abandon(reqId);
167
+ throw new RtcError(ErrorCode.networkUnreachable, { forType: type, cause });
168
+ }
169
+ return waiting;
170
+ }
171
+ /** send 发一帧但不等应答(服务端主动事件的回应,如 sub 的 answer)。 */
172
+ sendFrame(type, reqId, fields, value) {
173
+ const socket = this.ws;
174
+ if (socket === null || socket.readyState !== WS_OPEN)
175
+ return;
176
+ socket.send(encodeEnvelope(type, reqId, encodeFields(fields, value)));
177
+ }
178
+ /**
179
+ * fire 发一个请求但**不等应答**:应答照常按 req_id 配对后丢掉,不会漏进事件流。
180
+ *
181
+ * 只给 `CallEngine.forceEnd()` 用:结束帧不许排在帧循环与在途请求后面
182
+ * (2026-09-13 iOS 那次挂断一帧没发出去,卡的正是那一段)。
183
+ * 帧在这次调用里就同步写进 socket(`dispatchRequest` 在第一个 await 之前就 send 了)。
184
+ * 未连接时只记一条日志——握手之前发业务帧会被服务端当成协议错误。
185
+ */
186
+ fire(type, fields, value) {
187
+ if (this.state !== 'connected') {
188
+ logger.warn('帧没发出去:连接不可用', { type, state: this.state });
189
+ return;
190
+ }
191
+ void this.dispatchRequest(type, fields, value).catch((err) => {
192
+ logger.info('不等应答的请求失败了', {
193
+ type,
194
+ code: err instanceof RtcError ? err.code : 'unknown',
195
+ });
196
+ });
197
+ }
198
+ nextReqId() {
199
+ this.seq += 1;
200
+ return `w-${this.seq}`;
201
+ }
202
+ async handshake() {
203
+ const hello = decodeFields(HELLO_FIELDS, {});
204
+ hello.token = this.token;
205
+ hello.deviceId = this.options.deviceId;
206
+ hello.sessionId = this.sessionId;
207
+ hello.sdk = this.options.sdk ?? DEFAULT_SDK;
208
+ logger.debug('发送 sys.hello', {
209
+ deviceId: hello.deviceId,
210
+ sessionId: hello.sessionId,
211
+ // 凭据只打前 6 位 + 长度(CONVENTIONS §6)。
212
+ token: redact(hello.token),
213
+ });
214
+ let reply;
215
+ try {
216
+ reply = await this.dispatchRequest(FrameType.hello, HELLO_FIELDS, hello);
217
+ }
218
+ catch (err) {
219
+ this.abortIfHandshakeRejected(err);
220
+ throw err;
221
+ }
222
+ const { envelope, data } = reply;
223
+ if (envelope.type !== okType(FrameType.hello)) {
224
+ throw new RtcError(ErrorCode.notAuthenticated, {
225
+ cause: new Error(`握手应答是 ${envelope.type}`),
226
+ });
227
+ }
228
+ // data 已经是线路形状(见 decodeData 的注释),直接解成 camelCase 给调用方。
229
+ const ok = decodeFields(HELLO_OK_FIELDS, data);
230
+ this.sessionId = ok.sessionId;
231
+ return ok;
232
+ }
233
+ /**
234
+ * 握手被拒且重试不可能变好时,一次就放弃,按 {@link handshakeGiveUpReason} 的分类抛给宿主。
235
+ *
236
+ * **握手应答类型不对那条不走这里**——那是对端的实现 bug,处置另说,
237
+ * 不该借这条路悄悄改掉。
238
+ */
239
+ abortIfHandshakeRejected(err) {
240
+ const reason = handshakeGiveUpReason(err);
241
+ if (reason === null)
242
+ return;
243
+ const rtc = err;
244
+ logger.error('握手被拒,不再重连', { code: rtc.code, name: rtc.name_, reason });
245
+ // stop() 是闩不是取消:connect() 被拒那条是微任务,排在 close 事件之后,
246
+ // 只取消定时器的话它会把重连又排回来(见 Reconnector.stop 的注释)。
247
+ this.reconnector.stop();
248
+ this.state = 'closed';
249
+ this.options.events?.onKickedOut?.({ reason });
250
+ }
251
+ handleMessage(raw) {
252
+ if (typeof raw !== 'string')
253
+ return;
254
+ // 收到**任何**帧都算对端活着,不只是 pong(§1.3)。
255
+ this.heartbeat.noteFrameReceived();
256
+ let envelope;
257
+ try {
258
+ envelope = decodeEnvelope(raw);
259
+ }
260
+ catch (err) {
261
+ // 解不开的帧是对端的实现 bug。抛给宿主并断开——继续读只会读到更多垃圾。
262
+ this.emitError(err);
263
+ this.ws?.close(CloseCode.badProtocol, 'undecodable frame');
264
+ return;
265
+ }
266
+ if (envelope.reqId !== '' && this.pending.settle(envelope, (e) => this.decodeData(e)))
267
+ return;
268
+ this.dispatchEvent(envelope);
269
+ }
270
+ dispatchEvent(envelope) {
271
+ if (envelope.type === FrameType.error) {
272
+ const error = rtcErrorFromWire(envelope.data);
273
+ if (error.code === ErrorCode.kickedOut)
274
+ this.options.events?.onKickedOut?.({ reason: 'takenOver' });
275
+ this.options.events?.onError?.(error);
276
+ return;
277
+ }
278
+ if (lookupFrame(envelope.type) === undefined) {
279
+ // §2.3:客户端收到未知 type **必须静默忽略**——服务端可能比我们新。
280
+ logger.debug('忽略未知帧', { type: envelope.type });
281
+ return;
282
+ }
283
+ this.options.events?.onEvent?.(envelope.type, this.decodeData(envelope), envelope);
284
+ }
285
+ /**
286
+ * decodeData 返回**线路形状**(snake_case)的规范化 data。
287
+ *
288
+ * 「解出来再编回去」看着多余,其实是在做三件事:填默认值、枚举兜底、数值钳制。
289
+ * 保持 snake_case 是因为**状态机吃的是线路形状**——它跑的一致性向量就是线路形状,
290
+ * 换成 camelCase 会让状态机与向量之间多一层翻译,而那层翻译没人测。
291
+ *
292
+ * # 解不动就按原始 data 放行,**绝不往上抛**
293
+ *
294
+ * 这里原先是裸的 `decodeFields`。字段类型对不上(服务端把 int 位置发成了字符串)时它抛
295
+ * `bad_params`,而调用点在 `PendingRequests.settle` 里——那时 waiter **已经被摘掉、
296
+ * 超时也已经清掉**,`resolve` 却还没执行:那个 `request()` 的 promise 从此永远不落定。
297
+ * 症状是 `room.join.ok` 明明回来了,房间机却永远停在 `joining`,之后每次 publish 被
298
+ * R1 拒成 2005,而宿主一条错误都收不到(异常从 `onmessage` 里冲出去成了未捕获错误)。
299
+ * iOS(`(try? decodedData()) ?? envelope.data`)与 Android(catch 后回退 `envelope.data`)
300
+ * 本来就是这么做的,本端是四端里唯一漏掉兜底的。
301
+ *
302
+ * 回退之后字段读取一律走 `Wire.*` 那套「读不出来就取零值」的取数器,状态机不会崩;
303
+ * 真正的原因进日志 + 一条 error 事件,不塞进应答里让业务分不清。
304
+ */
305
+ decodeData(envelope) {
306
+ const fields = lookupFrame(envelope.type);
307
+ if (fields === undefined)
308
+ return { ...envelope.data };
309
+ try {
310
+ return encodeFields(fields, decodeFields(fields, envelope.data));
311
+ }
312
+ catch (err) {
313
+ logger.warn('帧解码失败,按原始 data 放行', { type: envelope.type, cause: String(err) });
314
+ this.emitError(err);
315
+ return { ...envelope.data };
316
+ }
317
+ }
318
+ handleClose(event) {
319
+ this.heartbeat.stop();
320
+ this.ws = null;
321
+ this.pending.rejectAll(new RtcError(ErrorCode.networkUnreachable, { cause: new Error('连接已断开') }));
322
+ if (event.code === CloseCode.kickedOut)
323
+ this.options.events?.onKickedOut?.({ reason: 'takenOver' });
324
+ /*
325
+ 4401 要计数。重连**带的是同一枚 token**,所以「换新 token 后重连」这条规则
326
+ 只有配上一个上限才成立——否则一枚废票能自己重试到天荒地老。
327
+ 连续 3 次之后按协议 §1.5 抛 onKickedOut,让宿主回登录页重新取票。
328
+ */
329
+ let exhausted = false;
330
+ if (event.code === CloseCode.unauthorized) {
331
+ this.authFailures += 1;
332
+ exhausted = this.authFailures >= MAX_AUTH_FAILURES;
333
+ if (exhausted) {
334
+ logger.info('鉴权连续失败,停止重连', { failures: this.authFailures });
335
+ this.reconnector.stop();
336
+ this.options.events?.onKickedOut?.({ reason: 'authExpired' });
337
+ }
338
+ }
339
+ const willReconnect = !exhausted && this.state !== 'closed' && shouldReconnect(event.code);
340
+ this.options.events?.onDisconnected?.({
341
+ code: event.code,
342
+ reason: event.reason,
343
+ willReconnect,
344
+ });
345
+ if (!willReconnect) {
346
+ this.state = 'closed';
347
+ return;
348
+ }
349
+ this.state = 'reconnecting';
350
+ this.resumeDeadline.arm();
351
+ this.reconnector.schedule();
352
+ }
353
+ /** sendPing 发一个心跳帧。连接不可用时静默跳过——心跳失败自有判死逻辑接手。 */
354
+ sendPing() {
355
+ const socket = this.ws;
356
+ if (socket !== null && socket.readyState === WS_OPEN) {
357
+ socket.send(encodeEnvelope(FrameType.ping, this.nextReqId(), {}));
358
+ }
359
+ }
360
+ emitError(err) {
361
+ const error = err instanceof RtcError ? err : new RtcError(ErrorCode.internal, { cause: err });
362
+ this.options.events?.onError?.(error);
363
+ }
364
+ }
365
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/signaling/connection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { RtcError } from '../errors.js';
2
+ import type { HelloOk, KickedOutReason } from './connection.js';
3
+ import { Connection } from './connection.js';
4
+ import type { WebSocketFactory } from './webSocket.js';
5
+ /**
6
+ * 按 engine 需要的形状装配一条 `Connection`。
7
+ *
8
+ * 单拎出来是因为它有一处**只有类型系统会提醒、但很容易写错**的地方:
9
+ * `exactOptionalPropertyTypes` 下,可选字段不能显式传 `undefined`,
10
+ * 只能整个不写——于是 `webSocketFactory` 必须用展开语法条件性地加进去。
11
+ * 那三行放在门面里既碍眼又容易在下次改动时被"顺手简化"掉。
12
+ */
13
+ export interface EngineConnectionHandlers {
14
+ /**
15
+ * 握手完成——**每一次**,包括自动重连那些。
16
+ *
17
+ * 一开始这一条整个漏了:门面只在 `login()` 里手工把 hello.ok 喂给状态机,
18
+ * 而重连是连接层自己发起的,它那次握手的结果谁都没接。后果不是「少一个事件」
19
+ * 而是**重连之后状态机根本不知道自己重连了**:`resumed=false` 时房间与通话
20
+ * 不归零(服务端那边早就没了)、`resumed=true` 时攒下的意图不重放,
21
+ * 宿主也永远收不到第二次 `connected`——界面卡在「重连中」不动。
22
+ */
23
+ onConnected: (hello: HelloOk) => void;
24
+ onEvent: (type: string, data: Record<string, unknown>) => void;
25
+ onDisconnected: (info: {
26
+ code: number;
27
+ willReconnect: boolean;
28
+ }) => void;
29
+ onKickedOut: (info: {
30
+ reason: KickedOutReason;
31
+ }) => void;
32
+ /**
33
+ * 断得太久,服务端那一侧的会话已经不可能再恢复(§1.4)。
34
+ *
35
+ * 与「重连上了但 `resumed=false`」是同一件事,只是**不必等重连成功**——
36
+ * 网络一直不回来的话那一刻永远不会到,界面就永远停在「正在重连」、
37
+ * 连挂断都点不动(真机 2026-09-08 的 iOS 端)。
38
+ */
39
+ onSessionUnrecoverable: () => void;
40
+ /** 票快到期,宿主该换票。 */
41
+ onTokenWillExpire: (info: {
42
+ expiresAtMs: number;
43
+ }) => void;
44
+ onError: (error: RtcError) => void;
45
+ }
46
+ /** EngineConnectionConfig 是装配一条连接需要的最小信息。 */
47
+ export interface EngineConnectionConfig {
48
+ url: string;
49
+ token: string;
50
+ deviceId: string;
51
+ webSocketFactory?: WebSocketFactory;
52
+ }
53
+ /** createConnection 造一条已挂好回调、尚未连接的 Connection。 */
54
+ export declare function createConnection(config: EngineConnectionConfig, handlers: EngineConnectionHandlers): Connection;
55
+ //# sourceMappingURL=connectionFactory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectionFactory.d.ts","sourceRoot":"","sources":["../../src/signaling/connectionFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAqB,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;;;;OAQG;IACH,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC/D,cAAc,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACzE,WAAW,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,eAAe,CAAA;KAAE,KAAK,IAAI,CAAC;IACzD;;;;;;OAMG;IACH,sBAAsB,EAAE,MAAM,IAAI,CAAC;IACnC,kBAAkB;IAClB,iBAAiB,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3D,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;CACpC;AAED,6CAA6C;AAC7C,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED,kDAAkD;AAClD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,sBAAsB,EAC9B,QAAQ,EAAE,wBAAwB,GACjC,UAAU,CAyBZ"}
@@ -0,0 +1,29 @@
1
+ import { checkDeviceId } from '../protocolId.js';
2
+ import { Connection } from './connection.js';
3
+ /** createConnection 造一条已挂好回调、尚未连接的 Connection。 */
4
+ export function createConnection(config, handlers) {
5
+ // **在开 socket 之前拦**:不拦的话服务端回 1004,而它那句「device_id 只允许
6
+ // [A-Za-z0-9_-]」到不了宿主手里——宿主看到的只有一个 bad_params,
7
+ // 界面上就是「登录失败」四个字。安卓真机上为此查了一轮(见 deviceId.ts)。
8
+ checkDeviceId(config.deviceId);
9
+ const options = {
10
+ url: config.url,
11
+ token: config.token,
12
+ deviceId: config.deviceId,
13
+ // exactOptionalPropertyTypes:没有工厂时**不能传 undefined**,只能不写这个键。
14
+ ...(config.webSocketFactory === undefined
15
+ ? {}
16
+ : { webSocketFactory: config.webSocketFactory }),
17
+ events: {
18
+ onConnected: (hello) => handlers.onConnected(hello),
19
+ onSessionUnrecoverable: () => handlers.onSessionUnrecoverable(),
20
+ onEvent: (type, data) => handlers.onEvent(type, data),
21
+ onDisconnected: (info) => handlers.onDisconnected({ code: info.code, willReconnect: info.willReconnect }),
22
+ onKickedOut: (info) => handlers.onKickedOut(info),
23
+ onTokenWillExpire: (info) => handlers.onTokenWillExpire(info),
24
+ onError: (error) => handlers.onError(error),
25
+ },
26
+ };
27
+ return new Connection(options);
28
+ }
29
+ //# sourceMappingURL=connectionFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectionFactory.js","sourceRoot":"","sources":["../../src/signaling/connectionFactory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA8C7C,kDAAkD;AAClD,MAAM,UAAU,gBAAgB,CAC9B,MAA8B,EAC9B,QAAkC;IAElC,qDAAqD;IACrD,+CAA+C;IAC/C,6CAA6C;IAC7C,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAsB;QACjC,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,6DAA6D;QAC7D,GAAG,CAAC,MAAM,CAAC,gBAAgB,KAAK,SAAS;YACvC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAClD,MAAM,EAAE;YACN,WAAW,EAAE,CAAC,KAAK,EAAQ,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC;YACzD,sBAAsB,EAAE,GAAS,EAAE,CAAC,QAAQ,CAAC,sBAAsB,EAAE;YACrE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAQ,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC;YAC3D,cAAc,EAAE,CAAC,IAAI,EAAQ,EAAE,CAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC;YACjF,WAAW,EAAE,CAAC,IAAI,EAAQ,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC;YACvD,iBAAiB,EAAE,CAAC,IAAI,EAAQ,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC;YACnE,OAAO,EAAE,CAAC,KAAK,EAAQ,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC;SAClD;KACF,CAAC;IACF,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,94 @@
1
+ import type { Envelope } from './envelope.js';
2
+ import type { RtcError } from '../errors.js';
3
+ import type { WebSocketFactory } from './webSocket.js';
4
+ /**
5
+ * 连接层的公开类型。
6
+ *
7
+ * 从 connection.ts 抽出来(CONVENTIONS §2 的 400 行红线)——那边留实现,这边留契约。
8
+ * 公开 API 不变:connection.ts 仍然把它们原样 re-export。
9
+ */
10
+ /** ConnectionState 是连接状态。 */
11
+ export type ConnectionState = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'closed';
12
+ /** HelloOk 是握手成功后的服务端信息。 */
13
+ export interface HelloOk {
14
+ uid: string;
15
+ deviceId: string;
16
+ sessionId: string;
17
+ resumed: boolean;
18
+ pingIntervalSec: number;
19
+ /** 本次握手用的那张票的到期时刻(Unix 毫秒)。**0 = 未知**。 */
20
+ tokenExpiresAtMs: number;
21
+ limits: {
22
+ maxFrameBytes: number;
23
+ maxCallees: number;
24
+ maxRoomParticipants: number;
25
+ maxUserDataBytes: number;
26
+ ringTimeoutSecDefault: number;
27
+ };
28
+ }
29
+ /**
30
+ * KickedOutReason 是被踢的原因。
31
+ *
32
+ * **不合并成一个「被踢」**:三种情况宿主的处置完全不同——一个该回登录页,
33
+ * 一个该悄悄换票重来,一个该去改配置。合并的结果是宿主只能都当登录失效处理,
34
+ * 把可以静默恢复的场景也变成了「请重新登录」,把该改配置的场景变成了让用户干瞪眼。
35
+ */
36
+ export type KickedOutReason = 'takenOver' | 'authExpired' | 'configRejected';
37
+ /** ConnectionEvents 是连接层对外的回调。 */
38
+ export interface ConnectionEvents {
39
+ /** 握手完成。resumed=true 表示恢复了旧会话,房间成员关系还在。 */
40
+ onConnected?: (hello: HelloOk) => void;
41
+ /** 连接断开。willReconnect=false 时不会再自动回来。 */
42
+ onDisconnected?: (info: {
43
+ code: number;
44
+ reason: string;
45
+ willReconnect: boolean;
46
+ }) => void;
47
+ /**
48
+ * 被踢,**不会自动重连**。
49
+ *
50
+ * reason 决定宿主该做什么,三者处置完全不同:
51
+ * - `takenOver` —— 同账号同设备号在别处登录,或宿主主动吊销(都是 4403/1104,
52
+ * 客户端无从区分)。**回登录页**,换票也没用。
53
+ * - `authExpired` —— 票不被接受且连续三次都没换上(4401 用尽)。
54
+ * 宿主应重新取一枚票再 `login`。
55
+ * - `configRejected` —— 服务端拒绝了这次接入的**参数**(握手应答里 retryable=false
56
+ * 的错误码:device_id 不合规、协议版本不受支持、应用被停用……)。
57
+ * **去改配置**——换票和重试都救不了。具体哪里不对看 `login()` 抛出的 RtcError;
58
+ * 如果是重连期间被拒,那条码走 onError。
59
+ */
60
+ onKickedOut?: (info: {
61
+ reason: KickedOutReason;
62
+ }) => void;
63
+ /**
64
+ * 断得太久了,**服务端那一侧的会话已经不可能再恢复**(§1.4 的恢复窗口过了)。
65
+ *
66
+ * 与「重连上了但 `resumed=false`」是同一件事,只是**不必等重连成功**——
67
+ * 网络一直不回来的话那一刻永远不会到。少了它,界面就永远停在「正在重连」、
68
+ * 连挂断都点不动(真机 2026-09-08 的 iOS 端)。
69
+ */
70
+ onSessionUnrecoverable?: () => void;
71
+ /** 票快到期了,宿主该去取新票并 updateToken。见 tokenExpiry.ts。 */
72
+ onTokenWillExpire?: (info: {
73
+ expiresAtMs: number;
74
+ }) => void;
75
+ /** 收到服务端主动推送的事件(req_id 为空的帧)。 */
76
+ onEvent?: (type: string, data: Record<string, unknown>, envelope: Envelope) => void;
77
+ /** 内部错误。 */
78
+ onError?: (error: RtcError) => void;
79
+ }
80
+ /** ConnectionOptions 是构造参数。带 Fn 后缀的都是为了测试可注入。 */
81
+ export interface ConnectionOptions {
82
+ url: string;
83
+ token: string;
84
+ deviceId: string;
85
+ sdk?: string;
86
+ events?: ConnectionEvents;
87
+ webSocketFactory?: WebSocketFactory;
88
+ /** 请求超时。协议建议 10 秒(§2.2)。 */
89
+ requestTimeoutMs?: number;
90
+ random?: () => number;
91
+ /** 票到期前多久提醒宿主换票。默认 60s,见 tokenExpiry.ts。 */
92
+ tokenExpiryLeadMs?: number;
93
+ }
94
+ //# sourceMappingURL=connectionTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectionTypes.d.ts","sourceRoot":"","sources":["../../src/signaling/connectionTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;;;;GAKG;AAEH,6BAA6B;AAC7B,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,QAAQ,CAAC;AAE9F,4BAA4B;AAC5B,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,0CAA0C;IAC1C,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE;QACN,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,gBAAgB,EAAE,MAAM,CAAC;QACzB,qBAAqB,EAAE,MAAM,CAAC;KAC/B,CAAC;CACH;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,aAAa,GAAG,gBAAgB,CAAC;AAE7E,kCAAkC;AAClC,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,yCAAyC;IACzC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1F;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,eAAe,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1D;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IACpC,mDAAmD;IACnD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC5D,iCAAiC;IACjC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpF,YAAY;IACZ,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;CACrC;AAED,iDAAiD;AACjD,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,4BAA4B;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=connectionTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectionTypes.js","sourceRoot":"","sources":["../../src/signaling/connectionTypes.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * checkDiscipline 检查一个已解析的 data 是否满足编码硬规则。
3
+ * 违规抛 RtcError(bad_envelope 或 bad_params)。
4
+ */
5
+ export declare function checkDiscipline(data: unknown): void;
6
+ //# sourceMappingURL=discipline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discipline.d.ts","sourceRoot":"","sources":["../../src/signaling/discipline.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAEnD"}