livekit-client 2.22.3 → 2.23.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 (272) hide show
  1. package/README.md +35 -0
  2. package/dist/livekit-client.e2ee.worker.js +1 -1
  3. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  4. package/dist/livekit-client.e2ee.worker.mjs +71 -8
  5. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  6. package/dist/livekit-client.esm.mjs +1746 -547
  7. package/dist/livekit-client.esm.mjs.map +1 -1
  8. package/dist/livekit-client.fm.worker.js +1 -1
  9. package/dist/livekit-client.fm.worker.js.map +1 -1
  10. package/dist/livekit-client.fm.worker.mjs +2 -1
  11. package/dist/livekit-client.fm.worker.mjs.map +1 -1
  12. package/dist/livekit-client.umd.js +1 -1
  13. package/dist/livekit-client.umd.js.map +1 -1
  14. package/dist/src/api/SignalClient.d.ts +4 -2
  15. package/dist/src/api/SignalClient.d.ts.map +1 -1
  16. package/dist/src/api/WebSocketStream.d.ts +1 -0
  17. package/dist/src/api/WebSocketStream.d.ts.map +1 -1
  18. package/dist/src/api/utils.d.ts +1 -1
  19. package/dist/src/api/utils.d.ts.map +1 -1
  20. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  21. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  22. package/dist/src/e2ee/worker/naluUtils.d.ts +12 -1
  23. package/dist/src/e2ee/worker/naluUtils.d.ts.map +1 -1
  24. package/dist/src/index.d.ts +2 -2
  25. package/dist/src/index.d.ts.map +1 -1
  26. package/dist/src/options.d.ts +14 -0
  27. package/dist/src/options.d.ts.map +1 -1
  28. package/dist/src/room/DefaultReconnectPolicy.d.ts.map +1 -1
  29. package/dist/src/room/PCTransport.d.ts +100 -7
  30. package/dist/src/room/PCTransport.d.ts.map +1 -1
  31. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  32. package/dist/src/room/RTCEngine.d.ts +16 -0
  33. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  34. package/dist/src/room/Room.d.ts +54 -1
  35. package/dist/src/room/Room.d.ts.map +1 -1
  36. package/dist/src/room/data-channel/DataChannelManager.d.ts +9 -8
  37. package/dist/src/room/data-channel/DataChannelManager.d.ts.map +1 -1
  38. package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts +25 -12
  39. package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts.map +1 -1
  40. package/dist/src/room/data-channel/LossyDataChannel.d.ts.map +1 -1
  41. package/dist/src/room/data-channel/ReliableDataChannel.d.ts.map +1 -1
  42. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
  43. package/dist/src/room/defaults.d.ts.map +1 -1
  44. package/dist/src/room/errors.d.ts +18 -0
  45. package/dist/src/room/errors.d.ts.map +1 -1
  46. package/dist/src/room/events.d.ts +3 -2
  47. package/dist/src/room/events.d.ts.map +1 -1
  48. package/dist/src/room/participant/LocalParticipant.d.ts +4 -0
  49. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  50. package/dist/src/room/participant/Participant.d.ts +1 -1
  51. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  52. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  53. package/dist/src/room/rpc/client/RpcClientManager.d.ts.map +1 -1
  54. package/dist/src/room/rpc/utils.d.ts.map +1 -1
  55. package/dist/src/room/track/LocalAudioTrack.d.ts +24 -1
  56. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  57. package/dist/src/room/track/LocalTrack.d.ts +18 -0
  58. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  59. package/dist/src/room/track/LocalVideoTrack.d.ts +9 -0
  60. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  61. package/dist/src/room/track/RemoteAudioTrack.d.ts +6 -0
  62. package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
  63. package/dist/src/room/track/processor/types.d.ts +11 -0
  64. package/dist/src/room/track/processor/types.d.ts.map +1 -1
  65. package/dist/src/room/track/utils.d.ts +13 -0
  66. package/dist/src/room/track/utils.d.ts.map +1 -1
  67. package/dist/src/room/utils.d.ts +60 -1
  68. package/dist/src/room/utils.d.ts.map +1 -1
  69. package/dist/src/test/compressionStreamStubs.d.ts +12 -0
  70. package/dist/src/test/compressionStreamStubs.d.ts.map +1 -0
  71. package/dist/ts4.2/api/SignalClient.d.ts +187 -185
  72. package/dist/ts4.2/api/SignalClientStateMachine.d.ts +85 -85
  73. package/dist/ts4.2/api/WebSocketStream.d.ts +34 -33
  74. package/dist/ts4.2/api/utils.d.ts +9 -9
  75. package/dist/ts4.2/connectionHelper/ConnectionCheck.d.ts +30 -30
  76. package/dist/ts4.2/connectionHelper/checks/Checker.d.ts +62 -62
  77. package/dist/ts4.2/connectionHelper/checks/cloudRegion.d.ts +18 -18
  78. package/dist/ts4.2/connectionHelper/checks/connectionProtocol.d.ts +20 -20
  79. package/dist/ts4.2/connectionHelper/checks/publishAudio.d.ts +6 -6
  80. package/dist/ts4.2/connectionHelper/checks/publishVideo.d.ts +7 -7
  81. package/dist/ts4.2/connectionHelper/checks/reconnect.d.ts +6 -6
  82. package/dist/ts4.2/connectionHelper/checks/turn.d.ts +6 -6
  83. package/dist/ts4.2/connectionHelper/checks/webrtc.d.ts +6 -6
  84. package/dist/ts4.2/connectionHelper/checks/websocket.d.ts +6 -6
  85. package/dist/ts4.2/e2ee/E2eeManager.d.ts +111 -111
  86. package/dist/ts4.2/e2ee/KeyProvider.d.ts +54 -54
  87. package/dist/ts4.2/e2ee/constants.d.ts +21 -21
  88. package/dist/ts4.2/e2ee/errors.d.ts +12 -12
  89. package/dist/ts4.2/e2ee/events.d.ts +39 -39
  90. package/dist/ts4.2/e2ee/index.d.ts +6 -6
  91. package/dist/ts4.2/e2ee/types.d.ts +231 -231
  92. package/dist/ts4.2/e2ee/utils.d.ts +32 -32
  93. package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +16 -16
  94. package/dist/ts4.2/e2ee/worker/ErrorRateLimiter.d.ts +21 -21
  95. package/dist/ts4.2/e2ee/worker/FrameCryptor.d.ts +209 -209
  96. package/dist/ts4.2/e2ee/worker/ParticipantKeyHandler.d.ts +81 -84
  97. package/dist/ts4.2/e2ee/worker/e2ee.worker.d.ts +2 -2
  98. package/dist/ts4.2/e2ee/worker/naluUtils.d.ts +39 -28
  99. package/dist/ts4.2/e2ee/worker/sifPayload.d.ts +23 -23
  100. package/dist/ts4.2/frameMetadata/FrameMetadataManager.d.ts +53 -53
  101. package/dist/ts4.2/frameMetadata/frameMetadata.d.ts +33 -33
  102. package/dist/ts4.2/frameMetadata/types.d.ts +62 -62
  103. package/dist/ts4.2/frameMetadata/utils.d.ts +9 -9
  104. package/dist/ts4.2/frameMetadata/worker/frameMetadata.worker.d.ts +2 -2
  105. package/dist/ts4.2/index.d.ts +76 -76
  106. package/dist/ts4.2/logger.d.ts +66 -66
  107. package/dist/ts4.2/options.d.ts +163 -149
  108. package/dist/ts4.2/room/BackOffStrategy.d.ts +27 -27
  109. package/dist/ts4.2/room/DefaultReconnectPolicy.d.ts +8 -8
  110. package/dist/ts4.2/room/DeviceManager.d.ts +12 -12
  111. package/dist/ts4.2/room/PCTransport.d.ts +268 -175
  112. package/dist/ts4.2/room/PCTransportManager.d.ts +77 -77
  113. package/dist/ts4.2/room/RTCEngine.d.ts +277 -262
  114. package/dist/ts4.2/room/ReconnectPolicy.d.ts +24 -24
  115. package/dist/ts4.2/room/RegionUrlProvider.d.ts +36 -36
  116. package/dist/ts4.2/room/Room.d.ts +407 -365
  117. package/dist/ts4.2/room/attribute-typings.d.ts +35 -35
  118. package/dist/ts4.2/room/data-channel/DataChannelManager.d.ts +63 -62
  119. package/dist/ts4.2/room/data-channel/FlowControlledDataChannel.d.ts +107 -95
  120. package/dist/ts4.2/room/data-channel/LossyDataChannel.d.ts +41 -41
  121. package/dist/ts4.2/room/data-channel/ReliableDataChannel.d.ts +57 -57
  122. package/dist/ts4.2/room/data-channel/types.d.ts +12 -12
  123. package/dist/ts4.2/room/data-stream/compression.d.ts +49 -49
  124. package/dist/ts4.2/room/data-stream/constants.d.ts +18 -18
  125. package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +29 -29
  126. package/dist/ts4.2/room/data-stream/incoming/StreamReader.d.ts +80 -80
  127. package/dist/ts4.2/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +73 -73
  128. package/dist/ts4.2/room/data-stream/outgoing/StreamWriter.d.ts +16 -16
  129. package/dist/ts4.2/room/data-stream/outgoing/header-utils.d.ts +18 -18
  130. package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +79 -80
  131. package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +50 -50
  132. package/dist/ts4.2/room/data-track/depacketizer.d.ts +59 -59
  133. package/dist/ts4.2/room/data-track/frame.d.ts +22 -22
  134. package/dist/ts4.2/room/data-track/handle.d.ts +27 -27
  135. package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +124 -124
  136. package/dist/ts4.2/room/data-track/incoming/errors.d.ts +24 -24
  137. package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +41 -41
  138. package/dist/ts4.2/room/data-track/incoming/types.d.ts +20 -20
  139. package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +126 -126
  140. package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +74 -74
  141. package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +23 -23
  142. package/dist/ts4.2/room/data-track/outgoing/types.d.ts +48 -48
  143. package/dist/ts4.2/room/data-track/packet/constants.d.ts +20 -20
  144. package/dist/ts4.2/room/data-track/packet/errors.d.ts +40 -40
  145. package/dist/ts4.2/room/data-track/packet/extensions.d.ts +69 -69
  146. package/dist/ts4.2/room/data-track/packet/index.d.ts +98 -98
  147. package/dist/ts4.2/room/data-track/packet/serializable.d.ts +13 -13
  148. package/dist/ts4.2/room/data-track/packetizer.d.ts +43 -43
  149. package/dist/ts4.2/room/data-track/track-interfaces.d.ts +23 -23
  150. package/dist/ts4.2/room/data-track/types.d.ts +22 -22
  151. package/dist/ts4.2/room/data-track/utils.d.ts +60 -60
  152. package/dist/ts4.2/room/debounce.d.ts +11 -11
  153. package/dist/ts4.2/room/defaults.d.ts +9 -9
  154. package/dist/ts4.2/room/errors.d.ts +180 -162
  155. package/dist/ts4.2/room/events.d.ts +638 -637
  156. package/dist/ts4.2/room/participant/LocalParticipant.d.ts +291 -287
  157. package/dist/ts4.2/room/participant/Participant.d.ts +142 -144
  158. package/dist/ts4.2/room/participant/ParticipantTrackPermission.d.ts +26 -26
  159. package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +85 -86
  160. package/dist/ts4.2/room/participant/publishUtils.d.ts +53 -53
  161. package/dist/ts4.2/room/rpc/client/RpcClientManager.d.ts +43 -43
  162. package/dist/ts4.2/room/rpc/client/events.d.ts +8 -8
  163. package/dist/ts4.2/room/rpc/index.d.ts +7 -7
  164. package/dist/ts4.2/room/rpc/server/RpcServerManager.d.ts +44 -44
  165. package/dist/ts4.2/room/rpc/server/events.d.ts +8 -8
  166. package/dist/ts4.2/room/rpc/utils.d.ts +131 -131
  167. package/dist/ts4.2/room/stats.d.ts +71 -71
  168. package/dist/ts4.2/room/statsSummary.d.ts +13 -13
  169. package/dist/ts4.2/room/timers.d.ts +12 -12
  170. package/dist/ts4.2/room/token-source/TokenSource.d.ts +82 -82
  171. package/dist/ts4.2/room/token-source/types.d.ts +70 -70
  172. package/dist/ts4.2/room/token-source/utils.d.ts +8 -8
  173. package/dist/ts4.2/room/track/FrameMetadataExtractor.d.ts +19 -19
  174. package/dist/ts4.2/room/track/LocalAudioTrack.d.ts +62 -42
  175. package/dist/ts4.2/room/track/LocalTrack.d.ts +143 -125
  176. package/dist/ts4.2/room/track/LocalTrackPublication.d.ts +42 -42
  177. package/dist/ts4.2/room/track/LocalVideoTrack.d.ts +109 -100
  178. package/dist/ts4.2/room/track/RemoteAudioTrack.d.ts +61 -55
  179. package/dist/ts4.2/room/track/RemoteTrack.d.ts +38 -38
  180. package/dist/ts4.2/room/track/RemoteTrackPublication.d.ts +72 -75
  181. package/dist/ts4.2/room/track/RemoteVideoTrack.d.ts +73 -76
  182. package/dist/ts4.2/room/track/Track.d.ts +156 -166
  183. package/dist/ts4.2/room/track/TrackPublication.d.ts +75 -81
  184. package/dist/ts4.2/room/track/create.d.ts +25 -25
  185. package/dist/ts4.2/room/track/facingMode.d.ts +41 -41
  186. package/dist/ts4.2/room/track/options.d.ts +403 -403
  187. package/dist/ts4.2/room/track/processor/types.d.ts +42 -31
  188. package/dist/ts4.2/room/track/record.d.ts +13 -13
  189. package/dist/ts4.2/room/track/types.d.ts +28 -28
  190. package/dist/ts4.2/room/track/utils.d.ts +60 -47
  191. package/dist/ts4.2/room/types.d.ts +127 -127
  192. package/dist/ts4.2/room/utils.d.ts +273 -214
  193. package/dist/ts4.2/test/MockMediaStreamTrack.d.ts +26 -26
  194. package/dist/ts4.2/test/compressionStreamStubs.d.ts +12 -0
  195. package/dist/ts4.2/test/mocks.d.ts +12 -12
  196. package/dist/ts4.2/test/promiseState.d.ts +12 -12
  197. package/dist/ts4.2/test/signalToken.d.ts +26 -26
  198. package/dist/ts4.2/type-polyfills/non-shared-typed-arrays.d.ts +2 -2
  199. package/dist/ts4.2/utils/AsyncQueue.d.ts +23 -23
  200. package/dist/ts4.2/utils/TypedPromise.d.ts +17 -17
  201. package/dist/ts4.2/utils/abort-signal-polyfill.d.ts +13 -13
  202. package/dist/ts4.2/utils/browserParser.d.ts +13 -13
  203. package/dist/ts4.2/utils/camelToSnakeCase.d.ts +8 -8
  204. package/dist/ts4.2/utils/cloneDeep.d.ts +2 -2
  205. package/dist/ts4.2/utils/dataPacketBuffer.d.ts +34 -34
  206. package/dist/ts4.2/utils/deferrable-map.d.ts +32 -32
  207. package/dist/ts4.2/utils/machineInspector.d.ts +54 -54
  208. package/dist/ts4.2/utils/serializer.d.ts +48 -48
  209. package/dist/ts4.2/utils/subscribeToEvents.d.ts +15 -15
  210. package/dist/ts4.2/utils/ttlmap.d.ts +20 -20
  211. package/dist/ts4.2/version.d.ts +15 -15
  212. package/package.json +5 -4
  213. package/src/api/SignalClient.e2e.test.ts +1 -3
  214. package/src/api/SignalClient.test.ts +394 -10
  215. package/src/api/SignalClient.ts +132 -76
  216. package/src/api/WebSocketStream.test.ts +11 -0
  217. package/src/api/WebSocketStream.ts +22 -1
  218. package/src/api/utils.ts +1 -1
  219. package/src/e2ee/E2eeManager.test.ts +43 -0
  220. package/src/e2ee/E2eeManager.ts +6 -2
  221. package/src/e2ee/worker/FrameCryptor.test.ts +212 -90
  222. package/src/e2ee/worker/FrameCryptor.ts +21 -4
  223. package/src/e2ee/worker/naluUtils.test.ts +123 -0
  224. package/src/e2ee/worker/naluUtils.ts +57 -2
  225. package/src/index.ts +2 -0
  226. package/src/options.ts +16 -0
  227. package/src/room/DefaultReconnectPolicy.test.ts +58 -0
  228. package/src/room/DefaultReconnectPolicy.ts +14 -2
  229. package/src/room/PCTransport.test.ts +327 -9
  230. package/src/room/PCTransport.ts +339 -36
  231. package/src/room/PCTransportManager.ts +3 -1
  232. package/src/room/RTCEngine.test.ts +318 -2
  233. package/src/room/RTCEngine.ts +126 -12
  234. package/src/room/Room.test.ts +148 -6
  235. package/src/room/Room.ts +312 -80
  236. package/src/room/data-channel/DataChannelManager.test.ts +24 -7
  237. package/src/room/data-channel/DataChannelManager.ts +43 -38
  238. package/src/room/data-channel/FlowControlledDataChannel.test.ts +60 -18
  239. package/src/room/data-channel/FlowControlledDataChannel.ts +40 -22
  240. package/src/room/data-channel/LossyDataChannel.test.ts +38 -0
  241. package/src/room/data-channel/LossyDataChannel.ts +10 -1
  242. package/src/room/data-channel/ReliableDataChannel.test.ts +72 -0
  243. package/src/room/data-channel/ReliableDataChannel.ts +16 -5
  244. package/src/room/data-stream/incoming/IncomingDataStreamManager.test.ts +47 -20
  245. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +16 -5
  246. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.test.ts +60 -39
  247. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +4 -4
  248. package/src/room/defaults.ts +1 -0
  249. package/src/room/errors.test.ts +36 -0
  250. package/src/room/errors.ts +27 -0
  251. package/src/room/events.ts +2 -1
  252. package/src/room/participant/LocalParticipant.test.ts +136 -1
  253. package/src/room/participant/LocalParticipant.ts +66 -17
  254. package/src/room/participant/Participant.ts +6 -3
  255. package/src/room/participant/publishUtils.ts +0 -19
  256. package/src/room/participant/restartOnDefaultAudioDevice.test.ts +177 -0
  257. package/src/room/rpc/client/RpcClientManager.test.ts +51 -0
  258. package/src/room/rpc/client/RpcClientManager.ts +32 -15
  259. package/src/room/rpc/utils.test.ts +32 -0
  260. package/src/room/rpc/utils.ts +7 -0
  261. package/src/room/track/LocalAudioTrack.ts +101 -10
  262. package/src/room/track/LocalTrack.ts +87 -19
  263. package/src/room/track/LocalVideoTrack.ts +17 -3
  264. package/src/room/track/RemoteAudioTrack.ts +59 -14
  265. package/src/room/track/RemoteVideoTrack.test.ts +210 -0
  266. package/src/room/track/RemoteVideoTrack.ts +29 -3
  267. package/src/room/track/processor/types.ts +11 -0
  268. package/src/room/track/utils.test.ts +206 -2
  269. package/src/room/track/utils.ts +117 -5
  270. package/src/room/utils.test.ts +250 -0
  271. package/src/room/utils.ts +152 -19
  272. package/src/test/compressionStreamStubs.ts +53 -0
@@ -1,15 +1,15 @@
1
- export declare const version: string;
2
- export declare const protocolVersion = 17;
3
- /** Initial client protocol. */
4
- export declare const CLIENT_PROTOCOL_DEFAULT = 0;
5
- /** Replaces RPC v1 protocol with a v2 data streams based one to support unlimited request /
6
- * response payload length. */
7
- export declare const CLIENT_PROTOCOL_DATA_STREAM_RPC = 1;
8
- /** "Data streams v2": the client knows how to receive a single-packet data stream (a stream whose
9
- * entire payload is smuggled into the header packet, with no chunk/trailer packets). Senders only
10
- * use the single-packet optimization when the recipient advertises at least this protocol. */
11
- export declare const CLIENT_PROTOCOL_DATA_STREAM_V2 = 2;
12
- /** The client protocol version indicates what level of support that the client has for
13
- * client <-> client api interactions. */
14
- export declare const clientProtocol = 2;
15
- //# sourceMappingURL=version.d.ts.map
1
+ export declare const version: string;
2
+ export declare const protocolVersion = 17;
3
+ /** Initial client protocol. */
4
+ export declare const CLIENT_PROTOCOL_DEFAULT = 0;
5
+ /** Replaces RPC v1 protocol with a v2 data streams based one to support unlimited request /
6
+ * response payload length. */
7
+ export declare const CLIENT_PROTOCOL_DATA_STREAM_RPC = 1;
8
+ /** "Data streams v2": the client knows how to receive a single-packet data stream (a stream whose
9
+ * entire payload is smuggled into the header packet, with no chunk/trailer packets). Senders only
10
+ * use the single-packet optimization when the recipient advertises at least this protocol. */
11
+ export declare const CLIENT_PROTOCOL_DATA_STREAM_V2 = 2;
12
+ /** The client protocol version indicates what level of support that the client has for
13
+ * client <-> client api interactions. */
14
+ export declare const clientProtocol = 2;
15
+ //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livekit-client",
3
- "version": "2.22.3",
3
+ "version": "2.23.0",
4
4
  "description": "JavaScript/TypeScript client SDK for LiveKit",
5
5
  "main": "./dist/livekit-client.umd.js",
6
6
  "unpkg": "./dist/livekit-client.umd.js",
@@ -50,7 +50,7 @@
50
50
  "license": "Apache-2.0",
51
51
  "dependencies": {
52
52
  "@livekit/mutex": "1.1.1",
53
- "@livekit/protocol": "1.50.4",
53
+ "@livekit/protocol": "1.52.1",
54
54
  "events": "^3.3.0",
55
55
  "jose": "^6.1.0",
56
56
  "loglevel": "^1.9.2",
@@ -85,9 +85,10 @@
85
85
  "@types/ua-parser-js": "0.7.39",
86
86
  "@typescript-eslint/eslint-plugin": "8.64.0",
87
87
  "@typescript-eslint/parser": "8.64.0",
88
+ "@typescript/native": "npm:typescript@^7.0.2",
88
89
  "@vitest/browser": "^4.1.10",
89
90
  "@vitest/browser-playwright": "^4.1.10",
90
- "downlevel-dts": "^0.11.0",
91
+ "@nbilyk/downlevel-dts": "^0.15.2",
91
92
  "eslint": "10.7.0",
92
93
  "eslint-config-airbnb-extended": "^2.3.2",
93
94
  "eslint-config-prettier": "10.1.8",
@@ -113,7 +114,7 @@
113
114
  "tsx": "^4.21.0",
114
115
  "typedoc": "0.28.20",
115
116
  "typedoc-plugin-no-inherit": "1.6.1",
116
- "typescript": "5.9.3",
117
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
117
118
  "typescript-eslint": "^8.47.0",
118
119
  "vite": "8.1.5",
119
120
  "vitest": "^4.1.10"
@@ -174,9 +174,7 @@ describe.skipIf(!!unavailable)('SignalClient e2e', () => {
174
174
 
175
175
  it('rejects a reconnect when a leave arrives as the first message', async () => {
176
176
  // Reconnecting into a room that sends leave-first exercises the client's
177
- // first-message validation while in RECONNECTING (path-independent: it
178
- // doesn't rely on the mock detecting reconnect, which v1 hides inside the
179
- // gzipped join_request the mock ignores).
177
+ // first-message validation while in RECONNECTING.
180
178
  await join('happy');
181
179
  const token = await createToken({ signal: 'leave_first_message' });
182
180
  const err = await client.reconnect(serverUrl, token, 'RM_session').then(
@@ -11,8 +11,10 @@ import {
11
11
  WrappedJoinRequest_Compression,
12
12
  } from '@livekit/protocol';
13
13
  import { beforeEach, describe, expect, it, vi } from 'vitest';
14
+ import type { Mutex } from '@livekit/mutex';
14
15
  import { ConnectionError, ConnectionErrorReason } from '../room/errors';
15
16
  import CriticalTimers from '../room/timers';
17
+ import { Future } from '../room/utils';
16
18
  import { SignalClient, SignalConnectionState } from './SignalClient';
17
19
  import type { WebSocketCloseInfo, WebSocketConnection } from './WebSocketStream';
18
20
  import { WebSocketStream } from './WebSocketStream';
@@ -147,6 +149,167 @@ describe('SignalClient.connect', () => {
147
149
  return JoinRequest.fromBinary(bytes);
148
150
  }
149
151
 
152
+ describe('Cancelled connection attempts', () => {
153
+ it.each(['before join', 'while waiting for the connection lock'])(
154
+ 'does not open a transport when aborted %s',
155
+ async (when) => {
156
+ const abortController = new AbortController();
157
+ const internals = signalClient as unknown as { connectionLock: Mutex };
158
+ const unlock = await internals.connectionLock.lock();
159
+ mockWebSocketStream({
160
+ connection: createMockConnection(
161
+ createMockReadableStream([createSignalResponse('join', createJoinResponse())]),
162
+ ),
163
+ });
164
+ if (when === 'before join') abortController.abort();
165
+ const joining = signalClient.join(
166
+ 'wss://test.livekit.io',
167
+ 'test-token',
168
+ defaultOptions,
169
+ abortController.signal,
170
+ );
171
+ abortController.abort();
172
+ unlock();
173
+
174
+ try {
175
+ await expect(joining).rejects.toMatchObject({ reason: ConnectionErrorReason.Cancelled });
176
+ expect(WebSocketStream).not.toHaveBeenCalled();
177
+ } finally {
178
+ await signalClient.close();
179
+ vi.mocked(WebSocketStream).mockReset();
180
+ }
181
+ },
182
+ );
183
+
184
+ it('does not replace a transport when aborted during its teardown', async () => {
185
+ const abortController = new AbortController();
186
+ const internals = signalClient as unknown as {
187
+ connectionLock: Mutex;
188
+ teardownTransport: (reason: string) => Promise<void>;
189
+ };
190
+ const teardownStarted = new Future<void, Error>();
191
+ const finishTeardown = new Future<void, Error>();
192
+ vi.spyOn(internals, 'teardownTransport').mockImplementationOnce(async () => {
193
+ teardownStarted.resolve?.();
194
+ await finishTeardown.promise;
195
+ });
196
+ signalClient.ws = {
197
+ close: () => {},
198
+ closed: Promise.resolve({ closeCode: 1000, reason: '' }),
199
+ } as WebSocketStream;
200
+ mockWebSocketStream({
201
+ connection: createMockConnection(
202
+ createMockReadableStream([createSignalResponse('join', createJoinResponse())]),
203
+ ),
204
+ });
205
+
206
+ const joining = signalClient.join(
207
+ 'wss://test.livekit.io',
208
+ 'test-token',
209
+ defaultOptions,
210
+ abortController.signal,
211
+ );
212
+ const cancelled = expect(joining).rejects.toMatchObject({
213
+ reason: ConnectionErrorReason.Cancelled,
214
+ });
215
+ await teardownStarted.promise;
216
+ abortController.abort();
217
+ finishTeardown.resolve?.();
218
+ await cancelled;
219
+ // The rejection can settle before the transport replacement resumes.
220
+ const unlock = await internals.connectionLock.lock();
221
+ unlock();
222
+
223
+ try {
224
+ expect(WebSocketStream).not.toHaveBeenCalled();
225
+ } finally {
226
+ await signalClient.close();
227
+ vi.mocked(WebSocketStream).mockReset();
228
+ }
229
+ });
230
+ it('does not validate the url when aborted while the websocket is opening', async () => {
231
+ const abortController = new AbortController();
232
+ const internals = signalClient as unknown as { connectionLock: Mutex };
233
+ const opened = new Future<WebSocketConnection, Error>();
234
+ opened.promise.catch(() => {});
235
+ mockWebSocketStream({ opened: opened.promise, readyState: 0 });
236
+ vi.mocked(global.fetch).mockClear();
237
+
238
+ const joining = signalClient.join(
239
+ 'wss://test.livekit.io',
240
+ 'test-token',
241
+ defaultOptions,
242
+ abortController.signal,
243
+ );
244
+ const cancelled = expect(joining).rejects.toMatchObject({
245
+ reason: ConnectionErrorReason.Cancelled,
246
+ });
247
+ await vi.waitFor(() => expect(WebSocketStream).toHaveBeenCalled());
248
+ abortController.abort();
249
+ opened.reject?.(new Error('closed by abort'));
250
+ await cancelled;
251
+ // let the attempt's continuation run to completion
252
+ const unlock = await internals.connectionLock.lock();
253
+ unlock();
254
+
255
+ try {
256
+ expect(global.fetch).not.toHaveBeenCalled();
257
+ } finally {
258
+ await signalClient.close();
259
+ vi.mocked(WebSocketStream).mockReset();
260
+ }
261
+ });
262
+
263
+ it('does not report a join that arrives after the attempt was aborted', async () => {
264
+ const abortController = new AbortController();
265
+ const internals = signalClient as unknown as { connectionLock: Mutex };
266
+ let enqueue: ((chunk: ArrayBuffer) => void) | undefined;
267
+ const readable = new ReadableStream<ArrayBuffer>({
268
+ start(controller) {
269
+ enqueue = (chunk) => controller.enqueue(chunk);
270
+ },
271
+ });
272
+ const writerReady = new Future<void, Error>();
273
+ const writable = new WritableStream();
274
+ const getWriter = writable.getWriter.bind(writable);
275
+ writable.getWriter = () => {
276
+ const writer = getWriter();
277
+ writerReady.resolve?.();
278
+ return writer;
279
+ };
280
+ mockWebSocketStream({
281
+ connection: { readable, writable, protocol: '', extensions: '' },
282
+ });
283
+ const onJoined = vi.fn();
284
+ signalClient.onJoined = onJoined;
285
+
286
+ const joining = signalClient.join(
287
+ 'wss://test.livekit.io',
288
+ 'test-token',
289
+ defaultOptions,
290
+ abortController.signal,
291
+ );
292
+ const cancelled = expect(joining).rejects.toMatchObject({
293
+ reason: ConnectionErrorReason.Cancelled,
294
+ });
295
+ await writerReady.promise;
296
+ abortController.abort();
297
+ enqueue?.(
298
+ createSignalResponse('join', createJoinResponse()).toBinary().buffer as ArrayBuffer,
299
+ );
300
+ await cancelled;
301
+ const unlock = await internals.connectionLock.lock();
302
+ unlock();
303
+
304
+ try {
305
+ expect(onJoined).not.toHaveBeenCalled();
306
+ } finally {
307
+ await signalClient.close();
308
+ vi.mocked(WebSocketStream).mockReset();
309
+ }
310
+ });
311
+ });
312
+
150
313
  describe('Happy Path - Initial Join', () => {
151
314
  it('should successfully connect and receive join response', async () => {
152
315
  const joinResponse = createJoinResponse();
@@ -162,6 +325,17 @@ describe('SignalClient.connect', () => {
162
325
  expect(signalClient.currentState).toBe(SignalConnectionState.CONNECTED);
163
326
  });
164
327
 
328
+ it('releases the connection lock when the url is malformed', async () => {
329
+ await expect(signalClient.join('not a url', 'test-token', defaultOptions)).rejects.toThrow();
330
+
331
+ const joinResponse = createJoinResponse();
332
+ const mockReadable = createMockReadableStream([createSignalResponse('join', joinResponse)]);
333
+ mockWebSocketStream({ connection: createMockConnection(mockReadable) });
334
+
335
+ const result = await signalClient.join('wss://test.livekit.io', 'test-token', defaultOptions);
336
+ expect(result).toEqual(joinResponse);
337
+ });
338
+
165
339
  it('does not advertise packet trailer capability by default', async () => {
166
340
  const joinResponse = createJoinResponse();
167
341
  const signalResponse = createSignalResponse('join', joinResponse);
@@ -206,6 +380,152 @@ describe('SignalClient.connect', () => {
206
380
  ClientInfo_Capability.CAP_PACKET_TRAILER,
207
381
  ]);
208
382
  });
383
+
384
+ it('does not request full ICE by default', async () => {
385
+ const joinResponse = createJoinResponse();
386
+ const signalResponse = createSignalResponse('join', joinResponse);
387
+ const mockReadable = createMockReadableStream([signalResponse]);
388
+ const mockConnection = createMockConnection(mockReadable);
389
+ let capturedUrl = '';
390
+
391
+ mockWebSocketStream({
392
+ connection: mockConnection,
393
+ onUrl: (url) => {
394
+ capturedUrl = url;
395
+ },
396
+ });
397
+
398
+ await signalClient.join('wss://test.livekit.io', 'test-token', defaultOptions);
399
+
400
+ const joinRequest = await decodeJoinRequestFromUrl(capturedUrl);
401
+ expect(joinRequest.connectionSettings?.disableIceLite).toBe(false);
402
+ });
403
+
404
+ it('requests full ICE in the join request when disableIceLite is set', async () => {
405
+ const joinResponse = createJoinResponse();
406
+ const signalResponse = createSignalResponse('join', joinResponse);
407
+ const mockReadable = createMockReadableStream([signalResponse]);
408
+ const mockConnection = createMockConnection(mockReadable);
409
+ let capturedUrl = '';
410
+
411
+ mockWebSocketStream({
412
+ connection: mockConnection,
413
+ onUrl: (url) => {
414
+ capturedUrl = url;
415
+ },
416
+ });
417
+
418
+ await signalClient.join('wss://test.livekit.io', 'test-token', {
419
+ ...defaultOptions,
420
+ disableIceLite: true,
421
+ });
422
+
423
+ const joinRequest = await decodeJoinRequestFromUrl(capturedUrl);
424
+ expect(joinRequest.connectionSettings?.disableIceLite).toBe(true);
425
+ });
426
+
427
+ it('requests full ICE on the v0 path when disableIceLite is set', async () => {
428
+ const joinResponse = createJoinResponse();
429
+ const signalResponse = createSignalResponse('join', joinResponse);
430
+ const mockReadable = createMockReadableStream([signalResponse]);
431
+ const mockConnection = createMockConnection(mockReadable);
432
+ let capturedUrl = '';
433
+
434
+ mockWebSocketStream({
435
+ connection: mockConnection,
436
+ onUrl: (url) => {
437
+ capturedUrl = url;
438
+ },
439
+ });
440
+
441
+ await signalClient.join(
442
+ 'wss://test.livekit.io',
443
+ 'test-token',
444
+ { ...defaultOptions, disableIceLite: true },
445
+ undefined,
446
+ true,
447
+ );
448
+
449
+ expect(new URL(capturedUrl).searchParams.get('disable_ice_lite')).toBe('1');
450
+ });
451
+
452
+ it('leaves subscriberAllowPause unset by default so the server default applies', async () => {
453
+ const joinResponse = createJoinResponse();
454
+ const signalResponse = createSignalResponse('join', joinResponse);
455
+ const mockReadable = createMockReadableStream([signalResponse]);
456
+ const mockConnection = createMockConnection(mockReadable);
457
+ let capturedUrl = '';
458
+
459
+ mockWebSocketStream({
460
+ connection: mockConnection,
461
+ onUrl: (url) => {
462
+ capturedUrl = url;
463
+ },
464
+ });
465
+
466
+ await signalClient.join('wss://test.livekit.io', 'test-token', defaultOptions);
467
+
468
+ const joinRequest = await decodeJoinRequestFromUrl(capturedUrl);
469
+ expect(joinRequest.connectionSettings?.subscriberAllowPause).toBeUndefined();
470
+ });
471
+
472
+ it.each([true, false])(
473
+ 'sets subscriberAllowPause=%s in the join request',
474
+ async (subscriberAllowPause) => {
475
+ const joinResponse = createJoinResponse();
476
+ const signalResponse = createSignalResponse('join', joinResponse);
477
+ const mockReadable = createMockReadableStream([signalResponse]);
478
+ const mockConnection = createMockConnection(mockReadable);
479
+ let capturedUrl = '';
480
+
481
+ mockWebSocketStream({
482
+ connection: mockConnection,
483
+ onUrl: (url) => {
484
+ capturedUrl = url;
485
+ },
486
+ });
487
+
488
+ await signalClient.join('wss://test.livekit.io', 'test-token', {
489
+ ...defaultOptions,
490
+ subscriberAllowPause,
491
+ });
492
+
493
+ const joinRequest = await decodeJoinRequestFromUrl(capturedUrl);
494
+ expect(joinRequest.connectionSettings?.subscriberAllowPause).toBe(subscriberAllowPause);
495
+ },
496
+ );
497
+
498
+ it.each([
499
+ [undefined, null],
500
+ [true, '1'],
501
+ [false, '0'],
502
+ ])(
503
+ 'sets subscriber_allow_pause on the v0 path when subscriberAllowPause=%s',
504
+ async (subscriberAllowPause, expected) => {
505
+ const joinResponse = createJoinResponse();
506
+ const signalResponse = createSignalResponse('join', joinResponse);
507
+ const mockReadable = createMockReadableStream([signalResponse]);
508
+ const mockConnection = createMockConnection(mockReadable);
509
+ let capturedUrl = '';
510
+
511
+ mockWebSocketStream({
512
+ connection: mockConnection,
513
+ onUrl: (url) => {
514
+ capturedUrl = url;
515
+ },
516
+ });
517
+
518
+ await signalClient.join(
519
+ 'wss://test.livekit.io',
520
+ 'test-token',
521
+ { ...defaultOptions, subscriberAllowPause },
522
+ undefined,
523
+ true,
524
+ );
525
+
526
+ expect(new URL(capturedUrl).searchParams.get('subscriber_allow_pause')).toBe(expected);
527
+ },
528
+ );
209
529
  });
210
530
 
211
531
  describe('Happy Path - Reconnect', () => {
@@ -236,7 +556,7 @@ describe('SignalClient.connect', () => {
236
556
  expect(signalClient.currentState).toBe(SignalConnectionState.CONNECTED);
237
557
  });
238
558
 
239
- it('should handle reconnect with non-reconnect message (edge case)', async () => {
559
+ it('drops stray messages during reconnect and waits for the reconnect response', async () => {
240
560
  // First, initial connection
241
561
  const joinResponse = createJoinResponse();
242
562
  const joinSignalResponse = createSignalResponse('join', joinResponse);
@@ -247,19 +567,24 @@ describe('SignalClient.connect', () => {
247
567
 
248
568
  await signalClient.join('wss://test.livekit.io', 'test-token', defaultOptions);
249
569
 
250
- // Setup reconnect with non-reconnect message (e.g., participant update)
570
+ // Server sends a stray update first, then the actual reconnect response.
571
+ // The stray should be dropped with a warning; the reconnect response should resolve.
251
572
  const updateSignalResponse = createSignalResponse('update', { participants: [] });
252
- const reconnectMockReadable = createMockReadableStream([updateSignalResponse]);
573
+ const reconnectResponse = new ReconnectResponse({ iceServers: [] });
574
+ const reconnectSignalResponse = createSignalResponse('reconnect', reconnectResponse);
575
+ const reconnectMockReadable = createMockReadableStream([
576
+ updateSignalResponse,
577
+ reconnectSignalResponse,
578
+ ]);
253
579
  const reconnectMockConnection = createMockConnection(reconnectMockReadable);
254
580
 
255
581
  mockWebSocketStream({ connection: reconnectMockConnection });
256
582
 
257
583
  const result = await signalClient.reconnect('wss://test.livekit.io', 'test-token', 'sid-123');
258
584
 
259
- // This is an edge case: reconnect resolves with undefined when non-reconnect message is received
260
- expect(result).toBeUndefined();
585
+ expect(result).toEqual(reconnectResponse);
261
586
  expect(signalClient.currentState).toBe(SignalConnectionState.CONNECTED);
262
- }, 1000);
587
+ });
263
588
  });
264
589
 
265
590
  describe('Failure Case - Timeout', () => {
@@ -742,6 +1067,66 @@ describe('SignalClient.connect', () => {
742
1067
  });
743
1068
  });
744
1069
 
1070
+ describe('Failure Case - Validate Request Hangs', () => {
1071
+ // A socket that never opens on its own (a lossy network) and whose `opened` rejects once the
1072
+ // client closes it, like a real WebSocketStream aborted during the handshake.
1073
+ function mockHangingWebSocket() {
1074
+ vi.mocked(WebSocketStream).mockImplementationOnce(function () {
1075
+ let rejectOpened!: (e: unknown) => void;
1076
+ let resolveClosed!: (info: WebSocketCloseInfo) => void;
1077
+ return {
1078
+ url: 'wss://test.livekit.io',
1079
+ opened: new Promise((_, reject) => (rejectOpened = reject)),
1080
+ closed: new Promise((resolve) => (resolveClosed = resolve)),
1081
+ close: vi.fn(() => {
1082
+ rejectOpened(new Error('aborted during handshake'));
1083
+ resolveClosed({ closeCode: 1006, reason: '' });
1084
+ }),
1085
+ readyState: 0,
1086
+ } as any;
1087
+ });
1088
+ }
1089
+
1090
+ it('does not let a timed-out attempt block the next one', async () => {
1091
+ // A validate request on a dead pooled connection only settles once its abort signal fires.
1092
+ // Without a deadline it held the connection lock, so the next attempt never started. A
1093
+ // timed-out attempt now skips validation entirely, since its caller has already been rejected.
1094
+ vi.useFakeTimers();
1095
+ try {
1096
+ const hangingFetch = (_url: unknown, init?: RequestInit) =>
1097
+ new Promise<Response>((_, reject) => {
1098
+ init?.signal?.addEventListener('abort', () => reject(init.signal!.reason));
1099
+ });
1100
+ vi.mocked(fetch).mockImplementationOnce(hangingFetch).mockImplementationOnce(hangingFetch);
1101
+ mockHangingWebSocket();
1102
+ mockHangingWebSocket();
1103
+
1104
+ // building the join request finishes on real I/O, so wait for each socket before advancing
1105
+ const first = expect(
1106
+ signalClient.join('wss://test.livekit.io', 'test-token', defaultOptions),
1107
+ ).rejects.toMatchObject({ reason: ConnectionErrorReason.Timeout });
1108
+ await vi.waitFor(() => expect(WebSocketStream).toHaveBeenCalledTimes(1));
1109
+ await vi.advanceTimersByTimeAsync(defaultOptions.websocketTimeout);
1110
+ await first;
1111
+
1112
+ const second = expect(
1113
+ signalClient.join('wss://test.livekit.io', 'test-token', defaultOptions),
1114
+ ).rejects.toMatchObject({ reason: ConnectionErrorReason.Timeout });
1115
+ // past the validate deadline, which is what releases the lock the first attempt holds
1116
+ await vi.advanceTimersByTimeAsync(10_000);
1117
+ await vi.waitFor(() => expect(WebSocketStream).toHaveBeenCalledTimes(2));
1118
+ await vi.advanceTimersByTimeAsync(defaultOptions.websocketTimeout);
1119
+ await second;
1120
+
1121
+ expect(fetch).not.toHaveBeenCalled();
1122
+ } finally {
1123
+ vi.useRealTimers();
1124
+ // drop the unused hanging fetches so they don't leak into later tests
1125
+ vi.mocked(fetch).mockReset();
1126
+ }
1127
+ });
1128
+ });
1129
+
745
1130
  describe('Establishing over an existing session', () => {
746
1131
  async function joinSuccessfully() {
747
1132
  mockWebSocketStream({
@@ -1026,7 +1411,7 @@ describe('SignalClient.validateFirstMessage', () => {
1026
1411
  }
1027
1412
  });
1028
1413
 
1029
- it('should accept non-reconnect message during reconnecting state', async () => {
1414
+ it('should reject non-reconnect message during reconnecting state', async () => {
1030
1415
  // First establish a connection
1031
1416
  const joinResponse = createJoinResponse();
1032
1417
  const joinSignalResponse = createSignalResponse('join', joinResponse);
@@ -1044,9 +1429,8 @@ describe('SignalClient.validateFirstMessage', () => {
1044
1429
  const validateMethod = (signalClient as any).validateFirstMessage;
1045
1430
  if (validateMethod) {
1046
1431
  const result = validateMethod.call(signalClient, updateSignalResponse, true);
1047
- expect(result.isValid).toBe(true);
1048
- expect(result.response).toBeUndefined();
1049
- expect(result.shouldProcessFirstMessage).toBe(true);
1432
+ expect(result.isValid).toBe(false);
1433
+ expect(result.error).toBeInstanceOf(ConnectionError);
1050
1434
  }
1051
1435
  });
1052
1436