livekit-client 2.22.0 → 2.22.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 (75) hide show
  1. package/dist/livekit-client.e2ee.worker.js +1 -1
  2. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  3. package/dist/livekit-client.e2ee.worker.mjs +187 -1
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +1764 -121
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.fm.worker.js +1 -1
  8. package/dist/livekit-client.fm.worker.js.map +1 -1
  9. package/dist/livekit-client.fm.worker.mjs +187 -1
  10. package/dist/livekit-client.fm.worker.mjs.map +1 -1
  11. package/dist/livekit-client.umd.js +1 -1
  12. package/dist/livekit-client.umd.js.map +1 -1
  13. package/dist/src/api/SignalClient.d.ts +25 -1
  14. package/dist/src/api/SignalClient.d.ts.map +1 -1
  15. package/dist/src/api/SignalClientStateMachine.d.ts +85 -0
  16. package/dist/src/api/SignalClientStateMachine.d.ts.map +1 -0
  17. package/dist/src/api/WebSocketStream.d.ts.map +1 -1
  18. package/dist/src/api/utils.d.ts.map +1 -1
  19. package/dist/src/index.d.ts +3 -2
  20. package/dist/src/index.d.ts.map +1 -1
  21. package/dist/src/logger.d.ts +2 -1
  22. package/dist/src/logger.d.ts.map +1 -1
  23. package/dist/src/options.d.ts +22 -0
  24. package/dist/src/options.d.ts.map +1 -1
  25. package/dist/src/room/PCTransport.d.ts +2 -1
  26. package/dist/src/room/PCTransport.d.ts.map +1 -1
  27. package/dist/src/room/RTCEngine.d.ts +7 -2
  28. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  29. package/dist/src/room/Room.d.ts +8 -1
  30. package/dist/src/room/Room.d.ts.map +1 -1
  31. package/dist/src/room/events.d.ts +1 -1
  32. package/dist/src/room/statsSummary.d.ts +13 -0
  33. package/dist/src/room/statsSummary.d.ts.map +1 -0
  34. package/dist/src/room/token-source/utils.d.ts.map +1 -1
  35. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  36. package/dist/src/room/track/Track.d.ts +10 -0
  37. package/dist/src/room/track/Track.d.ts.map +1 -1
  38. package/dist/src/utils/machineInspector.d.ts +54 -0
  39. package/dist/src/utils/machineInspector.d.ts.map +1 -0
  40. package/dist/ts4.2/api/SignalClient.d.ts +25 -1
  41. package/dist/ts4.2/api/SignalClientStateMachine.d.ts +85 -0
  42. package/dist/ts4.2/index.d.ts +3 -2
  43. package/dist/ts4.2/logger.d.ts +2 -1
  44. package/dist/ts4.2/options.d.ts +22 -0
  45. package/dist/ts4.2/room/PCTransport.d.ts +2 -1
  46. package/dist/ts4.2/room/RTCEngine.d.ts +7 -2
  47. package/dist/ts4.2/room/Room.d.ts +8 -1
  48. package/dist/ts4.2/room/events.d.ts +1 -1
  49. package/dist/ts4.2/room/statsSummary.d.ts +13 -0
  50. package/dist/ts4.2/room/track/Track.d.ts +10 -0
  51. package/dist/ts4.2/utils/machineInspector.d.ts +54 -0
  52. package/package.json +7 -1
  53. package/src/api/SignalClient.test.ts +320 -8
  54. package/src/api/SignalClient.ts +260 -82
  55. package/src/api/SignalClientStateMachine.test.ts +472 -0
  56. package/src/api/SignalClientStateMachine.ts +180 -0
  57. package/src/api/WebSocketStream.ts +19 -3
  58. package/src/api/utils.test.ts +20 -1
  59. package/src/api/utils.ts +5 -0
  60. package/src/index.ts +5 -0
  61. package/src/logger.ts +1 -0
  62. package/src/options.ts +24 -0
  63. package/src/room/PCTransport.ts +2 -1
  64. package/src/room/RTCEngine.ts +16 -7
  65. package/src/room/Room.ts +70 -5
  66. package/src/room/events.ts +1 -1
  67. package/src/room/statsSummary.ts +187 -0
  68. package/src/room/token-source/test-tokens.ts +20 -0
  69. package/src/room/token-source/utils.test.ts +27 -0
  70. package/src/room/token-source/utils.ts +12 -5
  71. package/src/room/track/LocalAudioTrack.ts +9 -3
  72. package/src/room/track/Track.ts +27 -0
  73. package/src/room/utils.test.ts +24 -1
  74. package/src/room/utils.ts +1 -1
  75. package/src/utils/machineInspector.ts +90 -0
@@ -11,6 +11,7 @@ import type { SignalClient } from '../../api/SignalClient';
11
11
  import log, { LoggerNames, type StructuredLogger, getLogger } from '../../logger';
12
12
  import type { NonSharedUint8Array } from '../../type-polyfills/non-shared-typed-arrays';
13
13
  import { TrackEvent } from '../events';
14
+ import { summarizeStatsReport } from '../statsSummary';
14
15
  import type { LoggerOptions } from '../types';
15
16
  import { isFireFox, isSafari, isWeb } from '../utils';
16
17
  import type { TrackProcessor } from './processor/types';
@@ -85,6 +86,8 @@ export abstract class Track<
85
86
 
86
87
  protected monitorInterval?: ReturnType<typeof setInterval>;
87
88
 
89
+ private finalStatsLogged = false;
90
+
88
91
  protected log: StructuredLogger = log;
89
92
 
90
93
  protected constructor(
@@ -271,6 +274,11 @@ export abstract class Track<
271
274
  /* @internal */
272
275
  abstract startMonitor(signalClient?: SignalClient): void;
273
276
 
277
+ /**
278
+ * Raw stats of the sender or receiver this track is attached to.
279
+ */
280
+ abstract getRTCStatsReport(): Promise<RTCStatsReport | undefined>;
281
+
274
282
  /* @internal */
275
283
  stopMonitor() {
276
284
  if (this.monitorInterval) {
@@ -280,6 +288,25 @@ export abstract class Track<
280
288
  cancelAnimationFrame(this.timeSyncHandle);
281
289
  this.timeSyncHandle = undefined;
282
290
  }
291
+ this.logFinalStats();
292
+ }
293
+
294
+ /**
295
+ * Dumps the raw stats of the track as it ends, once: a track that stops
296
+ * between two of the room's stats dumps is gone by the time the next one runs.
297
+ */
298
+ private logFinalStats() {
299
+ if (this.finalStatsLogged) {
300
+ return;
301
+ }
302
+ this.finalStatsLogged = true;
303
+ this.getRTCStatsReport()
304
+ .then((report) => {
305
+ if (report) {
306
+ this.log.info('final track stats', summarizeStatsReport(report));
307
+ }
308
+ })
309
+ .catch((error) => this.log.debug('could not collect final track stats', { error }));
283
310
  }
284
311
 
285
312
  /** @internal */
@@ -1,11 +1,12 @@
1
1
  import { ClientInfo_Capability } from '@livekit/protocol';
2
- import { describe, expect, it, vi } from 'vitest';
2
+ import { afterEach, describe, expect, it, vi } from 'vitest';
3
3
  import {
4
4
  ddExtensionURI,
5
5
  extractMaxAgeFromRequestHeaders,
6
6
  getClientInfo,
7
7
  negotiateDependencyDescriptor,
8
8
  splitUtf8,
9
+ supportsAdaptiveStream,
9
10
  toWebsocketUrl,
10
11
  } from './utils';
11
12
 
@@ -257,3 +258,25 @@ describe('negotiateDependencyDescriptor', () => {
257
258
  expect(negotiateDependencyDescriptor(transceiver)).toBe(false);
258
259
  });
259
260
  });
261
+
262
+ describe('supportsAdaptiveStream', () => {
263
+ afterEach(() => {
264
+ vi.unstubAllGlobals();
265
+ });
266
+
267
+ it('reports support where both observers exist', () => {
268
+ expect(supportsAdaptiveStream()).toBe(true);
269
+ });
270
+
271
+ it('reports no support where ResizeObserver is missing', () => {
272
+ vi.stubGlobal('ResizeObserver', undefined);
273
+
274
+ expect(supportsAdaptiveStream()).toBe(false);
275
+ });
276
+
277
+ it('reports no support where IntersectionObserver is missing', () => {
278
+ vi.stubGlobal('IntersectionObserver', undefined);
279
+
280
+ expect(supportsAdaptiveStream()).toBe(false);
281
+ });
282
+ });
package/src/room/utils.ts CHANGED
@@ -58,7 +58,7 @@ export function supportsAddTrack() {
58
58
  }
59
59
 
60
60
  export function supportsAdaptiveStream() {
61
- return typeof ResizeObserver !== undefined && typeof IntersectionObserver !== undefined;
61
+ return typeof ResizeObserver !== 'undefined' && typeof IntersectionObserver !== 'undefined';
62
62
  }
63
63
 
64
64
  export function supportsDynacast() {
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Opt-in registry that lets a development tool observe the state machines driving the connection
3
+ * layer.
4
+ *
5
+ * The machines are private to the objects that own them and, more importantly, do not outlive them:
6
+ * a full reconnect replaces the whole engine along with its `SignalClient`, so anything watching a
7
+ * single machine reference goes blind exactly when the interesting part starts. Machines therefore
8
+ * announce themselves here as they are constructed, and a subscriber sees the whole succession.
9
+ *
10
+ * Nothing is recorded until {@link enableMachineInspector} is called, which no shipping code does —
11
+ * with the registry off, announcing is a comparison and a return.
12
+ */
13
+
14
+ /**
15
+ * The slice of machina's `Fsm` surface an inspector needs. Structural rather than machina's own
16
+ * type so that any machine satisfies it regardless of its state and input unions.
17
+ */
18
+ export interface InspectableMachine {
19
+ readonly id: string;
20
+ readonly initialState: string;
21
+ readonly states: Record<string, Record<string, unknown>>;
22
+ readonly context?: unknown;
23
+ currentState(): string;
24
+ canHandle(input: string): boolean;
25
+ on(eventName: string, callback: (data: any) => void): { off(): void };
26
+ }
27
+
28
+ export interface MachineAnnouncement {
29
+ /** What the machine drives, e.g. `signal`. Not unique: each new `SignalClient` announces one. */
30
+ label: string;
31
+ /** How many machines have been announced before this one, so successive instances are tellable apart. */
32
+ seq: number;
33
+ /**
34
+ * When the machine was constructed. Carried on the announcement because subscribers receive the
35
+ * recorded ones on subscribe, and stamping those at replay time would date them all to whenever
36
+ * the tool happened to open.
37
+ */
38
+ at: number;
39
+ machine: InspectableMachine;
40
+ }
41
+
42
+ /** Kept small: enough for a panel opened mid-session to see how the connection got where it is. */
43
+ const HISTORY_LIMIT = 16;
44
+
45
+ let enabled = false;
46
+ let announced: MachineAnnouncement[] = [];
47
+ let subscribers: Array<(announcement: MachineAnnouncement) => void> = [];
48
+
49
+ /**
50
+ * Starts recording machine announcements. Call before the machines of interest are constructed —
51
+ * for the signal machine that means before the `Room` is created.
52
+ */
53
+ export function enableMachineInspector() {
54
+ enabled = true;
55
+ }
56
+
57
+ export function isMachineInspectorEnabled() {
58
+ return enabled;
59
+ }
60
+
61
+ /** Announces a machine to whatever is watching. A no-op unless the inspector was enabled. */
62
+ export function announceMachine(label: string, machine: InspectableMachine) {
63
+ if (!enabled) {
64
+ return;
65
+ }
66
+ const announcement: MachineAnnouncement = {
67
+ label,
68
+ seq: announced.length,
69
+ at: Date.now(),
70
+ machine,
71
+ };
72
+ announced = [...announced.slice(-(HISTORY_LIMIT - 1)), announcement];
73
+ for (const subscriber of subscribers) {
74
+ subscriber(announcement);
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Subscribes to machine announcements, replaying the ones already recorded so that a panel opened
80
+ * after connecting still sees the machine currently in charge. Returns an unsubscribe function.
81
+ */
82
+ export function onMachineAnnounced(callback: (announcement: MachineAnnouncement) => void) {
83
+ subscribers = [...subscribers, callback];
84
+ for (const announcement of announced) {
85
+ callback(announcement);
86
+ }
87
+ return () => {
88
+ subscribers = subscribers.filter((subscriber) => subscriber !== callback);
89
+ };
90
+ }