@livekit/rtc-node 0.13.20 → 0.13.21

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 (51) hide show
  1. package/Cargo.toml +6 -3
  2. package/dist/index.d.cts +1 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/participant.cjs +43 -32
  5. package/dist/participant.cjs.map +1 -1
  6. package/dist/participant.d.cts +3 -0
  7. package/dist/participant.d.ts +3 -0
  8. package/dist/participant.d.ts.map +1 -1
  9. package/dist/participant.js +43 -32
  10. package/dist/participant.js.map +1 -1
  11. package/dist/proto/data_stream_pb.cjs +91 -2
  12. package/dist/proto/data_stream_pb.cjs.map +1 -1
  13. package/dist/proto/data_stream_pb.d.cts +92 -1
  14. package/dist/proto/data_stream_pb.d.ts +92 -1
  15. package/dist/proto/data_stream_pb.d.ts.map +1 -1
  16. package/dist/proto/data_stream_pb.js +88 -2
  17. package/dist/proto/data_stream_pb.js.map +1 -1
  18. package/dist/proto/ffi_pb.cjs +6 -3
  19. package/dist/proto/ffi_pb.cjs.map +1 -1
  20. package/dist/proto/ffi_pb.d.cts +19 -1
  21. package/dist/proto/ffi_pb.d.ts +19 -1
  22. package/dist/proto/ffi_pb.d.ts.map +1 -1
  23. package/dist/proto/ffi_pb.js +7 -4
  24. package/dist/proto/ffi_pb.js.map +1 -1
  25. package/dist/proto/room_pb.cjs +32 -2
  26. package/dist/proto/room_pb.cjs.map +1 -1
  27. package/dist/proto/room_pb.d.cts +38 -2
  28. package/dist/proto/room_pb.d.ts +38 -2
  29. package/dist/proto/room_pb.d.ts.map +1 -1
  30. package/dist/proto/room_pb.js +31 -2
  31. package/dist/proto/room_pb.js.map +1 -1
  32. package/dist/room.cjs +300 -205
  33. package/dist/room.cjs.map +1 -1
  34. package/dist/room.d.cts +16 -3
  35. package/dist/room.d.ts +16 -3
  36. package/dist/room.d.ts.map +1 -1
  37. package/dist/room.js +301 -206
  38. package/dist/room.js.map +1 -1
  39. package/dist/version.cjs +1 -1
  40. package/dist/version.cjs.map +1 -1
  41. package/dist/version.d.cts +1 -1
  42. package/dist/version.d.ts +1 -1
  43. package/dist/version.js +1 -1
  44. package/dist/version.js.map +1 -1
  45. package/package.json +6 -6
  46. package/src/participant.ts +48 -30
  47. package/src/proto/data_stream_pb.ts +159 -0
  48. package/src/proto/ffi_pb.ts +22 -1
  49. package/src/proto/room_pb.ts +64 -1
  50. package/src/room.ts +319 -214
  51. package/src/version.ts +1 -1
package/dist/room.cjs CHANGED
@@ -35,6 +35,7 @@ __export(room_exports, {
35
35
  defaultRtcConfiguration: () => defaultRtcConfiguration
36
36
  });
37
37
  module.exports = __toCommonJS(room_exports);
38
+ var import_mutex = require("@livekit/mutex");
38
39
  var import_events = __toESM(require("events"), 1);
39
40
  var import_stream_reader = require("./data_streams/stream_reader.cjs");
40
41
  var import_e2ee = require("./e2ee.cjs");
@@ -56,6 +57,7 @@ const defaultRoomOptions = new import_room_pb.RoomOptions({
56
57
  autoSubscribe: true,
57
58
  dynacast: false,
58
59
  e2ee: void 0,
60
+ encryption: void 0,
59
61
  rtcConfig: void 0,
60
62
  adaptiveStream: false,
61
63
  joinRetries: 1
@@ -63,6 +65,12 @@ const defaultRoomOptions = new import_room_pb.RoomOptions({
63
65
  class Room extends import_events.default {
64
66
  constructor() {
65
67
  super();
68
+ /**
69
+ * used to ensure events are processed sequentially and allow for
70
+ * the local participant to acquire the lock while doing state updates related to FFI events
71
+ * before processing the next events
72
+ */
73
+ this.roomEventLock = new import_mutex.Mutex();
66
74
  this.byteStreamControllers = /* @__PURE__ */ new Map();
67
75
  this.textStreamControllers = /* @__PURE__ */ new Map();
68
76
  this.byteStreamHandlers = /* @__PURE__ */ new Map();
@@ -70,226 +78,305 @@ class Room extends import_events.default {
70
78
  this.preConnectEvents = [];
71
79
  this.connectionState = import_room_pb.ConnectionState.CONN_DISCONNECTED;
72
80
  this.remoteParticipants = /* @__PURE__ */ new Map();
73
- this.onFfiEvent = (ffiEvent) => {
81
+ this.onFfiEvent = async (ffiEvent) => {
74
82
  if (!this.localParticipant || !this.ffiHandle || !this.info) {
75
83
  this.preConnectEvents.push(ffiEvent);
76
84
  return;
77
85
  }
78
86
  for (const ev of this.preConnectEvents) {
79
- this.processFfiEvent(ev);
87
+ await this.processFfiEvent(ev);
80
88
  }
81
89
  this.preConnectEvents = [];
82
- this.processFfiEvent(ffiEvent);
90
+ await this.processFfiEvent(ffiEvent);
83
91
  };
84
- this.processFfiEvent = (ffiEvent) => {
92
+ this.processFfiEvent = async (ffiEvent) => {
85
93
  if (!this.localParticipant || !this.ffiHandle || !this.info) {
86
94
  throw new Error("processFfiEvent called before connect");
87
95
  }
88
- if (ffiEvent.message.case == "rpcMethodInvocation") {
89
- if (ffiEvent.message.value.localParticipantHandle == this.localParticipant.ffi_handle.handle) {
90
- this.localParticipant.handleRpcMethodInvocation(
91
- ffiEvent.message.value.invocationId,
92
- ffiEvent.message.value.method,
93
- ffiEvent.message.value.requestId,
94
- ffiEvent.message.value.callerIdentity,
95
- ffiEvent.message.value.payload,
96
- ffiEvent.message.value.responseTimeoutMs
97
- );
98
- }
99
- return;
100
- } else if (ffiEvent.message.case != "roomEvent" || ffiEvent.message.value.roomHandle != this.ffiHandle.handle) {
101
- return;
102
- }
103
- const ev = ffiEvent.message.value.message;
104
- if (process.env.LIVEKIT_DEBUG_LOG_ROOM_EVENTS) {
105
- console.log("Room event:", ev);
106
- }
107
- if (ev.case == "participantConnected") {
108
- const participant = this.createRemoteParticipant(ev.value.info);
109
- this.remoteParticipants.set(participant.identity, participant);
110
- this.emit("participantConnected" /* ParticipantConnected */, participant);
111
- } else if (ev.case == "participantDisconnected") {
112
- const participant = this.remoteParticipants.get(ev.value.participantIdentity);
113
- this.remoteParticipants.delete(participant.identity);
114
- participant.info.disconnectReason = ev.value.disconnectReason;
115
- this.emit("participantDisconnected" /* ParticipantDisconnected */, participant);
116
- } else if (ev.case == "localTrackPublished") {
117
- const publication = this.localParticipant.trackPublications.get(ev.value.trackSid);
118
- this.emit("localTrackPublished" /* LocalTrackPublished */, publication, this.localParticipant);
119
- } else if (ev.case == "localTrackUnpublished") {
120
- const publication = this.localParticipant.trackPublications.get(ev.value.publicationSid);
121
- this.localParticipant.trackPublications.delete(ev.value.publicationSid);
122
- this.emit("localTrackUnpublished" /* LocalTrackUnpublished */, publication, this.localParticipant);
123
- } else if (ev.case == "localTrackSubscribed") {
124
- const publication = this.localParticipant.trackPublications.get(ev.value.trackSid);
125
- publication.resolveFirstSubscription();
126
- this.emit("localTrackSubscribed" /* LocalTrackSubscribed */, publication.track);
127
- } else if (ev.case == "trackPublished") {
128
- const participant = this.remoteParticipants.get(ev.value.participantIdentity);
129
- const publication = new import_track_publication.RemoteTrackPublication(ev.value.publication);
130
- participant.trackPublications.set(publication.sid, publication);
131
- this.emit("trackPublished" /* TrackPublished */, publication, participant);
132
- } else if (ev.case == "trackUnpublished") {
133
- const participant = this.requireRemoteParticipant(ev.value.participantIdentity);
134
- const publication = participant.trackPublications.get(ev.value.publicationSid);
135
- participant.trackPublications.delete(ev.value.publicationSid);
136
- if (publication) {
137
- this.emit("trackUnpublished" /* TrackUnpublished */, publication, participant);
138
- }
139
- } else if (ev.case == "trackSubscribed") {
140
- const ownedTrack = ev.value.track;
141
- const trackInfo = ownedTrack.info;
142
- const { participant, publication } = this.requirePublicationOfRemoteParticipant(
143
- ev.value.participantIdentity,
144
- trackInfo.sid
145
- );
146
- publication.subscribed = true;
147
- if (trackInfo.kind == import_track_pb.TrackKind.KIND_VIDEO) {
148
- publication.track = new import_track.RemoteVideoTrack(ownedTrack);
149
- } else if (trackInfo.kind == import_track_pb.TrackKind.KIND_AUDIO) {
150
- publication.track = new import_track.RemoteAudioTrack(ownedTrack);
151
- }
152
- this.emit("trackSubscribed" /* TrackSubscribed */, publication.track, publication, participant);
153
- } else if (ev.case == "trackUnsubscribed") {
154
- const { participant, publication } = this.requirePublicationOfRemoteParticipant(
155
- ev.value.participantIdentity,
156
- ev.value.trackSid
157
- );
158
- const track = publication.track;
159
- publication.track = void 0;
160
- publication.subscribed = false;
161
- this.emit("trackUnsubscribed" /* TrackUnsubscribed */, track, publication, participant);
162
- } else if (ev.case == "trackSubscriptionFailed") {
163
- const participant = this.requireRemoteParticipant(ev.value.participantIdentity);
164
- this.emit("trackSubscriptionFailed" /* TrackSubscriptionFailed */, ev.value.trackSid, participant, ev.value.error);
165
- } else if (ev.case == "trackMuted") {
166
- const { participant, publication } = this.requirePublicationOfParticipant(
167
- ev.value.participantIdentity,
168
- ev.value.trackSid
169
- );
170
- publication.info.muted = true;
171
- if (publication.track) {
172
- publication.track.info.muted = true;
173
- }
174
- this.emit("trackMuted" /* TrackMuted */, publication, participant);
175
- } else if (ev.case == "trackUnmuted") {
176
- const { participant, publication } = this.requirePublicationOfParticipant(
177
- ev.value.participantIdentity,
178
- ev.value.trackSid
179
- );
180
- publication.info.muted = false;
181
- if (publication.track) {
182
- publication.track.info.muted = false;
96
+ const unlock = await this.roomEventLock.lock();
97
+ try {
98
+ if (ffiEvent.message.case == "rpcMethodInvocation") {
99
+ if (ffiEvent.message.value.localParticipantHandle == this.localParticipant.ffi_handle.handle) {
100
+ this.localParticipant.handleRpcMethodInvocation(
101
+ ffiEvent.message.value.invocationId,
102
+ ffiEvent.message.value.method,
103
+ ffiEvent.message.value.requestId,
104
+ ffiEvent.message.value.callerIdentity,
105
+ ffiEvent.message.value.payload,
106
+ ffiEvent.message.value.responseTimeoutMs
107
+ );
108
+ }
109
+ return;
110
+ } else if (ffiEvent.message.case != "roomEvent" || ffiEvent.message.value.roomHandle != this.ffiHandle.handle) {
111
+ return;
183
112
  }
184
- this.emit("trackUnmuted" /* TrackUnmuted */, publication, participant);
185
- } else if (ev.case == "activeSpeakersChanged") {
186
- const activeSpeakers = ev.value.participantIdentities.map(
187
- (identity) => this.requireParticipantByIdentity(identity)
188
- );
189
- this.emit("activeSpeakersChanged" /* ActiveSpeakersChanged */, activeSpeakers);
190
- } else if (ev.case == "roomMetadataChanged") {
191
- this.info.metadata = ev.value.metadata;
192
- this.emit("roomMetadataChanged" /* RoomMetadataChanged */, this.info.metadata);
193
- } else if (ev.case == "participantMetadataChanged") {
194
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
195
- participant.info.metadata = ev.value.metadata;
196
- this.emit("participantMetadataChanged" /* ParticipantMetadataChanged */, participant.metadata, participant);
197
- } else if (ev.case == "participantNameChanged") {
198
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
199
- participant.info.name = ev.value.name;
200
- this.emit("participantNameChanged" /* ParticipantNameChanged */, participant.name, participant);
201
- } else if (ev.case == "participantAttributesChanged") {
202
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
203
- participant.info.attributes = ev.value.attributes.reduce(
204
- (acc, value) => {
205
- acc[value.key] = value.value;
206
- return acc;
207
- },
208
- {}
209
- );
210
- if (Object.keys(ev.value.changedAttributes).length > 0) {
211
- const changedAttributes = ev.value.changedAttributes.reduce(
212
- (acc, value) => {
213
- acc[value.key] = value.value;
214
- return acc;
215
- },
216
- {}
217
- );
218
- this.emit("participantAttributesChanged" /* ParticipantAttributesChanged */, changedAttributes, participant);
113
+ const ev = ffiEvent.message.value.message;
114
+ if (process.env.LIVEKIT_DEBUG_LOG_ROOM_EVENTS) {
115
+ console.log("Room event:", ev);
219
116
  }
220
- } else if (ev.case == "connectionQualityChanged") {
221
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
222
- this.emit("connectionQualityChanged" /* ConnectionQualityChanged */, ev.value.quality, participant);
223
- } else if (ev.case == "chatMessage") {
224
- const participant = this.retrieveParticipantByIdentity(ev.value.participantIdentity);
225
- const { id, message: messageText, timestamp, editTimestamp, generated } = ev.value.message;
226
- const message = {
227
- id,
228
- message: messageText,
229
- timestamp: Number(timestamp),
230
- editTimestamp: Number(editTimestamp),
231
- generated
232
- };
233
- this.emit("chatMessage" /* ChatMessage */, message, participant);
234
- } else if (ev.case == "dataPacketReceived") {
235
- const participant = this.remoteParticipants.get(ev.value.participantIdentity);
236
- const dataPacket = ev.value.value;
237
- switch (dataPacket.case) {
238
- case "user":
239
- const buffer = import_ffi_client.FfiClient.instance.copyBuffer(
240
- dataPacket.value.data.data.dataPtr,
241
- Number(dataPacket.value.data.data.dataLen)
117
+ if (ev.case == "participantConnected") {
118
+ const participant = this.createRemoteParticipant(ev.value.info);
119
+ this.remoteParticipants.set(participant.identity, participant);
120
+ this.emit("participantConnected" /* ParticipantConnected */, participant);
121
+ } else if (ev.case == "participantDisconnected") {
122
+ const participant = this.remoteParticipants.get(ev.value.participantIdentity);
123
+ if (participant) {
124
+ this.remoteParticipants.delete(participant.identity);
125
+ participant.info.disconnectReason = ev.value.disconnectReason;
126
+ this.emit("participantDisconnected" /* ParticipantDisconnected */, participant);
127
+ } else {
128
+ console.log(`RoomEvent.ParticipantDisconnected: Could not find participant`);
129
+ }
130
+ } else if (ev.case == "localTrackPublished") {
131
+ const publication = this.localParticipant.trackPublications.get(ev.value.trackSid);
132
+ this.emit("localTrackPublished" /* LocalTrackPublished */, publication, this.localParticipant);
133
+ } else if (ev.case == "localTrackUnpublished") {
134
+ const publication = this.localParticipant.trackPublications.get(ev.value.publicationSid);
135
+ this.localParticipant.trackPublications.delete(ev.value.publicationSid);
136
+ this.emit("localTrackUnpublished" /* LocalTrackUnpublished */, publication, this.localParticipant);
137
+ } else if (ev.case == "localTrackSubscribed") {
138
+ const publication = this.localParticipant.trackPublications.get(ev.value.trackSid);
139
+ if (publication) {
140
+ publication.resolveFirstSubscription();
141
+ this.emit("localTrackSubscribed" /* LocalTrackSubscribed */, publication.track);
142
+ } else {
143
+ console.warn(
144
+ `RoomEvent.LocalTrackSubscribed: Publication not found: ${ev.value.trackSid}`
145
+ );
146
+ }
147
+ } else if (ev.case == "trackPublished") {
148
+ const participant = this.remoteParticipants.get(ev.value.participantIdentity);
149
+ const publication = new import_track_publication.RemoteTrackPublication(ev.value.publication);
150
+ if (participant) {
151
+ participant.trackPublications.set(publication.sid, publication);
152
+ } else {
153
+ console.warn(
154
+ `RoomEvent.TrackPublished: Could not find participant: ${ev.value.participantIdentity}`
155
+ );
156
+ }
157
+ this.emit("trackPublished" /* TrackPublished */, publication, participant);
158
+ } else if (ev.case == "trackUnpublished") {
159
+ const participant = this.requireRemoteParticipant(ev.value.participantIdentity);
160
+ const publication = participant.trackPublications.get(ev.value.publicationSid);
161
+ participant.trackPublications.delete(ev.value.publicationSid);
162
+ if (publication) {
163
+ this.emit("trackUnpublished" /* TrackUnpublished */, publication, participant);
164
+ } else {
165
+ console.warn(`RoomEvent.TrackUnpublished: Could not find publication`);
166
+ }
167
+ } else if (ev.case == "trackSubscribed") {
168
+ const ownedTrack = ev.value.track;
169
+ const trackInfo = ownedTrack.info;
170
+ try {
171
+ const { participant, publication } = this.requirePublicationOfRemoteParticipant(
172
+ ev.value.participantIdentity,
173
+ trackInfo.sid
174
+ );
175
+ publication.subscribed = true;
176
+ if (trackInfo.kind == import_track_pb.TrackKind.KIND_VIDEO) {
177
+ publication.track = new import_track.RemoteVideoTrack(ownedTrack);
178
+ } else if (trackInfo.kind == import_track_pb.TrackKind.KIND_AUDIO) {
179
+ publication.track = new import_track.RemoteAudioTrack(ownedTrack);
180
+ }
181
+ this.emit("trackSubscribed" /* TrackSubscribed */, publication.track, publication, participant);
182
+ } catch (e) {
183
+ console.warn(`RoomEvent.TrackSubscribed: ${e.message}`);
184
+ }
185
+ } else if (ev.case == "trackUnsubscribed") {
186
+ try {
187
+ const { participant, publication } = this.requirePublicationOfRemoteParticipant(
188
+ ev.value.participantIdentity,
189
+ ev.value.trackSid
242
190
  );
243
- new import_ffi_client.FfiHandle(dataPacket.value.data.handle.id).dispose();
191
+ const track = publication.track;
192
+ publication.track = void 0;
193
+ publication.subscribed = false;
194
+ this.emit("trackUnsubscribed" /* TrackUnsubscribed */, track, publication, participant);
195
+ } catch (e) {
196
+ console.warn(`RoomEvent.TrackUnsubscribed: ${e.message}`);
197
+ }
198
+ } else if (ev.case == "trackSubscriptionFailed") {
199
+ try {
200
+ const participant = this.requireRemoteParticipant(ev.value.participantIdentity);
244
201
  this.emit(
245
- "dataReceived" /* DataReceived */,
246
- buffer,
202
+ "trackSubscriptionFailed" /* TrackSubscriptionFailed */,
203
+ ev.value.trackSid,
247
204
  participant,
248
- ev.value.kind,
249
- dataPacket.value.topic
205
+ ev.value.error
250
206
  );
251
- break;
252
- case "sipDtmf":
253
- const { code, digit } = dataPacket.value;
254
- this.emit("dtmfReceived" /* DtmfReceived */, code, digit, participant);
255
- break;
256
- default:
257
- break;
258
- }
259
- } else if (ev.case == "e2eeStateChanged") {
260
- if (ev.value.state == import_e2ee_pb.EncryptionState.INTERNAL_ERROR) {
261
- this.emit("encryptionError" /* EncryptionError */, new Error("internal server error"));
262
- }
263
- } else if (ev.case == "connectionStateChanged") {
264
- this.connectionState = ev.value.state;
265
- this.emit("connectionStateChanged" /* ConnectionStateChanged */, this.connectionState);
266
- } else if (ev.case == "disconnected") {
267
- this.emit("disconnected" /* Disconnected */, ev.value.reason);
268
- } else if (ev.case == "reconnecting") {
269
- this.emit("reconnecting" /* Reconnecting */);
270
- } else if (ev.case == "reconnected") {
271
- this.emit("reconnected" /* Reconnected */);
272
- } else if (ev.case == "roomSidChanged") {
273
- this.emit("roomSidChanged" /* RoomSidChanged */, ev.value.sid);
274
- } else if (ev.case === "streamHeaderReceived" && ev.value.header) {
275
- this.handleStreamHeader(ev.value.header, ev.value.participantIdentity);
276
- } else if (ev.case === "streamChunkReceived" && ev.value.chunk) {
277
- this.handleStreamChunk(ev.value.chunk);
278
- } else if (ev.case === "streamTrailerReceived" && ev.value.trailer) {
279
- this.handleStreamTrailer(ev.value.trailer);
280
- } else if (ev.case === "roomUpdated") {
281
- this.info = ev.value;
282
- this.emit("roomUpdated" /* RoomUpdated */);
283
- } else if (ev.case === "moved") {
284
- this.info = ev.value;
285
- this.emit("moved" /* Moved */);
286
- } else if (ev.case === "participantsUpdated") {
287
- for (const info of ev.value.participants) {
288
- const participant = this.retrieveParticipantByIdentity(info.identity);
289
- if (participant) {
290
- participant.info = info;
207
+ } catch (e) {
208
+ console.warn(`RoomEvent.TrackSubscriptionFailed: ${e.message}`);
209
+ }
210
+ } else if (ev.case == "trackMuted") {
211
+ try {
212
+ const { participant, publication } = this.requirePublicationOfParticipant(
213
+ ev.value.participantIdentity,
214
+ ev.value.trackSid
215
+ );
216
+ publication.info.muted = true;
217
+ if (publication.track) {
218
+ publication.track.info.muted = true;
219
+ }
220
+ this.emit("trackMuted" /* TrackMuted */, publication, participant);
221
+ } catch (e) {
222
+ console.warn(`RoomEvent.TrackMuted: ${e.message}`);
223
+ }
224
+ } else if (ev.case == "trackUnmuted") {
225
+ try {
226
+ const { participant, publication } = this.requirePublicationOfParticipant(
227
+ ev.value.participantIdentity,
228
+ ev.value.trackSid
229
+ );
230
+ publication.info.muted = false;
231
+ if (publication.track) {
232
+ publication.track.info.muted = false;
233
+ }
234
+ this.emit("trackUnmuted" /* TrackUnmuted */, publication, participant);
235
+ } catch (e) {
236
+ console.warn(`RoomEvent.TrackUnmuted: ${e.message}`);
237
+ }
238
+ } else if (ev.case == "activeSpeakersChanged") {
239
+ try {
240
+ const activeSpeakers = ev.value.participantIdentities.map(
241
+ (identity) => this.requireParticipantByIdentity(identity)
242
+ );
243
+ this.emit("activeSpeakersChanged" /* ActiveSpeakersChanged */, activeSpeakers);
244
+ } catch (e) {
245
+ console.warn(`RoomEvent.ActiveSpeakersChanged: ${e.message}`);
246
+ }
247
+ } else if (ev.case == "roomMetadataChanged") {
248
+ this.info.metadata = ev.value.metadata ?? "";
249
+ this.emit("roomMetadataChanged" /* RoomMetadataChanged */, this.info.metadata);
250
+ } else if (ev.case == "participantMetadataChanged") {
251
+ try {
252
+ const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
253
+ participant.info.metadata = ev.value.metadata;
254
+ this.emit("participantMetadataChanged" /* ParticipantMetadataChanged */, participant.metadata, participant);
255
+ } catch (e) {
256
+ console.warn(`RoomEvent.ParticipantMetadataChanged: ${e.message}`);
257
+ }
258
+ } else if (ev.case == "participantNameChanged") {
259
+ try {
260
+ const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
261
+ participant.info.name = ev.value.name;
262
+ this.emit("participantNameChanged" /* ParticipantNameChanged */, participant.name, participant);
263
+ } catch (e) {
264
+ console.warn(`RoomEvent.ParticipantNameChanged: ${e.message}`);
265
+ }
266
+ } else if (ev.case == "participantAttributesChanged") {
267
+ try {
268
+ const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
269
+ participant.info.attributes = ev.value.attributes.reduce(
270
+ (acc, value) => {
271
+ acc[value.key] = value.value;
272
+ return acc;
273
+ },
274
+ {}
275
+ );
276
+ if (Object.keys(ev.value.changedAttributes).length > 0) {
277
+ const changedAttributes = ev.value.changedAttributes.reduce(
278
+ (acc, value) => {
279
+ acc[value.key] = value.value;
280
+ return acc;
281
+ },
282
+ {}
283
+ );
284
+ this.emit("participantAttributesChanged" /* ParticipantAttributesChanged */, changedAttributes, participant);
285
+ }
286
+ } catch (e) {
287
+ console.warn(`RoomEvent.ParticipantAttributesChanged: ${e.message}`);
288
+ }
289
+ } else if (ev.case == "connectionQualityChanged") {
290
+ try {
291
+ const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
292
+ this.emit("connectionQualityChanged" /* ConnectionQualityChanged */, ev.value.quality, participant);
293
+ } catch (e) {
294
+ console.warn(`RoomEvent.ConnectionQualityChanged: ${e.message}`);
295
+ }
296
+ } else if (ev.case == "chatMessage") {
297
+ const participant = this.retrieveParticipantByIdentity(ev.value.participantIdentity);
298
+ const { id, message: messageText, timestamp, editTimestamp, generated } = ev.value.message;
299
+ const message = {
300
+ id,
301
+ message: messageText,
302
+ timestamp: Number(timestamp),
303
+ editTimestamp: Number(editTimestamp),
304
+ generated
305
+ };
306
+ this.emit("chatMessage" /* ChatMessage */, message, participant);
307
+ } else if (ev.case == "dataPacketReceived") {
308
+ const participant = this.remoteParticipants.get(ev.value.participantIdentity);
309
+ const dataPacket = ev.value.value;
310
+ switch (dataPacket.case) {
311
+ case "user":
312
+ const buffer = import_ffi_client.FfiClient.instance.copyBuffer(
313
+ dataPacket.value.data.data.dataPtr,
314
+ Number(dataPacket.value.data.data.dataLen)
315
+ );
316
+ new import_ffi_client.FfiHandle(dataPacket.value.data.handle.id).dispose();
317
+ this.emit(
318
+ "dataReceived" /* DataReceived */,
319
+ buffer,
320
+ participant,
321
+ ev.value.kind,
322
+ dataPacket.value.topic
323
+ );
324
+ break;
325
+ case "sipDtmf":
326
+ const { code, digit } = dataPacket.value;
327
+ this.emit("dtmfReceived" /* DtmfReceived */, code, digit, participant);
328
+ break;
329
+ default:
330
+ break;
331
+ }
332
+ } else if (ev.case == "e2eeStateChanged") {
333
+ if (ev.value.state == import_e2ee_pb.EncryptionState.INTERNAL_ERROR) {
334
+ this.emit("encryptionError" /* EncryptionError */, new Error("internal server error"));
335
+ }
336
+ } else if (ev.case == "connectionStateChanged") {
337
+ this.connectionState = ev.value.state;
338
+ this.emit("connectionStateChanged" /* ConnectionStateChanged */, this.connectionState);
339
+ } else if (ev.case == "disconnected") {
340
+ this.emit("disconnected" /* Disconnected */, ev.value.reason);
341
+ } else if (ev.case == "reconnecting") {
342
+ this.emit("reconnecting" /* Reconnecting */);
343
+ } else if (ev.case == "reconnected") {
344
+ this.emit("reconnected" /* Reconnected */);
345
+ } else if (ev.case == "roomSidChanged") {
346
+ this.emit("roomSidChanged" /* RoomSidChanged */, ev.value.sid);
347
+ } else if (ev.case === "streamHeaderReceived" && ev.value.header) {
348
+ this.handleStreamHeader(ev.value.header, ev.value.participantIdentity);
349
+ } else if (ev.case === "streamChunkReceived" && ev.value.chunk) {
350
+ this.handleStreamChunk(ev.value.chunk);
351
+ } else if (ev.case === "streamTrailerReceived" && ev.value.trailer) {
352
+ this.handleStreamTrailer(ev.value.trailer);
353
+ } else if (ev.case === "roomUpdated") {
354
+ this.info = ev.value;
355
+ this.emit("roomUpdated" /* RoomUpdated */);
356
+ } else if (ev.case === "moved") {
357
+ this.info = ev.value;
358
+ this.emit("moved" /* Moved */);
359
+ } else if (ev.case === "participantsUpdated") {
360
+ for (const info of ev.value.participants) {
361
+ const participant = this.retrieveParticipantByIdentity(info.identity);
362
+ if (participant) {
363
+ participant.info = info;
364
+ }
365
+ }
366
+ } else if (ev.case === "participantEncryptionStatusChanged") {
367
+ try {
368
+ const participant = this.requireParticipantByIdentity(ev.value.participantIdentity);
369
+ this.emit(
370
+ "participantEncryptionStatusChanged" /* ParticipantEncryptionStatusChanged */,
371
+ !!ev.value.isEncrypted,
372
+ participant
373
+ );
374
+ } catch (e) {
375
+ console.warn(`RoomEvent.ParticipantEncryptionStatusChanged: ${e.message}`);
291
376
  }
292
377
  }
378
+ } finally {
379
+ unlock();
293
380
  }
294
381
  };
295
382
  }
@@ -372,7 +459,8 @@ class Room extends import_events.default {
372
459
  */
373
460
  async connect(url, token, opts) {
374
461
  const options = { ...defaultRoomOptions, ...opts };
375
- const e2eeOptions = { ...import_e2ee.defaultE2EEOptions, ...options.e2ee };
462
+ const e2eeEnabled = options.encryption || options.e2ee;
463
+ const e2eeOptions = options.encryption ? { ...import_e2ee.defaultE2EEOptions, ...options.encryption } : { ...import_e2ee.defaultE2EEOptions, ...options.e2ee };
376
464
  const req = new import_room_pb.ConnectRequest({
377
465
  url,
378
466
  token,
@@ -392,10 +480,13 @@ class Room extends import_events.default {
392
480
  switch (cb.message.case) {
393
481
  case "result":
394
482
  this.ffiHandle = new import_ffi_client.FfiHandle(cb.message.value.room.handle.id);
395
- this.e2eeManager = options.e2ee && new import_e2ee.E2EEManager(this.ffiHandle.handle, e2eeOptions);
483
+ this.e2eeManager = e2eeEnabled && new import_e2ee.E2EEManager(this.ffiHandle.handle, e2eeOptions);
396
484
  this.info = cb.message.value.room.info;
397
485
  this.connectionState = import_room_pb.ConnectionState.CONN_CONNECTED;
398
- this.localParticipant = new import_participant.LocalParticipant(cb.message.value.localParticipant);
486
+ this.localParticipant = new import_participant.LocalParticipant(
487
+ cb.message.value.localParticipant,
488
+ this.roomEventLock
489
+ );
399
490
  for (const pt of cb.message.value.participants) {
400
491
  const rp = this.createRemoteParticipant(pt.participant);
401
492
  for (const pub of pt.publications) {
@@ -418,7 +509,7 @@ class Room extends import_events.default {
418
509
  if (!this.isConnected) {
419
510
  return;
420
511
  }
421
- import_ffi_client.FfiClient.instance.request({
512
+ const res = import_ffi_client.FfiClient.instance.request({
422
513
  message: {
423
514
  case: "disconnect",
424
515
  value: {
@@ -426,6 +517,9 @@ class Room extends import_events.default {
426
517
  }
427
518
  }
428
519
  });
520
+ await import_ffi_client.FfiClient.instance.waitFor((ev) => {
521
+ return ev.message.case == "connect" && ev.message.value.asyncId == res.asyncId;
522
+ });
429
523
  import_ffi_client.FfiClient.instance.removeListener(import_ffi_client.FfiClientEvent.FfiEvent, this.onFfiEvent);
430
524
  this.removeAllListeners();
431
525
  }
@@ -630,6 +724,7 @@ var RoomEvent = /* @__PURE__ */ ((RoomEvent2) => {
630
724
  RoomEvent2["ParticipantMetadataChanged"] = "participantMetadataChanged";
631
725
  RoomEvent2["ParticipantNameChanged"] = "participantNameChanged";
632
726
  RoomEvent2["ParticipantAttributesChanged"] = "participantAttributesChanged";
727
+ RoomEvent2["ParticipantEncryptionStatusChanged"] = "participantEncryptionStatusChanged";
633
728
  RoomEvent2["ConnectionQualityChanged"] = "connectionQualityChanged";
634
729
  RoomEvent2["DataReceived"] = "dataReceived";
635
730
  RoomEvent2["ChatMessage"] = "chatMessage";