livekit-client 1.6.6 → 1.6.8

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 (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