livekit-client 2.22.3 → 2.22.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +2 -1
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +602 -247
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.fm.worker.js +1 -1
- package/dist/livekit-client.fm.worker.js.map +1 -1
- package/dist/livekit-client.fm.worker.mjs +2 -1
- package/dist/livekit-client.fm.worker.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts +4 -2
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/options.d.ts +14 -0
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +78 -7
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/PCTransportManager.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +8 -0
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +1 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/defaults.d.ts.map +1 -1
- package/dist/src/room/errors.d.ts +18 -0
- package/dist/src/room/errors.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +3 -2
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
- package/dist/src/room/rpc/client/RpcClientManager.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +2 -0
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/utils.d.ts +13 -0
- package/dist/src/room/track/utils.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +32 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/test/compressionStreamStubs.d.ts +12 -0
- package/dist/src/test/compressionStreamStubs.d.ts.map +1 -0
- package/dist/ts4.2/api/SignalClient.d.ts +187 -185
- package/dist/ts4.2/api/SignalClientStateMachine.d.ts +85 -85
- package/dist/ts4.2/api/WebSocketStream.d.ts +33 -33
- package/dist/ts4.2/api/utils.d.ts +9 -9
- package/dist/ts4.2/connectionHelper/ConnectionCheck.d.ts +30 -30
- package/dist/ts4.2/connectionHelper/checks/Checker.d.ts +62 -62
- package/dist/ts4.2/connectionHelper/checks/cloudRegion.d.ts +18 -18
- package/dist/ts4.2/connectionHelper/checks/connectionProtocol.d.ts +20 -20
- package/dist/ts4.2/connectionHelper/checks/publishAudio.d.ts +6 -6
- package/dist/ts4.2/connectionHelper/checks/publishVideo.d.ts +7 -7
- package/dist/ts4.2/connectionHelper/checks/reconnect.d.ts +6 -6
- package/dist/ts4.2/connectionHelper/checks/turn.d.ts +6 -6
- package/dist/ts4.2/connectionHelper/checks/webrtc.d.ts +6 -6
- package/dist/ts4.2/connectionHelper/checks/websocket.d.ts +6 -6
- package/dist/ts4.2/e2ee/E2eeManager.d.ts +111 -111
- package/dist/ts4.2/e2ee/KeyProvider.d.ts +54 -54
- package/dist/ts4.2/e2ee/constants.d.ts +21 -21
- package/dist/ts4.2/e2ee/errors.d.ts +12 -12
- package/dist/ts4.2/e2ee/events.d.ts +39 -39
- package/dist/ts4.2/e2ee/index.d.ts +6 -6
- package/dist/ts4.2/e2ee/types.d.ts +231 -231
- package/dist/ts4.2/e2ee/utils.d.ts +32 -32
- package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +16 -16
- package/dist/ts4.2/e2ee/worker/ErrorRateLimiter.d.ts +21 -21
- package/dist/ts4.2/e2ee/worker/FrameCryptor.d.ts +209 -209
- package/dist/ts4.2/e2ee/worker/ParticipantKeyHandler.d.ts +81 -84
- package/dist/ts4.2/e2ee/worker/e2ee.worker.d.ts +2 -2
- package/dist/ts4.2/e2ee/worker/naluUtils.d.ts +28 -28
- package/dist/ts4.2/e2ee/worker/sifPayload.d.ts +23 -23
- package/dist/ts4.2/frameMetadata/FrameMetadataManager.d.ts +53 -53
- package/dist/ts4.2/frameMetadata/frameMetadata.d.ts +33 -33
- package/dist/ts4.2/frameMetadata/types.d.ts +62 -62
- package/dist/ts4.2/frameMetadata/utils.d.ts +9 -9
- package/dist/ts4.2/frameMetadata/worker/frameMetadata.worker.d.ts +2 -2
- package/dist/ts4.2/index.d.ts +76 -76
- package/dist/ts4.2/logger.d.ts +66 -66
- package/dist/ts4.2/options.d.ts +163 -149
- package/dist/ts4.2/room/BackOffStrategy.d.ts +27 -27
- package/dist/ts4.2/room/DefaultReconnectPolicy.d.ts +8 -8
- package/dist/ts4.2/room/DeviceManager.d.ts +12 -12
- package/dist/ts4.2/room/PCTransport.d.ts +246 -175
- package/dist/ts4.2/room/PCTransportManager.d.ts +77 -77
- package/dist/ts4.2/room/RTCEngine.d.ts +269 -262
- package/dist/ts4.2/room/ReconnectPolicy.d.ts +24 -24
- package/dist/ts4.2/room/RegionUrlProvider.d.ts +36 -36
- package/dist/ts4.2/room/Room.d.ts +354 -365
- package/dist/ts4.2/room/attribute-typings.d.ts +35 -35
- package/dist/ts4.2/room/data-channel/DataChannelManager.d.ts +62 -62
- package/dist/ts4.2/room/data-channel/FlowControlledDataChannel.d.ts +94 -95
- package/dist/ts4.2/room/data-channel/LossyDataChannel.d.ts +41 -41
- package/dist/ts4.2/room/data-channel/ReliableDataChannel.d.ts +57 -57
- package/dist/ts4.2/room/data-channel/types.d.ts +12 -12
- package/dist/ts4.2/room/data-stream/compression.d.ts +49 -49
- package/dist/ts4.2/room/data-stream/constants.d.ts +18 -18
- package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +29 -29
- package/dist/ts4.2/room/data-stream/incoming/StreamReader.d.ts +80 -80
- package/dist/ts4.2/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +73 -73
- package/dist/ts4.2/room/data-stream/outgoing/StreamWriter.d.ts +16 -16
- package/dist/ts4.2/room/data-stream/outgoing/header-utils.d.ts +18 -18
- package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +79 -80
- package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +50 -50
- package/dist/ts4.2/room/data-track/depacketizer.d.ts +59 -59
- package/dist/ts4.2/room/data-track/frame.d.ts +22 -22
- package/dist/ts4.2/room/data-track/handle.d.ts +27 -27
- package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +124 -124
- package/dist/ts4.2/room/data-track/incoming/errors.d.ts +24 -24
- package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +41 -41
- package/dist/ts4.2/room/data-track/incoming/types.d.ts +20 -20
- package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +126 -126
- package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +74 -74
- package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +23 -23
- package/dist/ts4.2/room/data-track/outgoing/types.d.ts +48 -48
- package/dist/ts4.2/room/data-track/packet/constants.d.ts +20 -20
- package/dist/ts4.2/room/data-track/packet/errors.d.ts +40 -40
- package/dist/ts4.2/room/data-track/packet/extensions.d.ts +69 -69
- package/dist/ts4.2/room/data-track/packet/index.d.ts +98 -98
- package/dist/ts4.2/room/data-track/packet/serializable.d.ts +13 -13
- package/dist/ts4.2/room/data-track/packetizer.d.ts +43 -43
- package/dist/ts4.2/room/data-track/track-interfaces.d.ts +23 -23
- package/dist/ts4.2/room/data-track/types.d.ts +22 -22
- package/dist/ts4.2/room/data-track/utils.d.ts +60 -60
- package/dist/ts4.2/room/debounce.d.ts +11 -11
- package/dist/ts4.2/room/defaults.d.ts +9 -9
- package/dist/ts4.2/room/errors.d.ts +180 -162
- package/dist/ts4.2/room/events.d.ts +638 -637
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +287 -287
- package/dist/ts4.2/room/participant/Participant.d.ts +142 -144
- package/dist/ts4.2/room/participant/ParticipantTrackPermission.d.ts +26 -26
- package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +85 -86
- package/dist/ts4.2/room/participant/publishUtils.d.ts +53 -53
- package/dist/ts4.2/room/rpc/client/RpcClientManager.d.ts +43 -43
- package/dist/ts4.2/room/rpc/client/events.d.ts +8 -8
- package/dist/ts4.2/room/rpc/index.d.ts +7 -7
- package/dist/ts4.2/room/rpc/server/RpcServerManager.d.ts +44 -44
- package/dist/ts4.2/room/rpc/server/events.d.ts +8 -8
- package/dist/ts4.2/room/rpc/utils.d.ts +131 -131
- package/dist/ts4.2/room/stats.d.ts +71 -71
- package/dist/ts4.2/room/statsSummary.d.ts +13 -13
- package/dist/ts4.2/room/timers.d.ts +12 -12
- package/dist/ts4.2/room/token-source/TokenSource.d.ts +82 -82
- package/dist/ts4.2/room/token-source/types.d.ts +70 -70
- package/dist/ts4.2/room/token-source/utils.d.ts +8 -8
- package/dist/ts4.2/room/track/FrameMetadataExtractor.d.ts +19 -19
- package/dist/ts4.2/room/track/LocalAudioTrack.d.ts +39 -42
- package/dist/ts4.2/room/track/LocalTrack.d.ts +125 -125
- package/dist/ts4.2/room/track/LocalTrackPublication.d.ts +42 -42
- package/dist/ts4.2/room/track/LocalVideoTrack.d.ts +102 -100
- package/dist/ts4.2/room/track/RemoteAudioTrack.d.ts +55 -55
- package/dist/ts4.2/room/track/RemoteTrack.d.ts +38 -38
- package/dist/ts4.2/room/track/RemoteTrackPublication.d.ts +72 -75
- package/dist/ts4.2/room/track/RemoteVideoTrack.d.ts +73 -76
- package/dist/ts4.2/room/track/Track.d.ts +156 -166
- package/dist/ts4.2/room/track/TrackPublication.d.ts +75 -81
- package/dist/ts4.2/room/track/create.d.ts +25 -25
- package/dist/ts4.2/room/track/facingMode.d.ts +41 -41
- package/dist/ts4.2/room/track/options.d.ts +403 -403
- package/dist/ts4.2/room/track/processor/types.d.ts +31 -31
- package/dist/ts4.2/room/track/record.d.ts +13 -13
- package/dist/ts4.2/room/track/types.d.ts +28 -28
- package/dist/ts4.2/room/track/utils.d.ts +60 -47
- package/dist/ts4.2/room/types.d.ts +127 -127
- package/dist/ts4.2/room/utils.d.ts +246 -214
- package/dist/ts4.2/test/MockMediaStreamTrack.d.ts +26 -26
- package/dist/ts4.2/test/compressionStreamStubs.d.ts +12 -0
- package/dist/ts4.2/test/mocks.d.ts +12 -12
- package/dist/ts4.2/test/promiseState.d.ts +12 -12
- package/dist/ts4.2/test/signalToken.d.ts +26 -26
- package/dist/ts4.2/type-polyfills/non-shared-typed-arrays.d.ts +2 -2
- package/dist/ts4.2/utils/AsyncQueue.d.ts +23 -23
- package/dist/ts4.2/utils/TypedPromise.d.ts +17 -17
- package/dist/ts4.2/utils/abort-signal-polyfill.d.ts +13 -13
- package/dist/ts4.2/utils/browserParser.d.ts +13 -13
- package/dist/ts4.2/utils/camelToSnakeCase.d.ts +8 -8
- package/dist/ts4.2/utils/cloneDeep.d.ts +2 -2
- package/dist/ts4.2/utils/dataPacketBuffer.d.ts +34 -34
- package/dist/ts4.2/utils/deferrable-map.d.ts +32 -32
- package/dist/ts4.2/utils/machineInspector.d.ts +54 -54
- package/dist/ts4.2/utils/serializer.d.ts +48 -48
- package/dist/ts4.2/utils/subscribeToEvents.d.ts +15 -15
- package/dist/ts4.2/utils/ttlmap.d.ts +20 -20
- package/dist/ts4.2/version.d.ts +15 -15
- package/package.json +5 -4
- package/src/api/SignalClient.e2e.test.ts +1 -3
- package/src/api/SignalClient.test.ts +160 -10
- package/src/api/SignalClient.ts +75 -54
- package/src/options.ts +16 -0
- package/src/room/PCTransport.test.ts +183 -7
- package/src/room/PCTransport.ts +197 -36
- package/src/room/PCTransportManager.ts +3 -1
- package/src/room/RTCEngine.test.ts +84 -0
- package/src/room/RTCEngine.ts +24 -5
- package/src/room/Room.test.ts +32 -6
- package/src/room/Room.ts +26 -6
- package/src/room/data-stream/incoming/IncomingDataStreamManager.test.ts +16 -20
- package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +5 -5
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.test.ts +60 -39
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +4 -4
- package/src/room/defaults.ts +1 -0
- package/src/room/errors.test.ts +36 -0
- package/src/room/errors.ts +27 -0
- package/src/room/events.ts +2 -1
- package/src/room/participant/publishUtils.ts +0 -19
- package/src/room/rpc/client/RpcClientManager.test.ts +51 -0
- package/src/room/rpc/client/RpcClientManager.ts +32 -15
- package/src/room/track/LocalTrack.ts +18 -7
- package/src/room/track/LocalVideoTrack.ts +10 -3
- package/src/room/track/RemoteAudioTrack.ts +3 -3
- package/src/room/track/RemoteVideoTrack.test.ts +210 -0
- package/src/room/track/RemoteVideoTrack.ts +29 -3
- package/src/room/track/utils.test.ts +201 -2
- package/src/room/track/utils.ts +88 -0
- package/src/room/utils.test.ts +124 -0
- package/src/room/utils.ts +70 -0
- package/src/test/compressionStreamStubs.ts +53 -0
package/dist/ts4.2/version.d.ts
CHANGED
|
@@ -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
|
+
"version": "2.22.4",
|
|
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.
|
|
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.
|
|
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": "
|
|
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
|
|
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(
|
|
@@ -206,6 +206,152 @@ describe('SignalClient.connect', () => {
|
|
|
206
206
|
ClientInfo_Capability.CAP_PACKET_TRAILER,
|
|
207
207
|
]);
|
|
208
208
|
});
|
|
209
|
+
|
|
210
|
+
it('does not request full ICE by default', async () => {
|
|
211
|
+
const joinResponse = createJoinResponse();
|
|
212
|
+
const signalResponse = createSignalResponse('join', joinResponse);
|
|
213
|
+
const mockReadable = createMockReadableStream([signalResponse]);
|
|
214
|
+
const mockConnection = createMockConnection(mockReadable);
|
|
215
|
+
let capturedUrl = '';
|
|
216
|
+
|
|
217
|
+
mockWebSocketStream({
|
|
218
|
+
connection: mockConnection,
|
|
219
|
+
onUrl: (url) => {
|
|
220
|
+
capturedUrl = url;
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
await signalClient.join('wss://test.livekit.io', 'test-token', defaultOptions);
|
|
225
|
+
|
|
226
|
+
const joinRequest = await decodeJoinRequestFromUrl(capturedUrl);
|
|
227
|
+
expect(joinRequest.connectionSettings?.disableIceLite).toBe(false);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('requests full ICE in the join request when disableIceLite is set', async () => {
|
|
231
|
+
const joinResponse = createJoinResponse();
|
|
232
|
+
const signalResponse = createSignalResponse('join', joinResponse);
|
|
233
|
+
const mockReadable = createMockReadableStream([signalResponse]);
|
|
234
|
+
const mockConnection = createMockConnection(mockReadable);
|
|
235
|
+
let capturedUrl = '';
|
|
236
|
+
|
|
237
|
+
mockWebSocketStream({
|
|
238
|
+
connection: mockConnection,
|
|
239
|
+
onUrl: (url) => {
|
|
240
|
+
capturedUrl = url;
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
await signalClient.join('wss://test.livekit.io', 'test-token', {
|
|
245
|
+
...defaultOptions,
|
|
246
|
+
disableIceLite: true,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const joinRequest = await decodeJoinRequestFromUrl(capturedUrl);
|
|
250
|
+
expect(joinRequest.connectionSettings?.disableIceLite).toBe(true);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('requests full ICE on the v0 path when disableIceLite is set', async () => {
|
|
254
|
+
const joinResponse = createJoinResponse();
|
|
255
|
+
const signalResponse = createSignalResponse('join', joinResponse);
|
|
256
|
+
const mockReadable = createMockReadableStream([signalResponse]);
|
|
257
|
+
const mockConnection = createMockConnection(mockReadable);
|
|
258
|
+
let capturedUrl = '';
|
|
259
|
+
|
|
260
|
+
mockWebSocketStream({
|
|
261
|
+
connection: mockConnection,
|
|
262
|
+
onUrl: (url) => {
|
|
263
|
+
capturedUrl = url;
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
await signalClient.join(
|
|
268
|
+
'wss://test.livekit.io',
|
|
269
|
+
'test-token',
|
|
270
|
+
{ ...defaultOptions, disableIceLite: true },
|
|
271
|
+
undefined,
|
|
272
|
+
true,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
expect(new URL(capturedUrl).searchParams.get('disable_ice_lite')).toBe('1');
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('leaves subscriberAllowPause unset by default so the server default applies', async () => {
|
|
279
|
+
const joinResponse = createJoinResponse();
|
|
280
|
+
const signalResponse = createSignalResponse('join', joinResponse);
|
|
281
|
+
const mockReadable = createMockReadableStream([signalResponse]);
|
|
282
|
+
const mockConnection = createMockConnection(mockReadable);
|
|
283
|
+
let capturedUrl = '';
|
|
284
|
+
|
|
285
|
+
mockWebSocketStream({
|
|
286
|
+
connection: mockConnection,
|
|
287
|
+
onUrl: (url) => {
|
|
288
|
+
capturedUrl = url;
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
await signalClient.join('wss://test.livekit.io', 'test-token', defaultOptions);
|
|
293
|
+
|
|
294
|
+
const joinRequest = await decodeJoinRequestFromUrl(capturedUrl);
|
|
295
|
+
expect(joinRequest.connectionSettings?.subscriberAllowPause).toBeUndefined();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it.each([true, false])(
|
|
299
|
+
'sets subscriberAllowPause=%s in the join request',
|
|
300
|
+
async (subscriberAllowPause) => {
|
|
301
|
+
const joinResponse = createJoinResponse();
|
|
302
|
+
const signalResponse = createSignalResponse('join', joinResponse);
|
|
303
|
+
const mockReadable = createMockReadableStream([signalResponse]);
|
|
304
|
+
const mockConnection = createMockConnection(mockReadable);
|
|
305
|
+
let capturedUrl = '';
|
|
306
|
+
|
|
307
|
+
mockWebSocketStream({
|
|
308
|
+
connection: mockConnection,
|
|
309
|
+
onUrl: (url) => {
|
|
310
|
+
capturedUrl = url;
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
await signalClient.join('wss://test.livekit.io', 'test-token', {
|
|
315
|
+
...defaultOptions,
|
|
316
|
+
subscriberAllowPause,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const joinRequest = await decodeJoinRequestFromUrl(capturedUrl);
|
|
320
|
+
expect(joinRequest.connectionSettings?.subscriberAllowPause).toBe(subscriberAllowPause);
|
|
321
|
+
},
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
it.each([
|
|
325
|
+
[undefined, null],
|
|
326
|
+
[true, '1'],
|
|
327
|
+
[false, '0'],
|
|
328
|
+
])(
|
|
329
|
+
'sets subscriber_allow_pause on the v0 path when subscriberAllowPause=%s',
|
|
330
|
+
async (subscriberAllowPause, expected) => {
|
|
331
|
+
const joinResponse = createJoinResponse();
|
|
332
|
+
const signalResponse = createSignalResponse('join', joinResponse);
|
|
333
|
+
const mockReadable = createMockReadableStream([signalResponse]);
|
|
334
|
+
const mockConnection = createMockConnection(mockReadable);
|
|
335
|
+
let capturedUrl = '';
|
|
336
|
+
|
|
337
|
+
mockWebSocketStream({
|
|
338
|
+
connection: mockConnection,
|
|
339
|
+
onUrl: (url) => {
|
|
340
|
+
capturedUrl = url;
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
await signalClient.join(
|
|
345
|
+
'wss://test.livekit.io',
|
|
346
|
+
'test-token',
|
|
347
|
+
{ ...defaultOptions, subscriberAllowPause },
|
|
348
|
+
undefined,
|
|
349
|
+
true,
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
expect(new URL(capturedUrl).searchParams.get('subscriber_allow_pause')).toBe(expected);
|
|
353
|
+
},
|
|
354
|
+
);
|
|
209
355
|
});
|
|
210
356
|
|
|
211
357
|
describe('Happy Path - Reconnect', () => {
|
|
@@ -236,7 +382,7 @@ describe('SignalClient.connect', () => {
|
|
|
236
382
|
expect(signalClient.currentState).toBe(SignalConnectionState.CONNECTED);
|
|
237
383
|
});
|
|
238
384
|
|
|
239
|
-
it('
|
|
385
|
+
it('drops stray messages during reconnect and waits for the reconnect response', async () => {
|
|
240
386
|
// First, initial connection
|
|
241
387
|
const joinResponse = createJoinResponse();
|
|
242
388
|
const joinSignalResponse = createSignalResponse('join', joinResponse);
|
|
@@ -247,19 +393,24 @@ describe('SignalClient.connect', () => {
|
|
|
247
393
|
|
|
248
394
|
await signalClient.join('wss://test.livekit.io', 'test-token', defaultOptions);
|
|
249
395
|
|
|
250
|
-
//
|
|
396
|
+
// Server sends a stray update first, then the actual reconnect response.
|
|
397
|
+
// The stray should be dropped with a warning; the reconnect response should resolve.
|
|
251
398
|
const updateSignalResponse = createSignalResponse('update', { participants: [] });
|
|
252
|
-
const
|
|
399
|
+
const reconnectResponse = new ReconnectResponse({ iceServers: [] });
|
|
400
|
+
const reconnectSignalResponse = createSignalResponse('reconnect', reconnectResponse);
|
|
401
|
+
const reconnectMockReadable = createMockReadableStream([
|
|
402
|
+
updateSignalResponse,
|
|
403
|
+
reconnectSignalResponse,
|
|
404
|
+
]);
|
|
253
405
|
const reconnectMockConnection = createMockConnection(reconnectMockReadable);
|
|
254
406
|
|
|
255
407
|
mockWebSocketStream({ connection: reconnectMockConnection });
|
|
256
408
|
|
|
257
409
|
const result = await signalClient.reconnect('wss://test.livekit.io', 'test-token', 'sid-123');
|
|
258
410
|
|
|
259
|
-
|
|
260
|
-
expect(result).toBeUndefined();
|
|
411
|
+
expect(result).toEqual(reconnectResponse);
|
|
261
412
|
expect(signalClient.currentState).toBe(SignalConnectionState.CONNECTED);
|
|
262
|
-
}
|
|
413
|
+
});
|
|
263
414
|
});
|
|
264
415
|
|
|
265
416
|
describe('Failure Case - Timeout', () => {
|
|
@@ -1026,7 +1177,7 @@ describe('SignalClient.validateFirstMessage', () => {
|
|
|
1026
1177
|
}
|
|
1027
1178
|
});
|
|
1028
1179
|
|
|
1029
|
-
it('should
|
|
1180
|
+
it('should reject non-reconnect message during reconnecting state', async () => {
|
|
1030
1181
|
// First establish a connection
|
|
1031
1182
|
const joinResponse = createJoinResponse();
|
|
1032
1183
|
const joinSignalResponse = createSignalResponse('join', joinResponse);
|
|
@@ -1044,9 +1195,8 @@ describe('SignalClient.validateFirstMessage', () => {
|
|
|
1044
1195
|
const validateMethod = (signalClient as any).validateFirstMessage;
|
|
1045
1196
|
if (validateMethod) {
|
|
1046
1197
|
const result = validateMethod.call(signalClient, updateSignalResponse, true);
|
|
1047
|
-
expect(result.isValid).toBe(
|
|
1048
|
-
expect(result.
|
|
1049
|
-
expect(result.shouldProcessFirstMessage).toBe(true);
|
|
1198
|
+
expect(result.isValid).toBe(false);
|
|
1199
|
+
expect(result.error).toBeInstanceOf(ConnectionError);
|
|
1050
1200
|
}
|
|
1051
1201
|
});
|
|
1052
1202
|
|
package/src/api/SignalClient.ts
CHANGED
|
@@ -92,6 +92,8 @@ interface ConnectOpts extends SignalOptions {
|
|
|
92
92
|
export interface SignalOptions {
|
|
93
93
|
autoSubscribe: boolean;
|
|
94
94
|
adaptiveStream?: boolean;
|
|
95
|
+
disableIceLite?: boolean;
|
|
96
|
+
subscriberAllowPause?: boolean;
|
|
95
97
|
clientInfoCapabilities?: ClientInfo_Capability[];
|
|
96
98
|
maxRetries: number;
|
|
97
99
|
e2eeEnabled: boolean;
|
|
@@ -581,38 +583,61 @@ export class SignalClient {
|
|
|
581
583
|
this.streamWriter = connection.writable.getWriter();
|
|
582
584
|
|
|
583
585
|
// wsTimeout only guarded the upgrade; guard the first-message read with
|
|
584
|
-
// its own
|
|
585
|
-
|
|
586
|
-
|
|
586
|
+
// its own deadline so a silent server can't hang join() forever.
|
|
587
|
+
// During reconnect, drop any stray messages that arrive before the
|
|
588
|
+
// ReconnectResponse so we do not declare the channel reconnected on the
|
|
589
|
+
// wrong signal.
|
|
590
|
+
let firstSignalResponse: SignalResponse;
|
|
587
591
|
try {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
592
|
+
const deadline = Date.now() + JOIN_RESPONSE_TIMEOUT;
|
|
593
|
+
// eslint-disable-next-line no-constant-condition
|
|
594
|
+
while (true) {
|
|
595
|
+
const remaining = deadline - Date.now();
|
|
596
|
+
if (remaining <= 0) {
|
|
597
|
+
throw ConnectionError.timeout(
|
|
598
|
+
'signal connection timed out while waiting for the first message',
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
let readTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
602
|
+
const readResult = await Promise.race([
|
|
603
|
+
signalReader.read(),
|
|
604
|
+
new Promise<never>((_, rejectRead) => {
|
|
605
|
+
readTimeout = setTimeout(() => {
|
|
606
|
+
rejectRead(
|
|
607
|
+
ConnectionError.timeout(
|
|
608
|
+
'signal connection timed out while waiting for the first message',
|
|
609
|
+
),
|
|
610
|
+
);
|
|
611
|
+
}, remaining);
|
|
612
|
+
}),
|
|
613
|
+
]);
|
|
614
|
+
clearTimeout(readTimeout);
|
|
615
|
+
if (!readResult.value) {
|
|
616
|
+
throw ConnectionError.internal('no message received as first message');
|
|
617
|
+
}
|
|
618
|
+
const parsed = parseSignalResponse(readResult.value);
|
|
619
|
+
if (
|
|
620
|
+
this.lifecycleState === 'reconnecting' &&
|
|
621
|
+
parsed.message?.case !== 'reconnect' &&
|
|
622
|
+
parsed.message?.case !== 'leave'
|
|
623
|
+
) {
|
|
624
|
+
this.log.warn('dropping signal message while awaiting reconnect response', {
|
|
625
|
+
messageCase: parsed.message?.case,
|
|
626
|
+
});
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
firstSignalResponse = parsed;
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
600
632
|
} catch (e) {
|
|
601
|
-
// No first message in time: release the reader and tear down the ws
|
|
633
|
+
// No usable first message in time: release the reader and tear down the ws
|
|
602
634
|
// so we surface the timeout instead of leaking an open connection.
|
|
603
635
|
signalReader.releaseLock();
|
|
604
636
|
reject(e);
|
|
605
637
|
this.close();
|
|
606
638
|
return;
|
|
607
|
-
} finally {
|
|
608
|
-
clearTimeout(firstMessageTimeout);
|
|
609
639
|
}
|
|
610
640
|
signalReader.releaseLock();
|
|
611
|
-
if (!firstMessage.value) {
|
|
612
|
-
throw ConnectionError.internal('no message received as first message');
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
const firstSignalResponse = parseSignalResponse(firstMessage.value);
|
|
616
641
|
|
|
617
642
|
// Validate the first message
|
|
618
643
|
const validation = this.validateFirstMessage(
|
|
@@ -643,11 +668,7 @@ export class SignalClient {
|
|
|
643
668
|
}
|
|
644
669
|
}
|
|
645
670
|
|
|
646
|
-
|
|
647
|
-
const firstMessageToProcess = validation.shouldProcessFirstMessage
|
|
648
|
-
? firstSignalResponse
|
|
649
|
-
: undefined;
|
|
650
|
-
this.handleSignalConnected(connection, wsTimeout, attemptId, firstMessageToProcess);
|
|
671
|
+
this.handleSignalConnected(connection, wsTimeout, attemptId);
|
|
651
672
|
resolve(validation.response);
|
|
652
673
|
} catch (e) {
|
|
653
674
|
reject(e);
|
|
@@ -660,13 +681,7 @@ export class SignalClient {
|
|
|
660
681
|
});
|
|
661
682
|
}
|
|
662
683
|
|
|
663
|
-
async startReadingLoop(
|
|
664
|
-
signalReader: ReadableStreamDefaultReader<string | ArrayBuffer>,
|
|
665
|
-
firstMessage?: SignalResponse,
|
|
666
|
-
) {
|
|
667
|
-
if (firstMessage) {
|
|
668
|
-
this.handleSignalResponse(firstMessage);
|
|
669
|
-
}
|
|
684
|
+
async startReadingLoop(signalReader: ReadableStreamDefaultReader<string | ArrayBuffer>) {
|
|
670
685
|
const attemptId = this.attemptId;
|
|
671
686
|
while (true) {
|
|
672
687
|
if (this.signalLatency) {
|
|
@@ -1099,7 +1114,11 @@ export class SignalClient {
|
|
|
1099
1114
|
* Handles a transport we lost without asking to. The client goes to `offline` rather than
|
|
1100
1115
|
* `closed`: whether this session gets resumed, restarted or given up on is the engine's call.
|
|
1101
1116
|
*/
|
|
1102
|
-
private async handleOnClose(
|
|
1117
|
+
private async handleOnClose(
|
|
1118
|
+
reason: string,
|
|
1119
|
+
attemptId: number = this.attemptId,
|
|
1120
|
+
simulateReconnectDelay?: number,
|
|
1121
|
+
) {
|
|
1103
1122
|
const onCloseCallback = this.onClose;
|
|
1104
1123
|
if (!this.sendLifecycleInput({ type: 'transportFailed', attemptId, reason })) {
|
|
1105
1124
|
// a close we caused ourselves, or one from a transport that has since been replaced: logged
|
|
@@ -1114,6 +1133,10 @@ export class SignalClient {
|
|
|
1114
1133
|
}
|
|
1115
1134
|
await this.teardownTransport(reason);
|
|
1116
1135
|
this.log.info(`websocket connection closed: ${reason}`, { reason });
|
|
1136
|
+
if (simulateReconnectDelay) {
|
|
1137
|
+
this.log.info(`waiting for ${simulateReconnectDelay}ms until reconnecting signal`);
|
|
1138
|
+
await sleep(simulateReconnectDelay);
|
|
1139
|
+
}
|
|
1117
1140
|
if (onCloseCallback) {
|
|
1118
1141
|
onCloseCallback(reason);
|
|
1119
1142
|
}
|
|
@@ -1184,7 +1207,6 @@ export class SignalClient {
|
|
|
1184
1207
|
connection: WebSocketConnection,
|
|
1185
1208
|
timeoutHandle: ReturnType<typeof setTimeout>,
|
|
1186
1209
|
attemptId: number,
|
|
1187
|
-
firstMessage?: SignalResponse,
|
|
1188
1210
|
) {
|
|
1189
1211
|
clearTimeout(timeoutHandle);
|
|
1190
1212
|
const established = this.sendLifecycleInput(
|
|
@@ -1205,7 +1227,7 @@ export class SignalClient {
|
|
|
1205
1227
|
}
|
|
1206
1228
|
this.log.info('signal connected');
|
|
1207
1229
|
this.startPingInterval();
|
|
1208
|
-
this.startReadingLoop(connection.readable.getReader()
|
|
1230
|
+
this.startReadingLoop(connection.readable.getReader());
|
|
1209
1231
|
}
|
|
1210
1232
|
|
|
1211
1233
|
/**
|
|
@@ -1222,7 +1244,6 @@ export class SignalClient {
|
|
|
1222
1244
|
isValid: boolean;
|
|
1223
1245
|
response?: JoinResponse | ReconnectResponse;
|
|
1224
1246
|
error?: ConnectionError;
|
|
1225
|
-
shouldProcessFirstMessage?: boolean;
|
|
1226
1247
|
} {
|
|
1227
1248
|
if (firstSignalResponse.message?.case === 'join') {
|
|
1228
1249
|
return {
|
|
@@ -1231,22 +1252,12 @@ export class SignalClient {
|
|
|
1231
1252
|
};
|
|
1232
1253
|
} else if (
|
|
1233
1254
|
this.lifecycleState === 'reconnecting' &&
|
|
1234
|
-
firstSignalResponse.message?.case
|
|
1255
|
+
firstSignalResponse.message?.case === 'reconnect'
|
|
1235
1256
|
) {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
};
|
|
1241
|
-
} else {
|
|
1242
|
-
// in reconnecting, any message received means signal reconnected and we still need to process it
|
|
1243
|
-
this.log.debug('declaring signal reconnected without reconnect response received');
|
|
1244
|
-
return {
|
|
1245
|
-
isValid: true,
|
|
1246
|
-
response: undefined,
|
|
1247
|
-
shouldProcessFirstMessage: true,
|
|
1248
|
-
};
|
|
1249
|
-
}
|
|
1257
|
+
return {
|
|
1258
|
+
isValid: true,
|
|
1259
|
+
response: firstSignalResponse.message.value,
|
|
1260
|
+
};
|
|
1250
1261
|
} else if (this.isEstablishingConnection && firstSignalResponse.message?.case === 'leave') {
|
|
1251
1262
|
return {
|
|
1252
1263
|
isValid: false,
|
|
@@ -1394,6 +1405,14 @@ function createConnectionParams(
|
|
|
1394
1405
|
params.set('adaptive_stream', '1');
|
|
1395
1406
|
}
|
|
1396
1407
|
|
|
1408
|
+
if (opts.disableIceLite) {
|
|
1409
|
+
params.set('disable_ice_lite', '1');
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
if (typeof opts.subscriberAllowPause !== 'undefined') {
|
|
1413
|
+
params.set('subscriber_allow_pause', opts.subscriberAllowPause ? '1' : '0');
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1397
1416
|
if (opts.reconnectReason) {
|
|
1398
1417
|
params.set('reconnect_reason', opts.reconnectReason.toString());
|
|
1399
1418
|
}
|
|
@@ -1421,6 +1440,8 @@ async function createJoinRequestConnectionParams(
|
|
|
1421
1440
|
connectionSettings: new ConnectionSettings({
|
|
1422
1441
|
autoSubscribe: !!opts.autoSubscribe,
|
|
1423
1442
|
adaptiveStream: !!opts.adaptiveStream,
|
|
1443
|
+
disableIceLite: !!opts.disableIceLite,
|
|
1444
|
+
subscriberAllowPause: opts.subscriberAllowPause,
|
|
1424
1445
|
}),
|
|
1425
1446
|
reconnect: !!opts.reconnect,
|
|
1426
1447
|
participantSid: opts.sid ? opts.sid : undefined,
|
package/src/options.ts
CHANGED
|
@@ -166,6 +166,22 @@ export interface InternalRoomConnectOptions {
|
|
|
166
166
|
*/
|
|
167
167
|
rtcConfig?: RTCConfiguration;
|
|
168
168
|
|
|
169
|
+
/**
|
|
170
|
+
* when the server runs ICE lite, asks it to use a full ICE agent for this participant.
|
|
171
|
+
* this is a server-side setting that the server may ignore, defaults to false
|
|
172
|
+
*/
|
|
173
|
+
disableIceLite: boolean;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* whether the SFU may pause subscribed video tracks when this participant's downstream
|
|
177
|
+
* connection is congested, so that other tracks can continue to stream smoothly.
|
|
178
|
+
* paused tracks resume automatically when conditions allow, and `RoomEvent.TrackStreamStateChanged`
|
|
179
|
+
* fires on each transition.
|
|
180
|
+
* when left undefined, the server's default applies (the project setting on LiveKit Cloud,
|
|
181
|
+
* or `rtc.congestion_control.allow_pause` when self-hosting)
|
|
182
|
+
*/
|
|
183
|
+
subscriberAllowPause?: boolean;
|
|
184
|
+
|
|
169
185
|
/** specifies how often an initial join connection is allowed to retry (only applicable if server is not reachable) */
|
|
170
186
|
maxRetries: number;
|
|
171
187
|
|