@afosecure/meetingsdk 1.1.4 → 1.1.6

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
@@ -109,9 +109,10 @@ declare class VideoSDKCore {
109
109
  private peers;
110
110
  private initiators;
111
111
  private lastPong;
112
+ private iceServers;
112
113
  private intentionalDisconnect;
113
114
  private myId;
114
- private roomId;
115
+ private room;
115
116
  private localStream;
116
117
  private screenStream;
117
118
  private isScreenSharing;
@@ -130,7 +131,10 @@ declare class VideoSDKCore {
130
131
  connect(roomId: string, name: string): Promise<void>;
131
132
  joinMeeting(config: MeetingConfig): Promise<void>;
132
133
  /** Expose the roomId without making it fully public */
133
- getMeetingId(): string | null;
134
+ getMeeting(): {
135
+ id: string | null;
136
+ name: string | null;
137
+ };
134
138
  toggleMic(): void;
135
139
  toggleCam(): void;
136
140
  private scheduleReconnect;
@@ -140,6 +144,7 @@ declare class VideoSDKCore {
140
144
  private handle;
141
145
  private createPeer;
142
146
  private createOffer;
147
+ private shouldInitiate;
143
148
  private handleOffer;
144
149
  private closePeer;
145
150
  startScreenShare(): Promise<MediaStream>;
@@ -163,7 +168,10 @@ type MeetingContextValue = {
163
168
  startScreenShare: () => Promise<MediaStream>;
164
169
  stopScreenShare: () => void;
165
170
  sendMessage: (input: ChatInput) => void;
166
- meetingId: string | null;
171
+ room: {
172
+ id: string | null;
173
+ name: string | null;
174
+ };
167
175
  localParticipant: Participant | null;
168
176
  participants: Map<string, Participant>;
169
177
  messages: ChatMessage[];
@@ -180,7 +188,6 @@ declare const useMeetingContext: () => MeetingContextValue;
180
188
  declare const useMeeting: (handlers?: {
181
189
  onError?: (err: any) => void;
182
190
  }) => {
183
- sdk: VideoSDKCore;
184
191
  join: (config: MeetingConfig) => Promise<void>;
185
192
  leave: () => void;
186
193
  toggleMic: () => void;
@@ -188,7 +195,10 @@ declare const useMeeting: (handlers?: {
188
195
  startScreenShare: () => Promise<MediaStream>;
189
196
  stopScreenShare: () => void;
190
197
  sendMessage: (input: ChatInput) => void;
191
- meetingId: string | null;
198
+ room: {
199
+ id: string | null;
200
+ name: string | null;
201
+ };
192
202
  localParticipant: Participant | null;
193
203
  participants: Map<string, Participant>;
194
204
  messages: ChatMessage[];
package/dist/index.d.ts CHANGED
@@ -109,9 +109,10 @@ declare class VideoSDKCore {
109
109
  private peers;
110
110
  private initiators;
111
111
  private lastPong;
112
+ private iceServers;
112
113
  private intentionalDisconnect;
113
114
  private myId;
114
- private roomId;
115
+ private room;
115
116
  private localStream;
116
117
  private screenStream;
117
118
  private isScreenSharing;
@@ -130,7 +131,10 @@ declare class VideoSDKCore {
130
131
  connect(roomId: string, name: string): Promise<void>;
131
132
  joinMeeting(config: MeetingConfig): Promise<void>;
132
133
  /** Expose the roomId without making it fully public */
133
- getMeetingId(): string | null;
134
+ getMeeting(): {
135
+ id: string | null;
136
+ name: string | null;
137
+ };
134
138
  toggleMic(): void;
135
139
  toggleCam(): void;
136
140
  private scheduleReconnect;
@@ -140,6 +144,7 @@ declare class VideoSDKCore {
140
144
  private handle;
141
145
  private createPeer;
142
146
  private createOffer;
147
+ private shouldInitiate;
143
148
  private handleOffer;
144
149
  private closePeer;
145
150
  startScreenShare(): Promise<MediaStream>;
@@ -163,7 +168,10 @@ type MeetingContextValue = {
163
168
  startScreenShare: () => Promise<MediaStream>;
164
169
  stopScreenShare: () => void;
165
170
  sendMessage: (input: ChatInput) => void;
166
- meetingId: string | null;
171
+ room: {
172
+ id: string | null;
173
+ name: string | null;
174
+ };
167
175
  localParticipant: Participant | null;
168
176
  participants: Map<string, Participant>;
169
177
  messages: ChatMessage[];
@@ -180,7 +188,6 @@ declare const useMeetingContext: () => MeetingContextValue;
180
188
  declare const useMeeting: (handlers?: {
181
189
  onError?: (err: any) => void;
182
190
  }) => {
183
- sdk: VideoSDKCore;
184
191
  join: (config: MeetingConfig) => Promise<void>;
185
192
  leave: () => void;
186
193
  toggleMic: () => void;
@@ -188,7 +195,10 @@ declare const useMeeting: (handlers?: {
188
195
  startScreenShare: () => Promise<MediaStream>;
189
196
  stopScreenShare: () => void;
190
197
  sendMessage: (input: ChatInput) => void;
191
- meetingId: string | null;
198
+ room: {
199
+ id: string | null;
200
+ name: string | null;
201
+ };
192
202
  localParticipant: Participant | null;
193
203
  participants: Map<string, Participant>;
194
204
  messages: ChatMessage[];
package/dist/index.js CHANGED
@@ -39,7 +39,7 @@ var import_react2 = require("react");
39
39
 
40
40
  // src/config/ws.ts
41
41
  var SDK_CONFIG = {
42
- wsUrl: "wss://rust-video-server-sfyf.onrender.com/ws"
42
+ wsUrl: "ws://localhost:8080/ws"
43
43
  };
44
44
 
45
45
  // src/core/MeetingState.ts
@@ -201,8 +201,12 @@ var VideoSDKCore = class {
201
201
  this.peers = {};
202
202
  this.initiators = /* @__PURE__ */ new Set();
203
203
  this.lastPong = Date.now();
204
+ this.iceServers = [];
204
205
  this.intentionalDisconnect = false;
205
- this.roomId = null;
206
+ this.room = {
207
+ id: null,
208
+ name: null
209
+ };
206
210
  this.localStream = null;
207
211
  this.screenStream = null;
208
212
  this.isScreenSharing = false;
@@ -222,7 +226,7 @@ var VideoSDKCore = class {
222
226
  code,
223
227
  message,
224
228
  raw,
225
- roomId: this.roomId,
229
+ roomId: this.room.id,
226
230
  userId: this.myId,
227
231
  recoverable
228
232
  };
@@ -234,28 +238,43 @@ var VideoSDKCore = class {
234
238
  // ---------------- STREAM ----------------
235
239
  async initLocal(video, name) {
236
240
  this.participantName = name;
237
- if (!this.localStream) {
241
+ try {
238
242
  this.localStream = await navigator.mediaDevices.getUserMedia({
239
- video: true,
240
- audio: true
243
+ video: {
244
+ width: { ideal: 1280 },
245
+ height: { ideal: 720 }
246
+ },
247
+ audio: {
248
+ echoCancellation: true,
249
+ noiseSuppression: true,
250
+ autoGainControl: true
251
+ }
241
252
  });
242
- }
243
- video.srcObject = this.localStream;
244
- this.state.updateLocalParticipant({
245
- id: this.myId,
246
- name: this.participantName,
247
- media: {
248
- stream: this.localStream,
249
- micEnabled: true,
250
- camEnabled: true,
251
- isScreenSharing: false
253
+ const hasVideo = this.localStream.getVideoTracks().some((t) => t.readyState === "live");
254
+ const hasAudio = this.localStream.getAudioTracks().some((t) => t.readyState === "live");
255
+ if (!hasVideo || !hasAudio) {
256
+ throw new Error(`Missing tracks: video=${hasVideo}, audio=${hasAudio}`);
252
257
  }
253
- });
254
- this.state.localStream = this.localStream;
258
+ video.srcObject = this.localStream;
259
+ this.state.updateLocalParticipant({
260
+ id: this.myId,
261
+ name: this.participantName,
262
+ media: {
263
+ stream: this.localStream,
264
+ micEnabled: true,
265
+ camEnabled: true,
266
+ isScreenSharing: false
267
+ }
268
+ });
269
+ this.state.localStream = this.localStream;
270
+ } catch (err) {
271
+ this.emitError("GET_USER_MEDIA_FAILED", err?.message, err, false);
272
+ throw err;
273
+ }
255
274
  }
256
275
  // ---------------- CONNECT ----------------
257
276
  async connect(roomId, name) {
258
- this.roomId = roomId;
277
+ this.room.id = roomId;
259
278
  this.reset();
260
279
  return new Promise((resolve, reject) => {
261
280
  this.joinResolver = resolve;
@@ -324,8 +343,8 @@ var VideoSDKCore = class {
324
343
  await this.connect(roomId, name);
325
344
  }
326
345
  /** Expose the roomId without making it fully public */
327
- getMeetingId() {
328
- return this.roomId;
346
+ getMeeting() {
347
+ return this.room;
329
348
  }
330
349
  toggleMic() {
331
350
  const mediaState = this.state.localParticipant?.media;
@@ -366,12 +385,12 @@ var VideoSDKCore = class {
366
385
  });
367
386
  }
368
387
  scheduleReconnect() {
369
- if (!this.roomId) return;
388
+ if (!this.room.id) return;
370
389
  const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 3e4);
371
390
  clearTimeout(this.reconnectTimer);
372
391
  this.reconnectTimer = window.setTimeout(async () => {
373
392
  try {
374
- await this.connect(this.roomId, this.participantName);
393
+ await this.connect(this.room.id, this.participantName);
375
394
  this.reconnectAttempts = 0;
376
395
  } catch {
377
396
  this.reconnectAttempts++;
@@ -412,10 +431,18 @@ var VideoSDKCore = class {
412
431
  this.lastPong = Date.now();
413
432
  break;
414
433
  case "OFFER":
434
+ console.log("[Offer] Received from", msg.sender, {
435
+ sdp: msg.payload.substring(0, 200)
436
+ });
415
437
  await this.handleOffer(msg.payload, msg.sender);
416
438
  break;
417
439
  case "ANSWER": {
418
440
  const pc = this.peers[msg.sender];
441
+ console.log("[Answer] Received from", msg.sender, {
442
+ signalingState: pc?.signalingState,
443
+ iceConnectionState: pc?.iceConnectionState,
444
+ connectionState: pc?.connectionState
445
+ });
419
446
  if (!pc) return;
420
447
  if (pc.signalingState !== "have-local-offer") {
421
448
  console.warn(
@@ -469,10 +496,16 @@ var VideoSDKCore = class {
469
496
  if (!p?.id || p.id === this.myId) continue;
470
497
  this.state.addParticipant(p);
471
498
  this.events.onUserJoined?.(p);
472
- await this.createOffer(p.id);
499
+ if (this.shouldInitiate(p.id)) {
500
+ await this.createOffer(p.id);
501
+ }
473
502
  }
474
503
  break;
475
504
  case "JOINED": {
505
+ if (msg.iceServers) {
506
+ this.iceServers = msg.iceServers;
507
+ }
508
+ this.room.name = msg.room_name;
476
509
  this.intentionalDisconnect = false;
477
510
  this.reconnectAttempts = 0;
478
511
  this.startHeartbeat();
@@ -486,7 +519,9 @@ var VideoSDKCore = class {
486
519
  if (!p?.id || p.id === this.myId) return;
487
520
  this.state.addParticipant(p);
488
521
  this.events.onUserJoined?.(p);
489
- await this.createOffer(p.id);
522
+ if (this.shouldInitiate(p.id)) {
523
+ await this.createOffer(p.id);
524
+ }
490
525
  break;
491
526
  }
492
527
  case "USER_LEFT":
@@ -561,6 +596,11 @@ var VideoSDKCore = class {
561
596
  // ---------------- PEER ----------------
562
597
  createPeer(id) {
563
598
  if (!this.localStream) throw new Error("No local stream");
599
+ if (!this.iceServers || this.iceServers.length === 0) {
600
+ throw new Error(
601
+ "ICE Servers not configured. Backend must provide iceServers on JOIN."
602
+ );
603
+ }
564
604
  console.log(
565
605
  "Adding tracks",
566
606
  this.localStream.getTracks().map((t) => ({
@@ -570,28 +610,15 @@ var VideoSDKCore = class {
570
610
  }))
571
611
  );
572
612
  const pc = new RTCPeerConnection({
573
- iceServers: [
574
- {
575
- urls: [
576
- "stun:stun1.1.google.com:19302",
577
- "stun:stun2.1.google.com:19302"
578
- ]
579
- }
580
- ],
581
- iceCandidatePoolSize: 3
613
+ iceServers: this.iceServers
582
614
  });
583
615
  pc.ontrack = (event) => {
584
- console.log(`Track received: ${event.track.kind}`, {
585
- trackId: event.track.id,
586
- streamCount: event.streams.length,
587
- streamTrackCount: event.streams[0]?.getTracks().length
588
- });
589
616
  const incomingStream = event.streams?.[0] || new MediaStream([event.track]);
590
617
  const participant = this.state.getParticipant(id);
591
618
  const isScreenStream = incomingStream.id === participant?.media?.remoteScreenStreamId;
592
- if (event.track.kind === "video") {
619
+ if (event.track.muted) {
593
620
  event.track.onunmute = () => {
594
- console.log("Video track unmuted for", id);
621
+ console.log(`${event.track.kind} track unmuted for ${id}`);
595
622
  };
596
623
  }
597
624
  if (isScreenStream) {
@@ -648,18 +675,17 @@ var VideoSDKCore = class {
648
675
  }
649
676
  // ---------------- OFFER ----------------
650
677
  async createOffer(id, isRenegotiation = false) {
678
+ if (!isRenegotiation && !this.shouldInitiate(id)) {
679
+ console.debug(
680
+ `[Offer] ${id} should initiate (${id} > ${this.myId}), skipping`
681
+ );
682
+ return;
683
+ }
651
684
  if (!isRenegotiation && this.initiators.has(id)) {
652
685
  console.debug(
653
686
  `[Offer] Already initiating with ${id}, skipping duplicate`
654
687
  );
655
- }
656
- if (isRenegotiation && this.peers[id]) {
657
- const pc2 = this.peers[id];
658
- if (pc2.signalingState !== "stable") {
659
- console.warn(
660
- `[Offer] Cannot renegotiate: peer in state "${pc2.signalingState}"`
661
- );
662
- }
688
+ return;
663
689
  }
664
690
  if (!isRenegotiation) {
665
691
  this.initiators.add(id);
@@ -680,14 +706,11 @@ var VideoSDKCore = class {
680
706
  console.debug(`[Offer] Sent to ${id}`);
681
707
  } catch (err) {
682
708
  console.error(`[Offer] Failed for ${id}:`, err);
683
- this.emitError(
684
- "OFFER_FAILED",
685
- `Failed to create offer for ${id}`,
686
- err,
687
- true
688
- );
689
709
  }
690
710
  }
711
+ shouldInitiate(peerId) {
712
+ return this.myId < peerId;
713
+ }
691
714
  // ---------------- ANSWER ----------------
692
715
  async handleOffer(sdp, id) {
693
716
  if (!this.peers[id]) {
@@ -695,28 +718,44 @@ var VideoSDKCore = class {
695
718
  }
696
719
  const pc = this.peers[id];
697
720
  try {
698
- if (pc.signalingState !== "stable" && pc.signalingState !== "have-local-offer") {
721
+ if (pc.signalingState === "have-local-offer") {
722
+ if (this.shouldInitiate(id)) {
723
+ console.warn(
724
+ `[Glare] Both sent OFFERs, we win (${this.myId} < ${id}), keeping our OFFER`
725
+ );
726
+ return;
727
+ } else {
728
+ console.warn(
729
+ `[Glare] Both sent OFFERs, they win (${id} < ${this.myId}), rolling back`
730
+ );
731
+ pc.close();
732
+ delete this.peers[id];
733
+ this.initiators.delete(id);
734
+ this.peers[id] = this.createPeer(id);
735
+ }
736
+ }
737
+ if (this.peers[id].signalingState !== "stable" && this.peers[id].signalingState !== "have-local-offer") {
699
738
  console.warn(
700
- `[Signaling] Cannot accept OFFER in state "${pc.signalingState}"`
739
+ `[Signaling] Cannot accept OFFER in state "${this.peers[id].signalingState}"`
701
740
  );
702
741
  return;
703
742
  }
704
- await pc.setRemoteDescription({
743
+ await this.peers[id].setRemoteDescription({
705
744
  type: "offer",
706
745
  sdp
707
746
  });
708
747
  const pending = this.pendingIceCandidates[id] || [];
709
748
  for (const candidate of pending) {
710
749
  try {
711
- await pc.addIceCandidate(candidate);
750
+ await this.peers[id].addIceCandidate(candidate);
712
751
  } catch (err) {
713
752
  console.warn("[ICE] Failed to add candidate:", err);
714
753
  }
715
754
  }
716
755
  delete this.pendingIceCandidates[id];
717
- const answer = await pc.createAnswer();
718
- await pc.setLocalDescription(answer);
719
- await this.flushIce(id, pc);
756
+ const answer = await this.peers[id].createAnswer();
757
+ await this.peers[id].setLocalDescription(answer);
758
+ await this.flushIce(id, this.peers[id]);
720
759
  this.send({
721
760
  type: "ANSWER",
722
761
  payload: answer.sdp,
@@ -781,7 +820,7 @@ var VideoSDKCore = class {
781
820
  this.send({
782
821
  type: "SCREEN_SHARE_START",
783
822
  sender: this.myId,
784
- room_id: this.roomId,
823
+ room_id: this.room.id,
785
824
  stream_id: this.screenStream.id.replace(/[{}]/g, "")
786
825
  });
787
826
  return this.screenStream;
@@ -827,7 +866,7 @@ var VideoSDKCore = class {
827
866
  this.send({
828
867
  type: "SCREEN_SHARE_STOP",
829
868
  sender: this.myId,
830
- room_id: this.roomId
869
+ room_id: this.room.id
831
870
  });
832
871
  }
833
872
  sendChatMessage(payload) {
@@ -835,7 +874,7 @@ var VideoSDKCore = class {
835
874
  console.warn("WS not connected");
836
875
  return;
837
876
  }
838
- if (!this.roomId) {
877
+ if (!this.room.id) {
839
878
  console.warn("No roomId set");
840
879
  return;
841
880
  }
@@ -856,7 +895,7 @@ var VideoSDKCore = class {
856
895
  message: payload.message.trim(),
857
896
  user_id: this.myId,
858
897
  sender_name: senderName,
859
- room_id: this.roomId,
898
+ room_id: this.room.id,
860
899
  target: isPrivate ? payload.target ?? null : null,
861
900
  reply_to: payload.reply_to ?? null,
862
901
  client_ts: Date.now()
@@ -877,7 +916,7 @@ var VideoSDKCore = class {
877
916
  this.localStream.getTracks().forEach((track) => track.stop());
878
917
  this.localStream = null;
879
918
  }
880
- this.roomId = null;
919
+ this.room.id = null;
881
920
  this.state.localParticipant = null;
882
921
  this.state.notify("localParticipant");
883
922
  this.state.participants.clear();
@@ -972,7 +1011,7 @@ var MeetingProvider = ({
972
1011
  startScreenShare: sdk.startScreenShare.bind(sdk),
973
1012
  stopScreenShare: sdk.stopScreenShare.bind(sdk),
974
1013
  sendMessage: sdk.sendChatMessage.bind(sdk),
975
- meetingId: sdk.getMeetingId(),
1014
+ room: sdk.getMeeting(),
976
1015
  localParticipant,
977
1016
  participants,
978
1017
  messages,
@@ -1055,7 +1094,8 @@ var useMeeting = (handlers) => {
1055
1094
  const unsubscribe = ctx.onError(handlers.onError);
1056
1095
  return unsubscribe;
1057
1096
  }, [handlers?.onError]);
1058
- return ctx;
1097
+ const { sdk: _, ...publicApi } = ctx;
1098
+ return publicApi;
1059
1099
  };
1060
1100
 
1061
1101
  // src/react/useParticipants.ts