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
@@ -0,0 +1,54 @@
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
+ * The slice of machina's `Fsm` surface an inspector needs. Structural rather than machina's own
15
+ * type so that any machine satisfies it regardless of its state and input unions.
16
+ */
17
+ export interface InspectableMachine {
18
+ readonly id: string;
19
+ readonly initialState: string;
20
+ readonly states: Record<string, Record<string, unknown>>;
21
+ readonly context?: unknown;
22
+ currentState(): string;
23
+ canHandle(input: string): boolean;
24
+ on(eventName: string, callback: (data: any) => void): {
25
+ 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
+ * Starts recording machine announcements. Call before the machines of interest are constructed —
43
+ * for the signal machine that means before the `Room` is created.
44
+ */
45
+ export declare function enableMachineInspector(): void;
46
+ export declare function isMachineInspectorEnabled(): boolean;
47
+ /** Announces a machine to whatever is watching. A no-op unless the inspector was enabled. */
48
+ export declare function announceMachine(label: string, machine: InspectableMachine): void;
49
+ /**
50
+ * Subscribes to machine announcements, replaying the ones already recorded so that a panel opened
51
+ * after connecting still sees the machine currently in charge. Returns an unsubscribe function.
52
+ */
53
+ export declare function onMachineAnnounced(callback: (announcement: MachineAnnouncement) => void): () => void;
54
+ //# sourceMappingURL=machineInspector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machineInspector.d.ts","sourceRoot":"","sources":["../../../src/utils/machineInspector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,IAAI,MAAM,CAAC;IACvB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,GAAG;QAAE,GAAG,IAAI,IAAI,CAAA;KAAE,CAAC;CACvE;AAED,MAAM,WAAW,mBAAmB;IAClC,iGAAiG;IACjG,KAAK,EAAE,MAAM,CAAC;IACd,yGAAyG;IACzG,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AASD;;;GAGG;AACH,wBAAgB,sBAAsB,SAErC;AAED,wBAAgB,yBAAyB,YAExC;AAED,6FAA6F;AAC7F,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,QAczE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAE,mBAAmB,KAAK,IAAI,cAQvF"}
@@ -70,6 +70,20 @@ export declare class SignalClient {
70
70
  ws?: WebSocketStream;
71
71
  get currentState(): SignalConnectionState;
72
72
  get isDisconnected(): boolean;
73
+ /** Runtime lifecycle state, finer grained than the public {@link currentState} projection. */
74
+ private get lifecycleState();
75
+ /** Id of the current connection attempt and of the transport it owns. */
76
+ private get attemptId();
77
+ /**
78
+ * Applies a lifecycle input and reports whether it moved the machine
79
+ */
80
+ private sendLifecycleInput;
81
+ /**
82
+ * Waits out a close that is already in flight. Establishing a session while one is tearing down
83
+ * would race it for the transport — the teardown can close the socket the new attempt just
84
+ * opened.
85
+ */
86
+ private settleInFlightClose;
73
87
  private get isEstablishingConnection();
74
88
  private getNextRequestId;
75
89
  private options?;
@@ -78,7 +92,7 @@ export declare class SignalClient {
78
92
  private pingIntervalDuration;
79
93
  private pingInterval;
80
94
  private closingLock;
81
- private state;
95
+ private machine;
82
96
  private connectionLock;
83
97
  private log;
84
98
  private loggerContextCb?;
@@ -94,6 +108,12 @@ export declare class SignalClient {
94
108
  /** @internal */
95
109
  resetCallbacks: () => void;
96
110
  close(updateState?: boolean, reason?: string): Promise<void>;
111
+ /**
112
+ * Releases the transport and everything tied to it, without touching the lifecycle state. Used
113
+ * both by {@link close} and by paths that replace the transport under a live lifecycle (a new
114
+ * attempt, or an unexpected close that leaves the client in `offline`).
115
+ */
116
+ private teardownTransport;
97
117
  sendOffer(offer: RTCSessionDescriptionInit, offerId: number): void;
98
118
  sendAnswer(answer: RTCSessionDescriptionInit, offerId: number): Promise<void>;
99
119
  sendIceCandidate(candidate: RTCIceCandidateInit, target: SignalTarget): Promise<void>;
@@ -118,6 +138,10 @@ export declare class SignalClient {
118
138
  private sendRequest;
119
139
  private handleSignalResponse;
120
140
  setReconnected(): void;
141
+ /**
142
+ * Handles a transport we lost without asking to. The client goes to `offline` rather than
143
+ * `closed`: whether this session gets resumed, restarted or given up on is the engine's call.
144
+ */
121
145
  private handleOnClose;
122
146
  private handleWSError;
123
147
  /**
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Runtime lifecycle states of the signal connection.
3
+ *
4
+ * `offline` is the resting state after the transport was lost without an explicit close: no
5
+ * attempt is in flight, but a resume is still legal. It is what makes retrying a failed resume
6
+ * expressible — `closed` is only reached by an explicit close or by a terminal attempt failure.
7
+ */
8
+ export type SignalLifecycleState = 'new' | 'connecting' | 'connected' | 'offline' | 'reconnecting' | 'disconnecting' | 'closed';
9
+ export interface SignalMachineContext {
10
+ /** Monotonic id of the current (re)connection attempt and of the transport it owns */
11
+ attemptId: number;
12
+ /** Error or reason that ended the last attempt, kept for diagnostics. */
13
+ lastError?: unknown;
14
+ /** Reason passed to the last close request. */
15
+ closeReason?: string;
16
+ }
17
+ export type SignalMachineInput =
18
+ /**
19
+ * Start an initial session, or restart from scratch (full reconnect).
20
+ *
21
+ * Establishing is legal exactly where no transport and no attempt are in play: `new`, `offline`
22
+ * and `closed`. `closed` is included because it means "no transport", not "session over" — a
23
+ * deliberate close and an unexpected loss leave the session equally resumable, and the engine
24
+ * recovers from both. Establishing over a live session, or over an attempt already in flight, is
25
+ * a caller error; `disconnecting` is waited out rather than refused.
26
+ */
27
+ {
28
+ type: 'connect';
29
+ }
30
+ /**
31
+ * Resume the existing session: legal wherever establishing is, minus `new` (nothing to resume
32
+ * yet), plus `connected` — the peer connection can be severed while signalling stays up.
33
+ */
34
+ | {
35
+ type: 'reconnect';
36
+ }
37
+ /**
38
+ * An attempt established its transport. Carries the attempt it belongs to: an attempt that a
39
+ * newer one has already superseded must not declare the session live.
40
+ */
41
+ | {
42
+ type: 'connectComplete';
43
+ attemptId: number;
44
+ } | {
45
+ type: 'connectFailed';
46
+ error?: unknown;
47
+ } | {
48
+ type: 'reconnectComplete';
49
+ attemptId: number;
50
+ }
51
+ /**
52
+ * A resume attempt ended. `recoverable` distinguishes "another resume may follow" (→ `offline`)
53
+ * from a terminal outcome such as a server leave or an expired token (→ `closed`).
54
+ */
55
+ | {
56
+ type: 'reconnectFailed';
57
+ error?: unknown;
58
+ recoverable: boolean;
59
+ }
60
+ /** The transport identified by `attemptId` was lost (unexpected ws close, ping timeout). */
61
+ | {
62
+ type: 'transportFailed';
63
+ attemptId: number;
64
+ reason: string;
65
+ } | {
66
+ type: 'close';
67
+ reason: string;
68
+ } | {
69
+ type: 'closeComplete';
70
+ };
71
+ /**
72
+ * Lifecycle model of the signal connection.
73
+ *
74
+ * The machine deliberately does not own connection attempts: `SignalClient` performs the
75
+ * asynchronous work and reports the outcome. It also does not decide *whether* to reconnect —
76
+ * that policy (backoff, resume vs. full reconnect, region failover, giving up) belongs to
77
+ * `RTCEngine`, so transport loss lands in `offline` rather than starting a reconnect on its own.
78
+ *
79
+ * Each client gets its own instance: the context is mutable and per-connection.
80
+ */
81
+ export declare function createSignalMachine(initialState?: SignalLifecycleState): import("machina").Fsm<SignalMachineContext, "new" | "connecting" | "connected" | "offline" | "reconnecting" | "disconnecting" | "closed", "connect" | "reconnect" | "connectComplete" | "connectFailed" | "reconnectComplete" | "reconnectFailed" | "transportFailed" | "close" | "closeComplete", never>;
82
+ export type SignalMachine = ReturnType<typeof createSignalMachine>;
83
+ /** All lifecycle states, derived from the machine itself so the two cannot drift. */
84
+ export declare const signalLifecycleStates: Array<SignalLifecycleState>;
85
+ //# sourceMappingURL=SignalClientStateMachine.d.ts.map
@@ -29,7 +29,7 @@ import type { PublicationEventCallbacks } from './room/track/TrackPublication';
29
29
  import { TrackPublication } from './room/track/TrackPublication';
30
30
  import type { LiveKitReactNativeInfo, TextStreamInfo } from './room/types';
31
31
  import type { AudioAnalyserOptions } from './room/utils';
32
- import { compareVersions, createAudioAnalyser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isAudioCodec, isAudioTrack, isBrowserSupported, isLocalParticipant, isLocalTrack, isRemoteParticipant, isRemoteTrack, isVideoCodec, isVideoTrack, supportsAV1, supportsAdaptiveStream, supportsAudioOutputSelection, supportsDynacast, supportsVP9 } from './room/utils';
32
+ import { compareVersions, createAudioAnalyser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isAudioCodec, isAudioTrack, isBrowserSupported, isLocalParticipant, isLocalTrack, isRemoteParticipant, isRemoteTrack, isSVCCodec, isVideoCodec, isVideoTrack, supportsAV1, supportsAdaptiveStream, supportsAudioOutputSelection, supportsDynacast, supportsH265, supportsVP9 } from './room/utils';
33
33
  import { getBrowser } from './utils/browserParser';
34
34
  export type { RpcInvocationData, PerformRpcParams } from './room/rpc';
35
35
  export { RpcError } from './room/rpc';
@@ -63,8 +63,9 @@ export type { DataPublishOptions, SimulationScenario, TranscriptionSegment, Chat
63
63
  export * from './version';
64
64
  export {
65
65
  /** @internal */
66
- attributes, ConnectionQuality, ConnectionState, CriticalTimers, DataPacket_Kind, Encryption_Type, DefaultReconnectPolicy, DisconnectReason, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, LoggerNames, Participant, RemoteAudioTrack, RemoteParticipant, ParticipantKind, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, SubscriptionError, TrackPublication, TrackType, compareVersions, createAudioAnalyser, getBrowser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, getLogger, isBrowserSupported, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsAudioOutputSelection, supportsDynacast, supportsVP9, Mutex, isAudioCodec, isAudioTrack, isLocalTrack, isRemoteTrack, isVideoCodec, isVideoTrack, isLocalParticipant, isRemoteParticipant, LocalDataTrack, RemoteDataTrack, };
66
+ attributes, ConnectionQuality, ConnectionState, CriticalTimers, DataPacket_Kind, Encryption_Type, DefaultReconnectPolicy, DisconnectReason, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, LoggerNames, Participant, RemoteAudioTrack, RemoteParticipant, ParticipantKind, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, SubscriptionError, TrackPublication, TrackType, compareVersions, createAudioAnalyser, getBrowser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, getLogger, isBrowserSupported, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsAudioOutputSelection, supportsDynacast, supportsH265, supportsVP9, Mutex, isAudioCodec, isAudioTrack, isLocalTrack, isRemoteTrack, isSVCCodec, isVideoCodec, isVideoTrack, isLocalParticipant, isRemoteParticipant, LocalDataTrack, RemoteDataTrack, };
67
67
  export type { AudioAnalyserOptions, ElementInfo, LiveKitReactNativeInfo, TextStreamInfo, ParticipantTrackPermission, AudioReceiverStats, AudioSenderStats, VideoReceiverStats, VideoSenderStats, ReconnectContext, ReconnectPolicy, RoomEventCallbacks, ParticipantEventCallbacks, PublicationEventCallbacks, DataTrackSubscribeOptions, RemoteDataTrackPipelineOptions, };
68
+ export type { DataTrackFrame } from './room/data-track/frame';
68
69
  export type { DataTrackPacketHeader } from './room/data-track/packet';
69
70
  export { DataTrackPacket } from './room/data-track/packet';
70
71
  export type { DataTrackExtensions, DataTrackUserTimestampExtension, DataTrackE2eeExtension } from './room/data-track/packet/extensions';
@@ -21,7 +21,8 @@ export declare enum LoggerNames {
21
21
  E2EE = "lk-e2ee",
22
22
  DataTracks = "livekit-data-tracks",
23
23
  Region = "livekit-region",
24
- ICE = "livekit-ice"
24
+ ICE = "livekit-ice",
25
+ Stats = "livekit-stats"
25
26
  }
26
27
  type LogLevelString = keyof typeof LogLevel;
27
28
  export type StructuredLogger = log.Logger & {
@@ -96,6 +96,28 @@ export interface InternalRoomOptions {
96
96
  * @default true
97
97
  */
98
98
  singlePeerConnection: boolean;
99
+ /**
100
+ * Options controlling data stream behavior for this room.
101
+ */
102
+ dataStream?: RoomDataStreamOptions;
103
+ }
104
+ /**
105
+ * Options controlling data stream behavior for a room.
106
+ */
107
+ export interface RoomDataStreamOptions {
108
+ /**
109
+ * Maximum size, in bytes, of the payload this client accepts from a single incoming data stream.
110
+ *
111
+ * A compressed stream can inflate to an arbitrarily large payload, so the decompressed output is
112
+ * bounded rather than trusting the size declared on the wire. An incoming stream that exceeds the
113
+ * cap fails with a `DataStreamErrorReason.PayloadTooLarge` error on the next read instead of
114
+ * buffering without bound.
115
+ *
116
+ * This is enforced on the receiving side only: raising it on a sender has no effect.
117
+ *
118
+ * @default 5_000_000_000 (5 GB)
119
+ */
120
+ maxPayloadByteLength?: number;
99
121
  }
100
122
  /**
101
123
  * Options for when creating a new room
@@ -91,7 +91,8 @@ export default class PCTransport extends PCTransport_base {
91
91
  getSenders(): RTCRtpSender[];
92
92
  getLocalDescription(): RTCSessionDescription | null | undefined;
93
93
  getRemoteDescription(): RTCSessionDescription | null;
94
- getStats(): Promise<RTCStatsReport>;
94
+ /** stats of the underlying connection, `undefined` when there is none */
95
+ getStats(): Promise<RTCStatsReport> | undefined;
95
96
  getMaxMessageSize(): number | undefined;
96
97
  getConnectedAddress(): Promise<string | undefined>;
97
98
  close: () => void;
@@ -98,10 +98,15 @@ export default class RTCEngine extends RTCEngine_base {
98
98
  joinResponse: JoinResponse;
99
99
  serverInfo: Partial<ServerInfo>;
100
100
  }>;
101
- close(): Promise<void>;
101
+ /**
102
+ * @param reason why the session is ending, recorded by the signal lifecycle. Worth passing
103
+ * wherever the caller knows more than "someone called close" — the server's leave reason, or
104
+ * having given up on reconnecting.
105
+ */
106
+ close(reason?: string): Promise<void>;
102
107
  cleanupPeerConnections(): Promise<void>;
103
108
  cleanupLossyDataStats(): void;
104
- cleanupClient(): Promise<void>;
109
+ cleanupClient(reason?: string): Promise<void>;
105
110
  addTrack(req: AddTrackRequest): Promise<TrackInfo>;
106
111
  /**
107
112
  * Removes sender from PeerConnection, returning true if it was removed successfully
@@ -72,10 +72,12 @@ declare class Room extends Room_base {
72
72
  private frameMetadataManager;
73
73
  private e2eeStateMutex;
74
74
  private connectionReconcileInterval?;
75
+ private statsLogInterval?;
75
76
  private regionUrlProvider?;
76
77
  private regionUrl?;
77
78
  private isVideoPlaybackBlocked;
78
79
  private log;
80
+ private statsLog;
79
81
  private bufferedEvents;
80
82
  private isResuming;
81
83
  private pendingTrackAddedCallbacks;
@@ -276,6 +278,11 @@ declare class Room extends Room_base {
276
278
  private getRemoteParticipantCapabilities;
277
279
  private getAllRemoteParticipantIdentities;
278
280
  private registerRpcDataStreamHandler;
281
+ private setStatsLogging;
282
+ /**
283
+ * Dumps stats of both peer connections.
284
+ */
285
+ private logWebRTCStats;
279
286
  private registerConnectionReconcile;
280
287
  private clearConnectionReconcile;
281
288
  private setAndEmitConnectionState;
@@ -313,7 +320,7 @@ export type RoomEventCallbacks = {
313
320
  moved: (name: string) => void;
314
321
  mediaDevicesChanged: () => void;
315
322
  participantConnected: (participant: RemoteParticipant) => void;
316
- participantDisconnected: (participant: RemoteParticipant) => void;
323
+ participantDisconnected: (participant: RemoteParticipant, disconnectReason?: DisconnectReason) => void;
317
324
  trackPublished: (publication: RemoteTrackPublication, participant: RemoteParticipant) => void;
318
325
  trackSubscribed: (track: RemoteTrack, publication: RemoteTrackPublication, participant: RemoteParticipant) => void;
319
326
  trackSubscriptionFailed: (trackSid: string, participant: RemoteParticipant, reason?: SubscriptionError) => void;
@@ -70,7 +70,7 @@ export declare enum RoomEvent {
70
70
  * When a [[RemoteParticipant]] leaves *after* the local
71
71
  * participant has joined.
72
72
  *
73
- * args: ([[RemoteParticipant]])
73
+ * args: ([[RemoteParticipant]], [[DisconnectReason]] | undefined)
74
74
  */
75
75
  ParticipantDisconnected = "participantDisconnected",
76
76
  /**
@@ -0,0 +1,13 @@
1
+ /** one summarised stats entry; keys without a value are dropped */
2
+ type Summary = Record<string, unknown>;
3
+ /**
4
+ * Picks the interesting fields out of a `getStats()` report and groups them by
5
+ * RTP stream, so a stats dump can be read without unfolding the raw report.
6
+ */
7
+ export declare function summarizeStatsReport(report: RTCStatsReport): {
8
+ connection: Summary | undefined;
9
+ outbound: Summary[] | undefined;
10
+ inbound: Summary[] | undefined;
11
+ };
12
+ export {};
13
+ //# sourceMappingURL=statsSummary.d.ts.map
@@ -42,6 +42,7 @@ export declare abstract class Track<TrackKind extends Track.Kind = Track.Kind> e
42
42
  protected timeSyncHandle: number | undefined;
43
43
  protected _currentBitrate: number;
44
44
  protected monitorInterval?: ReturnType<typeof setInterval>;
45
+ private finalStatsLogged;
45
46
  protected log: StructuredLogger;
46
47
  protected constructor(mediaTrack: MediaStreamTrack, kind: TrackKind, loggerOptions?: LoggerOptions);
47
48
  protected get logContext(): {
@@ -78,7 +79,16 @@ export declare abstract class Track<TrackKind extends Track.Kind = Track.Kind> e
78
79
  protected enable(): void;
79
80
  protected disable(): void;
80
81
  abstract startMonitor(signalClient?: SignalClient): void;
82
+ /**
83
+ * Raw stats of the sender or receiver this track is attached to.
84
+ */
85
+ abstract getRTCStatsReport(): Promise<RTCStatsReport | undefined>;
81
86
  stopMonitor(): void;
87
+ /**
88
+ * Dumps the raw stats of the track as it ends, once: a track that stops
89
+ * between two of the room's stats dumps is gone by the time the next one runs.
90
+ */
91
+ private logFinalStats;
82
92
  /** @internal */
83
93
  updateLoggerOptions(loggerOptions: LoggerOptions): void;
84
94
  private recycleElement;
@@ -0,0 +1,54 @@
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
+ * The slice of machina's `Fsm` surface an inspector needs. Structural rather than machina's own
15
+ * type so that any machine satisfies it regardless of its state and input unions.
16
+ */
17
+ export interface InspectableMachine {
18
+ readonly id: string;
19
+ readonly initialState: string;
20
+ readonly states: Record<string, Record<string, unknown>>;
21
+ readonly context?: unknown;
22
+ currentState(): string;
23
+ canHandle(input: string): boolean;
24
+ on(eventName: string, callback: (data: any) => void): {
25
+ 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
+ * Starts recording machine announcements. Call before the machines of interest are constructed —
43
+ * for the signal machine that means before the `Room` is created.
44
+ */
45
+ export declare function enableMachineInspector(): void;
46
+ export declare function isMachineInspectorEnabled(): boolean;
47
+ /** Announces a machine to whatever is watching. A no-op unless the inspector was enabled. */
48
+ export declare function announceMachine(label: string, machine: InspectableMachine): void;
49
+ /**
50
+ * Subscribes to machine announcements, replaying the ones already recorded so that a panel opened
51
+ * after connecting still sees the machine currently in charge. Returns an unsubscribe function.
52
+ */
53
+ export declare function onMachineAnnounced(callback: (announcement: MachineAnnouncement) => void): () => void;
54
+ //# sourceMappingURL=machineInspector.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livekit-client",
3
- "version": "2.22.0",
3
+ "version": "2.22.1",
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",
@@ -54,6 +54,7 @@
54
54
  "events": "^3.3.0",
55
55
  "jose": "^6.1.0",
56
56
  "loglevel": "^1.9.2",
57
+ "machina": "^7.0.0",
57
58
  "sdp-transform": "^2.15.0",
58
59
  "tslib": "2.8.1",
59
60
  "typed-emitter": "^2.1.0",
@@ -92,12 +93,16 @@
92
93
  "eslint-config-prettier": "10.1.8",
93
94
  "eslint-plugin-compat": "^6.0.2",
94
95
  "eslint-plugin-import-x": "^4.16.1",
96
+ "eslint-plugin-machina": "^0.1.4",
95
97
  "eslint-plugin-prettier": "^5.5.4",
96
98
  "gh-pages": "6.3.0",
97
99
  "glob": "^13.0.6",
98
100
  "happy-dom": "^20.0.0",
99
101
  "jsdom": "^26.1.0",
100
102
  "livekit-server-sdk": "^2.17.0",
103
+ "machina-inspect": "^4.0.0",
104
+ "machina-test": "^3.0.0",
105
+ "mermaid": "^11.16.1",
101
106
  "playwright": "^1.61.1",
102
107
  "prettier": "^3.4.2",
103
108
  "publint": "^0.3.21",
@@ -121,6 +126,7 @@
121
126
  "build-docs": "typedoc && mkdir -p docs/assets/github && cp .github/*.png docs/assets/github/ && find docs -name '*.html' -type f -exec sed -i.bak 's|=\"/.github/|=\"assets/github/|g' {} + && find docs -name '*.bak' -delete",
122
127
  "proto": "protoc --es_out src/proto --es_opt target=ts -I./protocol ./protocol/livekit_rtc.proto ./protocol/livekit_models.proto",
123
128
  "examples:demo": "vite examples/demo -c vite.config.mjs",
129
+ "examples:machine": "vite examples/state-machine -c vite.config.mjs",
124
130
  "dev": "pnpm examples:demo",
125
131
  "lint": "eslint src",
126
132
  "test": "vitest run src",