@livekit/rtc-node 0.13.31 → 0.13.33

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 (120) hide show
  1. package/dist/async_queue.d.ts +2 -3
  2. package/dist/audio_filter.d.ts +2 -3
  3. package/dist/audio_frame.d.ts +5 -6
  4. package/dist/audio_mixer.d.ts +3 -6
  5. package/dist/audio_resampler.d.ts +3 -6
  6. package/dist/audio_source.d.ts +5 -6
  7. package/dist/{audio_stream-B4hGVcKz.d.cts → audio_stream-CnODFAmo.d.cts} +26 -0
  8. package/dist/audio_stream.cjs +31 -18
  9. package/dist/audio_stream.cjs.map +1 -1
  10. package/dist/audio_stream.d.cts +1 -1
  11. package/dist/audio_stream.d.ts +61 -18
  12. package/dist/audio_stream.d.ts.map +1 -1
  13. package/dist/data_streams/index.d.ts +4 -3
  14. package/dist/data_streams/stream_reader.d.ts +48 -2
  15. package/dist/data_streams/stream_writer.d.ts +5 -7
  16. package/dist/data_streams/types.d.ts +41 -2
  17. package/dist/e2ee.d.ts +8 -10
  18. package/dist/ffi_client.d.ts +8 -10
  19. package/dist/frame_processor.d.ts +8 -11
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +2 -2
  22. package/dist/index.d.ts +22 -17
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1 -107
  25. package/dist/log.d.ts +5 -7
  26. package/dist/participant.cjs +7 -2
  27. package/dist/participant.cjs.map +1 -1
  28. package/dist/participant.d.cts +1 -1
  29. package/dist/participant.d.ts +152 -18
  30. package/dist/participant.d.ts.map +1 -1
  31. package/dist/room.cjs +7 -0
  32. package/dist/room.cjs.map +1 -1
  33. package/dist/room.d.cts +2 -2
  34. package/dist/room.d.ts +220 -18
  35. package/dist/room.d.ts.map +1 -1
  36. package/dist/rpc.d.ts +7 -9
  37. package/dist/track.cjs +16 -0
  38. package/dist/track.cjs.map +1 -1
  39. package/dist/track.d.cts +1 -1
  40. package/dist/track.d.ts +67 -18
  41. package/dist/track.d.ts.map +1 -1
  42. package/dist/track_publication.d.cts +1 -1
  43. package/dist/track_publication.d.ts +45 -18
  44. package/dist/transcription.d.ts +3 -4
  45. package/dist/types.d.ts +2 -3
  46. package/dist/utils.d.ts +4 -5
  47. package/dist/version.cjs +1 -1
  48. package/dist/version.cjs.map +1 -1
  49. package/dist/version.d.cts +1 -1
  50. package/dist/version.d.ts +2 -3
  51. package/dist/video_frame.d.ts +4 -5
  52. package/dist/video_source.d.ts +6 -8
  53. package/dist/video_stream.d.cts +1 -1
  54. package/dist/video_stream.d.ts +5 -22
  55. package/package.json +5 -6
  56. package/src/audio_stream.ts +37 -24
  57. package/src/audio_stream_room_lifecycle.test.ts +50 -3
  58. package/src/index.ts +8 -1
  59. package/src/participant.ts +7 -1
  60. package/src/room.ts +14 -0
  61. package/src/tests/e2e.test.ts +137 -16
  62. package/src/track.ts +17 -0
  63. package/src/version.ts +1 -1
  64. package/dist/async_queue.js +0 -56
  65. package/dist/async_queue.js.map +0 -1
  66. package/dist/audio_filter.js +0 -24
  67. package/dist/audio_filter.js.map +0 -1
  68. package/dist/audio_frame.js +0 -78
  69. package/dist/audio_frame.js.map +0 -1
  70. package/dist/audio_mixer.js +0 -269
  71. package/dist/audio_mixer.js.map +0 -1
  72. package/dist/audio_resampler.js +0 -154
  73. package/dist/audio_resampler.js.map +0 -1
  74. package/dist/audio_source.js +0 -134
  75. package/dist/audio_source.js.map +0 -1
  76. package/dist/audio_stream-DEG1JKge.d.ts +0 -498
  77. package/dist/audio_stream.js +0 -109
  78. package/dist/audio_stream.js.map +0 -1
  79. package/dist/data_streams/index.js +0 -3
  80. package/dist/data_streams/index.js.map +0 -1
  81. package/dist/data_streams/stream_reader.js +0 -124
  82. package/dist/data_streams/stream_reader.js.map +0 -1
  83. package/dist/data_streams/stream_writer.js +0 -26
  84. package/dist/data_streams/stream_writer.js.map +0 -1
  85. package/dist/data_streams/types.js +0 -1
  86. package/dist/data_streams/types.js.map +0 -1
  87. package/dist/e2ee.js +0 -251
  88. package/dist/e2ee.js.map +0 -1
  89. package/dist/ffi_client.js +0 -89
  90. package/dist/ffi_client.js.map +0 -1
  91. package/dist/frame_processor.js +0 -29
  92. package/dist/frame_processor.js.map +0 -1
  93. package/dist/index.js.map +0 -1
  94. package/dist/log.js +0 -9
  95. package/dist/log.js.map +0 -1
  96. package/dist/participant.js +0 -715
  97. package/dist/participant.js.map +0 -1
  98. package/dist/room.js +0 -899
  99. package/dist/room.js.map +0 -1
  100. package/dist/rpc.js +0 -65
  101. package/dist/rpc.js.map +0 -1
  102. package/dist/track.js +0 -218
  103. package/dist/track.js.map +0 -1
  104. package/dist/track_publication.js +0 -100
  105. package/dist/track_publication.js.map +0 -1
  106. package/dist/transcription.js +0 -1
  107. package/dist/transcription.js.map +0 -1
  108. package/dist/types-_PdPVish.d.ts +0 -87
  109. package/dist/types.js +0 -1
  110. package/dist/types.js.map +0 -1
  111. package/dist/utils.js +0 -35
  112. package/dist/utils.js.map +0 -1
  113. package/dist/version.js +0 -5
  114. package/dist/version.js.map +0 -1
  115. package/dist/video_frame.js +0 -207
  116. package/dist/video_frame.js.map +0 -1
  117. package/dist/video_source.js +0 -53
  118. package/dist/video_source.js.map +0 -1
  119. package/dist/video_stream.js +0 -70
  120. package/dist/video_stream.js.map +0 -1
package/dist/room.js DELETED
@@ -1,899 +0,0 @@
1
- import { Mutex } from "@livekit/mutex";
2
- import { EncryptionState } from "@livekit/rtc-ffi-bindings";
3
- import { DisconnectReason } from "@livekit/rtc-ffi-bindings";
4
- import {
5
- ConnectRequest,
6
- ConnectionState,
7
- ContinualGatheringPolicy,
8
- RoomOptions as FfiRoomOptions,
9
- IceTransportType
10
- } from "@livekit/rtc-ffi-bindings";
11
- import { TrackKind } from "@livekit/rtc-ffi-bindings";
12
- import EventEmitter from "events";
13
- import { ByteStreamReader, TextStreamReader } from "./data_streams/stream_reader.js";
14
- import { E2EEManager, defaultE2EEOptions } from "./e2ee.js";
15
- import { FfiClient, FfiClientEvent, FfiHandle } from "./ffi_client.js";
16
- import { log } from "./log.js";
17
- import { LocalParticipant, RemoteParticipant } from "./participant.js";
18
- import { RemoteAudioTrack, RemoteVideoTrack } from "./track.js";
19
- import { RemoteTrackPublication } from "./track_publication.js";
20
- import { bigIntToNumber } from "./utils.js";
21
- const defaultRtcConfiguration = {
22
- iceTransportType: IceTransportType.TRANSPORT_ALL,
23
- continualGatheringPolicy: ContinualGatheringPolicy.GATHER_CONTINUALLY,
24
- iceServers: []
25
- };
26
- const defaultRoomOptions = new FfiRoomOptions({
27
- autoSubscribe: true,
28
- dynacast: false,
29
- e2ee: void 0,
30
- encryption: void 0,
31
- rtcConfig: void 0,
32
- adaptiveStream: false,
33
- joinRetries: 1
34
- });
35
- class Room extends EventEmitter {
36
- constructor() {
37
- super();
38
- /**
39
- * used to ensure events are processed sequentially and allow for
40
- * the local participant to acquire the lock while doing state updates related to FFI events
41
- * before processing the next events
42
- */
43
- this.ffiEventLock = new Mutex();
44
- this.byteStreamControllers = /* @__PURE__ */ new Map();
45
- this.textStreamControllers = /* @__PURE__ */ new Map();
46
- this.byteStreamHandlers = /* @__PURE__ */ new Map();
47
- this.textStreamHandlers = /* @__PURE__ */ new Map();
48
- this.preConnectEvents = [];
49
- // Aborted on disconnect to cancel any pending FfiClient.waitFor() listeners,
50
- // preventing them from leaking when the room goes away.
51
- this.disconnectController = new AbortController();
52
- // Guards cleanupOnDisconnect so the ConnectionStateChanged/Disconnected
53
- // events fire exactly once, no matter which path (explicit disconnect()
54
- // vs. FFI 'disconnected' event) wins the race.
55
- this.hasCleanedUp = false;
56
- this._connectionState = ConnectionState.CONN_DISCONNECTED;
57
- this.remoteParticipants = /* @__PURE__ */ new Map();
58
- this.onFfiEvent = async (ffiEvent) => {
59
- const unlock = await this.ffiEventLock.lock();
60
- try {
61
- if (!this.localParticipant || !this.ffiHandle || !this.info) {
62
- this.preConnectEvents.push(ffiEvent);
63
- return;
64
- }
65
- for (const ev of this.preConnectEvents) {
66
- await this.processFfiEvent(ev);
67
- }
68
- this.preConnectEvents = [];
69
- await this.processFfiEvent(ffiEvent);
70
- } finally {
71
- unlock();
72
- }
73
- };
74
- this.processFfiEvent = async (ffiEvent) => {
75
- var _a, _b;
76
- if (!this.localParticipant || !this.ffiHandle || !this.info) {
77
- throw new Error("processFfiEvent called before connect");
78
- }
79
- if (ffiEvent.message.case == "rpcMethodInvocation") {
80
- if (ffiEvent.message.value.localParticipantHandle == this.localParticipant.ffi_handle.handle) {
81
- this.localParticipant.handleRpcMethodInvocation(
82
- ffiEvent.message.value.invocationId,
83
- ffiEvent.message.value.method,
84
- ffiEvent.message.value.requestId,
85
- ffiEvent.message.value.callerIdentity,
86
- ffiEvent.message.value.payload,
87
- ffiEvent.message.value.responseTimeoutMs
88
- );
89
- }
90
- return;
91
- } else if (ffiEvent.message.case != "roomEvent" || ffiEvent.message.value.roomHandle != this.ffiHandle.handle) {
92
- return;
93
- }
94
- const ev = ffiEvent.message.value.message;
95
- if (process.env.LIVEKIT_DEBUG_LOG_ROOM_EVENTS) {
96
- console.log("Room event:", ev);
97
- }
98
- if (ev.case == "participantConnected") {
99
- const participant = this.createRemoteParticipant(ev.value.info);
100
- this.remoteParticipants.set(participant.identity, participant);
101
- this.emit("participantConnected" /* ParticipantConnected */, participant);
102
- } else if (ev.case == "participantDisconnected") {
103
- const participant = this.remoteParticipants.get(ev.value.participantIdentity);
104
- if (participant) {
105
- this.remoteParticipants.delete(participant.identity);
106
- participant.info.disconnectReason = ev.value.disconnectReason;
107
- this.emit("participantDisconnected" /* ParticipantDisconnected */, participant);
108
- } else {
109
- log.warn(`RoomEvent.ParticipantDisconnected: Could not find participant`);
110
- }
111
- } else if (ev.case == "localTrackPublished") {
112
- const publication = this.localParticipant.trackPublications.get(ev.value.trackSid);
113
- if (publication == null ? void 0 : publication.track) {
114
- publication.track.setRoom(this);
115
- }
116
- this.emit("localTrackPublished" /* LocalTrackPublished */, publication, this.localParticipant);
117
- } else if (ev.case == "localTrackUnpublished") {
118
- const publication = this.localParticipant.trackPublications.get(ev.value.publicationSid);
119
- const track = publication == null ? void 0 : publication.track;
120
- if (track) {
121
- track.setRoom(null);
122
- }
123
- this.localParticipant.trackPublications.delete(ev.value.publicationSid);
124
- this.emit("localTrackUnpublished" /* LocalTrackUnpublished */, publication, this.localParticipant);
125
- if (track && publication) {
126
- publication.track = void 0;
127
- }
128
- } else if (ev.case == "localTrackRepublished") {
129
- const value = ev.value;
130
- const previousSid = value.previousSid;
131
- const newInfo = value.info;
132
- const publication = this.localParticipant.trackPublications.get(previousSid);
133
- if (publication) {
134
- publication.updateInfo(newInfo);
135
- this.localParticipant.trackPublications.delete(previousSid);
136
- this.localParticipant.trackPublications.set(publication.sid, publication);
137
- if ((_a = publication.track) == null ? void 0 : _a.info) {
138
- publication.track.info.sid = publication.sid;
139
- publication.track.setRoom(this);
140
- }
141
- this.emit("localTrackRepublished" /* LocalTrackRepublished */, publication, previousSid, this.localParticipant);
142
- } else {
143
- log.warn(`RoomEvent.LocalTrackRepublished: previous publication not found: ${previousSid}`);
144
- }
145
- } else if (ev.case == "localTrackSubscribed") {
146
- const publication = this.localParticipant.trackPublications.get(ev.value.trackSid);
147
- if (publication) {
148
- publication.resolveFirstSubscription();
149
- this.emit("localTrackSubscribed" /* LocalTrackSubscribed */, publication.track);
150
- } else {
151
- log.warn(`RoomEvent.LocalTrackSubscribed: Publication not found: ${ev.value.trackSid}`);
152
- }
153
- } else if (ev.case == "trackPublished") {
154
- const participant = this.remoteParticipants.get(ev.value.participantIdentity);
155
- const publication = new RemoteTrackPublication(ev.value.publication);
156
- if (participant) {
157
- participant.trackPublications.set(publication.sid, publication);
158
- } else {
159
- log.warn(
160
- `RoomEvent.TrackPublished: Could not find participant: ${ev.value.participantIdentity}`
161
- );
162
- }
163
- this.emit("trackPublished" /* TrackPublished */, publication, participant);
164
- } else if (ev.case == "trackUnpublished") {
165
- const participant = this.requireRemoteParticipant(ev.value.participantIdentity);
166
- const publication = participant.trackPublications.get(ev.value.publicationSid);
167
- participant.trackPublications.delete(ev.value.publicationSid);
168
- if (publication) {
169
- this.emit("trackUnpublished" /* TrackUnpublished */, publication, participant);
170
- publication.ffiHandle.dispose();
171
- } else {
172
- log.warn(`RoomEvent.TrackUnpublished: Could not find publication`);
173
- }
174
- } else if (ev.case == "trackSubscribed") {
175
- const ownedTrack = ev.value.track;
176
- const trackInfo = ownedTrack.info;
177
- try {
178
- const { participant, publication } = this.requirePublicationOfRemoteParticipant(
179
- ev.value.participantIdentity,
180
- trackInfo.sid
181
- );
182
- publication.subscribed = true;
183
- if (trackInfo.kind == TrackKind.KIND_VIDEO) {
184
- publication.track = new RemoteVideoTrack(ownedTrack);
185
- } else if (trackInfo.kind == TrackKind.KIND_AUDIO) {
186
- publication.track = new RemoteAudioTrack(ownedTrack);
187
- }
188
- (_b = publication.track) == null ? void 0 : _b.setRoom(this);
189
- this.emit("trackSubscribed" /* TrackSubscribed */, publication.track, publication, participant);
190
- } catch (e) {
191
- log.warn(`RoomEvent.TrackSubscribed: ${e.message}`);
192
- }
193
- } else if (ev.case == "trackUnsubscribed") {
194
- try {
195
- const { participant, publication } = this.requirePublicationOfRemoteParticipant(
196
- ev.value.participantIdentity,
197
- ev.value.trackSid
198
- );
199
- const track = publication.track;
200
- track.setRoom(null);
201
- publication.track = void 0;
202
- publication.subscribed = false;
203
- this.emit("trackUnsubscribed" /* TrackUnsubscribed */, track, publication, participant);
204
- } catch (e) {
205
- log.warn(`RoomEvent.TrackUnsubscribed: ${e.message}`);
206
- }
207
- } else if (ev.case == "trackSubscriptionFailed") {
208
- try {
209
- const participant = this.requireRemoteParticipant(ev.value.participantIdentity);
210
- this.emit(
211
- "trackSubscriptionFailed" /* TrackSubscriptionFailed */,
212
- ev.value.trackSid,
213
- participant,
214
- ev.value.error
215
- );
216
- } catch (e) {
217
- log.warn(`RoomEvent.TrackSubscriptionFailed: ${e.message}`);
218
- }
219
- } else if (ev.case == "trackMuted") {
220
- try {
221
- const { participant, publication } = this.requirePublicationOfParticipant(
222
- ev.value.participantIdentity,
223
- ev.value.trackSid
224
- );
225
- publication.info.muted = true;
226
- if (publication.track) {
227
- publication.track.info.muted = true;
228
- }
229
- this.emit("trackMuted" /* TrackMuted */, publication, participant);
230
- } catch (e) {
231
- log.warn(`RoomEvent.TrackMuted: ${e.message}`);
232
- }
233
- } else if (ev.case == "trackUnmuted") {
234
- try {
235
- const { participant, publication } = this.requirePublicationOfParticipant(
236
- ev.value.participantIdentity,
237
- ev.value.trackSid
238
- );
239
- publication.info.muted = false;
240
- if (publication.track) {
241
- publication.track.info.muted = false;
242
- }
243
- this.emit("trackUnmuted" /* TrackUnmuted */, publication, participant);
244
- } catch (e) {
245
- log.warn(`RoomEvent.TrackUnmuted: ${e.message}`);
246
- }
247
- } else if (ev.case == "activeSpeakersChanged") {
248
- try {
249
- const activeSpeakers = ev.value.participantIdentities.map(
250
- (identity) => this.requireParticipantByIdentity(identity)
251
- );
252
- this.emit("activeSpeakersChanged" /* ActiveSpeakersChanged */, activeSpeakers);
253
- } catch (e) {
254
- log.warn(`RoomEvent.ActiveSpeakersChanged: ${e.message}`);
255
- }
256
- } else if (ev.case == "roomMetadataChanged") {
257
- this.info.metadata = ev.value.metadata ?? "";
258
- this.emit("roomMetadataChanged" /* RoomMetadataChanged */, this.info.metadata);
259
- } else if (ev.case == "participantMetadataChanged") {
260
- try {
261
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
262
- participant.info.metadata = ev.value.metadata;
263
- this.emit("participantMetadataChanged" /* ParticipantMetadataChanged */, participant.metadata, participant);
264
- } catch (e) {
265
- log.warn(`RoomEvent.ParticipantMetadataChanged: ${e.message}`);
266
- }
267
- } else if (ev.case == "participantNameChanged") {
268
- try {
269
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
270
- participant.info.name = ev.value.name;
271
- this.emit("participantNameChanged" /* ParticipantNameChanged */, participant.name, participant);
272
- } catch (e) {
273
- log.warn(`RoomEvent.ParticipantNameChanged: ${e.message}`);
274
- }
275
- } else if (ev.case == "participantAttributesChanged") {
276
- try {
277
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
278
- participant.info.attributes = ev.value.attributes.reduce(
279
- (acc, value) => {
280
- acc[value.key] = value.value;
281
- return acc;
282
- },
283
- {}
284
- );
285
- if (Object.keys(ev.value.changedAttributes).length > 0) {
286
- const changedAttributes = ev.value.changedAttributes.reduce(
287
- (acc, value) => {
288
- acc[value.key] = value.value;
289
- return acc;
290
- },
291
- {}
292
- );
293
- this.emit("participantAttributesChanged" /* ParticipantAttributesChanged */, changedAttributes, participant);
294
- }
295
- } catch (e) {
296
- log.warn(`RoomEvent.ParticipantAttributesChanged: ${e.message}`);
297
- }
298
- } else if (ev.case == "connectionQualityChanged") {
299
- try {
300
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
301
- this.emit("connectionQualityChanged" /* ConnectionQualityChanged */, ev.value.quality, participant);
302
- } catch (e) {
303
- log.warn(`RoomEvent.ConnectionQualityChanged: ${e.message}`);
304
- }
305
- } else if (ev.case == "chatMessage") {
306
- const participant = this.retrieveParticipantByIdentity(ev.value.participantIdentity);
307
- const { id, message: messageText, timestamp, editTimestamp, generated } = ev.value.message;
308
- const message = {
309
- id,
310
- message: messageText,
311
- timestamp: Number(timestamp),
312
- editTimestamp: Number(editTimestamp),
313
- generated
314
- };
315
- this.emit("chatMessage" /* ChatMessage */, message, participant);
316
- } else if (ev.case == "dataPacketReceived") {
317
- const participant = this.remoteParticipants.get(ev.value.participantIdentity);
318
- const dataPacket = ev.value.value;
319
- switch (dataPacket.case) {
320
- case "user":
321
- const buffer = FfiClient.instance.copyBuffer(
322
- dataPacket.value.data.data.dataPtr,
323
- Number(dataPacket.value.data.data.dataLen)
324
- );
325
- new FfiHandle(dataPacket.value.data.handle.id).dispose();
326
- this.emit(
327
- "dataReceived" /* DataReceived */,
328
- buffer,
329
- participant,
330
- ev.value.kind,
331
- dataPacket.value.topic
332
- );
333
- break;
334
- case "sipDtmf":
335
- const { code, digit } = dataPacket.value;
336
- this.emit("dtmfReceived" /* DtmfReceived */, code, digit, participant);
337
- break;
338
- default:
339
- break;
340
- }
341
- } else if (ev.case == "e2eeStateChanged") {
342
- if (ev.value.state == EncryptionState.INTERNAL_ERROR) {
343
- this.emit("encryptionError" /* EncryptionError */, new Error("internal server error"));
344
- }
345
- } else if (ev.case == "connectionStateChanged") {
346
- this.updateConnectionState(ev.value.state);
347
- } else if (ev.case == "disconnected") {
348
- this.cleanupOnDisconnect(ev.value.reason);
349
- } else if (ev.case == "reconnecting") {
350
- this.emit("reconnecting" /* Reconnecting */);
351
- } else if (ev.case == "reconnected") {
352
- this.emit("reconnected" /* Reconnected */);
353
- } else if (ev.case == "roomSidChanged") {
354
- this.emit("roomSidChanged" /* RoomSidChanged */, ev.value.sid);
355
- } else if (ev.case === "streamHeaderReceived" && ev.value.header) {
356
- this.handleStreamHeader(ev.value.header, ev.value.participantIdentity);
357
- } else if (ev.case === "streamChunkReceived" && ev.value.chunk) {
358
- this.handleStreamChunk(ev.value.chunk);
359
- } else if (ev.case === "streamTrailerReceived" && ev.value.trailer) {
360
- this.handleStreamTrailer(ev.value.trailer);
361
- } else if (ev.case === "roomUpdated") {
362
- this.info = ev.value;
363
- this.emit("roomUpdated" /* RoomUpdated */);
364
- } else if (ev.case === "moved") {
365
- this.info = ev.value;
366
- this.emit("moved" /* Moved */);
367
- } else if (ev.case === "participantsUpdated") {
368
- for (const info of ev.value.participants) {
369
- const participant = this.retrieveParticipantByIdentity(info.identity);
370
- if (participant) {
371
- participant.info = info;
372
- }
373
- }
374
- } else if (ev.case === "participantEncryptionStatusChanged") {
375
- try {
376
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
377
- this.emit(
378
- "participantEncryptionStatusChanged" /* ParticipantEncryptionStatusChanged */,
379
- !!ev.value.isEncrypted,
380
- participant
381
- );
382
- } catch (e) {
383
- log.warn(`RoomEvent.ParticipantEncryptionStatusChanged: ${e.message}`);
384
- }
385
- } else if (ev.case === "tokenRefreshed") {
386
- this._token = ev.value.token;
387
- this.emit("tokenRefreshed");
388
- }
389
- };
390
- }
391
- get connectionState() {
392
- return this._connectionState;
393
- }
394
- get name() {
395
- var _a;
396
- return (_a = this.info) == null ? void 0 : _a.name;
397
- }
398
- get metadata() {
399
- var _a;
400
- return (_a = this.info) == null ? void 0 : _a.metadata;
401
- }
402
- get isConnected() {
403
- return this.ffiHandle != void 0 && this.connectionState != ConnectionState.CONN_DISCONNECTED;
404
- }
405
- /** @internal */
406
- get token() {
407
- return this._token;
408
- }
409
- /** @internal */
410
- get serverUrl() {
411
- return this._serverUrl;
412
- }
413
- /**
414
- * Gets the room's server ID. This ID is assigned by the LiveKit server
415
- * and is unique for each room session.
416
- * SID is assigned asynchronously after connection.
417
- * @returns Promise that resolves to the room's server ID, or empty string if not connected
418
- */
419
- async getSid() {
420
- var _a;
421
- if (!this.isConnected) {
422
- return "";
423
- }
424
- if (((_a = this.info) == null ? void 0 : _a.sid) && this.info.sid !== "") {
425
- return this.info.sid;
426
- }
427
- if (!this.sidPromise) {
428
- this.sidPromise = new Promise((resolve, reject) => {
429
- const handleDisconnect = () => {
430
- this.off("roomSidChanged" /* RoomSidChanged */, handleRoomUpdate);
431
- this.sidPromise = void 0;
432
- reject("Room disconnected before room server id was available");
433
- };
434
- const handleRoomUpdate = (sid) => {
435
- if (sid !== "") {
436
- this.off("roomSidChanged" /* RoomSidChanged */, handleRoomUpdate);
437
- this.off("disconnected" /* Disconnected */, handleDisconnect);
438
- this.sidPromise = void 0;
439
- resolve(sid);
440
- }
441
- };
442
- this.on("roomSidChanged" /* RoomSidChanged */, handleRoomUpdate);
443
- this.once("disconnected" /* Disconnected */, handleDisconnect);
444
- });
445
- }
446
- return this.sidPromise;
447
- }
448
- async getRtcStats() {
449
- if (!this.isConnected || !this.ffiHandle) {
450
- throw new Error("getRtcStats requires a connected room");
451
- }
452
- const requestId = FfiClient.instance.getNextRequestAsyncId();
453
- const cbPromise = FfiClient.instance.waitFor(
454
- (ev) => ev.message.case === "getSessionStats" && ev.message.value.asyncId === requestId,
455
- { signal: this.disconnectController.signal }
456
- );
457
- FfiClient.instance.request({
458
- message: {
459
- case: "getSessionStats",
460
- value: {
461
- roomHandle: this.ffiHandle.handle,
462
- requestAsyncId: requestId
463
- }
464
- }
465
- });
466
- const cb = await cbPromise;
467
- if (cb.message.case === "error") {
468
- throw new Error(cb.message.value);
469
- } else if (cb.message.case === "result") {
470
- return cb.message.value;
471
- } else {
472
- throw new Error("could not retrieve rtc stats");
473
- }
474
- }
475
- get numParticipants() {
476
- var _a;
477
- return ((_a = this.info) == null ? void 0 : _a.numParticipants) ?? 0;
478
- }
479
- get numPublishers() {
480
- var _a;
481
- return ((_a = this.info) == null ? void 0 : _a.numPublishers) ?? 0;
482
- }
483
- get creationTime() {
484
- var _a;
485
- let creationTimeMs = Number(((_a = this.info) == null ? void 0 : _a.creationTime) ?? 0);
486
- if (creationTimeMs > 0 && creationTimeMs < 1e12) {
487
- creationTimeMs *= 1e3;
488
- }
489
- return new Date(creationTimeMs);
490
- }
491
- get isRecording() {
492
- var _a;
493
- return ((_a = this.info) == null ? void 0 : _a.activeRecording) ?? false;
494
- }
495
- /**
496
- * The time in seconds after which a room will be closed after the last
497
- * participant has disconnected.
498
- */
499
- get departureTimeout() {
500
- var _a;
501
- return ((_a = this.info) == null ? void 0 : _a.departureTimeout) ?? 0;
502
- }
503
- /**
504
- * The time in seconds after which an empty room will be automatically closed.
505
- */
506
- get emptyTimeout() {
507
- var _a;
508
- return ((_a = this.info) == null ? void 0 : _a.emptyTimeout) ?? 0;
509
- }
510
- /**
511
- * Connects to a LiveKit room using the provided URL and access token.
512
- * @param url - The WebSocket URL of the LiveKit server
513
- * @param token - A valid LiveKit access token for authentication
514
- * @param opts - Optional room configuration options
515
- * @throws ConnectError - if connection fails
516
- */
517
- async connect(url, token, opts) {
518
- const options = { ...defaultRoomOptions, ...opts };
519
- const e2eeEnabled = options.encryption || options.e2ee;
520
- const e2eeOptions = options.encryption ? { ...defaultE2EEOptions, ...options.encryption } : { ...defaultE2EEOptions, ...options.e2ee };
521
- const req = new ConnectRequest({
522
- url,
523
- token,
524
- options
525
- });
526
- FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onFfiEvent);
527
- try {
528
- const res = FfiClient.instance.request({
529
- message: {
530
- case: "connect",
531
- value: req
532
- }
533
- });
534
- const cb = await FfiClient.instance.waitFor((ev) => {
535
- return ev.message.case == "connect" && ev.message.value.asyncId == res.asyncId;
536
- });
537
- log.debug("Connect callback received");
538
- switch (cb.message.case) {
539
- case "result":
540
- this.ffiHandle = new FfiHandle(cb.message.value.room.handle.id);
541
- this.e2eeManager = e2eeEnabled && new E2EEManager(this.ffiHandle.handle, e2eeOptions);
542
- this._token = token;
543
- this._serverUrl = url;
544
- this.info = cb.message.value.room.info;
545
- this.disconnectController = new AbortController();
546
- this.hasCleanedUp = false;
547
- this.localParticipant = new LocalParticipant(
548
- cb.message.value.localParticipant,
549
- this.ffiEventLock,
550
- this.disconnectController.signal
551
- );
552
- for (const pt of cb.message.value.participants) {
553
- const rp = this.createRemoteParticipant(pt.participant);
554
- for (const pub of pt.publications) {
555
- const publication = new RemoteTrackPublication(pub);
556
- rp.trackPublications.set(publication.sid, publication);
557
- }
558
- }
559
- this.updateConnectionState(ConnectionState.CONN_CONNECTED);
560
- FfiClient.instance.request({
561
- message: {
562
- case: "readyForRoomEvent",
563
- value: {
564
- roomHandle: this.ffiHandle.handle
565
- }
566
- }
567
- });
568
- break;
569
- case "error":
570
- default:
571
- throw new ConnectError(cb.message.value || "");
572
- }
573
- } catch (e) {
574
- FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onFfiEvent);
575
- this.preConnectEvents = [];
576
- throw e;
577
- }
578
- }
579
- /**
580
- * Disconnects from the room and cleans up all resources.
581
- * This will stop all tracks and close the connection.
582
- */
583
- async disconnect() {
584
- var _a;
585
- if (!this.isConnected) {
586
- return;
587
- }
588
- const res = FfiClient.instance.request({
589
- message: {
590
- case: "disconnect",
591
- value: {
592
- roomHandle: (_a = this.ffiHandle) == null ? void 0 : _a.handle
593
- }
594
- }
595
- });
596
- await FfiClient.instance.waitFor((ev) => {
597
- return ev.message.case == "disconnect" && ev.message.value.asyncId == res.asyncId;
598
- });
599
- this.cleanupOnDisconnect(DisconnectReason.CLIENT_INITIATED);
600
- FfiClient.instance.removeListener(FfiClientEvent.FfiEvent, this.onFfiEvent);
601
- this.removeAllListeners();
602
- }
603
- /**
604
- * Trigger a reconnection / chaos scenario for testing. Most useful in
605
- * tests to deterministically force a Resume (signal-only reconnect that
606
- * preserves the PeerConnection and existing publications) or a full
607
- * reconnect (the SDK rebuilds the RtcSession and re-publishes existing
608
- * local tracks; `RoomEvent.Reconnected` fires).
609
- */
610
- async simulateScenario(scenario) {
611
- if (!this.isConnected || !this.ffiHandle) {
612
- throw new Error("simulateScenario requires a connected room");
613
- }
614
- const res = FfiClient.instance.request({
615
- message: {
616
- case: "simulateScenario",
617
- value: {
618
- roomHandle: this.ffiHandle.handle,
619
- scenario
620
- }
621
- }
622
- });
623
- const cb = await FfiClient.instance.waitFor(
624
- (ev) => ev.message.case === "simulateScenario" && ev.message.value.asyncId === res.asyncId,
625
- { signal: this.disconnectController.signal }
626
- );
627
- if (cb.error) {
628
- throw new Error(`simulateScenario failed: ${cb.error}`);
629
- }
630
- }
631
- updateConnectionState(newState) {
632
- if (this._connectionState === newState) {
633
- return;
634
- }
635
- this._connectionState = newState;
636
- this.emit("connectionStateChanged" /* ConnectionStateChanged */, this._connectionState);
637
- }
638
- // Runs at most once per connection session. The FFI layer and explicit
639
- // disconnect() both race to get here — whichever wins emits the events,
640
- // the other is a no-op. A reconnect via connect() clears hasCleanedUp.
641
- cleanupOnDisconnect(reason = DisconnectReason.CLIENT_INITIATED) {
642
- var _a, _b;
643
- if (this.hasCleanedUp) return;
644
- this.hasCleanedUp = true;
645
- for (const [, streamController] of this.byteStreamControllers) {
646
- try {
647
- streamController.controller.error(new Error("Disconnected while receiving"));
648
- } catch {
649
- }
650
- }
651
- this.byteStreamControllers.clear();
652
- for (const [, streamController] of this.textStreamControllers) {
653
- try {
654
- streamController.controller.error(new Error("Disconnected while receiving"));
655
- } catch {
656
- }
657
- }
658
- this.textStreamControllers.clear();
659
- if (this.localParticipant) {
660
- for (const pub of this.localParticipant.trackPublications.values()) {
661
- (_a = pub.track) == null ? void 0 : _a.setRoom(null);
662
- }
663
- }
664
- for (const participant of this.remoteParticipants.values()) {
665
- for (const pub of participant.trackPublications.values()) {
666
- (_b = pub.track) == null ? void 0 : _b.setRoom(null);
667
- }
668
- }
669
- this.sidPromise = void 0;
670
- this.disconnectController.abort();
671
- this.updateConnectionState(ConnectionState.CONN_DISCONNECTED);
672
- this.emit("disconnected" /* Disconnected */, reason);
673
- }
674
- /**
675
- * Registers a handler for incoming text data streams on a specific topic.
676
- * Text streams are used for receiving structured text data from other participants.
677
- * @param topic - The topic to listen for text streams on
678
- * @param callback - Function to handle incoming text stream data
679
- * @throws Error - if a handler for this topic is already registered
680
- */
681
- registerTextStreamHandler(topic, callback) {
682
- if (this.textStreamHandlers.has(topic)) {
683
- throw new Error(`A text stream handler for topic "${topic}" has already been set.`);
684
- }
685
- this.textStreamHandlers.set(topic, callback);
686
- }
687
- unregisterTextStreamHandler(topic) {
688
- this.textStreamHandlers.delete(topic);
689
- }
690
- /**
691
- * Registers a handler for incoming byte data streams on a specific topic.
692
- * Byte streams are used for receiving binary data like files from other participants.
693
- * @param topic - The topic to listen for byte streams on
694
- * @param callback - Function to handle incoming byte stream data
695
- * @throws Error - if a handler for this topic is already registered
696
- */
697
- registerByteStreamHandler(topic, callback) {
698
- if (this.byteStreamHandlers.has(topic)) {
699
- throw new Error(`A byte stream handler for topic "${topic}" has already been set.`);
700
- }
701
- this.byteStreamHandlers.set(topic, callback);
702
- }
703
- unregisterByteStreamHandler(topic) {
704
- this.byteStreamHandlers.delete(topic);
705
- }
706
- retrieveParticipantByIdentity(identity) {
707
- var _a;
708
- if (((_a = this.localParticipant) == null ? void 0 : _a.identity) === identity) {
709
- return this.localParticipant;
710
- } else {
711
- return this.remoteParticipants.get(identity);
712
- }
713
- }
714
- requireParticipantByIdentity(identity) {
715
- var _a;
716
- if (((_a = this.localParticipant) == null ? void 0 : _a.identity) === identity) {
717
- return this.localParticipant;
718
- } else if (this.remoteParticipants.has(identity)) {
719
- return this.remoteParticipants.get(identity);
720
- } else {
721
- throw new TypeError(`participant ${identity} not found`);
722
- }
723
- }
724
- requireRemoteParticipant(identity) {
725
- const participant = this.remoteParticipants.get(identity);
726
- if (!participant) {
727
- throw new TypeError(`participant ${identity} not found`);
728
- }
729
- return participant;
730
- }
731
- requirePublicationOfParticipant(identity, trackSid) {
732
- const participant = this.requireParticipantByIdentity(identity);
733
- const publication = participant.trackPublications.get(trackSid);
734
- if (!publication) {
735
- throw new TypeError(`publication ${trackSid} not found`);
736
- }
737
- return { participant, publication };
738
- }
739
- requirePublicationOfRemoteParticipant(identity, trackSid) {
740
- const participant = this.requireRemoteParticipant(identity);
741
- const publication = participant.trackPublications.get(trackSid);
742
- if (!publication) {
743
- throw new TypeError(`publication ${trackSid} not found`);
744
- }
745
- return { participant, publication };
746
- }
747
- createRemoteParticipant(ownedInfo) {
748
- if (this.remoteParticipants.has(ownedInfo.info.identity)) {
749
- throw new Error("Participant already exists");
750
- }
751
- const participant = new RemoteParticipant(ownedInfo);
752
- this.remoteParticipants.set(ownedInfo.info.identity, participant);
753
- return participant;
754
- }
755
- handleStreamHeader(streamHeader, participantIdentity) {
756
- if (streamHeader.contentHeader.case === "byteHeader") {
757
- const streamHandlerCallback = this.byteStreamHandlers.get(streamHeader.topic ?? "");
758
- if (!streamHandlerCallback) {
759
- log.debug(
760
- "ignoring incoming byte stream due to no handler for topic: %s",
761
- streamHeader.topic ?? "undefined"
762
- );
763
- return;
764
- }
765
- let streamController;
766
- const stream = new ReadableStream({
767
- start: (controller) => {
768
- streamController = controller;
769
- this.byteStreamControllers.set(streamHeader.streamId, {
770
- header: streamHeader,
771
- controller: streamController,
772
- startTime: Date.now()
773
- });
774
- }
775
- });
776
- const info = {
777
- streamId: streamHeader.streamId,
778
- name: streamHeader.contentHeader.value.name ?? "unknown",
779
- mimeType: streamHeader.mimeType,
780
- totalSize: streamHeader.totalLength ? Number(streamHeader.totalLength) : void 0,
781
- topic: streamHeader.topic,
782
- timestamp: bigIntToNumber(streamHeader.timestamp),
783
- attributes: streamHeader.attributes
784
- };
785
- streamHandlerCallback(
786
- new ByteStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength)),
787
- { identity: participantIdentity }
788
- );
789
- } else if (streamHeader.contentHeader.case === "textHeader") {
790
- const streamHandlerCallback = this.textStreamHandlers.get(streamHeader.topic ?? "");
791
- if (!streamHandlerCallback) {
792
- log.debug(
793
- "ignoring incoming text stream due to no handler for topic: %s",
794
- streamHeader.topic ?? "undefined"
795
- );
796
- return;
797
- }
798
- let streamController;
799
- const stream = new ReadableStream({
800
- start: (controller) => {
801
- streamController = controller;
802
- this.textStreamControllers.set(streamHeader.streamId, {
803
- header: streamHeader,
804
- controller: streamController,
805
- startTime: Date.now()
806
- });
807
- }
808
- });
809
- const info = {
810
- streamId: streamHeader.streamId,
811
- mimeType: streamHeader.mimeType,
812
- totalSize: streamHeader.totalLength ? Number(streamHeader.totalLength) : void 0,
813
- topic: streamHeader.topic,
814
- timestamp: Number(streamHeader.timestamp),
815
- attributes: streamHeader.attributes
816
- };
817
- streamHandlerCallback(
818
- new TextStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength)),
819
- { identity: participantIdentity }
820
- );
821
- }
822
- }
823
- handleStreamChunk(chunk) {
824
- const fileBuffer = this.byteStreamControllers.get(chunk.streamId);
825
- if (fileBuffer) {
826
- if (chunk.content.length > 0) {
827
- fileBuffer.controller.enqueue(chunk);
828
- }
829
- }
830
- const textBuffer = this.textStreamControllers.get(chunk.streamId);
831
- if (textBuffer) {
832
- if (chunk.content.length > 0) {
833
- textBuffer.controller.enqueue(chunk);
834
- }
835
- }
836
- }
837
- handleStreamTrailer(trailer) {
838
- const streamId = trailer.streamId;
839
- const fileBuffer = this.byteStreamControllers.get(streamId);
840
- if (fileBuffer) {
841
- fileBuffer.controller.close();
842
- this.byteStreamControllers.delete(streamId);
843
- }
844
- const textBuffer = this.textStreamControllers.get(streamId);
845
- if (textBuffer) {
846
- textBuffer.controller.close();
847
- this.textStreamControllers.delete(streamId);
848
- }
849
- }
850
- }
851
- class ConnectError extends Error {
852
- constructor(message) {
853
- super(message);
854
- }
855
- }
856
- var RoomEvent = /* @__PURE__ */ ((RoomEvent2) => {
857
- RoomEvent2["ParticipantConnected"] = "participantConnected";
858
- RoomEvent2["ParticipantDisconnected"] = "participantDisconnected";
859
- RoomEvent2["LocalTrackPublished"] = "localTrackPublished";
860
- RoomEvent2["LocalTrackUnpublished"] = "localTrackUnpublished";
861
- RoomEvent2["LocalTrackRepublished"] = "localTrackRepublished";
862
- RoomEvent2["LocalTrackSubscribed"] = "localTrackSubscribed";
863
- RoomEvent2["TrackPublished"] = "trackPublished";
864
- RoomEvent2["TrackUnpublished"] = "trackUnpublished";
865
- RoomEvent2["TrackSubscribed"] = "trackSubscribed";
866
- RoomEvent2["TrackUnsubscribed"] = "trackUnsubscribed";
867
- RoomEvent2["TrackSubscriptionFailed"] = "trackSubscriptionFailed";
868
- RoomEvent2["TrackMuted"] = "trackMuted";
869
- RoomEvent2["TrackUnmuted"] = "trackUnmuted";
870
- RoomEvent2["ActiveSpeakersChanged"] = "activeSpeakersChanged";
871
- RoomEvent2["RoomMetadataChanged"] = "roomMetadataChanged";
872
- RoomEvent2["RoomSidChanged"] = "roomSidChanged";
873
- RoomEvent2["ParticipantMetadataChanged"] = "participantMetadataChanged";
874
- RoomEvent2["ParticipantNameChanged"] = "participantNameChanged";
875
- RoomEvent2["ParticipantAttributesChanged"] = "participantAttributesChanged";
876
- RoomEvent2["ParticipantEncryptionStatusChanged"] = "participantEncryptionStatusChanged";
877
- RoomEvent2["ConnectionQualityChanged"] = "connectionQualityChanged";
878
- RoomEvent2["DataReceived"] = "dataReceived";
879
- RoomEvent2["ChatMessage"] = "chatMessage";
880
- RoomEvent2["DtmfReceived"] = "dtmfReceived";
881
- RoomEvent2["EncryptionError"] = "encryptionError";
882
- RoomEvent2["ConnectionStateChanged"] = "connectionStateChanged";
883
- RoomEvent2["Connected"] = "connected";
884
- RoomEvent2["Disconnected"] = "disconnected";
885
- RoomEvent2["Reconnecting"] = "reconnecting";
886
- RoomEvent2["Reconnected"] = "reconnected";
887
- RoomEvent2["RoomUpdated"] = "roomUpdated";
888
- RoomEvent2["Moved"] = "moved";
889
- RoomEvent2["TokenRefreshed"] = "tokenRefreshed";
890
- return RoomEvent2;
891
- })(RoomEvent || {});
892
- export {
893
- ConnectError,
894
- Room,
895
- RoomEvent,
896
- defaultRoomOptions,
897
- defaultRtcConfiguration
898
- };
899
- //# sourceMappingURL=room.js.map