livekit-client 1.6.6 → 1.6.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. package/dist/livekit-client.esm.mjs +508 -317
  2. package/dist/livekit-client.esm.mjs.map +1 -1
  3. package/dist/livekit-client.umd.js +1 -1
  4. package/dist/livekit-client.umd.js.map +1 -1
  5. package/dist/src/proto/google/protobuf/timestamp.d.ts +9 -2
  6. package/dist/src/proto/google/protobuf/timestamp.d.ts.map +1 -1
  7. package/dist/src/proto/livekit_models.d.ts +958 -58
  8. package/dist/src/proto/livekit_models.d.ts.map +1 -1
  9. package/dist/src/proto/livekit_rtc.d.ts +8394 -3725
  10. package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
  11. package/dist/src/room/PCTransport.d.ts.map +1 -1
  12. package/dist/src/room/RTCEngine.d.ts +10 -3
  13. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  14. package/dist/src/room/Room.d.ts +1 -1
  15. package/dist/src/room/Room.d.ts.map +1 -1
  16. package/dist/src/room/events.d.ts +1 -1
  17. package/dist/src/room/participant/LocalParticipant.d.ts +18 -0
  18. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  19. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  20. package/dist/src/room/track/RemoteTrackPublication.d.ts +1 -1
  21. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
  22. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  23. package/dist/src/room/track/Track.d.ts +2 -1
  24. package/dist/src/room/track/Track.d.ts.map +1 -1
  25. package/dist/src/room/track/options.d.ts +10 -2
  26. package/dist/src/room/track/options.d.ts.map +1 -1
  27. package/dist/src/room/types.d.ts +7 -0
  28. package/dist/src/room/types.d.ts.map +1 -1
  29. package/dist/src/version.d.ts +1 -1
  30. package/dist/ts4.2/src/proto/google/protobuf/timestamp.d.ts +11 -2
  31. package/dist/ts4.2/src/proto/livekit_models.d.ts +1199 -177
  32. package/dist/ts4.2/src/proto/livekit_rtc.d.ts +9260 -4023
  33. package/dist/ts4.2/src/room/RTCEngine.d.ts +10 -3
  34. package/dist/ts4.2/src/room/Room.d.ts +1 -1
  35. package/dist/ts4.2/src/room/events.d.ts +1 -1
  36. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +18 -0
  37. package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +1 -1
  38. package/dist/ts4.2/src/room/track/Track.d.ts +2 -1
  39. package/dist/ts4.2/src/room/track/options.d.ts +10 -2
  40. package/dist/ts4.2/src/room/types.d.ts +7 -0
  41. package/dist/ts4.2/src/version.d.ts +1 -1
  42. package/package.json +14 -14
  43. package/src/proto/google/protobuf/timestamp.ts +4 -0
  44. package/src/proto/livekit_models.ts +128 -31
  45. package/src/proto/livekit_rtc.ts +262 -161
  46. package/src/room/PCTransport.ts +2 -0
  47. package/src/room/RTCEngine.ts +50 -55
  48. package/src/room/Room.ts +60 -43
  49. package/src/room/events.ts +1 -1
  50. package/src/room/participant/LocalParticipant.ts +118 -28
  51. package/src/room/participant/RemoteParticipant.ts +2 -3
  52. package/src/room/track/RemoteTrackPublication.ts +3 -2
  53. package/src/room/track/RemoteVideoTrack.ts +0 -3
  54. package/src/room/track/Track.ts +2 -1
  55. package/src/room/track/options.ts +14 -2
  56. package/src/room/types.ts +9 -0
  57. package/src/version.ts +1 -1
@@ -189,6 +189,8 @@ export default class PCTransport extends EventEmitter {
189
189
  }
190
190
 
191
191
  close() {
192
+ this.pc.onconnectionstatechange = null;
193
+ this.pc.oniceconnectionstatechange = null;
192
194
  this.pc.close();
193
195
  }
194
196
 
@@ -118,8 +118,6 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
118
118
 
119
119
  private clientConfiguration?: ClientConfiguration;
120
120
 
121
- private connectedServerAddr?: string;
122
-
123
121
  private attemptingReconnect: boolean = false;
124
122
 
125
123
  private reconnectPolicy: ReconnectPolicy;
@@ -136,6 +134,8 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
136
134
 
137
135
  private closingLock: Mutex;
138
136
 
137
+ private shouldFailNext: boolean = false;
138
+
139
139
  constructor(private options: InternalRoomOptions) {
140
140
  super();
141
141
  this.client = new SignalClient();
@@ -247,7 +247,13 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
247
247
  });
248
248
  }
249
249
 
250
- removeTrack(sender: RTCRtpSender) {
250
+ /**
251
+ * Removes sender from PeerConnection, returning true if it was removed successfully
252
+ * and a negotiation is necessary
253
+ * @param sender
254
+ * @returns
255
+ */
256
+ removeTrack(sender: RTCRtpSender): boolean {
251
257
  if (sender.track && this.pendingTrackResolvers[sender.track.id]) {
252
258
  const { reject } = this.pendingTrackResolvers[sender.track.id];
253
259
  if (reject) {
@@ -257,9 +263,11 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
257
263
  }
258
264
  try {
259
265
  this.publisher?.pc.removeTrack(sender);
266
+ return true;
260
267
  } catch (e: unknown) {
261
268
  log.warn('failed to remove track', { error: e, method: 'removeTrack' });
262
269
  }
270
+ return false;
263
271
  }
264
272
 
265
273
  updateMuteStatus(trackSid: string, muted: boolean) {
@@ -270,8 +278,11 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
270
278
  return this.reliableDCSub?.readyState;
271
279
  }
272
280
 
273
- get connectedServerAddress(): string | undefined {
274
- return this.connectedServerAddr;
281
+ async getConnectedServerAddress(): Promise<string | undefined> {
282
+ if (this.primaryPC === undefined) {
283
+ return undefined;
284
+ }
285
+ return getConnectedAddress(this.primaryPC);
275
286
  }
276
287
 
277
288
  private configure(joinResponse: JoinResponse) {
@@ -317,11 +328,6 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
317
328
  primaryPC.onconnectionstatechange = async () => {
318
329
  log.debug(`primary PC state changed ${primaryPC.connectionState}`);
319
330
  if (primaryPC.connectionState === 'connected') {
320
- try {
321
- this.connectedServerAddr = await getConnectedAddress(primaryPC);
322
- } catch (e) {
323
- log.warn('could not get connected server address', { error: e });
324
- }
325
331
  const shouldEmit = this.pcState === PCState.New;
326
332
  this.pcState = PCState.Connected;
327
333
  if (shouldEmit) {
@@ -334,10 +340,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
334
340
 
335
341
  this.handleDisconnect(
336
342
  'primary peerconnection',
337
- false,
338
343
  subscriberPrimary
339
- ? ReconnectReason.REASON_SUBSCRIBER_FAILED
340
- : ReconnectReason.REASON_PUBLISHER_FAILED,
344
+ ? ReconnectReason.RR_SUBSCRIBER_FAILED
345
+ : ReconnectReason.RR_PUBLISHER_FAILED,
341
346
  );
342
347
  }
343
348
  }
@@ -348,10 +353,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
348
353
  if (secondaryPC.connectionState === 'failed') {
349
354
  this.handleDisconnect(
350
355
  'secondary peerconnection',
351
- false,
352
356
  subscriberPrimary
353
- ? ReconnectReason.REASON_PUBLISHER_FAILED
354
- : ReconnectReason.REASON_SUBSCRIBER_FAILED,
357
+ ? ReconnectReason.RR_PUBLISHER_FAILED
358
+ : ReconnectReason.RR_SUBSCRIBER_FAILED,
355
359
  );
356
360
  }
357
361
  };
@@ -419,7 +423,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
419
423
  };
420
424
 
421
425
  this.client.onClose = () => {
422
- this.handleDisconnect('signal', false, ReconnectReason.REASON_SIGNAL_DISCONNECTED);
426
+ this.handleDisconnect('signal', ReconnectReason.RR_SIGNAL_DISCONNECTED);
423
427
  };
424
428
 
425
429
  this.client.onLeave = (leave?: LeaveRequest) => {
@@ -690,11 +694,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
690
694
  // websocket reconnect behavior. if websocket is interrupted, and the PeerConnection
691
695
  // continues to work, we can reconnect to websocket to continue the session
692
696
  // after a number of retries, we'll close and give up permanently
693
- private handleDisconnect = (
694
- connection: string,
695
- signalEvents: boolean = false,
696
- disconnectReason?: ReconnectReason,
697
- ) => {
697
+ private handleDisconnect = (connection: string, disconnectReason?: ReconnectReason) => {
698
698
  if (this._isClosed) {
699
699
  return;
700
700
  }
@@ -731,12 +731,12 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
731
731
 
732
732
  this.clearReconnectTimeout();
733
733
  this.reconnectTimeout = CriticalTimers.setTimeout(
734
- () => this.attemptReconnect(signalEvents, disconnectReason),
734
+ () => this.attemptReconnect(disconnectReason),
735
735
  delay,
736
736
  );
737
737
  };
738
738
 
739
- private async attemptReconnect(signalEvents: boolean = false, reason?: ReconnectReason) {
739
+ private async attemptReconnect(reason?: ReconnectReason) {
740
740
  if (this._isClosed) {
741
741
  return;
742
742
  }
@@ -756,35 +756,26 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
756
756
  try {
757
757
  this.attemptingReconnect = true;
758
758
  if (this.fullReconnectOnNext) {
759
- await this.restartConnection(signalEvents);
759
+ await this.restartConnection();
760
760
  } else {
761
- await this.resumeConnection(signalEvents, reason);
761
+ await this.resumeConnection(reason);
762
762
  }
763
763
  this.clearPendingReconnect();
764
764
  this.fullReconnectOnNext = false;
765
765
  } catch (e) {
766
766
  this.reconnectAttempts += 1;
767
- let reconnectRequired = false;
768
767
  let recoverable = true;
769
- let requireSignalEvents = false;
770
768
  if (e instanceof UnexpectedConnectionState) {
771
769
  log.debug('received unrecoverable error', { error: e });
772
770
  // unrecoverable
773
771
  recoverable = false;
774
772
  } else if (!(e instanceof SignalReconnectError)) {
775
773
  // cannot resume
776
- reconnectRequired = true;
777
- }
778
-
779
- // when we flip from resume to reconnect
780
- // we need to fire the right reconnecting events
781
- if (reconnectRequired && !this.fullReconnectOnNext) {
782
774
  this.fullReconnectOnNext = true;
783
- requireSignalEvents = true;
784
775
  }
785
776
 
786
777
  if (recoverable) {
787
- this.handleDisconnect('reconnect', requireSignalEvents, ReconnectReason.REASON_UNKOWN);
778
+ this.handleDisconnect('reconnect', ReconnectReason.RR_UNKOWN);
788
779
  } else {
789
780
  log.info(
790
781
  `could not recover connection after ${this.reconnectAttempts} attempts, ${
@@ -810,16 +801,14 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
810
801
  return null;
811
802
  }
812
803
 
813
- private async restartConnection(emitRestarting: boolean = false) {
804
+ private async restartConnection() {
814
805
  if (!this.url || !this.token) {
815
806
  // permanent failure, don't attempt reconnection
816
807
  throw new UnexpectedConnectionState('could not reconnect, url or token not saved');
817
808
  }
818
809
 
819
810
  log.info(`reconnecting, attempt: ${this.reconnectAttempts}`);
820
- if (emitRestarting || this.reconnectAttempts === 0) {
821
- this.emit(EngineEvent.Restarting);
822
- }
811
+ this.emit(EngineEvent.Restarting);
823
812
 
824
813
  if (this.client.isConnected) {
825
814
  await this.client.sendLeave();
@@ -842,6 +831,11 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
842
831
  throw new SignalReconnectError();
843
832
  }
844
833
 
834
+ if (this.shouldFailNext) {
835
+ this.shouldFailNext = false;
836
+ throw new Error('simulated failure');
837
+ }
838
+
845
839
  await this.waitForPCConnected();
846
840
  this.client.setReconnected();
847
841
 
@@ -849,10 +843,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
849
843
  this.emit(EngineEvent.Restarted, joinResponse);
850
844
  }
851
845
 
852
- private async resumeConnection(
853
- emitResuming: boolean = false,
854
- reason?: ReconnectReason,
855
- ): Promise<void> {
846
+ private async resumeConnection(reason?: ReconnectReason): Promise<void> {
856
847
  if (!this.url || !this.token) {
857
848
  // permanent failure, don't attempt reconnection
858
849
  throw new UnexpectedConnectionState('could not reconnect, url or token not saved');
@@ -863,9 +854,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
863
854
  }
864
855
 
865
856
  log.info(`resuming signal connection, attempt ${this.reconnectAttempts}`);
866
- if (emitResuming || this.reconnectAttempts === 0) {
867
- this.emit(EngineEvent.Resuming);
868
- }
857
+ this.emit(EngineEvent.Resuming);
869
858
 
870
859
  try {
871
860
  const res = await this.client.reconnect(this.url, this.token, this.participantSid, reason);
@@ -883,6 +872,11 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
883
872
  }
884
873
  this.emit(EngineEvent.SignalResumed);
885
874
 
875
+ if (this.shouldFailNext) {
876
+ this.shouldFailNext = false;
877
+ throw new Error('simulated failure');
878
+ }
879
+
886
880
  this.subscriber.restartingIce = true;
887
881
 
888
882
  // only restart publisher if it's needed
@@ -921,11 +915,6 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
921
915
  this.primaryPC?.connectionState === 'connected'
922
916
  ) {
923
917
  this.pcState = PCState.Connected;
924
- try {
925
- this.connectedServerAddr = await getConnectedAddress(this.primaryPC);
926
- } catch (e) {
927
- log.warn('could not get connected server address', { error: e });
928
- }
929
918
  }
930
919
  if (this.pcState === PCState.Connected) {
931
920
  return;
@@ -1022,7 +1011,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1022
1011
 
1023
1012
  const negotiationTimeout = setTimeout(() => {
1024
1013
  reject('negotiation timed out');
1025
- this.handleDisconnect('negotiation', false, ReconnectReason.REASON_SIGNAL_DISCONNECTED);
1014
+ this.handleDisconnect('negotiation', ReconnectReason.RR_SIGNAL_DISCONNECTED);
1026
1015
  }, this.peerConnectionTimeout);
1027
1016
 
1028
1017
  const cleanup = () => {
@@ -1043,7 +1032,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1043
1032
  if (e instanceof NegotiationError) {
1044
1033
  this.fullReconnectOnNext = true;
1045
1034
  }
1046
- this.handleDisconnect('negotiation', false, ReconnectReason.REASON_UNKOWN);
1035
+ this.handleDisconnect('negotiation', ReconnectReason.RR_UNKOWN);
1047
1036
  });
1048
1037
  });
1049
1038
  }
@@ -1066,6 +1055,12 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1066
1055
  }
1067
1056
  }
1068
1057
 
1058
+ /* @internal */
1059
+ failNext() {
1060
+ // debugging method to fail the next reconnect/resume attempt
1061
+ this.shouldFailNext = true;
1062
+ }
1063
+
1069
1064
  private clearReconnectTimeout() {
1070
1065
  if (this.reconnectTimeout) {
1071
1066
  CriticalTimers.clearTimeout(this.reconnectTimeout);
@@ -1081,7 +1076,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
1081
1076
  // in case the engine is currently reconnecting, attempt a reconnect immediately after the browser state has changed to 'onLine'
1082
1077
  if (this.client.isReconnecting) {
1083
1078
  this.clearReconnectTimeout();
1084
- this.attemptReconnect(true, ReconnectReason.REASON_SIGNAL_DISCONNECTED);
1079
+ this.attemptReconnect(ReconnectReason.RR_SIGNAL_DISCONNECTED);
1085
1080
  }
1086
1081
  };
1087
1082
 
package/src/room/Room.ts CHANGED
@@ -56,8 +56,8 @@ import type { AdaptiveStreamSettings } from './track/types';
56
56
  import { getNewAudioContext } from './track/utils';
57
57
  import type { SimulationOptions } from './types';
58
58
  import {
59
- Future,
60
59
  createDummyVideoStreamTrack,
60
+ Future,
61
61
  getEmptyAudioStreamTrack,
62
62
  isWeb,
63
63
  Mutex,
@@ -516,6 +516,13 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
516
516
  },
517
517
  });
518
518
  break;
519
+ case 'resume-reconnect':
520
+ this.engine.failNext();
521
+ await this.engine.client.close();
522
+ if (this.engine.client.onClose) {
523
+ this.engine.client.onClose('simulate resume-reconnect');
524
+ }
525
+ break;
519
526
  case 'force-tcp':
520
527
  case 'force-tls':
521
528
  req = SimulateScenario.fromPartial({
@@ -751,45 +758,53 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
751
758
  log.debug(`reconnected to server`, {
752
759
  region: joinResponse.serverRegion,
753
760
  });
754
- this.setAndEmitConnectionState(ConnectionState.Connected);
755
- this.emit(RoomEvent.Reconnected);
756
761
 
757
- // rehydrate participants
758
- if (joinResponse.participant) {
759
- // with a restart, the sid will have changed, we'll map our understanding to it
760
- this.localParticipant.sid = joinResponse.participant.sid;
761
- this.handleParticipantUpdates([joinResponse.participant]);
762
- }
763
- this.handleParticipantUpdates(joinResponse.otherParticipants);
764
-
765
- // unpublish & republish tracks
766
- const localPubs: LocalTrackPublication[] = [];
767
- this.localParticipant.tracks.forEach((pub) => {
768
- if (pub.track) {
769
- localPubs.push(pub);
762
+ try {
763
+ // rehydrate participants
764
+ if (joinResponse.participant) {
765
+ // with a restart, the sid will have changed, we'll map our understanding to it
766
+ this.localParticipant.sid = joinResponse.participant.sid;
767
+ this.handleParticipantUpdates([joinResponse.participant]);
770
768
  }
771
- });
769
+ this.handleParticipantUpdates(joinResponse.otherParticipants);
772
770
 
773
- await Promise.all(
774
- localPubs.map(async (pub) => {
775
- const track = pub.track!;
776
- this.localParticipant.unpublishTrack(track, false);
777
- if (!track.isMuted) {
778
- if (
779
- (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) &&
780
- !track.isUserProvided
781
- ) {
782
- // we need to restart the track before publishing, often a full reconnect
783
- // is necessary because computer had gone to sleep.
784
- log.debug('restarting existing track', {
771
+ // unpublish & republish tracks
772
+ const localPubs: LocalTrackPublication[] = [];
773
+ this.localParticipant.tracks.forEach((pub) => {
774
+ if (pub.track) {
775
+ localPubs.push(pub);
776
+ }
777
+ });
778
+
779
+ await Promise.all(
780
+ localPubs.map(async (pub) => {
781
+ const track = pub.track!;
782
+ this.localParticipant.unpublishTrack(track, false);
783
+ if (!track.isMuted) {
784
+ if (
785
+ (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) &&
786
+ !track.isUserProvided
787
+ ) {
788
+ // we need to restart the track before publishing, often a full reconnect
789
+ // is necessary because computer had gone to sleep.
790
+ log.debug('restarting existing track', {
791
+ track: pub.trackSid,
792
+ });
793
+ await track.restartTrack();
794
+ }
795
+ log.debug('publishing new track', {
785
796
  track: pub.trackSid,
786
797
  });
787
- await track.restartTrack();
798
+ await this.localParticipant.publishTrack(track, pub.options);
788
799
  }
789
- await this.localParticipant.publishTrack(track, pub.options);
790
- }
791
- }),
792
- );
800
+ }),
801
+ );
802
+ } catch (error) {
803
+ log.error('error trying to re-publish tracks after reconnection', { error });
804
+ } finally {
805
+ this.setAndEmitConnectionState(ConnectionState.Connected);
806
+ this.emit(RoomEvent.Reconnected);
807
+ }
793
808
  };
794
809
 
795
810
  private handleDisconnect(shouldStopTracks = true, reason?: DisconnectReason) {
@@ -862,15 +877,16 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
862
877
  let remoteParticipant = this.participants.get(info.sid);
863
878
  const isNewParticipant = !remoteParticipant;
864
879
 
865
- // create participant if doesn't exist
866
- remoteParticipant = this.getOrCreateParticipant(info.sid, info);
867
-
868
880
  // when it's disconnected, send updates
869
881
  if (info.state === ParticipantInfo_State.DISCONNECTED) {
870
882
  this.handleParticipantDisconnected(info.sid, remoteParticipant);
871
- } else if (!isNewParticipant) {
872
- // just update, no events
873
- remoteParticipant.updateInfo(info);
883
+ } else {
884
+ // create participant if doesn't exist
885
+ remoteParticipant = this.getOrCreateParticipant(info.sid, info);
886
+ if (!isNewParticipant) {
887
+ // just update, no events
888
+ remoteParticipant.updateInfo(info);
889
+ }
874
890
  }
875
891
  });
876
892
  };
@@ -886,7 +902,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
886
902
  participant.tracks.forEach((publication) => {
887
903
  participant.unpublishTrack(publication.trackSid, true);
888
904
  });
889
- this.emitWhenConnected(RoomEvent.ParticipantDisconnected, participant);
905
+ this.emit(RoomEvent.ParticipantDisconnected, participant);
890
906
  }
891
907
 
892
908
  // updates are sent only when there's a change to speaker ordering
@@ -991,7 +1007,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
991
1007
  // find the participant
992
1008
  const participant = this.participants.get(userPacket.participantSid);
993
1009
 
994
- this.emit(RoomEvent.DataReceived, userPacket.payload, participant, kind);
1010
+ this.emit(RoomEvent.DataReceived, userPacket.payload, participant, kind, userPacket.topic);
995
1011
 
996
1012
  // also emit on the participant
997
1013
  participant?.emit(ParticipantEvent.DataReceived, userPacket.payload, kind);
@@ -1114,7 +1130,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
1114
1130
  },
1115
1131
  )
1116
1132
  .on(ParticipantEvent.TrackUnpublished, (publication: RemoteTrackPublication) => {
1117
- this.emitWhenConnected(RoomEvent.TrackUnpublished, publication, participant);
1133
+ this.emit(RoomEvent.TrackUnpublished, publication, participant);
1118
1134
  })
1119
1135
  .on(
1120
1136
  ParticipantEvent.TrackUnsubscribed,
@@ -1452,6 +1468,7 @@ export type RoomEventCallbacks = {
1452
1468
  payload: Uint8Array,
1453
1469
  participant?: RemoteParticipant,
1454
1470
  kind?: DataPacket_Kind,
1471
+ topic?: string,
1455
1472
  ) => void;
1456
1473
  connectionQualityChanged: (quality: ConnectionQuality, participant: Participant) => void;
1457
1474
  mediaDevicesError: (error: Error) => void;
@@ -175,7 +175,7 @@ export enum RoomEvent {
175
175
  * Data packets provides the ability to use LiveKit to send/receive arbitrary payloads.
176
176
  * All participants in the room will receive the messages sent to the room.
177
177
  *
178
- * args: (payload: Uint8Array, participant: [[Participant]], kind: [[DataPacket_Kind]])
178
+ * args: (payload: Uint8Array, participant: [[Participant]], kind: [[DataPacket_Kind]], topic?: string)
179
179
  */
180
180
  DataReceived = 'dataReceived',
181
181