livekit-client 2.16.0 → 2.16.1

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 (63) hide show
  1. package/README.md +105 -1
  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 +1 -0
  5. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  6. package/dist/livekit-client.esm.mjs +1079 -1329
  7. package/dist/livekit-client.esm.mjs.map +1 -1
  8. package/dist/livekit-client.umd.js +1 -1
  9. package/dist/livekit-client.umd.js.map +1 -1
  10. package/dist/src/api/SignalClient.d.ts.map +1 -1
  11. package/dist/src/api/utils.d.ts +1 -0
  12. package/dist/src/api/utils.d.ts.map +1 -1
  13. package/dist/src/connectionHelper/checks/turn.d.ts.map +1 -1
  14. package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
  15. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  16. package/dist/src/room/RTCEngine.d.ts +5 -0
  17. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  18. package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
  19. package/dist/src/room/Room.d.ts +1 -1
  20. package/dist/src/room/Room.d.ts.map +1 -1
  21. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +0 -1
  22. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
  23. package/dist/src/room/errors.d.ts +74 -5
  24. package/dist/src/room/errors.d.ts.map +1 -1
  25. package/dist/src/room/token-source/TokenSource.d.ts +10 -2
  26. package/dist/src/room/token-source/TokenSource.d.ts.map +1 -1
  27. package/dist/src/room/track/LocalTrack.d.ts +0 -4
  28. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  29. package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
  30. package/dist/src/room/track/processor/types.d.ts +0 -6
  31. package/dist/src/room/track/processor/types.d.ts.map +1 -1
  32. package/dist/src/room/utils.d.ts +1 -1
  33. package/dist/src/room/utils.d.ts.map +1 -1
  34. package/dist/src/test/mocks.d.ts.map +1 -1
  35. package/dist/ts4.2/api/utils.d.ts +1 -0
  36. package/dist/ts4.2/room/RTCEngine.d.ts +5 -0
  37. package/dist/ts4.2/room/Room.d.ts +1 -1
  38. package/dist/ts4.2/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +0 -1
  39. package/dist/ts4.2/room/errors.d.ts +74 -5
  40. package/dist/ts4.2/room/token-source/TokenSource.d.ts +1 -1
  41. package/dist/ts4.2/room/track/LocalTrack.d.ts +0 -4
  42. package/dist/ts4.2/room/track/processor/types.d.ts +0 -6
  43. package/package.json +10 -6
  44. package/src/api/SignalClient.test.ts +12 -19
  45. package/src/api/SignalClient.ts +13 -28
  46. package/src/api/utils.ts +1 -1
  47. package/src/connectionHelper/checks/turn.ts +7 -0
  48. package/src/connectionHelper/checks/websocket.ts +40 -11
  49. package/src/room/PCTransport.ts +1 -1
  50. package/src/room/PCTransportManager.ts +4 -19
  51. package/src/room/RTCEngine.ts +56 -18
  52. package/src/room/RegionUrlProvider.test.ts +8 -9
  53. package/src/room/RegionUrlProvider.ts +13 -12
  54. package/src/room/Room.ts +14 -16
  55. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +0 -1
  56. package/src/room/errors.ts +144 -16
  57. package/src/room/participant/LocalParticipant.ts +1 -1
  58. package/src/room/token-source/TokenSource.ts +5 -1
  59. package/src/room/track/LocalTrack.ts +0 -4
  60. package/src/room/track/TrackPublication.ts +1 -1
  61. package/src/room/track/processor/types.ts +0 -6
  62. package/src/room/utils.ts +2 -1
  63. package/src/test/mocks.ts +0 -1
@@ -62,6 +62,7 @@ import {
62
62
  ConnectionError,
63
63
  ConnectionErrorReason,
64
64
  NegotiationError,
65
+ SignalReconnectError,
65
66
  TrackInvalidError,
66
67
  UnexpectedConnectionState,
67
68
  } from './errors';
@@ -92,6 +93,8 @@ const reliableDataChannel = '_reliable';
92
93
  const minReconnectWait = 2 * 1000;
93
94
  const leaveReconnect = 'leave-reconnect';
94
95
  const reliabeReceiveStateTTL = 30_000;
96
+ const lossyDataChannelBufferThresholdMin = 8 * 1024;
97
+ const lossyDataChannelBufferThresholdMax = 256 * 1024;
95
98
 
96
99
  enum PCState {
97
100
  New,
@@ -203,6 +206,14 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
203
206
 
204
207
  private reliableReceivedState: TTLMap<string, number> = new TTLMap(reliabeReceiveStateTTL);
205
208
 
209
+ private lossyDataStatCurrentBytes: number = 0;
210
+
211
+ private lossyDataStatByterate: number = 0;
212
+
213
+ private lossyDataStatInterval: ReturnType<typeof setInterval> | undefined;
214
+
215
+ private lossyDataDropCount: number = 0;
216
+
206
217
  private midToTrackId: { [key: string]: string } = {};
207
218
 
208
219
  /** used to indicate whether the browser is currently waiting to reconnect */
@@ -312,6 +323,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
312
323
  this.removeAllListeners();
313
324
  this.deregisterOnLineListener();
314
325
  this.clearPendingReconnect();
326
+ this.cleanupLossyDataStats();
315
327
  await this.cleanupPeerConnections();
316
328
  await this.cleanupClient();
317
329
  } finally {
@@ -347,6 +359,16 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
347
359
  this.reliableReceivedState.clear();
348
360
  }
349
361
 
362
+ cleanupLossyDataStats() {
363
+ this.lossyDataStatByterate = 0;
364
+ this.lossyDataStatCurrentBytes = 0;
365
+ if (this.lossyDataStatInterval) {
366
+ clearInterval(this.lossyDataStatInterval);
367
+ this.lossyDataStatInterval = undefined;
368
+ }
369
+ this.lossyDataDropCount = 0;
370
+ }
371
+
350
372
  async cleanupClient() {
351
373
  await this.client.close();
352
374
  this.client.resetCallbacks();
@@ -360,10 +382,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
360
382
  const publicationTimeout = setTimeout(() => {
361
383
  delete this.pendingTrackResolvers[req.cid];
362
384
  reject(
363
- new ConnectionError(
364
- 'publication of local track timed out, no response from server',
365
- ConnectionErrorReason.Timeout,
366
- ),
385
+ ConnectionError.timeout('publication of local track timed out, no response from server'),
367
386
  );
368
387
  }, 10_000);
369
388
  this.pendingTrackResolvers[req.cid] = {
@@ -713,6 +732,22 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
713
732
  // handle buffer amount low events
714
733
  this.lossyDC.onbufferedamountlow = this.handleBufferedAmountLow;
715
734
  this.reliableDC.onbufferedamountlow = this.handleBufferedAmountLow;
735
+
736
+ this.cleanupLossyDataStats();
737
+ this.lossyDataStatInterval = setInterval(() => {
738
+ this.lossyDataStatByterate = this.lossyDataStatCurrentBytes;
739
+ this.lossyDataStatCurrentBytes = 0;
740
+
741
+ const dc = this.dataChannelForKind(DataPacket_Kind.LOSSY);
742
+ if (dc) {
743
+ // control buffered latency to ~100ms
744
+ const threshold = this.lossyDataStatByterate / 10;
745
+ dc.bufferedAmountLowThreshold = Math.min(
746
+ Math.max(threshold, lossyDataChannelBufferThresholdMin),
747
+ lossyDataChannelBufferThresholdMax,
748
+ );
749
+ }
750
+ }, 1000);
716
751
  }
717
752
 
718
753
  private handleDataChannel = async ({ channel }: RTCDataChannelEvent) => {
@@ -1191,10 +1226,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1191
1226
  } catch (e: any) {
1192
1227
  // TODO do we need a `failed` state here for the PC?
1193
1228
  this.pcState = PCState.Disconnected;
1194
- throw new ConnectionError(
1195
- `could not establish PC connection, ${e.message}`,
1196
- ConnectionErrorReason.InternalError,
1197
- );
1229
+ throw ConnectionError.internal(`could not establish PC connection, ${e.message}`);
1198
1230
  }
1199
1231
  }
1200
1232
 
@@ -1282,12 +1314,24 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1282
1314
 
1283
1315
  const msg = packet.toBinary();
1284
1316
 
1285
- await this.waitForBufferStatusLow(kind);
1286
-
1287
1317
  const dc = this.dataChannelForKind(kind);
1288
1318
  if (dc) {
1289
1319
  if (kind === DataPacket_Kind.RELIABLE) {
1320
+ await this.waitForBufferStatusLow(kind);
1290
1321
  this.reliableMessageBuffer.push({ data: msg, sequence: packet.sequence });
1322
+ } else {
1323
+ // lossy channel, drop messages to reduce latency
1324
+ if (!this.isBufferStatusLow(kind)) {
1325
+ this.lossyDataDropCount += 1;
1326
+ if (this.lossyDataDropCount % 100 === 0) {
1327
+ this.log.warn(
1328
+ `dropping lossy data channel messages, total dropped: ${this.lossyDataDropCount}`,
1329
+ this.logContext,
1330
+ );
1331
+ }
1332
+ return;
1333
+ }
1334
+ this.lossyDataStatCurrentBytes += msg.byteLength;
1291
1335
  }
1292
1336
 
1293
1337
  if (this.attemptingReconnect) {
@@ -1363,10 +1407,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1363
1407
  const transport = subscriber ? this.pcManager.subscriber : this.pcManager.publisher;
1364
1408
  const transportName = subscriber ? 'Subscriber' : 'Publisher';
1365
1409
  if (!transport) {
1366
- throw new ConnectionError(
1367
- `${transportName} connection not set`,
1368
- ConnectionErrorReason.InternalError,
1369
- );
1410
+ throw ConnectionError.internal(`${transportName} connection not set`);
1370
1411
  }
1371
1412
 
1372
1413
  let needNegotiation = false;
@@ -1407,9 +1448,8 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1407
1448
  await sleep(50);
1408
1449
  }
1409
1450
 
1410
- throw new ConnectionError(
1451
+ throw ConnectionError.internal(
1411
1452
  `could not establish ${transportName} connection, state: ${transport.getICEConnectionState()}`,
1412
- ConnectionErrorReason.InternalError,
1413
1453
  );
1414
1454
  }
1415
1455
 
@@ -1699,8 +1739,6 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1699
1739
  }
1700
1740
  }
1701
1741
 
1702
- class SignalReconnectError extends Error {}
1703
-
1704
1742
  export type EngineEventCallbacks = {
1705
1743
  connected: (joinResp: JoinResponse) => void;
1706
1744
  disconnected: (reason?: DisconnectReason) => void;
@@ -1,5 +1,5 @@
1
- import type { RegionInfo, RegionSettings } from '@livekit/protocol';
2
1
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import type { RegionInfo, RegionSettings } from '@livekit/protocol';
3
3
  import { RegionUrlProvider } from './RegionUrlProvider';
4
4
  import { ConnectionError, ConnectionErrorReason } from './errors';
5
5
 
@@ -180,8 +180,9 @@ describe('RegionUrlProvider', () => {
180
180
  const provider = new RegionUrlProvider('wss://test.livekit.cloud', 'token');
181
181
  fetchMock.mockResolvedValue(createMockResponse(401));
182
182
 
183
- await expect(provider.fetchRegionSettings()).rejects.toThrow(ConnectionError);
184
- await expect(provider.fetchRegionSettings()).rejects.toMatchObject({
183
+ const error = await provider.fetchRegionSettings().catch((e) => e);
184
+ expect(error).toBeInstanceOf(ConnectionError);
185
+ expect(error).toMatchObject({
185
186
  reason: ConnectionErrorReason.NotAllowed,
186
187
  status: 401,
187
188
  });
@@ -191,11 +192,9 @@ describe('RegionUrlProvider', () => {
191
192
  const provider = new RegionUrlProvider('wss://test.livekit.cloud', 'token');
192
193
  fetchMock.mockResolvedValue(createMockResponse(500));
193
194
 
194
- await expect(provider.fetchRegionSettings()).rejects.toThrow(ConnectionError);
195
- await expect(provider.fetchRegionSettings()).rejects.toMatchObject({
196
- reason: ConnectionErrorReason.InternalError,
197
- status: 500,
198
- });
195
+ const error = await provider.fetchRegionSettings().catch((e) => e);
196
+ expect(error).toBeInstanceOf(ConnectionError);
197
+ expect(error.reason).toBe(ConnectionErrorReason.InternalError);
199
198
  });
200
199
 
201
200
  it('extracts max-age from Cache-Control header', async () => {
@@ -725,7 +724,7 @@ describe('RegionUrlProvider', () => {
725
724
 
726
725
  expect(error).toBeInstanceOf(ConnectionError);
727
726
  expect(error.reason).toBe(ConnectionErrorReason.ServerUnreachable);
728
- expect(error.status).toBe(500);
727
+ expect(error.status).toBe(undefined);
729
728
  expect(error.message).toContain('Failed to fetch');
730
729
  });
731
730
 
@@ -45,26 +45,27 @@ export class RegionUrlProvider {
45
45
  const regionSettings = (await regionSettingsResponse.json()) as RegionSettings;
46
46
  return { regionSettings, updatedAtInMs: Date.now(), maxAgeInMs };
47
47
  } else {
48
- throw new ConnectionError(
49
- `Could not fetch region settings: ${regionSettingsResponse.statusText}`,
50
- regionSettingsResponse.status === 401
51
- ? ConnectionErrorReason.NotAllowed
52
- : ConnectionErrorReason.InternalError,
53
- regionSettingsResponse.status,
54
- );
48
+ if (regionSettingsResponse.status === 401) {
49
+ throw ConnectionError.notAllowed(
50
+ `Could not fetch region settings: ${regionSettingsResponse.statusText}`,
51
+ regionSettingsResponse.status,
52
+ );
53
+ } else {
54
+ throw ConnectionError.internal(
55
+ `Could not fetch region settings: ${regionSettingsResponse.statusText}`,
56
+ );
57
+ }
55
58
  }
56
59
  } catch (e: unknown) {
57
60
  if (e instanceof ConnectionError) {
58
61
  // rethrow connection errors
59
62
  throw e;
60
63
  } else if (signal?.aborted) {
61
- throw new ConnectionError(`Region fetching was aborted`, ConnectionErrorReason.Cancelled);
64
+ throw ConnectionError.cancelled(`Region fetching was aborted`);
62
65
  } else {
63
- // wrap other errors as connection errors (e.g. timeouts)
64
- throw new ConnectionError(
66
+ // wrap other errors as connection errors
67
+ throw ConnectionError.serverUnreachable(
65
68
  `Could not fetch region settings, ${e instanceof Error ? `${e.name}: ${e.message}` : e}`,
66
- ConnectionErrorReason.ServerUnreachable,
67
- 500, // using 500 as a catch-all manually set error code here
68
69
  );
69
70
  }
70
71
  } finally {
package/src/room/Room.ts CHANGED
@@ -31,8 +31,9 @@ import {
31
31
  protoInt64,
32
32
  } from '@livekit/protocol';
33
33
  import { EventEmitter } from 'events';
34
- import type TypedEmitter from 'typed-emitter';
35
34
  import 'webrtc-adapter';
35
+ import type TypedEmitter from 'typed-emitter';
36
+ import { ensureTrailingSlash } from '../api/utils';
36
37
  import { EncryptionEvent } from '../e2ee';
37
38
  import { type BaseE2EEManager, E2EEManager } from '../e2ee/E2eeManager';
38
39
  import log, { LoggerNames, getLogger } from '../logger';
@@ -644,7 +645,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
644
645
  }
645
646
 
646
647
  this.setAndEmitConnectionState(ConnectionState.Connecting);
647
- if (this.regionUrlProvider?.getServerUrl().toString() !== url) {
648
+ if (this.regionUrlProvider?.getServerUrl().toString() !== ensureTrailingSlash(url)) {
648
649
  this.regionUrl = undefined;
649
650
  this.regionUrlProvider = undefined;
650
651
  }
@@ -686,7 +687,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
686
687
  try {
687
688
  await BackOffStrategy.getInstance().getBackOffPromise(url);
688
689
  if (abortController.signal.aborted) {
689
- throw new ConnectionError('Connection attempt aborted', ConnectionErrorReason.Cancelled);
690
+ throw ConnectionError.cancelled('Connection attempt aborted');
690
691
  }
691
692
  await this.attemptConnection(regionUrl ?? url, token, opts, abortController);
692
693
  this.abortController = undefined;
@@ -894,12 +895,11 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
894
895
  } catch (err) {
895
896
  await this.engine.close();
896
897
  this.recreateEngine();
897
- const resultingError = new ConnectionError(
898
- `could not establish signal connection`,
899
- abortController.signal.aborted
900
- ? ConnectionErrorReason.Cancelled
901
- : ConnectionErrorReason.ServerUnreachable,
902
- );
898
+
899
+ const resultingError = abortController.signal.aborted
900
+ ? ConnectionError.cancelled('Signal connection aborted')
901
+ : ConnectionError.serverUnreachable('could not establish signal connection');
902
+
903
903
  if (err instanceof Error) {
904
904
  resultingError.message = `${resultingError.message}: ${err.message}`;
905
905
  }
@@ -917,7 +917,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
917
917
  if (abortController.signal.aborted) {
918
918
  await this.engine.close();
919
919
  this.recreateEngine();
920
- throw new ConnectionError(`Connection attempt aborted`, ConnectionErrorReason.Cancelled);
920
+ throw ConnectionError.cancelled(`Connection attempt aborted`);
921
921
  }
922
922
 
923
923
  try {
@@ -938,7 +938,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
938
938
  window.addEventListener('beforeunload', this.onPageLeave);
939
939
  }
940
940
  if (isWeb()) {
941
- document.addEventListener('freeze', this.onPageLeave);
941
+ window.addEventListener('freeze', this.onPageLeave);
942
942
  }
943
943
  this.setAndEmitConnectionState(ConnectionState.Connected);
944
944
  this.emit(RoomEvent.Connected);
@@ -974,9 +974,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
974
974
  this.log.warn(msg, this.logContext);
975
975
  this.abortController?.abort(msg);
976
976
  // in case the abort controller didn't manage to cancel the connection attempt, reject the connect promise explicitly
977
- this.connectFuture?.reject?.(
978
- new ConnectionError('Client initiated disconnect', ConnectionErrorReason.Cancelled),
979
- );
977
+ this.connectFuture?.reject?.(ConnectionError.cancelled('Client initiated disconnect'));
980
978
  this.connectFuture = undefined;
981
979
  }
982
980
 
@@ -1925,7 +1923,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
1925
1923
  });
1926
1924
  if (byteLength(response) > MAX_PAYLOAD_BYTES) {
1927
1925
  responseError = RpcError.builtIn('RESPONSE_PAYLOAD_TOO_LARGE');
1928
- console.warn(`RPC Response payload too large for ${method}`);
1926
+ this.log.warn(`RPC Response payload too large for ${method}`);
1929
1927
  } else {
1930
1928
  responsePayload = response;
1931
1929
  }
@@ -1933,7 +1931,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
1933
1931
  if (error instanceof RpcError) {
1934
1932
  responseError = error;
1935
1933
  } else {
1936
- console.warn(
1934
+ this.log.warn(
1937
1935
  `Uncaught error returned by RPC handler for ${method}. Returning APPLICATION_ERROR instead.`,
1938
1936
  error,
1939
1937
  );
@@ -93,7 +93,6 @@ export default class OutgoingDataStreamManager {
93
93
 
94
94
  /**
95
95
  * @internal
96
- * @experimental CAUTION, might get removed in a minor release
97
96
  */
98
97
  async streamText(options?: StreamTextOptions): Promise<TextStreamWriter> {
99
98
  const streamId = options?.streamId ?? crypto.randomUUID();
@@ -10,6 +10,14 @@ export class LivekitError extends Error {
10
10
  }
11
11
  }
12
12
 
13
+ export class SimulatedError extends LivekitError {
14
+ readonly name = 'simulated';
15
+
16
+ constructor(message = 'Simulated failure') {
17
+ super(-1, message);
18
+ }
19
+ }
20
+
13
21
  export enum ConnectionErrorReason {
14
22
  NotAllowed,
15
23
  ServerUnreachable,
@@ -17,80 +25,189 @@ export enum ConnectionErrorReason {
17
25
  Cancelled,
18
26
  LeaveRequest,
19
27
  Timeout,
28
+ WebSocket,
20
29
  }
21
30
 
22
- export class ConnectionError extends LivekitError {
31
+ type NotAllowed = {
32
+ reason: ConnectionErrorReason.NotAllowed;
33
+ status: number;
34
+ context?: unknown;
35
+ };
36
+
37
+ type InternalError = {
38
+ reason: ConnectionErrorReason.InternalError;
39
+ status: never;
40
+ context?: { status?: number; statusText?: string };
41
+ };
42
+
43
+ type ConnectionTimeout = {
44
+ reason: ConnectionErrorReason.Timeout;
45
+ status: never;
46
+ context: never;
47
+ };
48
+
49
+ type LeaveRequest = {
50
+ reason: ConnectionErrorReason.LeaveRequest;
51
+ status: never;
52
+ context: DisconnectReason;
53
+ };
54
+
55
+ type Cancelled = {
56
+ reason: ConnectionErrorReason.Cancelled;
57
+ status: never;
58
+ context: never;
59
+ };
60
+
61
+ type ServerUnreachable = {
62
+ reason: ConnectionErrorReason.ServerUnreachable;
63
+ status?: number;
64
+ context?: never;
65
+ };
66
+
67
+ type WebSocket = {
68
+ reason: ConnectionErrorReason.WebSocket;
23
69
  status?: number;
70
+ context?: string;
71
+ };
72
+
73
+ type ConnectionErrorVariants =
74
+ | NotAllowed
75
+ | ConnectionTimeout
76
+ | LeaveRequest
77
+ | InternalError
78
+ | Cancelled
79
+ | ServerUnreachable
80
+ | WebSocket;
24
81
 
25
- context?: unknown | DisconnectReason;
82
+ export class ConnectionError<
83
+ Variant extends ConnectionErrorVariants = ConnectionErrorVariants,
84
+ > extends LivekitError {
85
+ status?: Variant['status'];
26
86
 
27
- reason: ConnectionErrorReason;
87
+ context: Variant['context'];
88
+
89
+ reason: Variant['reason'];
28
90
 
29
91
  reasonName: string;
30
92
 
31
- constructor(
93
+ readonly name = 'ConnectionError';
94
+
95
+ protected constructor(
32
96
  message: string,
33
- reason: ConnectionErrorReason,
34
- status?: number,
35
- context?: unknown | DisconnectReason,
97
+ reason: Variant['reason'],
98
+ status?: Variant['status'],
99
+ context?: Variant['context'],
36
100
  ) {
37
101
  super(1, message);
38
- this.name = 'ConnectionError';
39
102
  this.status = status;
40
103
  this.reason = reason;
41
104
  this.context = context;
42
105
  this.reasonName = ConnectionErrorReason[reason];
43
106
  }
107
+
108
+ static notAllowed(message: string, status: number, context?: unknown) {
109
+ return new ConnectionError<NotAllowed>(
110
+ message,
111
+ ConnectionErrorReason.NotAllowed,
112
+ status,
113
+ context,
114
+ );
115
+ }
116
+
117
+ static timeout(message: string) {
118
+ return new ConnectionError<ConnectionTimeout>(message, ConnectionErrorReason.Timeout);
119
+ }
120
+
121
+ static leaveRequest(message: string, context: DisconnectReason) {
122
+ return new ConnectionError<LeaveRequest>(
123
+ message,
124
+ ConnectionErrorReason.LeaveRequest,
125
+ undefined,
126
+ context,
127
+ );
128
+ }
129
+
130
+ static internal(message: string, context?: { status?: number; statusText?: string }) {
131
+ return new ConnectionError<InternalError>(
132
+ message,
133
+ ConnectionErrorReason.InternalError,
134
+ undefined,
135
+ context,
136
+ );
137
+ }
138
+
139
+ static cancelled(message: string) {
140
+ return new ConnectionError<Cancelled>(message, ConnectionErrorReason.Cancelled);
141
+ }
142
+
143
+ static serverUnreachable(message: string, status?: number) {
144
+ return new ConnectionError<ServerUnreachable>(
145
+ message,
146
+ ConnectionErrorReason.ServerUnreachable,
147
+ status,
148
+ );
149
+ }
150
+
151
+ static websocket(message: string, status?: number, reason?: string) {
152
+ return new ConnectionError<WebSocket>(message, ConnectionErrorReason.WebSocket, status, reason);
153
+ }
44
154
  }
45
155
 
46
156
  export class DeviceUnsupportedError extends LivekitError {
157
+ readonly name = 'DeviceUnsupportedError';
158
+
47
159
  constructor(message?: string) {
48
160
  super(21, message ?? 'device is unsupported');
49
- this.name = 'DeviceUnsupportedError';
50
161
  }
51
162
  }
52
163
 
53
164
  export class TrackInvalidError extends LivekitError {
165
+ readonly name = 'TrackInvalidError';
166
+
54
167
  constructor(message?: string) {
55
168
  super(20, message ?? 'track is invalid');
56
- this.name = 'TrackInvalidError';
57
169
  }
58
170
  }
59
171
 
60
172
  export class UnsupportedServer extends LivekitError {
173
+ readonly name = 'UnsupportedServer';
174
+
61
175
  constructor(message?: string) {
62
176
  super(10, message ?? 'unsupported server');
63
- this.name = 'UnsupportedServer';
64
177
  }
65
178
  }
66
179
 
67
180
  export class UnexpectedConnectionState extends LivekitError {
181
+ readonly name = 'UnexpectedConnectionState';
182
+
68
183
  constructor(message?: string) {
69
184
  super(12, message ?? 'unexpected connection state');
70
- this.name = 'UnexpectedConnectionState';
71
185
  }
72
186
  }
73
187
 
74
188
  export class NegotiationError extends LivekitError {
189
+ readonly name = 'NegotiationError';
190
+
75
191
  constructor(message?: string) {
76
192
  super(13, message ?? 'unable to negotiate');
77
- this.name = 'NegotiationError';
78
193
  }
79
194
  }
80
195
 
81
196
  export class PublishDataError extends LivekitError {
197
+ readonly name = 'PublishDataError';
198
+
82
199
  constructor(message?: string) {
83
200
  super(14, message ?? 'unable to publish data');
84
- this.name = 'PublishDataError';
85
201
  }
86
202
  }
87
203
 
88
204
  export class PublishTrackError extends LivekitError {
205
+ readonly name = 'PublishTrackError';
206
+
89
207
  status: number;
90
208
 
91
209
  constructor(message: string, status: number) {
92
210
  super(15, message);
93
- this.name = 'PublishTrackError';
94
211
  this.status = status;
95
212
  }
96
213
  }
@@ -100,6 +217,8 @@ export type RequestErrorReason =
100
217
  | 'TimeoutError';
101
218
 
102
219
  export class SignalRequestError extends LivekitError {
220
+ readonly name = 'SignalRequestError';
221
+
103
222
  reason: RequestErrorReason;
104
223
 
105
224
  reasonName: string;
@@ -136,18 +255,27 @@ export enum DataStreamErrorReason {
136
255
  }
137
256
 
138
257
  export class DataStreamError extends LivekitError {
258
+ readonly name = 'DataStreamError';
259
+
139
260
  reason: DataStreamErrorReason;
140
261
 
141
262
  reasonName: string;
142
263
 
143
264
  constructor(message: string, reason: DataStreamErrorReason) {
144
265
  super(16, message);
145
- this.name = 'DataStreamError';
146
266
  this.reason = reason;
147
267
  this.reasonName = DataStreamErrorReason[reason];
148
268
  }
149
269
  }
150
270
 
271
+ export class SignalReconnectError extends LivekitError {
272
+ readonly name = 'SignalReconnectError';
273
+
274
+ constructor(message?: string) {
275
+ super(18, message);
276
+ }
277
+ }
278
+
151
279
  export enum MediaDeviceFailure {
152
280
  // user rejected permissions
153
281
  PermissionDenied = 'PermissionDenied',
@@ -1824,7 +1824,7 @@ export default class LocalParticipant extends Participant {
1824
1824
  resolve: (responsePayload: string | null, responseError: RpcError | null) => {
1825
1825
  clearTimeout(responseTimeoutId);
1826
1826
  if (this.pendingAcks.has(id)) {
1827
- console.warn('RPC response received before ack', id);
1827
+ this.log.warn('RPC response received before ack', id);
1828
1828
  this.pendingAcks.delete(id);
1829
1829
  clearTimeout(ackTimeoutId);
1830
1830
  }
@@ -245,9 +245,13 @@ class TokenSourceSandboxTokenServer extends TokenSourceEndpoint {
245
245
  }
246
246
 
247
247
  export {
248
+ /** The return type of {@link TokenSource.literal} */
248
249
  type TokenSourceLiteral,
250
+ /** The return type of {@link TokenSource.custom} */
249
251
  type TokenSourceCustom,
252
+ /** The return type of {@link TokenSource.endpoint} */
250
253
  type TokenSourceEndpoint,
254
+ /** The return type of {@link TokenSource.sandboxTokenServer} */
251
255
  type TokenSourceSandboxTokenServer,
252
256
  decodeTokenPayload,
253
257
  areTokenSourceFetchOptionsEqual,
@@ -273,7 +277,7 @@ export const TokenSource = {
273
277
  /**
274
278
  * TokenSource.endpoint creates a token source that fetches credentials from a given URL using
275
279
  * the standard endpoint format:
276
- * FIXME: add docs link here in the future!
280
+ * @see https://cloud.livekit.io/projects/p_/sandbox/templates/token-server
277
281
  */
278
282
  endpoint(url: string, options: EndpointOptions = {}) {
279
283
  return new TokenSourceEndpoint(url, options);
@@ -518,8 +518,6 @@ export default abstract class LocalTrack<
518
518
  * Sets a processor on this track.
519
519
  * See https://github.com/livekit/track-processors-js for example usage
520
520
  *
521
- * @experimental
522
- *
523
521
  * @param processor
524
522
  * @param showProcessedStreamLocally
525
523
  * @returns
@@ -592,8 +590,6 @@ export default abstract class LocalTrack<
592
590
  * Stops the track processor
593
591
  * See https://github.com/livekit/track-processors-js for example usage
594
592
  *
595
- * @experimental
596
- * @returns
597
593
  */
598
594
  async stopProcessor(keepElement = true) {
599
595
  const unlock = await this.trackChangeLock.lock();
@@ -1,11 +1,11 @@
1
1
  import { Encryption_Type } from '@livekit/protocol';
2
+ import { EventEmitter } from 'events';
2
3
  import type {
3
4
  SubscriptionError,
4
5
  TrackInfo,
5
6
  UpdateSubscription,
6
7
  UpdateTrackSettings,
7
8
  } from '@livekit/protocol';
8
- import { EventEmitter } from 'events';
9
9
  import type TypedEventEmitter from 'typed-emitter';
10
10
  import log, { LoggerNames, getLogger } from '../../logger';
11
11
  import { TrackEvent } from '../events';
@@ -11,9 +11,6 @@ export type ProcessorOptions<T extends Track.Kind> = {
11
11
  audioContext?: AudioContext;
12
12
  };
13
13
 
14
- /**
15
- * @experimental
16
- */
17
14
  export interface AudioProcessorOptions extends ProcessorOptions<Track.Kind.Audio> {
18
15
  audioContext: AudioContext;
19
16
  }
@@ -23,9 +20,6 @@ export interface AudioProcessorOptions extends ProcessorOptions<Track.Kind.Audio
23
20
  */
24
21
  export interface VideoProcessorOptions extends ProcessorOptions<Track.Kind.Video> {}
25
22
 
26
- /**
27
- * @experimental
28
- */
29
23
  export interface TrackProcessor<
30
24
  T extends Track.Kind,
31
25
  U extends ProcessorOptions<T> = ProcessorOptions<T>,