@defcomm/meetingsdk 0.2.6 → 0.2.7

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.
package/dist/index.d.mts CHANGED
@@ -130,6 +130,9 @@ interface VideoSDKCoreOptions {
130
130
  onTrackStateChanged?: (track: any) => void;
131
131
  onRecordingStarted?: (recording: any) => void;
132
132
  onRecordingStopped?: (recording: any) => void;
133
+ onRoomUpdate?: (data: any) => void;
134
+ onChatMessage?: (message: ChatMessage) => void;
135
+ onMuteStateChanged?: (peerId: string, kind: "audio" | "video", muted: boolean) => void;
133
136
  }
134
137
 
135
138
  declare const useLocalParticipant: () => {
@@ -281,6 +284,9 @@ interface SFUClientOptions {
281
284
  stopped_at: string;
282
285
  }) => void;
283
286
  onError?: (error: any) => void;
287
+ onChatMessage?: (message: any) => void;
288
+ onRoomUpdate?: (data: any) => void;
289
+ onParticipantMediaState?: (participantId: string, audioMuted: boolean, videoMuted: boolean) => void;
284
290
  autoSubscribe?: boolean;
285
291
  }
286
292
  declare class SFUClient {
@@ -311,6 +317,8 @@ declare class SFUClient {
311
317
  private intentionalClose;
312
318
  private leaveAcknowledged;
313
319
  private joiningWithResume;
320
+ private desiredAudioMuted;
321
+ private desiredVideoMuted;
314
322
  private reconnectTimer;
315
323
  private reconnectAttempt;
316
324
  private maxReconnectAttempts;
@@ -353,6 +361,9 @@ declare class SFUClient {
353
361
  private onRecordingStarted;
354
362
  private onRecordingStopped;
355
363
  private onError;
364
+ private onChatMessage;
365
+ private onRoomUpdate;
366
+ private onParticipantMediaState;
356
367
  private autoSubscribe;
357
368
  constructor(options?: SFUClientOptions);
358
369
  private heartbeatTimer;
@@ -384,6 +395,8 @@ declare class SFUClient {
384
395
  displayName?: string;
385
396
  avatarUrl?: string;
386
397
  participantId?: string;
398
+ audioMuted?: boolean;
399
+ videoMuted?: boolean;
387
400
  }): Promise<void>;
388
401
  private refreshAccessToken;
389
402
  private connect;
@@ -392,6 +405,7 @@ declare class SFUClient {
392
405
  private failJoin;
393
406
  private resolveJoin;
394
407
  private handleServerMessage;
408
+ private onRoomStateReconciled;
395
409
  private rememberTrack;
396
410
  private handleJoined;
397
411
  private initializePeerConnections;
@@ -414,6 +428,11 @@ declare class SFUClient {
414
428
  stopScreenShare(): Promise<void>;
415
429
  muteAudio(muted: boolean): void;
416
430
  muteVideo(muted: boolean): void;
431
+ sendChatMessage(input: {
432
+ message: string;
433
+ reply_to?: any;
434
+ target?: string | null;
435
+ }): boolean;
417
436
  startRecording(): Promise<void>;
418
437
  stopRecording(): Promise<void>;
419
438
  getRecordingStatus(): Promise<any>;
package/dist/index.d.ts CHANGED
@@ -130,6 +130,9 @@ interface VideoSDKCoreOptions {
130
130
  onTrackStateChanged?: (track: any) => void;
131
131
  onRecordingStarted?: (recording: any) => void;
132
132
  onRecordingStopped?: (recording: any) => void;
133
+ onRoomUpdate?: (data: any) => void;
134
+ onChatMessage?: (message: ChatMessage) => void;
135
+ onMuteStateChanged?: (peerId: string, kind: "audio" | "video", muted: boolean) => void;
133
136
  }
134
137
 
135
138
  declare const useLocalParticipant: () => {
@@ -281,6 +284,9 @@ interface SFUClientOptions {
281
284
  stopped_at: string;
282
285
  }) => void;
283
286
  onError?: (error: any) => void;
287
+ onChatMessage?: (message: any) => void;
288
+ onRoomUpdate?: (data: any) => void;
289
+ onParticipantMediaState?: (participantId: string, audioMuted: boolean, videoMuted: boolean) => void;
284
290
  autoSubscribe?: boolean;
285
291
  }
286
292
  declare class SFUClient {
@@ -311,6 +317,8 @@ declare class SFUClient {
311
317
  private intentionalClose;
312
318
  private leaveAcknowledged;
313
319
  private joiningWithResume;
320
+ private desiredAudioMuted;
321
+ private desiredVideoMuted;
314
322
  private reconnectTimer;
315
323
  private reconnectAttempt;
316
324
  private maxReconnectAttempts;
@@ -353,6 +361,9 @@ declare class SFUClient {
353
361
  private onRecordingStarted;
354
362
  private onRecordingStopped;
355
363
  private onError;
364
+ private onChatMessage;
365
+ private onRoomUpdate;
366
+ private onParticipantMediaState;
356
367
  private autoSubscribe;
357
368
  constructor(options?: SFUClientOptions);
358
369
  private heartbeatTimer;
@@ -384,6 +395,8 @@ declare class SFUClient {
384
395
  displayName?: string;
385
396
  avatarUrl?: string;
386
397
  participantId?: string;
398
+ audioMuted?: boolean;
399
+ videoMuted?: boolean;
387
400
  }): Promise<void>;
388
401
  private refreshAccessToken;
389
402
  private connect;
@@ -392,6 +405,7 @@ declare class SFUClient {
392
405
  private failJoin;
393
406
  private resolveJoin;
394
407
  private handleServerMessage;
408
+ private onRoomStateReconciled;
395
409
  private rememberTrack;
396
410
  private handleJoined;
397
411
  private initializePeerConnections;
@@ -414,6 +428,11 @@ declare class SFUClient {
414
428
  stopScreenShare(): Promise<void>;
415
429
  muteAudio(muted: boolean): void;
416
430
  muteVideo(muted: boolean): void;
431
+ sendChatMessage(input: {
432
+ message: string;
433
+ reply_to?: any;
434
+ target?: string | null;
435
+ }): boolean;
417
436
  startRecording(): Promise<void>;
418
437
  stopRecording(): Promise<void>;
419
438
  getRecordingStatus(): Promise<any>;
package/dist/index.js CHANGED
@@ -403,6 +403,8 @@ var SFUClient = class {
403
403
  this.intentionalClose = false;
404
404
  this.leaveAcknowledged = false;
405
405
  this.joiningWithResume = false;
406
+ this.desiredAudioMuted = false;
407
+ this.desiredVideoMuted = false;
406
408
  this.reconnectTimer = null;
407
409
  this.reconnectAttempt = 0;
408
410
  this.maxReconnectAttempts = 8;
@@ -471,6 +473,12 @@ var SFUClient = class {
471
473
  this.onRecordingStopped = options.onRecordingStopped || (() => {
472
474
  });
473
475
  this.onError = options.onError || console.error;
476
+ this.onChatMessage = options.onChatMessage || (() => {
477
+ });
478
+ this.onRoomUpdate = options.onRoomUpdate || (() => {
479
+ });
480
+ this.onParticipantMediaState = options.onParticipantMediaState || (() => {
481
+ });
474
482
  this.autoSubscribe = options.autoSubscribe !== false;
475
483
  }
476
484
  startHeartbeat() {
@@ -593,6 +601,8 @@ var SFUClient = class {
593
601
  avatarUrl: options.avatarUrl
594
602
  };
595
603
  if (options.participantId) this.participantId = options.participantId;
604
+ if (typeof options.audioMuted === "boolean") this.desiredAudioMuted = options.audioMuted;
605
+ if (typeof options.videoMuted === "boolean") this.desiredVideoMuted = options.videoMuted;
596
606
  if (!this.resumeToken) {
597
607
  this.resumeToken = this.loadResumeToken(this.room);
598
608
  }
@@ -659,7 +669,9 @@ var SFUClient = class {
659
669
  room_code: this.room,
660
670
  resume_token: this.resumeToken,
661
671
  display_name: options.displayName,
662
- avatar_url: options.avatarUrl
672
+ avatar_url: options.avatarUrl,
673
+ audio_muted: this.desiredAudioMuted,
674
+ video_muted: this.desiredVideoMuted
663
675
  };
664
676
  if (this.pendingJoinRequestId) {
665
677
  this.send({
@@ -834,6 +846,16 @@ var SFUClient = class {
834
846
  this.onParticipantLeft(participantId);
835
847
  break;
836
848
  }
849
+ case "participant_media_state": {
850
+ const participantId = payload.participant_id;
851
+ if (!participantId) break;
852
+ this.onParticipantMediaState(
853
+ participantId,
854
+ Boolean(payload.audio_muted),
855
+ Boolean(payload.video_muted)
856
+ );
857
+ break;
858
+ }
837
859
  case "participant_presence":
838
860
  case "participant_presence_changed": {
839
861
  const participantId = payload.participant_id;
@@ -956,6 +978,37 @@ var SFUClient = class {
956
978
  this.onScreenShareStopped(participantId, trackId);
957
979
  break;
958
980
  }
981
+ case "room_state": {
982
+ const participants = payload.participants || [];
983
+ this.onRoomStateReconciled(payload);
984
+ this.onRoomUpdate(payload);
985
+ for (const participant of participants) {
986
+ const pid = participant?.participant_id;
987
+ if (!pid) continue;
988
+ if (pid !== this.currentParticipantId) {
989
+ this.participants.set(pid, participant);
990
+ this.onParticipantPresence(pid, String(participant.presence || "connected"));
991
+ this.onParticipantMediaState(pid, Boolean(participant.audio_muted), Boolean(participant.video_muted));
992
+ }
993
+ }
994
+ for (const [pid] of this.participants) {
995
+ if (!participants.some((p) => p?.participant_id === pid)) {
996
+ this.participants.delete(pid);
997
+ this.removeRemoteParticipant(pid);
998
+ this.onParticipantLeft(pid);
999
+ }
1000
+ }
1001
+ for (const track of payload.tracks || []) this.rememberTrack(track);
1002
+ if (Array.isArray(payload.chat_history)) {
1003
+ for (const message2 of payload.chat_history) this.onChatMessage(message2);
1004
+ }
1005
+ break;
1006
+ }
1007
+ case "chat_message": {
1008
+ const msg = payload.message || payload;
1009
+ if (msg?.id) this.onChatMessage(msg);
1010
+ break;
1011
+ }
959
1012
  case "error": {
960
1013
  const message2 = payload.message || "SFU signaling error";
961
1014
  if (this.state === "reconnecting" && message2.includes("participant session is stale")) {
@@ -978,6 +1031,14 @@ var SFUClient = class {
978
1031
  console.debug("[SFU] Ignoring unknown server message", message);
979
1032
  }
980
1033
  }
1034
+ onRoomStateReconciled(payload) {
1035
+ const participants = payload.participants || [];
1036
+ const local = participants.find((p) => p?.participant_id === this.currentParticipantId);
1037
+ if (local) {
1038
+ this.desiredAudioMuted = Boolean(local.audio_muted);
1039
+ this.desiredVideoMuted = Boolean(local.video_muted);
1040
+ }
1041
+ }
981
1042
  rememberTrack(track) {
982
1043
  this.trackMetadata.set(track.track_id, track);
983
1044
  this.trackToParticipant.set(track.track_id, track.participant_id);
@@ -1013,8 +1074,21 @@ var SFUClient = class {
1013
1074
  for (const participant of this.participants.values()) {
1014
1075
  if (participant.participant_id !== this.currentParticipantId) {
1015
1076
  this.onParticipantJoined(participant);
1077
+ this.onParticipantPresence(
1078
+ participant.participant_id,
1079
+ String(participant.presence || "connected")
1080
+ );
1081
+ this.onParticipantMediaState(
1082
+ participant.participant_id,
1083
+ Boolean(participant.audio_muted),
1084
+ Boolean(participant.video_muted)
1085
+ );
1016
1086
  }
1017
1087
  }
1088
+ if (Array.isArray(payload.chat_history)) {
1089
+ for (const message of payload.chat_history) this.onChatMessage(message);
1090
+ }
1091
+ this.onRoomUpdate(payload);
1018
1092
  if (this.localStream) {
1019
1093
  await this.publishExistingTracks();
1020
1094
  }
@@ -1387,9 +1461,6 @@ var SFUClient = class {
1387
1461
  }
1388
1462
  this.screenStream = screenStream;
1389
1463
  try {
1390
- if (!this.send({ type: "start_screen_share" })) {
1391
- throw new Error("Failed to signal start_screen_share");
1392
- }
1393
1464
  await this.screenSender.replaceTrack(screenTrack);
1394
1465
  this.screenTransceiver.direction = "sendonly";
1395
1466
  const offer = await this.publisher.createOffer();
@@ -1401,6 +1472,7 @@ var SFUClient = class {
1401
1472
  sdp: offer.sdp
1402
1473
  }
1403
1474
  });
1475
+ this.send({ type: "start_screen_share" });
1404
1476
  screenTrack.onended = () => {
1405
1477
  void this.stopScreenShare();
1406
1478
  };
@@ -1461,9 +1533,14 @@ var SFUClient = class {
1461
1533
  }
1462
1534
  }
1463
1535
  muteAudio(muted) {
1536
+ this.desiredAudioMuted = muted;
1464
1537
  this.localStream?.getAudioTracks().forEach((track) => {
1465
1538
  track.enabled = !muted;
1466
1539
  });
1540
+ this.send({
1541
+ type: "set_media_state",
1542
+ payload: { audio_muted: muted, video_muted: this.desiredVideoMuted }
1543
+ });
1467
1544
  const trackId = this.localTrackIds.get("audio");
1468
1545
  if (trackId) {
1469
1546
  this.send({
@@ -1476,9 +1553,14 @@ var SFUClient = class {
1476
1553
  }
1477
1554
  }
1478
1555
  muteVideo(muted) {
1556
+ this.desiredVideoMuted = muted;
1479
1557
  this.localStream?.getVideoTracks().forEach((track) => {
1480
1558
  track.enabled = !muted;
1481
1559
  });
1560
+ this.send({
1561
+ type: "set_media_state",
1562
+ payload: { audio_muted: this.desiredAudioMuted, video_muted: muted }
1563
+ });
1482
1564
  const trackId = this.localTrackIds.get("camera");
1483
1565
  if (trackId) {
1484
1566
  this.send({
@@ -1490,6 +1572,16 @@ var SFUClient = class {
1490
1572
  });
1491
1573
  }
1492
1574
  }
1575
+ sendChatMessage(input) {
1576
+ return this.send({
1577
+ type: "send_chat",
1578
+ payload: {
1579
+ message: input.message,
1580
+ reply_to: input.reply_to ?? null,
1581
+ target: input.target ?? null
1582
+ }
1583
+ });
1584
+ }
1493
1585
  async startRecording() {
1494
1586
  if (!this.send({ type: "start_recording" })) {
1495
1587
  throw new Error("SFU signaling is not connected");
@@ -1957,6 +2049,41 @@ var VideoSDKCore = class {
1957
2049
  console.warn(`[SFU] Reconnecting, attempt ${attempt}`);
1958
2050
  },
1959
2051
  // ADMISSION CONTROL
2052
+ onRoomUpdate: (payload) => {
2053
+ const participants = payload?.participants || [];
2054
+ const local = participants.find((p) => p?.participant_id === this.myId);
2055
+ if (local) {
2056
+ const media = this.state.localParticipant?.media;
2057
+ this.state.updateLocalParticipant({
2058
+ id: this.myId,
2059
+ name: this.participantName,
2060
+ media: {
2061
+ micEnabled: !Boolean(local.audio_muted),
2062
+ camEnabled: !Boolean(local.video_muted),
2063
+ isScreenSharing: Boolean(local.screen_sharing),
2064
+ screenStream: this.screenStream,
2065
+ screenTrack: this.screenStream?.getVideoTracks()[0]
2066
+ }
2067
+ });
2068
+ if (this.localStream) {
2069
+ this.localStream.getAudioTracks().forEach((track) => {
2070
+ track.enabled = !Boolean(local.audio_muted);
2071
+ });
2072
+ this.localStream.getVideoTracks().forEach((track) => {
2073
+ track.enabled = !Boolean(local.video_muted);
2074
+ });
2075
+ }
2076
+ if (local.screen_sharing) this.state.setPresenterId(this.myId);
2077
+ else if (this.state.presenterId === this.myId) this.state.setPresenterId(null);
2078
+ }
2079
+ const presenter = participants.find((p) => p?.screen_sharing);
2080
+ if (presenter) this.state.setPresenterId(presenter.participant_id);
2081
+ else if (!local?.screen_sharing) this.state.setPresenterId(null);
2082
+ for (const message of payload?.chat_history || []) {
2083
+ this.state.addChatMessage(message);
2084
+ }
2085
+ this.events.onRoomUpdate?.(payload);
2086
+ },
1960
2087
  onJoinPending: (info) => {
1961
2088
  console.log("[SFU] Join request pending", info);
1962
2089
  },
@@ -1991,6 +2118,19 @@ var VideoSDKCore = class {
1991
2118
  false
1992
2119
  );
1993
2120
  },
2121
+ onChatMessage: (message) => {
2122
+ const msg = {
2123
+ id: message.id,
2124
+ text: message.text,
2125
+ sender_id: message.sender_id,
2126
+ sender_name: message.sender_name,
2127
+ timestamp: Number(message.timestamp),
2128
+ reply_to: message.reply_to ?? null,
2129
+ target: message.target ?? null
2130
+ };
2131
+ this.state.addChatMessage(msg);
2132
+ this.events.onChatMessage?.(msg);
2133
+ },
1994
2134
  onJoinRequested: (request) => {
1995
2135
  const requestId = request?.request_id || request?.requestId;
1996
2136
  const participantId = request?.user_id || request?.participantId;
@@ -2059,6 +2199,17 @@ var VideoSDKCore = class {
2059
2199
  this.events.onUserLeft?.(pid);
2060
2200
  },
2061
2201
  // TRACKS
2202
+ onParticipantMediaState: (participantId, audioMuted, videoMuted) => {
2203
+ if (participantId === this.myId) return;
2204
+ const participant = this.state.getParticipant(participantId);
2205
+ if (!participant) return;
2206
+ this.state.updateParticipantMedia(participantId, {
2207
+ micEnabled: !audioMuted,
2208
+ camEnabled: !videoMuted
2209
+ });
2210
+ this.events.onMuteStateChanged?.(participantId, "audio", audioMuted);
2211
+ this.events.onMuteStateChanged?.(participantId, "video", videoMuted);
2212
+ },
2062
2213
  onTrackPublished: (track) => {
2063
2214
  console.log("[SFU] Track published", track);
2064
2215
  },
@@ -2128,6 +2279,9 @@ var VideoSDKCore = class {
2128
2279
  // SCREEN SHARE
2129
2280
  onScreenShareStarted: (participantId, _track) => {
2130
2281
  console.log("[SFU] Screen share started", participantId);
2282
+ if (participantId === this.myId && !this.screenStream) {
2283
+ this.screenStream = this.sfuClient?.getScreenStream() ?? null;
2284
+ }
2131
2285
  this.state.setPresenterId(participantId);
2132
2286
  if (participantId === this.myId) {
2133
2287
  this.state.updateLocalParticipant({
@@ -2408,18 +2562,13 @@ var VideoSDKCore = class {
2408
2562
  console.warn("SFU Client not connected");
2409
2563
  return;
2410
2564
  }
2411
- const senderName = this.state.localParticipant?.name || "Anonymous";
2412
- const msg = {
2413
- id: crypto.randomUUID(),
2414
- sender_id: this.myId,
2415
- sender_name: senderName,
2416
- text: payload.message.trim(),
2417
- timestamp: Date.now(),
2565
+ const message = payload.message.trim();
2566
+ if (!message) return;
2567
+ this.sfuClient.sendChatMessage({
2568
+ message,
2418
2569
  reply_to: payload.reply_to ?? null,
2419
2570
  target: payload.target ?? null
2420
- };
2421
- this.state.addChatMessage(msg);
2422
- this.events.onChatMessage?.(msg);
2571
+ });
2423
2572
  }
2424
2573
  // CLEANUP
2425
2574
  cleanupRemoteParticipant(participantId) {