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,63 @@
1
+ /**
2
+ * 通话结束原因:**一套 reason,三处共用**——`call.ended` 帧、`onCallEnd` 事件、
3
+ * 服务端 webhook。单一真相源是 `im-rtc-server/docs/conformance/reasons.json`
4
+ * (由 RTC_PROTOCOL.md §6 定稿)。
5
+ */
6
+ /** CallEndReason 是通话结束原因的封闭集合。 */
7
+ export declare const CallEndReason: {
8
+ /** 已接通成员主动挂断。时长 > 0。 */
9
+ readonly hangup: "hangup";
10
+ /** 主叫在接通前取消。 */
11
+ readonly cancel: "cancel";
12
+ /** 被叫主动拒接。 */
13
+ readonly reject: "reject";
14
+ /** 服务端振铃超时。 */
15
+ readonly noAnswer: "no_answer";
16
+ /** 被叫已在别的通话中。 */
17
+ readonly busy: "busy";
18
+ /** 被叫无在线设备。 */
19
+ readonly offline: "offline";
20
+ /** 本账号另一台设备接听了。 */
21
+ readonly answeredElsewhere: "answered_elsewhere";
22
+ /** 本账号另一台设备拒绝了。 */
23
+ readonly rejectedElsewhere: "rejected_elsewhere";
24
+ /** 被主持人或管理 API 移出。 */
25
+ readonly kicked: "kicked";
26
+ /** 房间被强制解散。 */
27
+ readonly roomClosed: "room_closed";
28
+ /** 掉线超过 30 秒恢复窗口。 */
29
+ readonly network: "network";
30
+ /** 服务端内部错误兜底,**也是未知值的兜底**。 */
31
+ readonly error: "error";
32
+ };
33
+ /** CallEndReasonValue 是 reason 的取值联合。 */
34
+ export type CallEndReasonValue = (typeof CallEndReason)[keyof typeof CallEndReason];
35
+ /**
36
+ * normalizeReason 把陌生的 reason 折成 'error'。
37
+ *
38
+ * 这条兜底是 RTC_PROTOCOL.md §10「新增枚举值不算破坏兼容」成立的前提:
39
+ * 服务端发一个老客户端不认识的 reason 时,老客户端**必须**不崩、不把原始字符串
40
+ * 显示给用户。
41
+ */
42
+ export declare function normalizeReason(value: unknown): CallEndReasonValue;
43
+ /**
44
+ * GROUP_DOMINANT_PRIORITY 是群通话「全员都没接听时取哪个 reason」的固定优先级
45
+ * (RTC_PROTOCOL.md §4.4 规则 3)。
46
+ *
47
+ * 定成有序表而不是散落的 if,是为了**四端算出同一个值**。
48
+ */
49
+ export declare const GROUP_DOMINANT_PRIORITY: readonly CallEndReasonValue[];
50
+ /**
51
+ * dominantReason 从一组成员裁决里挑出群通话的主导 reason。
52
+ * 没有一个落在优先级表里时返回 no_answer——保守地按「没人接」记。
53
+ */
54
+ export declare function dominantReason(outcomes: readonly string[]): CallEndReasonValue;
55
+ /**
56
+ * callDurationSec 按协议算通话时长:未接通恒为 0,接通则向下取整到秒。
57
+ *
58
+ * **正常路径下客户端不该自己算**——一律用 `call.ended` 帧里的 `duration_sec`
59
+ * (时钟偏移)。这个函数只服务一个例外:重连恢复失败时服务端的 ended 帧送不到,
60
+ * engine 要本地合成 `onCallEnd(network)`(RTC_PROTOCOL.md 不变量 I8)。
61
+ */
62
+ export declare function callDurationSec(connectedAtMs: number, endedAtMs: number): number;
63
+ //# sourceMappingURL=reasons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reasons.d.ts","sourceRoot":"","sources":["../src/reasons.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,kCAAkC;AAClC,eAAO,MAAM,aAAa;IACxB,wBAAwB;;IAExB,gBAAgB;;IAEhB,cAAc;;IAEd,eAAe;;IAEf,iBAAiB;;IAEjB,eAAe;;IAEf,mBAAmB;;IAEnB,mBAAmB;;IAEnB,sBAAsB;;IAEtB,eAAe;;IAEf,qBAAqB;;IAErB,8BAA8B;;CAEtB,CAAC;AAEX,yCAAyC;AACzC,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AAIpF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAIlE;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,kBAAkB,EAKhE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,kBAAkB,CAK9E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGhF"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * 通话结束原因:**一套 reason,三处共用**——`call.ended` 帧、`onCallEnd` 事件、
3
+ * 服务端 webhook。单一真相源是 `im-rtc-server/docs/conformance/reasons.json`
4
+ * (由 RTC_PROTOCOL.md §6 定稿)。
5
+ */
6
+ /** CallEndReason 是通话结束原因的封闭集合。 */
7
+ export const CallEndReason = {
8
+ /** 已接通成员主动挂断。时长 > 0。 */
9
+ hangup: 'hangup',
10
+ /** 主叫在接通前取消。 */
11
+ cancel: 'cancel',
12
+ /** 被叫主动拒接。 */
13
+ reject: 'reject',
14
+ /** 服务端振铃超时。 */
15
+ noAnswer: 'no_answer',
16
+ /** 被叫已在别的通话中。 */
17
+ busy: 'busy',
18
+ /** 被叫无在线设备。 */
19
+ offline: 'offline',
20
+ /** 本账号另一台设备接听了。 */
21
+ answeredElsewhere: 'answered_elsewhere',
22
+ /** 本账号另一台设备拒绝了。 */
23
+ rejectedElsewhere: 'rejected_elsewhere',
24
+ /** 被主持人或管理 API 移出。 */
25
+ kicked: 'kicked',
26
+ /** 房间被强制解散。 */
27
+ roomClosed: 'room_closed',
28
+ /** 掉线超过 30 秒恢复窗口。 */
29
+ network: 'network',
30
+ /** 服务端内部错误兜底,**也是未知值的兜底**。 */
31
+ error: 'error',
32
+ };
33
+ const KNOWN_REASONS = new Set(Object.values(CallEndReason));
34
+ /**
35
+ * normalizeReason 把陌生的 reason 折成 'error'。
36
+ *
37
+ * 这条兜底是 RTC_PROTOCOL.md §10「新增枚举值不算破坏兼容」成立的前提:
38
+ * 服务端发一个老客户端不认识的 reason 时,老客户端**必须**不崩、不把原始字符串
39
+ * 显示给用户。
40
+ */
41
+ export function normalizeReason(value) {
42
+ return typeof value === 'string' && KNOWN_REASONS.has(value)
43
+ ? value
44
+ : CallEndReason.error;
45
+ }
46
+ /**
47
+ * GROUP_DOMINANT_PRIORITY 是群通话「全员都没接听时取哪个 reason」的固定优先级
48
+ * (RTC_PROTOCOL.md §4.4 规则 3)。
49
+ *
50
+ * 定成有序表而不是散落的 if,是为了**四端算出同一个值**。
51
+ */
52
+ export const GROUP_DOMINANT_PRIORITY = [
53
+ CallEndReason.reject,
54
+ CallEndReason.busy,
55
+ CallEndReason.noAnswer,
56
+ CallEndReason.offline,
57
+ ];
58
+ /**
59
+ * dominantReason 从一组成员裁决里挑出群通话的主导 reason。
60
+ * 没有一个落在优先级表里时返回 no_answer——保守地按「没人接」记。
61
+ */
62
+ export function dominantReason(outcomes) {
63
+ for (const candidate of GROUP_DOMINANT_PRIORITY) {
64
+ if (outcomes.includes(candidate))
65
+ return candidate;
66
+ }
67
+ return CallEndReason.noAnswer;
68
+ }
69
+ /**
70
+ * callDurationSec 按协议算通话时长:未接通恒为 0,接通则向下取整到秒。
71
+ *
72
+ * **正常路径下客户端不该自己算**——一律用 `call.ended` 帧里的 `duration_sec`
73
+ * (时钟偏移)。这个函数只服务一个例外:重连恢复失败时服务端的 ended 帧送不到,
74
+ * engine 要本地合成 `onCallEnd(network)`(RTC_PROTOCOL.md 不变量 I8)。
75
+ */
76
+ export function callDurationSec(connectedAtMs, endedAtMs) {
77
+ if (connectedAtMs <= 0)
78
+ return 0;
79
+ return Math.max(0, Math.floor((endedAtMs - connectedAtMs) / 1000));
80
+ }
81
+ //# sourceMappingURL=reasons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reasons.js","sourceRoot":"","sources":["../src/reasons.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,kCAAkC;AAClC,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,wBAAwB;IACxB,MAAM,EAAE,QAAQ;IAChB,gBAAgB;IAChB,MAAM,EAAE,QAAQ;IAChB,cAAc;IACd,MAAM,EAAE,QAAQ;IAChB,eAAe;IACf,QAAQ,EAAE,WAAW;IACrB,iBAAiB;IACjB,IAAI,EAAE,MAAM;IACZ,eAAe;IACf,OAAO,EAAE,SAAS;IAClB,mBAAmB;IACnB,iBAAiB,EAAE,oBAAoB;IACvC,mBAAmB;IACnB,iBAAiB,EAAE,oBAAoB;IACvC,sBAAsB;IACtB,MAAM,EAAE,QAAQ;IAChB,eAAe;IACf,UAAU,EAAE,aAAa;IACzB,qBAAqB;IACrB,OAAO,EAAE,SAAS;IAClB,8BAA8B;IAC9B,KAAK,EAAE,OAAO;CACN,CAAC;AAKX,MAAM,aAAa,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;QAC1D,CAAC,CAAE,KAA4B;QAC/B,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAkC;IACpE,aAAa,CAAC,MAAM;IACpB,aAAa,CAAC,IAAI;IAClB,aAAa,CAAC,QAAQ;IACtB,aAAa,CAAC,OAAO;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,QAA2B;IACxD,KAAK,MAAM,SAAS,IAAI,uBAAuB,EAAE,CAAC;QAChD,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IACrD,CAAC;IACD,OAAO,aAAa,CAAC,QAAQ,CAAC;AAChC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,aAAqB,EAAE,SAAiB;IACtE,IAAI,aAAa,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,aAAa,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AACrE,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 脱敏:与服务端 `internal/observability` 的 Redact* 系列**行为一致**。
3
+ *
4
+ * 为什么 SDP 也算敏感:它带 ICE 候选(暴露内网拓扑与公网地址)、DTLS 指纹、
5
+ * 以及 SRTP 的密钥协商材料。整条打进日志等于把一次通话的传输面摊开。
6
+ * 要看完整 SDP 请用 Chrome 的 `webrtc-internals`,不要靠日志。
7
+ */
8
+ /** redact 把凭据折成「前 6 位 + 长度」。token / roomToken 进日志前必须过它。 */
9
+ export declare function redact(secret: string | undefined | null): string;
10
+ /** redactSdp 把 SDP 折成「行数 + m= 行摘要」,够判断协商的是音频还是视频。 */
11
+ export declare function redactSdp(sdp: string | undefined | null): string;
12
+ /**
13
+ * redactCandidate 把 ICE 候选折成「传输协议/类型」,不带地址与端口。
14
+ *
15
+ * 排查连通性看的是**候选类型分布**(host/srflx/relay)与最终选中的那一对,
16
+ * 不是每条候选的地址。
17
+ */
18
+ export declare function redactCandidate(candidate: string | undefined | null): string;
19
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../src/redact.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,2DAA2D;AAC3D,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAIhE;AAED,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAOhE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAa5E"}
package/dist/redact.js ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 脱敏:与服务端 `internal/observability` 的 Redact* 系列**行为一致**。
3
+ *
4
+ * 为什么 SDP 也算敏感:它带 ICE 候选(暴露内网拓扑与公网地址)、DTLS 指纹、
5
+ * 以及 SRTP 的密钥协商材料。整条打进日志等于把一次通话的传输面摊开。
6
+ * 要看完整 SDP 请用 Chrome 的 `webrtc-internals`,不要靠日志。
7
+ */
8
+ /** 凭据保留的前缀长度。6 位够在日志里比对「是不是同一枚票」,又不足以伪造。 */
9
+ const REDACT_PREFIX_LEN = 6;
10
+ /** redact 把凭据折成「前 6 位 + 长度」。token / roomToken 进日志前必须过它。 */
11
+ export function redact(secret) {
12
+ if (secret === undefined || secret === null || secret === '')
13
+ return '(empty)';
14
+ if (secret.length <= REDACT_PREFIX_LEN)
15
+ return `(len=${secret.length})`;
16
+ return `${secret.slice(0, REDACT_PREFIX_LEN)}…(len=${secret.length})`;
17
+ }
18
+ /** redactSdp 把 SDP 折成「行数 + m= 行摘要」,够判断协商的是音频还是视频。 */
19
+ export function redactSdp(sdp) {
20
+ if (sdp === undefined || sdp === null || sdp === '')
21
+ return '(empty)';
22
+ const lines = sdp.split('\n');
23
+ const media = lines
24
+ .filter((line) => line.startsWith('m='))
25
+ .map((line) => line.trim().split(/\s+/)[0] ?? '');
26
+ return `sdp(lines=${lines.length}, ${media.join(',')})`;
27
+ }
28
+ /**
29
+ * redactCandidate 把 ICE 候选折成「传输协议/类型」,不带地址与端口。
30
+ *
31
+ * 排查连通性看的是**候选类型分布**(host/srflx/relay)与最终选中的那一对,
32
+ * 不是每条候选的地址。
33
+ */
34
+ export function redactCandidate(candidate) {
35
+ if (candidate === undefined || candidate === null || candidate === '') {
36
+ return '(end-of-candidates)';
37
+ }
38
+ const fields = candidate.split(/\s+/);
39
+ let transport = '?';
40
+ let type = '?';
41
+ fields.forEach((field, i) => {
42
+ const lower = field.toLowerCase();
43
+ if (lower === 'udp' || lower === 'tcp')
44
+ transport = lower;
45
+ if (lower === 'typ')
46
+ type = fields[i + 1] ?? '?';
47
+ });
48
+ return `candidate(${transport}/${type})`;
49
+ }
50
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../src/redact.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,4CAA4C;AAC5C,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B,2DAA2D;AAC3D,MAAM,UAAU,MAAM,CAAC,MAAiC;IACtD,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC/E,IAAI,MAAM,CAAC,MAAM,IAAI,iBAAiB;QAAE,OAAO,QAAQ,MAAM,CAAC,MAAM,GAAG,CAAC;IACxE,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,SAAS,MAAM,CAAC,MAAM,GAAG,CAAC;AACxE,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,SAAS,CAAC,GAA8B;IACtD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IACtE,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,KAAK;SAChB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;SACvC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACpD,OAAO,aAAa,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,SAAoC;IAClE,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,EAAE,EAAE,CAAC;QACtE,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IACD,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,SAAS,GAAG,GAAG,CAAC;IACpB,IAAI,IAAI,GAAG,GAAG,CAAC;IACf,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAClC,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK;YAAE,SAAS,GAAG,KAAK,CAAC;QAC1D,IAAI,KAAK,KAAK,KAAK;YAAE,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC;IACnD,CAAC,CAAC,CAAC;IACH,OAAO,aAAa,SAAS,IAAI,IAAI,GAAG,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 重连退避:**三端同一份档位**(RTC_PROTOCOL.md §1.4)。
3
+ *
4
+ * 档位写死成表而不是算指数,是为了四端一眼能对上;抖动是为了避免
5
+ * 「服务端重启后所有客户端在同一毫秒回来」把它再打挂一次。
6
+ */
7
+ /** BACKOFF_STEPS_MS 是退避档位,之后固定用最后一档。 */
8
+ export declare const BACKOFF_STEPS_MS: readonly number[];
9
+ /** JITTER_RATIO 是抖动幅度:每档 ±20%。 */
10
+ export declare const JITTER_RATIO = 0.2;
11
+ /**
12
+ * backoffDelayMs 返回第 attempt 次重连该等多久(attempt 从 0 开始)。
13
+ *
14
+ * `random` 可注入以便测试;默认 Math.random。
15
+ */
16
+ export declare function backoffDelayMs(attempt: number, random?: () => number): number;
17
+ //# sourceMappingURL=backoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backoff.d.ts","sourceRoot":"","sources":["../../src/signaling/backoff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wCAAwC;AACxC,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAAiD,CAAC;AAEhG,kCAAkC;AAClC,eAAO,MAAM,YAAY,MAAM,CAAC;AAEhC;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,MAAoB,GAAG,MAAM,CAM1F"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 重连退避:**三端同一份档位**(RTC_PROTOCOL.md §1.4)。
3
+ *
4
+ * 档位写死成表而不是算指数,是为了四端一眼能对上;抖动是为了避免
5
+ * 「服务端重启后所有客户端在同一毫秒回来」把它再打挂一次。
6
+ */
7
+ /** BACKOFF_STEPS_MS 是退避档位,之后固定用最后一档。 */
8
+ export const BACKOFF_STEPS_MS = [1_000, 2_000, 4_000, 8_000, 15_000, 30_000];
9
+ /** JITTER_RATIO 是抖动幅度:每档 ±20%。 */
10
+ export const JITTER_RATIO = 0.2;
11
+ /**
12
+ * backoffDelayMs 返回第 attempt 次重连该等多久(attempt 从 0 开始)。
13
+ *
14
+ * `random` 可注入以便测试;默认 Math.random。
15
+ */
16
+ export function backoffDelayMs(attempt, random = Math.random) {
17
+ const index = Math.min(Math.max(attempt, 0), BACKOFF_STEPS_MS.length - 1);
18
+ // 上面的 clamp 保证 index 落在数组范围内,这个断言与它一一对应。
19
+ const base = BACKOFF_STEPS_MS[index];
20
+ const jitter = base * JITTER_RATIO * (random() * 2 - 1);
21
+ return Math.max(0, Math.round(base + jitter));
22
+ }
23
+ //# sourceMappingURL=backoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backoff.js","sourceRoot":"","sources":["../../src/signaling/backoff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wCAAwC;AACxC,MAAM,CAAC,MAAM,gBAAgB,GAAsB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAEhG,kCAAkC;AAClC,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAC;AAEhC;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,SAAuB,IAAI,CAAC,MAAM;IAChF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC1E,yCAAyC;IACzC,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAW,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,GAAG,YAAY,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { PcRole } from './enums.js';
2
+ /**
3
+ * 把线路上的 `room.ice_candidate` data 解析成浏览器要的 `RTCIceCandidateInit`。
4
+ *
5
+ * 抽成纯函数不只是为了门面的体量:它有两条容易写错的规则,值得直接单测——
6
+ * 而门面上的方法只能靠端到端间接覆盖。
7
+ */
8
+ export interface ParsedCandidate {
9
+ readonly pc: PcRole;
10
+ readonly init: RTCIceCandidateInit;
11
+ }
12
+ /**
13
+ * parseCandidate 解析一条候选;返回 `null` 表示**不需要加**。
14
+ *
15
+ * `candidate` 为空串是「收集结束」的信号(协议 §3.3),接收方必须容忍——
16
+ * 把它当成一条真候选喂给 `addIceCandidate` 在部分浏览器上会抛。
17
+ */
18
+ export declare function parseCandidate(data: Readonly<Record<string, unknown>>): ParsedCandidate | null;
19
+ //# sourceMappingURL=candidate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"candidate.d.ts","sourceRoot":"","sources":["../../src/signaling/candidate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;CACpC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,eAAe,GAAG,IAAI,CAc9F"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * parseCandidate 解析一条候选;返回 `null` 表示**不需要加**。
3
+ *
4
+ * `candidate` 为空串是「收集结束」的信号(协议 §3.3),接收方必须容忍——
5
+ * 把它当成一条真候选喂给 `addIceCandidate` 在部分浏览器上会抛。
6
+ */
7
+ export function parseCandidate(data) {
8
+ const candidate = typeof data['candidate'] === 'string' ? data['candidate'] : '';
9
+ if (candidate === '')
10
+ return null;
11
+ const rawIndex = data['sdp_mline_index'];
12
+ return {
13
+ // 只有两条 PC;认不出来的一律当 sub——下行才是候选真正要紧的方向。
14
+ pc: data['pc'] === 'pub' ? 'pub' : 'sub',
15
+ init: {
16
+ candidate,
17
+ sdpMid: typeof data['sdp_mid'] === 'string' ? data['sdp_mid'] : '',
18
+ sdpMLineIndex: typeof rawIndex === 'number' ? rawIndex : 0,
19
+ },
20
+ };
21
+ }
22
+ //# sourceMappingURL=candidate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"candidate.js","sourceRoot":"","sources":["../../src/signaling/candidate.ts"],"names":[],"mappings":"AAaA;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAuC;IACpE,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,IAAI,SAAS,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAElC,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACzC,OAAO;QACL,uCAAuC;QACvC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK;QACxC,IAAI,EAAE;YACJ,SAAS;YACT,MAAM,EAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE;YAClE,aAAa,EAAE,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;SAC3D;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { FrameFields } from './fieldSpec.js';
2
+ /**
3
+ * 线路的 snake_case 与 TS 惯用的 camelCase 之间的两个方向。
4
+ *
5
+ * 抽成独立模块不只是为了 engine.ts 的体量:这两个函数是**纯的**,
6
+ * 值得直接单测,而门面上的方法只能靠端到端间接覆盖。
7
+ */
8
+ /** snakeToCamel 把 `media_type` 变成 `mediaType`。 */
9
+ export declare function snakeToCamel(key: string): string;
10
+ /** camelizeArgs 把状态机产出的 snake_case 参数整体转成 camelCase。 */
11
+ export declare function camelizeArgs(args: Readonly<Record<string, unknown>>): Record<string, unknown>;
12
+ /**
13
+ * toFrameProps 把线路形状的 data 转成字段声明用的属性名。
14
+ *
15
+ * 两种键都认:状态机产出的是线路名(`track_id`),而调用方手写时更可能用属性名。
16
+ * **认两种不是含糊**——字段声明里 `wire` 与属性名是一对一的,转换没有歧义。
17
+ */
18
+ export declare function toFrameProps(fields: FrameFields, data: Readonly<Record<string, unknown>>): Record<string, unknown>;
19
+ //# sourceMappingURL=caseMapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caseMapping.d.ts","sourceRoot":"","sources":["../../src/signaling/caseMapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;GAKG;AAEH,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAI7F;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACtC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 线路的 snake_case 与 TS 惯用的 camelCase 之间的两个方向。
3
+ *
4
+ * 抽成独立模块不只是为了 engine.ts 的体量:这两个函数是**纯的**,
5
+ * 值得直接单测,而门面上的方法只能靠端到端间接覆盖。
6
+ */
7
+ /** snakeToCamel 把 `media_type` 变成 `mediaType`。 */
8
+ export function snakeToCamel(key) {
9
+ return key.replace(/_([a-z])/g, (_, ch) => ch.toUpperCase());
10
+ }
11
+ /** camelizeArgs 把状态机产出的 snake_case 参数整体转成 camelCase。 */
12
+ export function camelizeArgs(args) {
13
+ const out = {};
14
+ for (const [key, value] of Object.entries(args))
15
+ out[snakeToCamel(key)] = value;
16
+ return out;
17
+ }
18
+ /**
19
+ * toFrameProps 把线路形状的 data 转成字段声明用的属性名。
20
+ *
21
+ * 两种键都认:状态机产出的是线路名(`track_id`),而调用方手写时更可能用属性名。
22
+ * **认两种不是含糊**——字段声明里 `wire` 与属性名是一对一的,转换没有歧义。
23
+ */
24
+ export function toFrameProps(fields, data) {
25
+ const out = {};
26
+ for (const [prop, spec] of Object.entries(fields)) {
27
+ if (Object.hasOwn(data, spec.wire))
28
+ out[prop] = data[spec.wire];
29
+ else if (Object.hasOwn(data, prop))
30
+ out[prop] = data[prop];
31
+ }
32
+ return out;
33
+ }
34
+ //# sourceMappingURL=caseMapping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caseMapping.js","sourceRoot":"","sources":["../../src/signaling/caseMapping.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,kDAAkD;AAClD,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAU,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,YAAY,CAAC,IAAuC;IAClE,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;IAChF,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAmB,EACnB,IAAuC;IAEvC,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aAC3D,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,110 @@
1
+ import type { Envelope } from './envelope.js';
2
+ import type { FrameFields } from './fieldSpec.js';
3
+ import type { ConnectionOptions, ConnectionState, HelloOk } from './connectionTypes.js';
4
+ /**
5
+ * 信令连接:握手、心跳、请求应答配对、退避重连。
6
+ *
7
+ * # 为什么按 req_id 配对而不是按帧类型
8
+ *
9
+ * pub 侧的 `room.offer` 是由 **`room.answer`** 应答的(协议 §3.3 固定 offerer),
10
+ * 只看类型对不上号。按 req_id 配对还顺带解决了「多个同类请求在途」的问题。
11
+ */
12
+ export type { ConnectionState, HelloOk, KickedOutReason, ConnectionEvents, ConnectionOptions, } from './connectionTypes.js';
13
+ /** Connection 是一条信令连接。断线会自动重连,除非关闭码明说不该重连。 */
14
+ export declare class Connection {
15
+ private readonly options;
16
+ private ws;
17
+ private state;
18
+ private sessionId;
19
+ private seq;
20
+ private readonly pending;
21
+ private readonly heartbeat;
22
+ private readonly reconnector;
23
+ private token;
24
+ /** 连续鉴权失败次数。握手一成功就清零——只有**连续**失败才说明票是死的。 */
25
+ private authFailures;
26
+ private readonly tokenExpiry;
27
+ /** 「服务端已经彻底放弃这条会话」的倒计时。见 `ResumeDeadline`。 */
28
+ private readonly resumeDeadline;
29
+ constructor(options: ConnectionOptions);
30
+ /** currentState 返回连接状态。 */
31
+ get currentState(): ConnectionState;
32
+ /** currentSessionId 返回会话 id;重连时会带上它请求恢复。 */
33
+ get currentSessionId(): string;
34
+ /**
35
+ * updateToken 换一枚新的接入票(旧票过期时用)。下次连接生效。
36
+ *
37
+ * **顺带把鉴权失败计数清零**:换票就是「这次不一样了」的唯一信号,
38
+ * 不清的话已经用光重试次数的连接换了新票也再没有机会试。
39
+ */
40
+ updateToken(token: string, expiresAtMs?: number): void;
41
+ /** connect 建立连接并完成握手。已连上时直接返回。 */
42
+ connect(): Promise<HelloOk>;
43
+ /** close 主动关闭,**不会**触发重连。 */
44
+ close(): void;
45
+ /**
46
+ * request 发一个请求并等它的应答。
47
+ *
48
+ * 返回的 data 是**已按帧声明解码**的对象;未注册的应答类型返回原始 data。
49
+ */
50
+ request(type: string, fields: FrameFields, value: Record<string, unknown>): Promise<{
51
+ envelope: Envelope;
52
+ data: Record<string, unknown>;
53
+ }>;
54
+ /**
55
+ * dispatchRequest 不检查连接状态。
56
+ *
57
+ * 独立出来是因为 **sys.hello 本身就要在 connecting 状态下发出去**——
58
+ * 让握手走公开的 request() 会被状态检查挡住(踩过一次:所有时序测试都挂在
59
+ * 「一帧都没发出去」)。
60
+ */
61
+ private dispatchRequest;
62
+ /** send 发一帧但不等应答(服务端主动事件的回应,如 sub 的 answer)。 */
63
+ sendFrame(type: string, reqId: string, fields: FrameFields, value: Record<string, unknown>): void;
64
+ /**
65
+ * fire 发一个请求但**不等应答**:应答照常按 req_id 配对后丢掉,不会漏进事件流。
66
+ *
67
+ * 只给 `CallEngine.forceEnd()` 用:结束帧不许排在帧循环与在途请求后面
68
+ * (2026-09-13 iOS 那次挂断一帧没发出去,卡的正是那一段)。
69
+ * 帧在这次调用里就同步写进 socket(`dispatchRequest` 在第一个 await 之前就 send 了)。
70
+ * 未连接时只记一条日志——握手之前发业务帧会被服务端当成协议错误。
71
+ */
72
+ fire(type: string, fields: FrameFields, value: Record<string, unknown>): void;
73
+ private nextReqId;
74
+ private handshake;
75
+ /**
76
+ * 握手被拒且重试不可能变好时,一次就放弃,按 {@link handshakeGiveUpReason} 的分类抛给宿主。
77
+ *
78
+ * **握手应答类型不对那条不走这里**——那是对端的实现 bug,处置另说,
79
+ * 不该借这条路悄悄改掉。
80
+ */
81
+ private abortIfHandshakeRejected;
82
+ private handleMessage;
83
+ private dispatchEvent;
84
+ /**
85
+ * decodeData 返回**线路形状**(snake_case)的规范化 data。
86
+ *
87
+ * 「解出来再编回去」看着多余,其实是在做三件事:填默认值、枚举兜底、数值钳制。
88
+ * 保持 snake_case 是因为**状态机吃的是线路形状**——它跑的一致性向量就是线路形状,
89
+ * 换成 camelCase 会让状态机与向量之间多一层翻译,而那层翻译没人测。
90
+ *
91
+ * # 解不动就按原始 data 放行,**绝不往上抛**
92
+ *
93
+ * 这里原先是裸的 `decodeFields`。字段类型对不上(服务端把 int 位置发成了字符串)时它抛
94
+ * `bad_params`,而调用点在 `PendingRequests.settle` 里——那时 waiter **已经被摘掉、
95
+ * 超时也已经清掉**,`resolve` 却还没执行:那个 `request()` 的 promise 从此永远不落定。
96
+ * 症状是 `room.join.ok` 明明回来了,房间机却永远停在 `joining`,之后每次 publish 被
97
+ * R1 拒成 2005,而宿主一条错误都收不到(异常从 `onmessage` 里冲出去成了未捕获错误)。
98
+ * iOS(`(try? decodedData()) ?? envelope.data`)与 Android(catch 后回退 `envelope.data`)
99
+ * 本来就是这么做的,本端是四端里唯一漏掉兜底的。
100
+ *
101
+ * 回退之后字段读取一律走 `Wire.*` 那套「读不出来就取零值」的取数器,状态机不会崩;
102
+ * 真正的原因进日志 + 一条 error 事件,不塞进应答里让业务分不清。
103
+ */
104
+ private decodeData;
105
+ private handleClose;
106
+ /** sendPing 发一个心跳帧。连接不可用时静默跳过——心跳失败自有判死逻辑接手。 */
107
+ private sendPing;
108
+ private emitError;
109
+ }
110
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/signaling/connection.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAWxF;;;;;;;GAOG;AAEH,YAAY,EACV,eAAe,EACf,OAAO,EACP,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAgB9B,8CAA8C;AAC9C,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CACoB;IAE5C,OAAO,CAAC,EAAE,CAA8B;IACxC,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAE1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,KAAK,CAAS;IACtB,4CAA4C;IAC5C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmB;IAC/C,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;gBAEpC,OAAO,EAAE,iBAAiB;IAkCtC,2BAA2B;IAC3B,IAAI,YAAY,IAAI,eAAe,CAElC;IAED,4CAA4C;IAC5C,IAAI,gBAAgB,IAAI,MAAM,CAE7B;IAED;;;;;OAKG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAQtD,kCAAkC;IAC5B,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IA4BjC,6BAA6B;IAC7B,KAAK,IAAI,IAAI;IAYb;;;;OAIG;IACG,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;IAUjE;;;;;;OAMG;YACW,eAAe;IAyB7B,gDAAgD;IAChD,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAMjG;;;;;;;OAOG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAa7E,OAAO,CAAC,SAAS;YAKH,SAAS;IAqCvB;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,aAAa;IAerB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,WAAW;IAwCnB,gDAAgD;IAChD,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,SAAS;CAKlB"}