livekit-client 2.18.1 → 2.18.2
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/dist/livekit-client.esm.mjs +244 -134
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +6 -5
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +1 -0
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +5 -1
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +1 -0
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/utils/serializer.d.ts +48 -0
- package/dist/src/utils/serializer.d.ts.map +1 -0
- package/dist/ts4.2/connectionHelper/ConnectionCheck.d.ts +2 -1
- package/dist/ts4.2/index.d.ts +2 -0
- package/dist/ts4.2/room/RTCEngine.d.ts +6 -5
- package/dist/ts4.2/room/Room.d.ts +1 -0
- package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +5 -1
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +1 -0
- package/dist/ts4.2/utils/serializer.d.ts +48 -0
- package/package.json +1 -1
- package/src/connectionHelper/ConnectionCheck.ts +1 -1
- package/src/index.ts +7 -0
- package/src/room/RTCEngine.ts +47 -15
- package/src/room/Room.ts +19 -23
- package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +26 -2
- package/src/room/data-track/incoming/IncomingDataTrackManager.ts +7 -0
- package/src/room/participant/LocalParticipant.ts +17 -2
- package/src/utils/serializer.ts +72 -0
package/src/room/Room.ts
CHANGED
|
@@ -187,6 +187,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
187
187
|
|
|
188
188
|
private e2eeManager: BaseE2EEManager | undefined;
|
|
189
189
|
|
|
190
|
+
private e2eeStateMutex: Mutex = new Mutex();
|
|
191
|
+
|
|
190
192
|
private connectionReconcileInterval?: ReturnType<typeof setInterval>;
|
|
191
193
|
|
|
192
194
|
private regionUrlProvider?: RegionUrlProvider;
|
|
@@ -293,7 +295,6 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
293
295
|
});
|
|
294
296
|
|
|
295
297
|
this.disconnectLock = new Mutex();
|
|
296
|
-
|
|
297
298
|
this.localParticipant = new LocalParticipant(
|
|
298
299
|
'',
|
|
299
300
|
'',
|
|
@@ -411,13 +412,21 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
411
412
|
* @experimental
|
|
412
413
|
*/
|
|
413
414
|
async setE2EEEnabled(enabled: boolean) {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (this.
|
|
417
|
-
this.
|
|
415
|
+
const unlock = await this.e2eeStateMutex.lock();
|
|
416
|
+
try {
|
|
417
|
+
if (this.e2eeManager) {
|
|
418
|
+
if (this.isE2EEEnabled !== enabled) {
|
|
419
|
+
await this.localParticipant.setE2EEEnabled(enabled);
|
|
420
|
+
|
|
421
|
+
if (this.localParticipant.identity !== '') {
|
|
422
|
+
this.e2eeManager.setParticipantCryptorEnabled(enabled, this.localParticipant.identity);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
throw Error('e2ee not configured, please set e2ee settings within the room options');
|
|
418
427
|
}
|
|
419
|
-
}
|
|
420
|
-
|
|
428
|
+
} finally {
|
|
429
|
+
unlock();
|
|
421
430
|
}
|
|
422
431
|
}
|
|
423
432
|
|
|
@@ -450,6 +459,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
450
459
|
this.emit(RoomEvent.EncryptionError, error, participant);
|
|
451
460
|
});
|
|
452
461
|
this.e2eeManager?.setup(this);
|
|
462
|
+
this.e2eeManager?.setupEngine(this.engine);
|
|
453
463
|
}
|
|
454
464
|
}
|
|
455
465
|
|
|
@@ -895,7 +905,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
895
905
|
roomOptions: InternalRoomOptions,
|
|
896
906
|
abortController: AbortController,
|
|
897
907
|
): Promise<JoinResponse> => {
|
|
898
|
-
const joinResponse = await engine.join(
|
|
908
|
+
const { joinResponse, serverInfo } = await engine.join(
|
|
899
909
|
url,
|
|
900
910
|
token,
|
|
901
911
|
{
|
|
@@ -910,23 +920,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
910
920
|
!roomOptions.singlePeerConnection,
|
|
911
921
|
);
|
|
912
922
|
|
|
913
|
-
let serverInfo: Partial<ServerInfo> | undefined = joinResponse.serverInfo;
|
|
914
|
-
if (!serverInfo) {
|
|
915
|
-
serverInfo = { version: joinResponse.serverVersion, region: joinResponse.serverRegion };
|
|
916
|
-
}
|
|
917
923
|
this.serverInfo = serverInfo;
|
|
918
924
|
|
|
919
|
-
this.log.debug(
|
|
920
|
-
`connected to Livekit Server ${Object.entries(serverInfo)
|
|
921
|
-
.map(([key, value]) => `${key}: ${value}`)
|
|
922
|
-
.join(', ')}`,
|
|
923
|
-
{
|
|
924
|
-
room: joinResponse.room?.name,
|
|
925
|
-
roomSid: joinResponse.room?.sid,
|
|
926
|
-
identity: joinResponse.participant?.identity,
|
|
927
|
-
},
|
|
928
|
-
);
|
|
929
|
-
|
|
930
925
|
if (!serverInfo.version) {
|
|
931
926
|
throw new UnsupportedServer('unknown server version');
|
|
932
927
|
}
|
|
@@ -2475,6 +2470,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
|
2475
2470
|
return false;
|
|
2476
2471
|
}
|
|
2477
2472
|
this.state = state;
|
|
2473
|
+
this.incomingDataStreamManager.setConnected(state === ConnectionState.Connected);
|
|
2478
2474
|
this.emit(RoomEvent.ConnectionStateChanged, this.state);
|
|
2479
2475
|
return true;
|
|
2480
2476
|
}
|
|
@@ -27,6 +27,25 @@ export default class IncomingDataStreamManager {
|
|
|
27
27
|
|
|
28
28
|
private textStreamHandlers = new Map<string, TextStreamHandler>();
|
|
29
29
|
|
|
30
|
+
private isConnected = false;
|
|
31
|
+
|
|
32
|
+
private bufferedPackets: Array<{ packet: DataPacket; encryptionType: Encryption_Type }> = [];
|
|
33
|
+
|
|
34
|
+
setConnected(connected: boolean) {
|
|
35
|
+
this.isConnected = connected;
|
|
36
|
+
if (connected) {
|
|
37
|
+
this.flushBufferedPackets();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private flushBufferedPackets() {
|
|
42
|
+
const packets = this.bufferedPackets;
|
|
43
|
+
this.bufferedPackets = [];
|
|
44
|
+
for (const { packet, encryptionType } of packets) {
|
|
45
|
+
this.handleDataStreamPacket(packet, encryptionType);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
30
49
|
registerTextStreamHandler(topic: string, callback: TextStreamHandler) {
|
|
31
50
|
if (this.textStreamHandlers.has(topic)) {
|
|
32
51
|
throw new DataStreamError(
|
|
@@ -58,6 +77,7 @@ export default class IncomingDataStreamManager {
|
|
|
58
77
|
clearControllers() {
|
|
59
78
|
this.byteStreamControllers.clear();
|
|
60
79
|
this.textStreamControllers.clear();
|
|
80
|
+
this.bufferedPackets = [];
|
|
61
81
|
}
|
|
62
82
|
|
|
63
83
|
validateParticipantHasNoActiveDataStreams(participantIdentity: string) {
|
|
@@ -88,7 +108,11 @@ export default class IncomingDataStreamManager {
|
|
|
88
108
|
}
|
|
89
109
|
}
|
|
90
110
|
|
|
91
|
-
|
|
111
|
+
handleDataStreamPacket(packet: DataPacket, encryptionType: Encryption_Type) {
|
|
112
|
+
if (!this.isConnected) {
|
|
113
|
+
this.bufferedPackets.push({ packet, encryptionType });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
92
116
|
switch (packet.value.case) {
|
|
93
117
|
case 'streamHeader':
|
|
94
118
|
return this.handleStreamHeader(
|
|
@@ -105,7 +129,7 @@ export default class IncomingDataStreamManager {
|
|
|
105
129
|
}
|
|
106
130
|
}
|
|
107
131
|
|
|
108
|
-
private
|
|
132
|
+
private handleStreamHeader(
|
|
109
133
|
streamHeader: DataStream_Header,
|
|
110
134
|
participantIdentity: string,
|
|
111
135
|
encryptionType: Encryption_Type,
|
|
@@ -436,6 +436,9 @@ export default class IncomingDataTrackManager extends (EventEmitter as new () =>
|
|
|
436
436
|
this.descriptors.delete(sid);
|
|
437
437
|
|
|
438
438
|
if (descriptor.subscription.type === 'active') {
|
|
439
|
+
descriptor.subscription.streamControllers.forEach((controller) => {
|
|
440
|
+
controller.close();
|
|
441
|
+
});
|
|
439
442
|
this.subscriptionHandles.delete(descriptor.subscription.subcriptionHandle);
|
|
440
443
|
}
|
|
441
444
|
|
|
@@ -583,6 +586,10 @@ export default class IncomingDataTrackManager extends (EventEmitter as new () =>
|
|
|
583
586
|
if (descriptor.subscription.type === 'pending') {
|
|
584
587
|
descriptor.subscription.completionFuture.reject?.(DataTrackSubscribeError.disconnected());
|
|
585
588
|
}
|
|
589
|
+
|
|
590
|
+
if (descriptor.subscription.type === 'active') {
|
|
591
|
+
descriptor.subscription.streamControllers.forEach((controller) => controller.close());
|
|
592
|
+
}
|
|
586
593
|
}
|
|
587
594
|
this.descriptors.clear();
|
|
588
595
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Mutex } from '@livekit/mutex';
|
|
1
2
|
import {
|
|
2
3
|
AddTrackRequest,
|
|
3
4
|
AudioTrackFeature,
|
|
@@ -143,6 +144,8 @@ export default class LocalParticipant extends Participant {
|
|
|
143
144
|
|
|
144
145
|
private encryptionType: Encryption_Type = Encryption_Type.NONE;
|
|
145
146
|
|
|
147
|
+
private e2eeStateMutex = new Mutex();
|
|
148
|
+
|
|
146
149
|
private reconnectFuture?: Future<void, Error>;
|
|
147
150
|
|
|
148
151
|
private signalConnectedFuture?: Future<void, Error>;
|
|
@@ -499,8 +502,20 @@ export default class LocalParticipant extends Participant {
|
|
|
499
502
|
|
|
500
503
|
/** @internal */
|
|
501
504
|
async setE2EEEnabled(enabled: boolean) {
|
|
502
|
-
|
|
503
|
-
|
|
505
|
+
const unlock = await this.e2eeStateMutex.lock();
|
|
506
|
+
try {
|
|
507
|
+
this.encryptionType = enabled ? Encryption_Type.GCM : Encryption_Type.NONE;
|
|
508
|
+
await Promise.all(this.pendingPublishPromises.values());
|
|
509
|
+
if (
|
|
510
|
+
this.trackPublications.size === 0 ||
|
|
511
|
+
Array.from(this.trackPublications.values()).every((pub) => pub.isEncrypted === enabled)
|
|
512
|
+
) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
await this.republishAllTracks(undefined, false);
|
|
516
|
+
} finally {
|
|
517
|
+
unlock();
|
|
518
|
+
}
|
|
504
519
|
}
|
|
505
520
|
|
|
506
521
|
/**
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const SerializerSymbol = Symbol.for('lk.serializer');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A bidirectional data format descriptor for message payloads.
|
|
5
|
+
*
|
|
6
|
+
* - `parse(raw)` decodes an incoming wire string into `Input` (used by handlers)
|
|
7
|
+
* - `serialize(val)` encodes an `Output` value to a wire string (used by handlers)
|
|
8
|
+
*
|
|
9
|
+
* For symmetric serializers (`serializers.raw`), `Input === Output === string`.
|
|
10
|
+
* For `serializers.json`, both default to `any` so each handler can annotate its own types.
|
|
11
|
+
* Use `serializers.custom` to supply your own `parse`/`serialize` pair.
|
|
12
|
+
*
|
|
13
|
+
* @beta
|
|
14
|
+
*/
|
|
15
|
+
export type Serializer<Input, Output> = {
|
|
16
|
+
symbol: typeof SerializerSymbol;
|
|
17
|
+
parse: (raw: string) => Input;
|
|
18
|
+
serialize: (val: Output) => string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function isSerializer(v: unknown): v is Serializer<any, any> {
|
|
22
|
+
return typeof v === 'object' && v !== null && 'symbol' in v && v.symbol === SerializerSymbol;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type SerializerInput<S> = S extends Serializer<infer Input, any> ? Input : any;
|
|
26
|
+
export type SerializerOutput<S> = S extends Serializer<any, infer Output> ? Output : any;
|
|
27
|
+
|
|
28
|
+
/** @internal */
|
|
29
|
+
function base<Input = any, Output = any>(
|
|
30
|
+
params: Omit<Serializer<Input, Output>, 'symbol'>,
|
|
31
|
+
): Serializer<Input, Output> {
|
|
32
|
+
return { ...params, symbol: SerializerSymbol };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* JSON serializer — `JSON.parse` on the way in, `JSON.stringify` on the way out.
|
|
37
|
+
* Defaults to `any` so individual handlers can annotate their own payload types.
|
|
38
|
+
*/
|
|
39
|
+
function json<Input = any, Output = any>(): Serializer<Input, Output> {
|
|
40
|
+
return base({
|
|
41
|
+
parse: (rawString: string) => JSON.parse(rawString) as Input,
|
|
42
|
+
serialize: (val: unknown) => JSON.stringify(val),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Raw string serializer — passes payloads through as plain strings with no encoding. */
|
|
47
|
+
function raw() {
|
|
48
|
+
return base({
|
|
49
|
+
parse: (rawString: string) => rawString,
|
|
50
|
+
serialize: (val: string) => val,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Custom serializer - allows custom defined parse and serialize functions */
|
|
55
|
+
function custom<Input = any, Output = any>(
|
|
56
|
+
params: Omit<Serializer<Input, Output>, 'symbol'>,
|
|
57
|
+
): Serializer<Input, Output> {
|
|
58
|
+
return base(params);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Serializer helpers for message payload encoding.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const a = serializers.raw(); // Serializer<string, string>
|
|
67
|
+
* const b = serializer.json<{ foo: string }, { bar: string }>(); // Serializer<{ foo: string }, { bar: string }>
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @beta
|
|
71
|
+
*/
|
|
72
|
+
export const serializers = { json, raw, custom };
|