@afosecure/meetingsdk 1.1.1 → 1.1.3

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
@@ -1,5 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React from 'react';
2
+ import * as react from 'react';
3
+ import react__default from 'react';
3
4
 
4
5
  type Events = {
5
6
  onMicToggled?: (peerId: string, enabled: boolean) => void;
@@ -107,6 +108,8 @@ declare class VideoSDKCore {
107
108
  private ws;
108
109
  private peers;
109
110
  private initiators;
111
+ private lastPong;
112
+ private intentionalDisconnect;
110
113
  private myId;
111
114
  private roomId;
112
115
  private localStream;
@@ -170,7 +173,7 @@ type MeetingContextValue = {
170
173
  };
171
174
  declare const MeetingProvider: ({ config, children, }: {
172
175
  config: MeetingConfig;
173
- children: React.ReactNode;
176
+ children: react__default.ReactNode;
174
177
  }) => react_jsx_runtime.JSX.Element;
175
178
  declare const useMeetingContext: () => MeetingContextValue;
176
179
 
@@ -200,8 +203,8 @@ declare const useMeeting: (handlers?: {
200
203
  declare const useParticipants: () => Participant[];
201
204
 
202
205
  declare const useRemoteMedia: (participantId: string) => {
203
- videoRef: (node: HTMLVideoElement | null) => void;
204
- audioRef: (node: HTMLAudioElement | null) => void;
206
+ videoRef: react.RefObject<HTMLVideoElement | null>;
207
+ audioRef: react.RefObject<HTMLAudioElement | null>;
205
208
  isCamActive: boolean;
206
209
  isMicEnabled: boolean;
207
210
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React from 'react';
2
+ import * as react from 'react';
3
+ import react__default from 'react';
3
4
 
4
5
  type Events = {
5
6
  onMicToggled?: (peerId: string, enabled: boolean) => void;
@@ -107,6 +108,8 @@ declare class VideoSDKCore {
107
108
  private ws;
108
109
  private peers;
109
110
  private initiators;
111
+ private lastPong;
112
+ private intentionalDisconnect;
110
113
  private myId;
111
114
  private roomId;
112
115
  private localStream;
@@ -170,7 +173,7 @@ type MeetingContextValue = {
170
173
  };
171
174
  declare const MeetingProvider: ({ config, children, }: {
172
175
  config: MeetingConfig;
173
- children: React.ReactNode;
176
+ children: react__default.ReactNode;
174
177
  }) => react_jsx_runtime.JSX.Element;
175
178
  declare const useMeetingContext: () => MeetingContextValue;
176
179
 
@@ -200,8 +203,8 @@ declare const useMeeting: (handlers?: {
200
203
  declare const useParticipants: () => Participant[];
201
204
 
202
205
  declare const useRemoteMedia: (participantId: string) => {
203
- videoRef: (node: HTMLVideoElement | null) => void;
204
- audioRef: (node: HTMLAudioElement | null) => void;
206
+ videoRef: react.RefObject<HTMLVideoElement | null>;
207
+ audioRef: react.RefObject<HTMLAudioElement | null>;
205
208
  isCamActive: boolean;
206
209
  isMicEnabled: boolean;
207
210
  };
package/dist/index.js CHANGED
@@ -200,6 +200,8 @@ var VideoSDKCore = class {
200
200
  this.ws = null;
201
201
  this.peers = {};
202
202
  this.initiators = /* @__PURE__ */ new Set();
203
+ this.lastPong = Date.now();
204
+ this.intentionalDisconnect = false;
203
205
  this.roomId = null;
204
206
  this.localStream = null;
205
207
  this.screenStream = null;
@@ -271,18 +273,18 @@ var VideoSDKCore = class {
271
273
  this.emitError("WS_ERROR", "WebSocket encountered an error", err, true);
272
274
  };
273
275
  this.ws.onclose = (e) => {
274
- this.emitError(
275
- "WS_CLOSED",
276
- `Connection closed (${e.code}) ${e.reason || ""}`,
277
- e,
278
- true
279
- );
280
276
  this.joinRejecter?.({
281
277
  code: "WS_CLOSED",
282
278
  message: "Connection closed before join completed",
283
279
  raw: e
284
280
  });
285
281
  this.joinRejecter = void 0;
282
+ if (this.intentionalDisconnect) {
283
+ return;
284
+ }
285
+ if (e.code === 1e3 || e.code === 1001) {
286
+ return;
287
+ }
286
288
  this.scheduleReconnect();
287
289
  };
288
290
  this.ws.onmessage = async (e) => {
@@ -406,33 +408,9 @@ var VideoSDKCore = class {
406
408
  var _a, _b, _c, _d;
407
409
  if (msg.sender === this.myId) return;
408
410
  switch (msg.type) {
409
- case "EXISTING_USERS":
410
- if (msg.presenterId) {
411
- this.state.setPresenterId(msg.presenterId);
412
- this.events.onScreenShareStarted?.(msg.presenterId, null);
413
- }
414
- for (const p of msg.participants || []) {
415
- if (!p?.id || p.id === this.myId) continue;
416
- this.state.addParticipant(p);
417
- this.events.onUserJoined?.(p);
418
- await this.createOffer(p.id);
419
- }
420
- break;
421
- case "JOINED": {
422
- this.startHeartbeat();
423
- this.joinResolver?.();
424
- this.joinResolver = void 0;
425
- this.joinRejecter = void 0;
426
- break;
427
- }
428
- case "USER_JOINED": {
429
- const p = msg.participant;
430
- if (!p?.id || p.id === this.myId) return;
431
- this.state.addParticipant(p);
432
- this.events.onUserJoined?.(p);
433
- await this.createOffer(p.id);
411
+ case "PONG":
412
+ this.lastPong = Date.now();
434
413
  break;
435
- }
436
414
  case "OFFER":
437
415
  await this.handleOffer(msg.payload, msg.sender);
438
416
  break;
@@ -482,6 +460,35 @@ var VideoSDKCore = class {
482
460
  }
483
461
  break;
484
462
  }
463
+ case "EXISTING_USERS":
464
+ if (msg.presenterId) {
465
+ this.state.setPresenterId(msg.presenterId);
466
+ this.events.onScreenShareStarted?.(msg.presenterId, null);
467
+ }
468
+ for (const p of msg.participants || []) {
469
+ if (!p?.id || p.id === this.myId) continue;
470
+ this.state.addParticipant(p);
471
+ this.events.onUserJoined?.(p);
472
+ await this.createOffer(p.id);
473
+ }
474
+ break;
475
+ case "JOINED": {
476
+ this.intentionalDisconnect = false;
477
+ this.reconnectAttempts = 0;
478
+ this.startHeartbeat();
479
+ this.joinResolver?.();
480
+ this.joinResolver = void 0;
481
+ this.joinRejecter = void 0;
482
+ break;
483
+ }
484
+ case "USER_JOINED": {
485
+ const p = msg.participant;
486
+ if (!p?.id || p.id === this.myId) return;
487
+ this.state.addParticipant(p);
488
+ this.events.onUserJoined?.(p);
489
+ await this.createOffer(p.id);
490
+ break;
491
+ }
485
492
  case "USER_LEFT":
486
493
  const peerId = msg.participant.id;
487
494
  this.closePeer(peerId);
@@ -567,27 +574,13 @@ var VideoSDKCore = class {
567
574
  {
568
575
  urls: "stun:stun.relay.metered.ca:80"
569
576
  },
570
- {
571
- urls: "turn:global.relay.metered.ca:80",
572
- username: "25aed888d2d360e9fae0e812",
573
- credential: "WPYstojO9Wf3+HsQ"
574
- },
575
577
  {
576
578
  urls: "turn:global.relay.metered.ca:80?transport=tcp",
577
579
  username: "25aed888d2d360e9fae0e812",
578
580
  credential: "WPYstojO9Wf3+HsQ"
579
- },
580
- {
581
- urls: "turn:global.relay.metered.ca:443",
582
- username: "25aed888d2d360e9fae0e812",
583
- credential: "WPYstojO9Wf3+HsQ"
584
- },
585
- {
586
- urls: "turns:global.relay.metered.ca:443?transport=tcp",
587
- username: "25aed888d2d360e9fae0e812",
588
- credential: "WPYstojO9Wf3+HsQ"
589
581
  }
590
- ]
582
+ ],
583
+ iceTransportPolicy: "relay"
591
584
  });
592
585
  pc.ontrack = (event) => {
593
586
  console.log(`Track received: ${event.track.kind}`, {
@@ -595,9 +588,14 @@ var VideoSDKCore = class {
595
588
  streamCount: event.streams.length,
596
589
  streamTrackCount: event.streams[0]?.getTracks().length
597
590
  });
598
- const incomingStream = event.streams[0];
591
+ const incomingStream = event.streams?.[0] || new MediaStream([event.track]);
599
592
  const participant = this.state.getParticipant(id);
600
593
  const isScreenStream = incomingStream.id === participant?.media?.remoteScreenStreamId;
594
+ if (event.track.kind === "video") {
595
+ event.track.onunmute = () => {
596
+ console.log("Video track unmuted for", id);
597
+ };
598
+ }
601
599
  if (isScreenStream) {
602
600
  const videoTrack = event.track.kind === "video" ? event.track : incomingStream.getVideoTracks()[0] || participant?.media?.screenTrack;
603
601
  this.state.updateParticipantMedia(id, {
@@ -629,12 +627,6 @@ var VideoSDKCore = class {
629
627
  };
630
628
  pc.oniceconnectionstatechange = () => {
631
629
  console.log(`ICE Connection State: ${pc.iceConnectionState}`);
632
- if (pc.iceConnectionState === "failed" || pc.iceConnectionState === "disconnected") {
633
- this.emitError(
634
- "ICE_FAILED",
635
- "Connection failed. Please check your network or refresh."
636
- );
637
- }
638
630
  };
639
631
  pc.onconnectionstatechange = () => {
640
632
  if (pc.connectionState === "failed") {
@@ -662,7 +654,6 @@ var VideoSDKCore = class {
662
654
  console.debug(
663
655
  `[Offer] Already initiating with ${id}, skipping duplicate`
664
656
  );
665
- return;
666
657
  }
667
658
  if (isRenegotiation && this.peers[id]) {
668
659
  const pc2 = this.peers[id];
@@ -670,7 +661,6 @@ var VideoSDKCore = class {
670
661
  console.warn(
671
662
  `[Offer] Cannot renegotiate: peer in state "${pc2.signalingState}"`
672
663
  );
673
- return;
674
664
  }
675
665
  }
676
666
  if (!isRenegotiation) {
@@ -702,15 +692,6 @@ var VideoSDKCore = class {
702
692
  }
703
693
  // ---------------- ANSWER ----------------
704
694
  async handleOffer(sdp, id) {
705
- if (this.initiators.has(id) && this.peers[id]) {
706
- const pc2 = this.peers[id];
707
- if (pc2.signalingState !== "stable") {
708
- console.warn(
709
- `[Signaling] Offer collision with ${id}. We initiated, ignoring their offer.`
710
- );
711
- return;
712
- }
713
- }
714
695
  if (!this.peers[id]) {
715
696
  this.peers[id] = this.createPeer(id);
716
697
  }
@@ -884,6 +865,7 @@ var VideoSDKCore = class {
884
865
  });
885
866
  }
886
867
  disconnect() {
868
+ this.intentionalDisconnect = true;
887
869
  this.stopScreenShare();
888
870
  Object.values(this.peers).forEach((pc) => pc.close());
889
871
  this.peers = {};
@@ -1100,18 +1082,11 @@ var useParticipants = () => {
1100
1082
  var import_react6 = require("react");
1101
1083
  var useRemoteMedia = (participantId) => {
1102
1084
  const { sdk } = useMeetingContext();
1085
+ const videoRef = (0, import_react6.useRef)(null);
1086
+ const audioRef = (0, import_react6.useRef)(null);
1103
1087
  const [participant, setParticipant] = (0, import_react6.useState)(
1104
1088
  () => sdk.state.getParticipant(participantId) || null
1105
1089
  );
1106
- const buildMediaStream = (participant2) => {
1107
- if (!participant2?.media) return null;
1108
- const stream = new MediaStream();
1109
- const videoTrack = participant2.media.stream?.getVideoTracks?.()?.[0];
1110
- const audioTrack = participant2.media.stream?.getAudioTracks?.()?.[0];
1111
- if (videoTrack) stream.addTrack(videoTrack);
1112
- if (audioTrack) stream.addTrack(audioTrack);
1113
- return stream;
1114
- };
1115
1090
  (0, import_react6.useEffect)(() => {
1116
1091
  const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {
1117
1092
  const p = sdk.state.getParticipant(participantId);
@@ -1119,34 +1094,22 @@ var useRemoteMedia = (participantId) => {
1119
1094
  });
1120
1095
  return unsub;
1121
1096
  }, [participantId, sdk]);
1122
- const videoRef = (0, import_react6.useCallback)(
1123
- (node) => {
1124
- if (!node) return;
1125
- const stream = participant?.media?.stream;
1126
- if (!stream) return;
1127
- node.srcObject = stream;
1128
- node.muted = true;
1129
- node.playsInline = true;
1130
- node.autoplay = true;
1131
- node.play().catch((err) => {
1132
- console.log("can't play video:", err);
1097
+ (0, import_react6.useEffect)(() => {
1098
+ const stream = participant?.media?.stream;
1099
+ if (!stream) return;
1100
+ if (videoRef.current) {
1101
+ videoRef.current.srcObject = stream;
1102
+ videoRef.current.muted = true;
1103
+ videoRef.current.playsInline = true;
1104
+ videoRef.current.play().catch(() => {
1133
1105
  });
1134
- },
1135
- [participant?.media?.stream]
1136
- );
1137
- const audioRef = (0, import_react6.useCallback)(
1138
- (node) => {
1139
- if (!node) return;
1140
- const stream = participant?.media?.stream;
1141
- if (!stream) return;
1142
- node.srcObject = stream;
1143
- node.muted = false;
1144
- node.play().catch((err) => {
1145
- console.log("can't play Audio:", err);
1106
+ }
1107
+ if (audioRef.current) {
1108
+ audioRef.current.srcObject = stream;
1109
+ audioRef.current.play().catch(() => {
1146
1110
  });
1147
- },
1148
- [participant?.media?.stream]
1149
- );
1111
+ }
1112
+ }, [participant?.media?.stream]);
1150
1113
  return {
1151
1114
  videoRef,
1152
1115
  audioRef,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/react/useLocalParticipant.tsx","../src/react/MeetingProvider.tsx","../src/config/ws.ts","../src/core/MeetingState.ts","../src/core/VideoCore.ts","../src/react/useMeetingStore.ts","../src/react/useMeeting.ts","../src/react/useParticipants.ts","../src/react/useRemoteMedia.ts"],"sourcesContent":["export { useLocalParticipant } from \"./react/useLocalParticipant\";\r\n\r\n// Core exports\r\nexport { VideoSDKCore } from \"./core/VideoCore\";\r\nexport { MeetingState } from \"./core/MeetingState\";\r\nexport type { Participant, ChatInput } from \"./types/meeting\";\r\n\r\n// React hooks and components\r\nexport { MeetingProvider, useMeetingContext } from \"./react/MeetingProvider\";\r\nexport { useMeeting } from \"./react/useMeeting\";\r\nexport { useParticipants } from \"./react/useParticipants\";\r\nexport { useRemoteMedia } from \"./react/useRemoteMedia\";\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useLocalParticipant = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n // Guard the initial state to ensure it matches Participant | null\r\n const [localParticipant, setLocalParticipant] = useState<Participant | null>(\r\n () => {\r\n const current = sdk.state.localParticipant;\r\n return current && current.id ? (current as Participant) : null;\r\n },\r\n );\r\n\r\n useEffect(() => {\r\n const unsubscribe = sdk.state.subscribe(\"localParticipant\", () => {\r\n const current = sdk.state.localParticipant;\r\n\r\n // Safe Type-Guard: Only update if the object has a finalized id\r\n if (current && current.id) {\r\n setLocalParticipant({ ...current } as Participant);\r\n } else {\r\n setLocalParticipant(null);\r\n }\r\n });\r\n\r\n return unsubscribe;\r\n }, [sdk]);\r\n\r\n const lastStreamRef = useRef<MediaStream | null>(null);\r\n\r\n const videoRef = useCallback(\r\n (video: HTMLVideoElement | null) => {\r\n if (!video) return;\r\n\r\n const stream = localParticipant?.media?.stream;\r\n if (!stream) return;\r\n\r\n if (lastStreamRef.current === stream) return;\r\n lastStreamRef.current = stream;\r\n\r\n video.srcObject = stream;\r\n video.autoplay = true;\r\n video.playsInline = true;\r\n\r\n video.play().catch((err) => {\r\n console.warn(`Autoplay failed for local view:`, err);\r\n });\r\n },\r\n [localParticipant?.media?.stream],\r\n );\r\n\r\n return {\r\n participant: localParticipant,\r\n videoRef,\r\n };\r\n};\r\n","import React, { createContext, useContext, useMemo, useRef } from \"react\";\r\nimport { VideoSDKCore } from \"../core/VideoCore\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n MeetingConfig,\r\n Participant,\r\n PubSubTopic,\r\n} from \"../types/meeting\";\r\nimport { useMeetingStore } from \"./useMeetingStore\";\r\n\r\ntype PubSubHandle = {\r\n messages: ChatMessage[];\r\n publish: (input: ChatInput) => void;\r\n};\r\n\r\ntype MeetingContextValue = {\r\n sdk: VideoSDKCore;\r\n\r\n join: (config: MeetingConfig) => Promise<void>;\r\n leave: () => void;\r\n\r\n toggleMic: () => void;\r\n toggleCam: () => void;\r\n\r\n startScreenShare: () => Promise<MediaStream>;\r\n stopScreenShare: () => void;\r\n\r\n sendMessage: (input: ChatInput) => void;\r\n\r\n meetingId: string | null;\r\n localParticipant: Participant | null;\r\n participants: Map<string, Participant>;\r\n messages: ChatMessage[];\r\n presenterId: string | null;\r\n usePubSub: (topic: PubSubTopic) => PubSubHandle;\r\n onError: (cb: (err: any) => void) => () => void;\r\n};\r\n\r\nconst MeetingContext = createContext<MeetingContextValue | null>(null);\r\n\r\nexport const MeetingProvider = ({\r\n config,\r\n children,\r\n}: {\r\n config: MeetingConfig;\r\n children: React.ReactNode;\r\n}) => {\r\n const sdkRef = useRef<VideoSDKCore | null>(null);\r\n const errorListeners = useRef(new Set<(err: any) => void>());\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n const sdk = sdkRef.current;\r\n const presenterId = useMeetingStore(\r\n sdk.state,\r\n \"presenter\",\r\n (s) => s.presenterId,\r\n );\r\n const participants = useMeetingStore(\r\n sdk.state,\r\n \"participants\",\r\n (s) => s.participants,\r\n );\r\n const localParticipant = useMeetingStore(\r\n sdk.state,\r\n \"localParticipant\",\r\n (s) => s.localParticipant,\r\n );\r\n const messages = useMeetingStore(sdk.state, \"chat\", (s) =>\r\n s.getChatMessages(),\r\n );\r\n\r\n const value = useMemo<MeetingContextValue>(() => {\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n return {\r\n sdk,\r\n\r\n join: (joinConfig: MeetingConfig) =>\r\n sdk.joinMeeting({\r\n ...config,\r\n ...joinConfig,\r\n }),\r\n leave: () => sdk.disconnect(),\r\n toggleMic: sdk.toggleMic.bind(sdk),\r\n toggleCam: sdk.toggleCam.bind(sdk),\r\n startScreenShare: sdk.startScreenShare.bind(sdk),\r\n stopScreenShare: sdk.stopScreenShare.bind(sdk),\r\n sendMessage: sdk.sendChatMessage.bind(sdk),\r\n\r\n meetingId: sdk.getMeetingId(),\r\n localParticipant,\r\n participants,\r\n messages,\r\n presenterId,\r\n usePubSub: (topic: PubSubTopic) => {\r\n if (topic !== \"SECURE_CHAT\") {\r\n throw new Error(`Unsupported PubSub argument: \"${topic}\"`);\r\n }\r\n return {\r\n messages: sdk.state.getChatMessages(),\r\n publish: sdk.sendChatMessage.bind(sdk),\r\n };\r\n },\r\n onError: (cb: (err: any) => void) => {\r\n errorListeners.current.add(cb);\r\n\r\n return () => {\r\n errorListeners.current.delete(cb);\r\n };\r\n },\r\n };\r\n }, [config, sdk, localParticipant, participants, messages, presenterId]);\r\n\r\n return (\r\n <MeetingContext.Provider value={value}>{children}</MeetingContext.Provider>\r\n );\r\n};\r\n\r\nexport const useMeetingContext = () => {\r\n const ctx = useContext(MeetingContext);\r\n if (!ctx)\r\n throw new Error(\"useMeetingContext must be used inside <MeetingProvider>\");\r\n return ctx;\r\n};\r\n","export const SDK_CONFIG = {\r\n wsUrl: \"wss://rust-video-server-sfyf.onrender.com/ws\",\r\n};\r\n","import {\r\n ChatMessage,\r\n Listener,\r\n Participant,\r\n ParticipantMedia,\r\n StateScope,\r\n} from \"../types/meeting\";\r\n\r\ntype LocalParticipantPatch = {\r\n id?: string;\r\n name?: string;\r\n media?: Partial<ParticipantMedia>;\r\n};\r\n\r\nexport class MeetingState {\r\n participants = new Map<string, Participant>();\r\n localParticipant: Participant | null = null;\r\n localStream: MediaStream | null = null;\r\n chatMessages = new Map<string, ChatMessage>();\r\n presenterId: string | null = null;\r\n\r\n private listeners = new Map<StateScope, Set<Listener>>();\r\n\r\n // ---- reactive system ----\r\n\r\n subscribe(scope: StateScope, fn: Listener): () => void {\r\n if (!this.listeners.has(scope)) {\r\n this.listeners.set(scope, new Set());\r\n }\r\n this.listeners.get(scope)!.add(fn);\r\n\r\n return () => {\r\n this.listeners.get(scope)?.delete(fn);\r\n };\r\n }\r\n\r\n notify(scope: StateScope) {\r\n this.listeners.get(scope)?.forEach((fn) => fn());\r\n }\r\n\r\n setPresenterId(id: string | null) {\r\n if (this.presenterId === id) return;\r\n this.presenterId = id;\r\n this.notify(\"presenter\");\r\n this.notify(\"participants\");\r\n }\r\n\r\n // ---- participants ----\r\n\r\n addParticipant(p: Participant) {\r\n if (this.participants.has(p.id)) return false;\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.set(p.id, p);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n return true;\r\n }\r\n\r\n removeParticipant(id: string) {\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.delete(id);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateParticipantMedia(\r\n id: string,\r\n patch: Partial<NonNullable<Participant[\"media\"]>>,\r\n ) {\r\n const p = this.participants.get(id);\r\n if (!p) return;\r\n\r\n const updated: Participant = {\r\n ...p,\r\n media: {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n ...p.media, // preserve existing media items if they happen to exist\r\n ...patch, // apply the incoming stream updates\r\n },\r\n };\r\n\r\n // Keep your clean immutable map update\r\n const next = new Map(this.participants);\r\n next.set(id, updated);\r\n this.participants = next;\r\n\r\n this.notify(`participant:${id}`);\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateLocalParticipant(patch: LocalParticipantPatch) {\r\n const prev = this.localParticipant;\r\n\r\n if (!prev) {\r\n this.localParticipant = {\r\n id: patch.id ?? \"\",\r\n name: patch.name ?? \"\",\r\n media: {\r\n stream: patch.media?.stream ?? null, // ◄ FIX: Capture the stream from the patch here\r\n screenStream: patch.media?.screenStream,\r\n cameraTrack: patch.media?.cameraTrack,\r\n screenTrack: patch.media?.screenTrack,\r\n audioTrack: patch.media?.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? true,\r\n camEnabled: patch.media?.camEnabled ?? true,\r\n isScreenSharing: patch.media?.isScreenSharing ?? false,\r\n },\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n return;\r\n }\r\n\r\n const prevMedia = prev.media ?? {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n };\r\n\r\n const nextMedia: ParticipantMedia = {\r\n stream: patch.media?.stream ?? prevMedia.stream,\r\n screenStream: patch.media?.screenStream ?? prevMedia.screenStream,\r\n cameraTrack: patch.media?.cameraTrack ?? prevMedia.cameraTrack,\r\n screenTrack: patch.media?.screenTrack ?? prevMedia.screenTrack,\r\n audioTrack: patch.media?.audioTrack ?? prevMedia.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? prevMedia.micEnabled,\r\n camEnabled: patch.media?.camEnabled ?? prevMedia.camEnabled,\r\n isScreenSharing:\r\n patch.media?.isScreenSharing ?? prevMedia.isScreenSharing,\r\n };\r\n\r\n this.localParticipant = {\r\n ...prev,\r\n id: patch.id ?? prev.id,\r\n name: patch.name ?? prev.name,\r\n media: nextMedia,\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n }\r\n\r\n // ---- chat ----\r\n\r\n addChatMessage(msg: ChatMessage) {\r\n this.chatMessages.set(msg.id, msg);\r\n this.notify(\"chat\");\r\n }\r\n\r\n getChatMessages() {\r\n return Array.from(this.chatMessages.values()).sort(\r\n (a, b) => a.timestamp - b.timestamp,\r\n );\r\n }\r\n\r\n clearChat() {\r\n this.chatMessages.clear();\r\n this.notify(\"chat\");\r\n }\r\n\r\n // ---- helpers ----\r\n\r\n getParticipants() {\r\n return Array.from(this.participants.values());\r\n }\r\n\r\n getParticipant(id: string) {\r\n return this.participants.get(id) ?? null;\r\n }\r\n\r\n resetRemoteState() {\r\n this.participants.clear();\r\n this.chatMessages.clear();\r\n this.presenterId = null;\r\n this.notify(\"participants\");\r\n this.notify(\"chat\");\r\n this.notify(\"presenter\");\r\n }\r\n}\r\n","import { SDK_CONFIG } from \"../config/ws\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n Events,\r\n MeetingConfig,\r\n SDKError,\r\n} from \"../types/meeting\";\r\nimport { MeetingState } from \"./MeetingState\";\r\n\r\nexport class VideoSDKCore {\r\n private ws: WebSocket | null = null;\r\n private peers: Record<string, RTCPeerConnection> = {};\r\n private initiators = new Set<string>();\r\n\r\n private myId: string;\r\n private roomId: string | null = null;\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n private screenSenders: Record<string, RTCRtpSender[]> = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private reconnectAttempts = 0;\r\n private reconnectTimer?: number;\r\n private participantName = \"\";\r\n public readonly state: MeetingState;\r\n private joinResolver?: () => void;\r\n private joinRejecter?: (e: any) => void;\r\n private emitError(\r\n code: string,\r\n message: string,\r\n raw?: any,\r\n recoverable = true,\r\n ) {\r\n const err: SDKError = {\r\n code,\r\n message,\r\n raw,\r\n roomId: this.roomId,\r\n userId: this.myId,\r\n recoverable,\r\n };\r\n\r\n this.events.onError?.(err);\r\n\r\n this.joinRejecter?.(err);\r\n this.joinRejecter = undefined;\r\n\r\n console.error(\"[MeetingSDK Error]\", err);\r\n }\r\n\r\n constructor(\r\n private events: Events = {},\r\n private url: string = SDK_CONFIG.wsUrl,\r\n ) {\r\n this.state = new MeetingState();\r\n this.events = events;\r\n this.url = url;\r\n\r\n this.myId = localStorage.getItem(\"vsdk_id\") || crypto.randomUUID();\r\n\r\n localStorage.setItem(\"vsdk_id\", this.myId);\r\n }\r\n\r\n // ---------------- STREAM ----------------\r\n async initLocal(video: HTMLVideoElement, name: string) {\r\n this.participantName = name;\r\n\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n video.srcObject = this.localStream;\r\n\r\n // Fix: Supply mandatory fields to satisfy the Participant type constraint\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n }\r\n\r\n // ---------------- CONNECT ----------------\r\n async connect(roomId: string, name: string) {\r\n this.roomId = roomId;\r\n\r\n this.reset();\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n this.joinResolver = resolve;\r\n this.joinRejecter = reject;\r\n this.ws = new WebSocket(this.url);\r\n\r\n this.ws.onopen = () => {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: roomId,\r\n user_id: this.myId,\r\n sender_name: name,\r\n });\r\n };\r\n\r\n this.ws.onerror = (err) => {\r\n this.emitError(\"WS_ERROR\", \"WebSocket encountered an error\", err, true);\r\n };\r\n\r\n this.ws.onclose = (e) => {\r\n this.emitError(\r\n \"WS_CLOSED\",\r\n `Connection closed (${e.code}) ${e.reason || \"\"}`,\r\n e,\r\n true,\r\n );\r\n\r\n // If join never resolved, fail the promise\r\n this.joinRejecter?.({\r\n code: \"WS_CLOSED\",\r\n message: \"Connection closed before join completed\",\r\n raw: e,\r\n });\r\n\r\n this.joinRejecter = undefined;\r\n\r\n this.scheduleReconnect();\r\n };\r\n\r\n this.ws.onmessage = async (e) => {\r\n await this.handle(JSON.parse(e.data));\r\n };\r\n });\r\n }\r\n\r\n async joinMeeting(config: MeetingConfig) {\r\n const { roomId, name, audioMuted = false, videoMuted = false } = config;\r\n\r\n if (!roomId || !name) {\r\n throw new Error(\"roomId and name are required to join meeting\");\r\n }\r\n\r\n this.participantName = name;\r\n\r\n // Reuse existing stream if initLocal already configured it\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n this.localStream.getAudioTracks().forEach((t) => {\r\n t.enabled = !audioMuted;\r\n });\r\n this.localStream.getVideoTracks().forEach((t) => {\r\n t.enabled = !videoMuted;\r\n });\r\n\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: !audioMuted,\r\n camEnabled: !videoMuted,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n\r\n await this.connect(roomId, name);\r\n }\r\n\r\n /** Expose the roomId without making it fully public */\r\n getMeetingId(): string | null {\r\n return this.roomId;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.micEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getAudioTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n micEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"audio\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n toggleCam() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.camEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getVideoTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n camEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"video\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n private scheduleReconnect() {\r\n if (!this.roomId) return;\r\n\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\r\n\r\n clearTimeout(this.reconnectTimer);\r\n\r\n this.reconnectTimer = window.setTimeout(async () => {\r\n try {\r\n await this.connect(this.roomId!, this.participantName);\r\n\r\n this.reconnectAttempts = 0;\r\n } catch {\r\n this.reconnectAttempts++;\r\n this.scheduleReconnect();\r\n }\r\n }, delay);\r\n }\r\n\r\n private startHeartbeat() {\r\n this.stopHeartbeat();\r\n\r\n this.pingInterval = setInterval(() => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\r\n\r\n this.send({\r\n type: \"PING\",\r\n client_ts: Date.now(),\r\n });\r\n }, 20000); // every 20s\r\n }\r\n private stopHeartbeat() {\r\n if (this.pingInterval) {\r\n clearInterval(this.pingInterval);\r\n this.pingInterval = null;\r\n }\r\n }\r\n\r\n // ---------------- RESET ----------------\r\n private reset() {\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n\r\n this.peers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n // ---------------- HANDLE SIGNALS ----------------\r\n private async handle(msg: any) {\r\n if (msg.sender === this.myId) return;\r\n\r\n switch (msg.type) {\r\n case \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\r\n\r\n // Trigger your event so the UI knows to render the stage\r\n this.events.onScreenShareStarted?.(msg.presenterId, null!);\r\n }\r\n\r\n for (const p of msg.participants || []) {\r\n if (!p?.id || p.id === this.myId) continue;\r\n this.state.addParticipant(p);\r\n this.events.onUserJoined?.(p);\r\n await this.createOffer(p.id);\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n this.startHeartbeat();\r\n this.joinResolver?.();\r\n this.joinResolver = undefined;\r\n this.joinRejecter = undefined;\r\n break;\r\n }\r\n case \"USER_JOINED\": {\r\n const p = msg.participant;\r\n\r\n if (!p?.id || p.id === this.myId) return;\r\n\r\n this.state.addParticipant(p);\r\n\r\n this.events.onUserJoined?.(p);\r\n await this.createOffer(p.id);\r\n\r\n break;\r\n }\r\n\r\n case \"OFFER\":\r\n await this.handleOffer(msg.payload, msg.sender);\r\n break;\r\n\r\n case \"ANSWER\": {\r\n const pc = this.peers[msg.sender];\r\n\r\n if (!pc) return;\r\n\r\n if (pc.signalingState !== \"have-local-offer\") {\r\n console.warn(\r\n `[Signaling] Unexpected ANSWER in state \"${pc.signalingState}\", ignoring`,\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n await this.flushIce(msg.sender, pc);\r\n } catch (err) {\r\n console.error(\"[Signaling] Failed to apply answer:\", err);\r\n this.emitError(\r\n \"ANSWER_FAILED\",\r\n `Failed to apply answer from ${msg.sender}`,\r\n err,\r\n true,\r\n );\r\n }\r\n break;\r\n }\r\n\r\n case \"ICE\": {\r\n const candidate = JSON.parse(msg.payload);\r\n\r\n let pc = this.peers[msg.sender];\r\n\r\n if (!pc) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n if (!pc.remoteDescription) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"ICE error:\", err);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"USER_LEFT\":\r\n const peerId = msg.participant.id;\r\n this.closePeer(peerId);\r\n\r\n this.state.removeParticipant(peerId);\r\n\r\n this.events.onUserLeft?.(peerId);\r\n\r\n break;\r\n\r\n case \"MEDIA_STATE_CHANGE\": {\r\n const peerId = msg.peerId;\r\n const { kind, enabled } = msg;\r\n\r\n // 1. Sync the app state layer for UI rendering components\r\n if (kind === \"audio\") {\r\n this.state.updateParticipantMedia(peerId, { micEnabled: enabled });\r\n this.events.onMicToggled?.(peerId, enabled);\r\n } else if (kind === \"video\") {\r\n this.state.updateParticipantMedia(peerId, { camEnabled: enabled });\r\n this.events.onCamToggled?.(peerId, enabled);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"CHAT_MESSAGE\": {\r\n const newMsg = msg.data;\r\n\r\n if (newMsg.sender_id === this.myId) break; // already added optimistically\r\n this.state.addChatMessage({\r\n id: newMsg.id,\r\n text: newMsg.message,\r\n sender_id: newMsg.sender_id,\r\n sender_name: newMsg.sender_name,\r\n timestamp: new Date(newMsg.timestamp).getTime(),\r\n target: newMsg.target,\r\n });\r\n\r\n this.events.onChatMessage?.(msg);\r\n break;\r\n }\r\n case \"SCREEN_SHARE_START\": {\r\n const peerId = msg.peerId;\r\n\r\n this.state.updateParticipantMedia(peerId, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: msg.stream_id,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(peerId);\r\n }\r\n\r\n // Fix: Use screenStream instead of the regular camera stream\r\n const screenStream =\r\n this.state.getParticipant(peerId)?.media?.screenStream;\r\n\r\n this.events.onScreenShareStarted?.(peerId, screenStream || null!);\r\n break;\r\n }\r\n\r\n case \"SCREEN_SHARE_STOP\": {\r\n const peerId = msg.peerId;\r\n this.state.updateParticipantMedia(peerId, { isScreenSharing: false });\r\n if (this.state.presenterId === peerId) {\r\n this.state.setPresenterId(null);\r\n }\r\n this.events.onScreenShareStopped?.(peerId);\r\n break;\r\n }\r\n case \"ERROR\": {\r\n const fatal = msg?.fatal === true;\r\n\r\n this.emitError(\r\n \"WS_ERROR\",\r\n msg?.message || \"Unknown error\",\r\n msg,\r\n !fatal,\r\n );\r\n\r\n if (fatal) {\r\n this.disconnect();\r\n }\r\n\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // ---------------- PEER ----------------\r\n private createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n console.log(\r\n \"Adding tracks\",\r\n this.localStream.getTracks().map((t) => ({\r\n kind: t.kind,\r\n enabled: t.enabled,\r\n state: t.readyState,\r\n })),\r\n );\r\n\r\n const pc = new RTCPeerConnection({\r\n iceServers: [\r\n {\r\n urls: \"stun:stun.relay.metered.ca:80\",\r\n },\r\n {\r\n urls: \"turn:global.relay.metered.ca:80\",\r\n username: \"25aed888d2d360e9fae0e812\",\r\n credential: \"WPYstojO9Wf3+HsQ\",\r\n },\r\n {\r\n urls: \"turn:global.relay.metered.ca:80?transport=tcp\",\r\n username: \"25aed888d2d360e9fae0e812\",\r\n credential: \"WPYstojO9Wf3+HsQ\",\r\n },\r\n {\r\n urls: \"turn:global.relay.metered.ca:443\",\r\n username: \"25aed888d2d360e9fae0e812\",\r\n credential: \"WPYstojO9Wf3+HsQ\",\r\n },\r\n {\r\n urls: \"turns:global.relay.metered.ca:443?transport=tcp\",\r\n username: \"25aed888d2d360e9fae0e812\",\r\n credential: \"WPYstojO9Wf3+HsQ\",\r\n },\r\n ],\r\n });\r\n\r\n pc.ontrack = (event) => {\r\n console.log(`Track received: ${event.track.kind}`, {\r\n trackId: event.track.id,\r\n streamCount: event.streams.length,\r\n streamTrackCount: event.streams[0]?.getTracks().length,\r\n });\r\n const incomingStream = event.streams[0];\r\n const participant = this.state.getParticipant(id);\r\n\r\n const isScreenStream =\r\n incomingStream.id === participant?.media?.remoteScreenStreamId;\r\n\r\n if (isScreenStream) {\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0] ||\r\n participant?.media?.screenTrack;\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\r\n\r\n isScreenSharing: true,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(id);\r\n }\r\n\r\n this.events.onScreenShareStarted?.(id, incomingStream);\r\n } else {\r\n this.state.updateParticipantMedia(id, {\r\n stream: incomingStream,\r\n cameraTrack: incomingStream.getVideoTracks()[0],\r\n audioTrack: incomingStream.getAudioTracks()[0],\r\n });\r\n this.events.onTrack?.(incomingStream, id);\r\n }\r\n };\r\n\r\n pc.onicecandidate = (e) => {\r\n if (!e.candidate) return;\r\n this.send({\r\n type: \"ICE\",\r\n payload: JSON.stringify(e.candidate),\r\n sender: this.myId,\r\n target: id,\r\n });\r\n };\r\n\r\n pc.oniceconnectionstatechange = () => {\r\n console.log(`ICE Connection State: ${pc.iceConnectionState}`);\r\n if (\r\n pc.iceConnectionState === \"failed\" ||\r\n pc.iceConnectionState === \"disconnected\"\r\n ) {\r\n // Trigger a UI notification to the user that the connection is unstable\r\n this.emitError(\r\n \"ICE_FAILED\",\r\n \"Connection failed. Please check your network or refresh.\",\r\n );\r\n }\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch {}\r\n }\r\n };\r\n\r\n this.localStream.getTracks().forEach((track) => {\r\n pc.addTrack(track, this.localStream!);\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n this.screenSenders[id] = [];\r\n this.screenStream.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[id].push(sender);\r\n });\r\n }\r\n\r\n return pc;\r\n }\r\n\r\n // ---------------- OFFER ----------------\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n if (!isRenegotiation && this.initiators.has(id)) {\r\n console.debug(\r\n `[Offer] Already initiating with ${id}, skipping duplicate`,\r\n );\r\n return;\r\n }\r\n\r\n if (isRenegotiation && this.peers[id]) {\r\n const pc = this.peers[id];\r\n if (pc.signalingState !== \"stable\") {\r\n console.warn(\r\n `[Offer] Cannot renegotiate: peer in state \"${pc.signalingState}\"`,\r\n );\r\n return;\r\n }\r\n }\r\n\r\n if (!isRenegotiation) {\r\n this.initiators.add(id);\r\n }\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n this.send({\r\n type: \"OFFER\",\r\n payload: offer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n\r\n console.debug(`[Offer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Offer] Failed for ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_FAILED\",\r\n `Failed to create offer for ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n // ---------------- ANSWER ----------------\r\n private async handleOffer(sdp: string, id: string) {\r\n if (this.initiators.has(id) && this.peers[id]) {\r\n const pc = this.peers[id];\r\n if (pc.signalingState !== \"stable\") {\r\n console.warn(\r\n `[Signaling] Offer collision with ${id}. We initiated, ignoring their offer.`,\r\n );\r\n return; // We initiated, so ignore their offer\r\n }\r\n }\r\n\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n // ✅ Only set remote description if we're not already in negotiation\r\n if (\r\n pc.signalingState !== \"stable\" &&\r\n pc.signalingState !== \"have-local-offer\"\r\n ) {\r\n console.warn(\r\n `[Signaling] Cannot accept OFFER in state \"${pc.signalingState}\"`,\r\n );\r\n return;\r\n }\r\n\r\n await pc.setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"[ICE] Failed to add candidate:\", err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await pc.createAnswer();\r\n\r\n await pc.setLocalDescription(answer);\r\n await this.flushIce(id, pc);\r\n\r\n this.send({\r\n type: \"ANSWER\",\r\n payload: answer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n\r\n console.debug(`[Answer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Signaling] Failed to handle OFFER from ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_HANDLING_FAILED\",\r\n `Failed to handle offer from ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n // ---------------- CLEANUP ----------------\r\n private closePeer(id: string) {\r\n const pc = this.peers[id];\r\n\r\n if (!pc) return;\r\n\r\n pc.ontrack = null;\r\n pc.onicecandidate = null;\r\n pc.onconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n async startScreenShare() {\r\n try {\r\n if (this.state.presenterId && this.state.presenterId !== this.myId) {\r\n throw new Error(\"Another user is already sharing their screen.\");\r\n }\r\n if (!navigator.mediaDevices?.getDisplayMedia) {\r\n throw new Error(\"Screen sharing not supported on this device\");\r\n }\r\n\r\n this.screenStream = await navigator.mediaDevices.getDisplayMedia({\r\n video: true,\r\n // audio: true,\r\n });\r\n\r\n this.isScreenSharing = true;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: true,\r\n screenStream: this.screenStream,\r\n screenTrack: this.screenStream.getVideoTracks()[0],\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n // Handle the user clicking browser's built-in \"Stop Sharing\" button\r\n this.screenStream.getVideoTracks()[0].onended = () => {\r\n this.stopScreenShare();\r\n };\r\n\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n this.screenSenders[peerId] = [];\r\n this.screenStream!.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[peerId].push(sender);\r\n });\r\n\r\n // Renegotiate peer connection descriptors to notify remote side of new track footprint\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n return this.screenStream;\r\n } catch (err: any) {\r\n this.emitError(\r\n \"SCREEN_SHARE_FAILED\",\r\n err?.message || \"Failed to start screen sharing\",\r\n err,\r\n true,\r\n );\r\n\r\n this.isScreenSharing = false;\r\n this.screenStream = null;\r\n throw err;\r\n }\r\n }\r\n\r\n stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Remove tracks cleanly from WebRTC channel pathways across your peers\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n const senders = this.screenSenders[peerId] || [];\r\n senders.forEach((sender) => {\r\n try {\r\n pc.removeTrack(sender);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n });\r\n delete this.screenSenders[peerId];\r\n\r\n // Renegotiate layout expectations to scale down stream bounds\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.screenStream = null;\r\n this.isScreenSharing = false;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: false,\r\n screenStream: null,\r\n screenTrack: undefined,\r\n },\r\n });\r\n\r\n if (this.state.presenterId === this.myId) {\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_STOP\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n });\r\n }\r\n\r\n sendChatMessage(payload: ChatInput) {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n console.warn(\"WS not connected\");\r\n return;\r\n }\r\n\r\n if (!this.roomId) {\r\n console.warn(\"No roomId set\");\r\n return;\r\n }\r\n\r\n const isPrivate = !!payload?.target;\r\n\r\n const senderName = this.state.localParticipant?.name || \"Anonymous\";\r\n\r\n const msg: ChatMessage = {\r\n id: crypto.randomUUID(),\r\n sender_id: this.myId,\r\n sender_name: senderName,\r\n text: payload.message.trim(),\r\n timestamp: Date.now(),\r\n reply_to: payload.reply_to ?? null,\r\n target: payload.target ?? null,\r\n };\r\n\r\n // optimistic UI update\r\n this.state.addChatMessage(msg);\r\n\r\n // send protocol payload (clean + consistent)\r\n this.send({\r\n type: \"CHAT_MESSAGE\",\r\n message: payload.message.trim(),\r\n user_id: this.myId,\r\n sender_name: senderName,\r\n room_id: this.roomId,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n disconnect() {\r\n this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n\r\n if (this.localStream) {\r\n this.localStream.getTracks().forEach((track) => track.stop());\r\n this.localStream = null;\r\n }\r\n\r\n this.roomId = null;\r\n\r\n // Clear and notify\r\n this.state.localParticipant = null;\r\n this.state.notify(\"localParticipant\");\r\n\r\n this.state.participants.clear();\r\n this.state.notify(\"participants\");\r\n\r\n this.state.clearChat();\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n private async flushIce(id: string, pc: RTCPeerConnection) {\r\n const pending = this.pendingIceCandidates[id];\r\n if (!pending?.length) return;\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (e) {\r\n console.warn(\"ICE flush error\", e);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n }\r\n\r\n private send(msg: any) {\r\n this.ws?.send(JSON.stringify(msg));\r\n }\r\n}\r\n","import { useEffect, useState } from \"react\";\r\nimport { StateScope } from \"../types/meeting\";\r\nimport { MeetingState } from \"../core/MeetingState\";\r\n\r\nexport function useMeetingStore<T>(\r\n stateManager: MeetingState,\r\n scope: StateScope,\r\n selector: (state: MeetingState) => T,\r\n): T {\r\n const [state, setState] = useState(() => selector(stateManager));\r\n\r\n useEffect(() => {\r\n // Update local react state whenever the SDK notifies this scope\r\n const unsubscribe = stateManager.subscribe(scope, () => {\r\n setState(selector(stateManager));\r\n });\r\n\r\n return unsubscribe;\r\n }, [stateManager, scope, selector]);\r\n\r\n return state;\r\n}\r\n","import { useEffect } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\n\r\nexport const useMeeting = (handlers?: { onError?: (err: any) => void }) => {\r\n const ctx = useMeetingContext();\r\n\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n\r\n const unsubscribe = ctx.onError(handlers.onError);\r\n return unsubscribe;\r\n }, [handlers?.onError]);\r\n\r\n return ctx;\r\n};\r\n","import { useEffect, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useParticipants = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n const [participants, setParticipants] = useState<Participant[]>(() =>\r\n sdk.state.getParticipants(),\r\n );\r\n\r\n useEffect(() => {\r\n const update = () => {\r\n setParticipants(sdk.state.getParticipants());\r\n };\r\n\r\n update();\r\n\r\n const unsub = sdk.state.subscribe(\"participants\", update);\r\n\r\n return unsub;\r\n }, [sdk]);\r\n\r\n return participants;\r\n};\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useRemoteMedia = (participantId: string) => {\r\n const { sdk } = useMeetingContext();\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\r\n );\r\n\r\n const buildMediaStream = (participant: Participant | null) => {\r\n if (!participant?.media) return null;\r\n\r\n const stream = new MediaStream();\r\n\r\n const videoTrack = participant.media.stream?.getVideoTracks?.()?.[0];\r\n const audioTrack = participant.media.stream?.getAudioTracks?.()?.[0];\r\n\r\n if (videoTrack) stream.addTrack(videoTrack);\r\n if (audioTrack) stream.addTrack(audioTrack);\r\n\r\n return stream;\r\n };\r\n\r\n useEffect(() => {\r\n const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {\r\n const p = sdk.state.getParticipant(participantId);\r\n setParticipant(p ? { ...p } : null);\r\n });\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n // Video Callback Ref\r\n const videoRef = useCallback(\r\n (node: HTMLVideoElement | null) => {\r\n if (!node) return;\r\n\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n // IMPORTANT: always force rebind (no conditions)\r\n node.srcObject = stream;\r\n\r\n node.muted = true;\r\n node.playsInline = true;\r\n node.autoplay = true;\r\n\r\n node.play().catch((err) => {\r\n // ignore autoplay restrictions\r\n console.log(\"can't play video:\", err);\r\n });\r\n },\r\n [participant?.media?.stream],\r\n );\r\n\r\n // Audio Callback Ref\r\n const audioRef = useCallback(\r\n (node: HTMLAudioElement | null) => {\r\n if (!node) return;\r\n\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n node.srcObject = stream;\r\n\r\n node.muted = false;\r\n\r\n node.play().catch((err) => {\r\n console.log(\"can't play Audio:\", err);\r\n });\r\n },\r\n [participant?.media?.stream],\r\n );\r\n\r\n return {\r\n videoRef,\r\n audioRef,\r\n isCamActive: !!participant?.media?.camEnabled,\r\n isMicEnabled: !!participant?.media?.micEnabled,\r\n };\r\n};\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAAyD;;;ACAzD,IAAAC,gBAAkE;;;ACA3D,IAAM,aAAa;AAAA,EACxB,OAAO;AACT;;;ACYO,IAAM,eAAN,MAAmB;AAAA,EAAnB;AACL,wBAAe,oBAAI,IAAyB;AAC5C,4BAAuC;AACvC,uBAAkC;AAClC,wBAAe,oBAAI,IAAyB;AAC5C,uBAA6B;AAE7B,SAAQ,YAAY,oBAAI,IAA+B;AAAA;AAAA;AAAA,EAIvD,UAAU,OAAmB,IAA0B;AACrD,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IACrC;AACA,SAAK,UAAU,IAAI,KAAK,EAAG,IAAI,EAAE;AAEjC,WAAO,MAAM;AACX,WAAK,UAAU,IAAI,KAAK,GAAG,OAAO,EAAE;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,OAAO,OAAmB;AACxB,SAAK,UAAU,IAAI,KAAK,GAAG,QAAQ,CAAC,OAAO,GAAG,CAAC;AAAA,EACjD;AAAA,EAEA,eAAe,IAAmB;AAChC,QAAI,KAAK,gBAAgB,GAAI;AAC7B,SAAK,cAAc;AACnB,SAAK,OAAO,WAAW;AACvB,SAAK,OAAO,cAAc;AAAA,EAC5B;AAAA;AAAA,EAIA,eAAe,GAAgB;AAC7B,QAAI,KAAK,aAAa,IAAI,EAAE,EAAE,EAAG,QAAO;AAExC,UAAM,OAAO,IAAI,IAAI,KAAK,YAAY;AACtC,SAAK,IAAI,EAAE,IAAI,CAAC;AAChB,SAAK,eAAe;AAEpB,SAAK,OAAO,cAAc;AAC1B,WAAO;AAAA,EACT;AAAA,EAEA,kBAAkB,IAAY;AAE5B,UAAM,OAAO,IAAI,IAAI,KAAK,YAAY;AACtC,SAAK,OAAO,EAAE;AACd,SAAK,eAAe;AAEpB,SAAK,OAAO,cAAc;AAAA,EAC5B;AAAA,EAEA,uBACE,IACA,OACA;AACA,UAAM,IAAI,KAAK,aAAa,IAAI,EAAE;AAClC,QAAI,CAAC,EAAG;AAER,UAAM,UAAuB;AAAA,MAC3B,GAAG;AAAA,MACH,OAAO;AAAA,QACL,QAAQ;AAAA,QACR,cAAc;AAAA,QACd,aAAa;AAAA,QACb,aAAa;AAAA,QACb,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,iBAAiB;AAAA,QACjB,GAAG,EAAE;AAAA;AAAA,QACL,GAAG;AAAA;AAAA,MACL;AAAA,IACF;AAGA,UAAM,OAAO,IAAI,IAAI,KAAK,YAAY;AACtC,SAAK,IAAI,IAAI,OAAO;AACpB,SAAK,eAAe;AAEpB,SAAK,OAAO,eAAe,EAAE,EAAE;AAC/B,SAAK,OAAO,cAAc;AAAA,EAC5B;AAAA,EAEA,uBAAuB,OAA8B;AACnD,UAAM,OAAO,KAAK;AAElB,QAAI,CAAC,MAAM;AACT,WAAK,mBAAmB;AAAA,QACtB,IAAI,MAAM,MAAM;AAAA,QAChB,MAAM,MAAM,QAAQ;AAAA,QACpB,OAAO;AAAA,UACL,QAAQ,MAAM,OAAO,UAAU;AAAA;AAAA,UAC/B,cAAc,MAAM,OAAO;AAAA,UAC3B,aAAa,MAAM,OAAO;AAAA,UAC1B,aAAa,MAAM,OAAO;AAAA,UAC1B,YAAY,MAAM,OAAO;AAAA,UACzB,YAAY,MAAM,OAAO,cAAc;AAAA,UACvC,YAAY,MAAM,OAAO,cAAc;AAAA,UACvC,iBAAiB,MAAM,OAAO,mBAAmB;AAAA,QACnD;AAAA,MACF;AAEA,WAAK,OAAO,kBAAkB;AAC9B;AAAA,IACF;AAEA,UAAM,YAAY,KAAK,SAAS;AAAA,MAC9B,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,aAAa;AAAA,MACb,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,iBAAiB;AAAA,IACnB;AAEA,UAAM,YAA8B;AAAA,MAClC,QAAQ,MAAM,OAAO,UAAU,UAAU;AAAA,MACzC,cAAc,MAAM,OAAO,gBAAgB,UAAU;AAAA,MACrD,aAAa,MAAM,OAAO,eAAe,UAAU;AAAA,MACnD,aAAa,MAAM,OAAO,eAAe,UAAU;AAAA,MACnD,YAAY,MAAM,OAAO,cAAc,UAAU;AAAA,MACjD,YAAY,MAAM,OAAO,cAAc,UAAU;AAAA,MACjD,YAAY,MAAM,OAAO,cAAc,UAAU;AAAA,MACjD,iBACE,MAAM,OAAO,mBAAmB,UAAU;AAAA,IAC9C;AAEA,SAAK,mBAAmB;AAAA,MACtB,GAAG;AAAA,MACH,IAAI,MAAM,MAAM,KAAK;AAAA,MACrB,MAAM,MAAM,QAAQ,KAAK;AAAA,MACzB,OAAO;AAAA,IACT;AAEA,SAAK,OAAO,kBAAkB;AAAA,EAChC;AAAA;AAAA,EAIA,eAAe,KAAkB;AAC/B,SAAK,aAAa,IAAI,IAAI,IAAI,GAAG;AACjC,SAAK,OAAO,MAAM;AAAA,EACpB;AAAA,EAEA,kBAAkB;AAChB,WAAO,MAAM,KAAK,KAAK,aAAa,OAAO,CAAC,EAAE;AAAA,MAC5C,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,YAAY;AACV,SAAK,aAAa,MAAM;AACxB,SAAK,OAAO,MAAM;AAAA,EACpB;AAAA;AAAA,EAIA,kBAAkB;AAChB,WAAO,MAAM,KAAK,KAAK,aAAa,OAAO,CAAC;AAAA,EAC9C;AAAA,EAEA,eAAe,IAAY;AACzB,WAAO,KAAK,aAAa,IAAI,EAAE,KAAK;AAAA,EACtC;AAAA,EAEA,mBAAmB;AACjB,SAAK,aAAa,MAAM;AACxB,SAAK,aAAa,MAAM;AACxB,SAAK,cAAc;AACnB,SAAK,OAAO,cAAc;AAC1B,SAAK,OAAO,MAAM;AAClB,SAAK,OAAO,WAAW;AAAA,EACzB;AACF;;;ACvLO,IAAM,eAAN,MAAmB;AAAA,EA2CxB,YACU,SAAiB,CAAC,GAClB,MAAc,WAAW,OACjC;AAFQ;AACA;AA5CV,SAAQ,KAAuB;AAC/B,SAAQ,QAA2C,CAAC;AACpD,SAAQ,aAAa,oBAAI,IAAY;AAGrC,SAAQ,SAAwB;AAChC,SAAQ,cAAkC;AAC1C,SAAQ,eAAmC;AAC3C,SAAQ,kBAAkB;AAC1B,SAAQ,gBAAgD,CAAC;AAEzD,SAAQ,eAAoB;AAC5B,SAAQ,uBAA8D,CAAC;AACvE,SAAQ,oBAAoB;AAE5B,SAAQ,kBAAkB;AA+BxB,SAAK,QAAQ,IAAI,aAAa;AAC9B,SAAK,SAAS;AACd,SAAK,MAAM;AAEX,SAAK,OAAO,aAAa,QAAQ,SAAS,KAAK,OAAO,WAAW;AAEjE,iBAAa,QAAQ,WAAW,KAAK,IAAI;AAAA,EAC3C;AAAA,EAlCQ,UACN,MACA,SACA,KACA,cAAc,MACd;AACA,UAAM,MAAgB;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,KAAK;AAAA,MACb,QAAQ,KAAK;AAAA,MACb;AAAA,IACF;AAEA,SAAK,OAAO,UAAU,GAAG;AAEzB,SAAK,eAAe,GAAG;AACvB,SAAK,eAAe;AAEpB,YAAQ,MAAM,sBAAsB,GAAG;AAAA,EACzC;AAAA;AAAA,EAgBA,MAAM,UAAU,OAAyB,MAAc;AACrD,SAAK,kBAAkB;AAEvB,QAAI,CAAC,KAAK,aAAa;AACrB,WAAK,cAAc,MAAM,UAAU,aAAa,aAAa;AAAA,QAC3D,OAAO;AAAA,QACP,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAEA,UAAM,YAAY,KAAK;AAGvB,SAAK,MAAM,uBAAuB;AAAA,MAChC,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX,OAAO;AAAA,QACL,QAAQ,KAAK;AAAA,QACb,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,iBAAiB;AAAA,MACnB;AAAA,IACF,CAAC;AAED,SAAK,MAAM,cAAc,KAAK;AAAA,EAChC;AAAA;AAAA,EAGA,MAAM,QAAQ,QAAgB,MAAc;AAC1C,SAAK,SAAS;AAEd,SAAK,MAAM;AAEX,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,WAAK,eAAe;AACpB,WAAK,eAAe;AACpB,WAAK,KAAK,IAAI,UAAU,KAAK,GAAG;AAEhC,WAAK,GAAG,SAAS,MAAM;AACrB,aAAK,KAAK;AAAA,UACR,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS,KAAK;AAAA,UACd,aAAa;AAAA,QACf,CAAC;AAAA,MACH;AAEA,WAAK,GAAG,UAAU,CAAC,QAAQ;AACzB,aAAK,UAAU,YAAY,kCAAkC,KAAK,IAAI;AAAA,MACxE;AAEA,WAAK,GAAG,UAAU,CAAC,MAAM;AACvB,aAAK;AAAA,UACH;AAAA,UACA,sBAAsB,EAAE,IAAI,KAAK,EAAE,UAAU,EAAE;AAAA,UAC/C;AAAA,UACA;AAAA,QACF;AAGA,aAAK,eAAe;AAAA,UAClB,MAAM;AAAA,UACN,SAAS;AAAA,UACT,KAAK;AAAA,QACP,CAAC;AAED,aAAK,eAAe;AAEpB,aAAK,kBAAkB;AAAA,MACzB;AAEA,WAAK,GAAG,YAAY,OAAO,MAAM;AAC/B,cAAM,KAAK,OAAO,KAAK,MAAM,EAAE,IAAI,CAAC;AAAA,MACtC;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YAAY,QAAuB;AACvC,UAAM,EAAE,QAAQ,MAAM,aAAa,OAAO,aAAa,MAAM,IAAI;AAEjE,QAAI,CAAC,UAAU,CAAC,MAAM;AACpB,YAAM,IAAI,MAAM,8CAA8C;AAAA,IAChE;AAEA,SAAK,kBAAkB;AAGvB,QAAI,CAAC,KAAK,aAAa;AACrB,WAAK,cAAc,MAAM,UAAU,aAAa,aAAa;AAAA,QAC3D,OAAO;AAAA,QACP,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAEA,SAAK,YAAY,eAAe,EAAE,QAAQ,CAAC,MAAM;AAC/C,QAAE,UAAU,CAAC;AAAA,IACf,CAAC;AACD,SAAK,YAAY,eAAe,EAAE,QAAQ,CAAC,MAAM;AAC/C,QAAE,UAAU,CAAC;AAAA,IACf,CAAC;AAED,SAAK,MAAM,uBAAuB;AAAA,MAChC,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX,OAAO;AAAA,QACL,QAAQ,KAAK;AAAA,QACb,YAAY,CAAC;AAAA,QACb,YAAY,CAAC;AAAA,QACb,iBAAiB;AAAA,MACnB;AAAA,IACF,CAAC;AAED,SAAK,MAAM,cAAc,KAAK;AAE9B,UAAM,KAAK,QAAQ,QAAQ,IAAI;AAAA,EACjC;AAAA;AAAA,EAGA,eAA8B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAAY;AACV,UAAM,aAAa,KAAK,MAAM,kBAAkB;AAChD,QAAI,CAAC,WAAY;AAGjB,UAAM,cAAc,CAAC,WAAW;AAGhC,SAAK,aACD,eAAe,EAChB,QAAQ,CAAC,MAAO,EAAE,UAAU,WAAY;AAG3C,SAAK,MAAM,uBAAuB;AAAA,MAChC,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX,OAAO;AAAA,QACL,GAAG;AAAA,QACH,YAAY;AAAA,MACd;AAAA,IACF,CAAC;AAGD,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA,EAEA,YAAY;AACV,UAAM,aAAa,KAAK,MAAM,kBAAkB;AAChD,QAAI,CAAC,WAAY;AAGjB,UAAM,cAAc,CAAC,WAAW;AAGhC,SAAK,aACD,eAAe,EAChB,QAAQ,CAAC,MAAO,EAAE,UAAU,WAAY;AAG3C,SAAK,MAAM,uBAAuB;AAAA,MAChC,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX,OAAO;AAAA,QACL,GAAG;AAAA,QACH,YAAY;AAAA,MACd;AAAA,IACF,CAAC;AAGD,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA,EAEQ,oBAAoB;AAC1B,QAAI,CAAC,KAAK,OAAQ;AAElB,UAAM,QAAQ,KAAK,IAAI,MAAO,KAAK,IAAI,GAAG,KAAK,iBAAiB,GAAG,GAAK;AAExE,iBAAa,KAAK,cAAc;AAEhC,SAAK,iBAAiB,OAAO,WAAW,YAAY;AAClD,UAAI;AACF,cAAM,KAAK,QAAQ,KAAK,QAAS,KAAK,eAAe;AAErD,aAAK,oBAAoB;AAAA,MAC3B,QAAQ;AACN,aAAK;AACL,aAAK,kBAAkB;AAAA,MACzB;AAAA,IACF,GAAG,KAAK;AAAA,EACV;AAAA,EAEQ,iBAAiB;AACvB,SAAK,cAAc;AAEnB,SAAK,eAAe,YAAY,MAAM;AACpC,UAAI,CAAC,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,KAAM;AAEvD,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,WAAW,KAAK,IAAI;AAAA,MACtB,CAAC;AAAA,IACH,GAAG,GAAK;AAAA,EACV;AAAA,EACQ,gBAAgB;AACtB,QAAI,KAAK,cAAc;AACrB,oBAAc,KAAK,YAAY;AAC/B,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA;AAAA,EAGQ,QAAQ;AACd,WAAO,OAAO,KAAK,KAAK,EAAE,QAAQ,CAAC,OAAO,GAAG,MAAM,CAAC;AAEpD,SAAK,QAAQ,CAAC;AACd,SAAK,WAAW,MAAM;AACtB,SAAK,uBAAuB,CAAC;AAE7B,SAAK,MAAM,iBAAiB;AAAA,EAC9B;AAAA;AAAA,EAGA,MAAc,OAAO,KAAU;AA3SjC;AA4SI,QAAI,IAAI,WAAW,KAAK,KAAM;AAE9B,YAAQ,IAAI,MAAM;AAAA,MAChB,KAAK;AACH,YAAI,IAAI,aAAa;AACnB,eAAK,MAAM,eAAe,IAAI,WAAW;AAGzC,eAAK,OAAO,uBAAuB,IAAI,aAAa,IAAK;AAAA,QAC3D;AAEA,mBAAW,KAAK,IAAI,gBAAgB,CAAC,GAAG;AACtC,cAAI,CAAC,GAAG,MAAM,EAAE,OAAO,KAAK,KAAM;AAClC,eAAK,MAAM,eAAe,CAAC;AAC3B,eAAK,OAAO,eAAe,CAAC;AAC5B,gBAAM,KAAK,YAAY,EAAE,EAAE;AAAA,QAC7B;AACA;AAAA,MAEF,KAAK,UAAU;AACb,aAAK,eAAe;AACpB,aAAK,eAAe;AACpB,aAAK,eAAe;AACpB,aAAK,eAAe;AACpB;AAAA,MACF;AAAA,MACA,KAAK,eAAe;AAClB,cAAM,IAAI,IAAI;AAEd,YAAI,CAAC,GAAG,MAAM,EAAE,OAAO,KAAK,KAAM;AAElC,aAAK,MAAM,eAAe,CAAC;AAE3B,aAAK,OAAO,eAAe,CAAC;AAC5B,cAAM,KAAK,YAAY,EAAE,EAAE;AAE3B;AAAA,MACF;AAAA,MAEA,KAAK;AACH,cAAM,KAAK,YAAY,IAAI,SAAS,IAAI,MAAM;AAC9C;AAAA,MAEF,KAAK,UAAU;AACb,cAAM,KAAK,KAAK,MAAM,IAAI,MAAM;AAEhC,YAAI,CAAC,GAAI;AAET,YAAI,GAAG,mBAAmB,oBAAoB;AAC5C,kBAAQ;AAAA,YACN,2CAA2C,GAAG,cAAc;AAAA,UAC9D;AACA;AAAA,QACF;AAEA,YAAI;AACF,gBAAM,GAAG,qBAAqB;AAAA,YAC5B,MAAM;AAAA,YACN,KAAK,IAAI;AAAA,UACX,CAAC;AAED,gBAAM,KAAK,SAAS,IAAI,QAAQ,EAAE;AAAA,QACpC,SAAS,KAAK;AACZ,kBAAQ,MAAM,uCAAuC,GAAG;AACxD,eAAK;AAAA,YACH;AAAA,YACA,+BAA+B,IAAI,MAAM;AAAA,YACzC;AAAA,YACA;AAAA,UACF;AAAA,QACF;AACA;AAAA,MACF;AAAA,MAEA,KAAK,OAAO;AACV,cAAM,YAAY,KAAK,MAAM,IAAI,OAAO;AAExC,YAAI,KAAK,KAAK,MAAM,IAAI,MAAM;AAE9B,YAAI,CAAC,IAAI;AACP,qBAAK,sBAAL,KAA0B,IAAI,YAA9B,SAA0C,CAAC;AAC3C,eAAK,qBAAqB,IAAI,MAAM,EAAE,KAAK,SAAS;AACpD;AAAA,QACF;AAEA,YAAI,CAAC,GAAG,mBAAmB;AACzB,qBAAK,sBAAL,KAA0B,IAAI,YAA9B,SAA0C,CAAC;AAC3C,eAAK,qBAAqB,IAAI,MAAM,EAAE,KAAK,SAAS;AACpD;AAAA,QACF;AAEA,YAAI;AACF,gBAAM,GAAG,gBAAgB,SAAS;AAAA,QACpC,SAAS,KAAK;AACZ,kBAAQ,KAAK,cAAc,GAAG;AAAA,QAChC;AAEA;AAAA,MACF;AAAA,MAEA,KAAK;AACH,cAAM,SAAS,IAAI,YAAY;AAC/B,aAAK,UAAU,MAAM;AAErB,aAAK,MAAM,kBAAkB,MAAM;AAEnC,aAAK,OAAO,aAAa,MAAM;AAE/B;AAAA,MAEF,KAAK,sBAAsB;AACzB,cAAMC,UAAS,IAAI;AACnB,cAAM,EAAE,MAAM,QAAQ,IAAI;AAG1B,YAAI,SAAS,SAAS;AACpB,eAAK,MAAM,uBAAuBA,SAAQ,EAAE,YAAY,QAAQ,CAAC;AACjE,eAAK,OAAO,eAAeA,SAAQ,OAAO;AAAA,QAC5C,WAAW,SAAS,SAAS;AAC3B,eAAK,MAAM,uBAAuBA,SAAQ,EAAE,YAAY,QAAQ,CAAC;AACjE,eAAK,OAAO,eAAeA,SAAQ,OAAO;AAAA,QAC5C;AAEA;AAAA,MACF;AAAA,MAEA,KAAK,gBAAgB;AACnB,cAAM,SAAS,IAAI;AAEnB,YAAI,OAAO,cAAc,KAAK,KAAM;AACpC,aAAK,MAAM,eAAe;AAAA,UACxB,IAAI,OAAO;AAAA,UACX,MAAM,OAAO;AAAA,UACb,WAAW,OAAO;AAAA,UAClB,aAAa,OAAO;AAAA,UACpB,WAAW,IAAI,KAAK,OAAO,SAAS,EAAE,QAAQ;AAAA,UAC9C,QAAQ,OAAO;AAAA,QACjB,CAAC;AAED,aAAK,OAAO,gBAAgB,GAAG;AAC/B;AAAA,MACF;AAAA,MACA,KAAK,sBAAsB;AACzB,cAAMA,UAAS,IAAI;AAEnB,aAAK,MAAM,uBAAuBA,SAAQ;AAAA,UACxC,iBAAiB;AAAA,UACjB,sBAAsB,IAAI;AAAA,QAC5B,CAAC;AAED,YAAI,CAAC,KAAK,MAAM,aAAa;AAC3B,eAAK,MAAM,eAAeA,OAAM;AAAA,QAClC;AAGA,cAAM,eACJ,KAAK,MAAM,eAAeA,OAAM,GAAG,OAAO;AAE5C,aAAK,OAAO,uBAAuBA,SAAQ,gBAAgB,IAAK;AAChE;AAAA,MACF;AAAA,MAEA,KAAK,qBAAqB;AACxB,cAAMA,UAAS,IAAI;AACnB,aAAK,MAAM,uBAAuBA,SAAQ,EAAE,iBAAiB,MAAM,CAAC;AACpE,YAAI,KAAK,MAAM,gBAAgBA,SAAQ;AACrC,eAAK,MAAM,eAAe,IAAI;AAAA,QAChC;AACA,aAAK,OAAO,uBAAuBA,OAAM;AACzC;AAAA,MACF;AAAA,MACA,KAAK,SAAS;AACZ,cAAM,QAAQ,KAAK,UAAU;AAE7B,aAAK;AAAA,UACH;AAAA,UACA,KAAK,WAAW;AAAA,UAChB;AAAA,UACA,CAAC;AAAA,QACH;AAEA,YAAI,OAAO;AACT,eAAK,WAAW;AAAA,QAClB;AAEA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGQ,WAAW,IAAY;AAC7B,QAAI,CAAC,KAAK,YAAa,OAAM,IAAI,MAAM,iBAAiB;AACxD,YAAQ;AAAA,MACN;AAAA,MACA,KAAK,YAAY,UAAU,EAAE,IAAI,CAAC,OAAO;AAAA,QACvC,MAAM,EAAE;AAAA,QACR,SAAS,EAAE;AAAA,QACX,OAAO,EAAE;AAAA,MACX,EAAE;AAAA,IACJ;AAEA,UAAM,KAAK,IAAI,kBAAkB;AAAA,MAC/B,YAAY;AAAA,QACV;AAAA,UACE,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,UAAU;AAAA,UACV,YAAY;AAAA,QACd;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,UAAU;AAAA,UACV,YAAY;AAAA,QACd;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,UAAU;AAAA,UACV,YAAY;AAAA,QACd;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,UAAU;AAAA,UACV,YAAY;AAAA,QACd;AAAA,MACF;AAAA,IACF,CAAC;AAED,OAAG,UAAU,CAAC,UAAU;AACtB,cAAQ,IAAI,mBAAmB,MAAM,MAAM,IAAI,IAAI;AAAA,QACjD,SAAS,MAAM,MAAM;AAAA,QACrB,aAAa,MAAM,QAAQ;AAAA,QAC3B,kBAAkB,MAAM,QAAQ,CAAC,GAAG,UAAU,EAAE;AAAA,MAClD,CAAC;AACD,YAAM,iBAAiB,MAAM,QAAQ,CAAC;AACtC,YAAM,cAAc,KAAK,MAAM,eAAe,EAAE;AAEhD,YAAM,iBACJ,eAAe,OAAO,aAAa,OAAO;AAE5C,UAAI,gBAAgB;AAClB,cAAM,aACJ,MAAM,MAAM,SAAS,UACjB,MAAM,QACN,eAAe,eAAe,EAAE,CAAC,KACjC,aAAa,OAAO;AAE1B,aAAK,MAAM,uBAAuB,IAAI;AAAA,UACpC,cAAc;AAAA,UACd,aAAa;AAAA,UAEb,iBAAiB;AAAA,QACnB,CAAC;AAED,YAAI,CAAC,KAAK,MAAM,aAAa;AAC3B,eAAK,MAAM,eAAe,EAAE;AAAA,QAC9B;AAEA,aAAK,OAAO,uBAAuB,IAAI,cAAc;AAAA,MACvD,OAAO;AACL,aAAK,MAAM,uBAAuB,IAAI;AAAA,UACpC,QAAQ;AAAA,UACR,aAAa,eAAe,eAAe,EAAE,CAAC;AAAA,UAC9C,YAAY,eAAe,eAAe,EAAE,CAAC;AAAA,QAC/C,CAAC;AACD,aAAK,OAAO,UAAU,gBAAgB,EAAE;AAAA,MAC1C;AAAA,IACF;AAEA,OAAG,iBAAiB,CAAC,MAAM;AACzB,UAAI,CAAC,EAAE,UAAW;AAClB,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,SAAS,KAAK,UAAU,EAAE,SAAS;AAAA,QACnC,QAAQ,KAAK;AAAA,QACb,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAEA,OAAG,6BAA6B,MAAM;AACpC,cAAQ,IAAI,yBAAyB,GAAG,kBAAkB,EAAE;AAC5D,UACE,GAAG,uBAAuB,YAC1B,GAAG,uBAAuB,gBAC1B;AAEA,aAAK;AAAA,UACH;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,OAAG,0BAA0B,MAAM;AACjC,UAAI,GAAG,oBAAoB,UAAU;AACnC,YAAI;AACF,aAAG,WAAW;AAAA,QAChB,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF;AAEA,SAAK,YAAY,UAAU,EAAE,QAAQ,CAAC,UAAU;AAC9C,SAAG,SAAS,OAAO,KAAK,WAAY;AAAA,IACtC,CAAC;AAED,QAAI,KAAK,mBAAmB,KAAK,cAAc;AAC7C,WAAK,cAAc,EAAE,IAAI,CAAC;AAC1B,WAAK,aAAa,UAAU,EAAE,QAAQ,CAAC,UAAU;AAC/C,cAAM,SAAS,GAAG,SAAS,OAAO,KAAK,YAAa;AACpD,aAAK,cAAc,EAAE,EAAE,KAAK,MAAM;AAAA,MACpC,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,YAAY,IAAY,kBAAkB,OAAO;AAC7D,QAAI,CAAC,mBAAmB,KAAK,WAAW,IAAI,EAAE,GAAG;AAC/C,cAAQ;AAAA,QACN,mCAAmC,EAAE;AAAA,MACvC;AACA;AAAA,IACF;AAEA,QAAI,mBAAmB,KAAK,MAAM,EAAE,GAAG;AACrC,YAAMC,MAAK,KAAK,MAAM,EAAE;AACxB,UAAIA,IAAG,mBAAmB,UAAU;AAClC,gBAAQ;AAAA,UACN,8CAA8CA,IAAG,cAAc;AAAA,QACjE;AACA;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,iBAAiB;AACpB,WAAK,WAAW,IAAI,EAAE;AAAA,IACxB;AACA,QAAI,CAAC,KAAK,MAAM,EAAE,GAAG;AACnB,WAAK,MAAM,EAAE,IAAI,KAAK,WAAW,EAAE;AAAA,IACrC;AAEA,UAAM,KAAK,KAAK,MAAM,EAAE;AAExB,QAAI;AACF,YAAM,QAAQ,MAAM,GAAG,YAAY;AACnC,YAAM,GAAG,oBAAoB,KAAK;AAElC,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,SAAS,MAAM;AAAA,QACf,QAAQ,KAAK;AAAA,QACb,QAAQ;AAAA,MACV,CAAC;AAED,cAAQ,MAAM,mBAAmB,EAAE,EAAE;AAAA,IACvC,SAAS,KAAK;AACZ,cAAQ,MAAM,sBAAsB,EAAE,KAAK,GAAG;AAC9C,WAAK;AAAA,QACH;AAAA,QACA,8BAA8B,EAAE;AAAA,QAChC;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,YAAY,KAAa,IAAY;AACjD,QAAI,KAAK,WAAW,IAAI,EAAE,KAAK,KAAK,MAAM,EAAE,GAAG;AAC7C,YAAMA,MAAK,KAAK,MAAM,EAAE;AACxB,UAAIA,IAAG,mBAAmB,UAAU;AAClC,gBAAQ;AAAA,UACN,oCAAoC,EAAE;AAAA,QACxC;AACA;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,MAAM,EAAE,GAAG;AACnB,WAAK,MAAM,EAAE,IAAI,KAAK,WAAW,EAAE;AAAA,IACrC;AAEA,UAAM,KAAK,KAAK,MAAM,EAAE;AAExB,QAAI;AAEF,UACE,GAAG,mBAAmB,YACtB,GAAG,mBAAmB,oBACtB;AACA,gBAAQ;AAAA,UACN,6CAA6C,GAAG,cAAc;AAAA,QAChE;AACA;AAAA,MACF;AAEA,YAAM,GAAG,qBAAqB;AAAA,QAC5B,MAAM;AAAA,QACN;AAAA,MACF,CAAC;AAED,YAAM,UAAU,KAAK,qBAAqB,EAAE,KAAK,CAAC;AAElD,iBAAW,aAAa,SAAS;AAC/B,YAAI;AACF,gBAAM,GAAG,gBAAgB,SAAS;AAAA,QACpC,SAAS,KAAK;AACZ,kBAAQ,KAAK,kCAAkC,GAAG;AAAA,QACpD;AAAA,MACF;AAEA,aAAO,KAAK,qBAAqB,EAAE;AAEnC,YAAM,SAAS,MAAM,GAAG,aAAa;AAErC,YAAM,GAAG,oBAAoB,MAAM;AACnC,YAAM,KAAK,SAAS,IAAI,EAAE;AAE1B,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,QAChB,QAAQ,KAAK;AAAA,QACb,QAAQ;AAAA,MACV,CAAC;AAED,cAAQ,MAAM,oBAAoB,EAAE,EAAE;AAAA,IACxC,SAAS,KAAK;AACZ,cAAQ,MAAM,2CAA2C,EAAE,KAAK,GAAG;AACnE,WAAK;AAAA,QACH;AAAA,QACA,+BAA+B,EAAE;AAAA,QACjC;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGQ,UAAU,IAAY;AAC5B,UAAM,KAAK,KAAK,MAAM,EAAE;AAExB,QAAI,CAAC,GAAI;AAET,OAAG,UAAU;AACb,OAAG,iBAAiB;AACpB,OAAG,0BAA0B;AAE7B,OAAG,MAAM;AAET,WAAO,KAAK,MAAM,EAAE;AAEpB,SAAK,WAAW,OAAO,EAAE;AAEzB,SAAK,MAAM,kBAAkB,EAAE;AAAA,EACjC;AAAA,EAEA,MAAM,mBAAmB;AACvB,QAAI;AACF,UAAI,KAAK,MAAM,eAAe,KAAK,MAAM,gBAAgB,KAAK,MAAM;AAClE,cAAM,IAAI,MAAM,+CAA+C;AAAA,MACjE;AACA,UAAI,CAAC,UAAU,cAAc,iBAAiB;AAC5C,cAAM,IAAI,MAAM,6CAA6C;AAAA,MAC/D;AAEA,WAAK,eAAe,MAAM,UAAU,aAAa,gBAAgB;AAAA,QAC/D,OAAO;AAAA;AAAA,MAET,CAAC;AAED,WAAK,kBAAkB;AAEvB,WAAK,MAAM,uBAAuB;AAAA,QAChC,OAAO;AAAA,UACL,iBAAiB;AAAA,UACjB,cAAc,KAAK;AAAA,UACnB,aAAa,KAAK,aAAa,eAAe,EAAE,CAAC;AAAA,QACnD;AAAA,MACF,CAAC;AAED,WAAK,MAAM,eAAe,KAAK,IAAI;AAEnC,WAAK,aAAa,eAAe,EAAE,CAAC,EAAE,UAAU,MAAM;AACpD,aAAK,gBAAgB;AAAA,MACvB;AAEA,aAAO,QAAQ,KAAK,KAAK,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,MAAM;AACnD,aAAK,cAAc,MAAM,IAAI,CAAC;AAC9B,aAAK,aAAc,UAAU,EAAE,QAAQ,CAAC,UAAU;AAChD,gBAAM,SAAS,GAAG,SAAS,OAAO,KAAK,YAAa;AACpD,eAAK,cAAc,MAAM,EAAE,KAAK,MAAM;AAAA,QACxC,CAAC;AAGD,aAAK,YAAY,QAAQ,IAAI;AAAA,MAC/B,CAAC;AAED,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,QAAQ,KAAK;AAAA,QACb,SAAS,KAAK;AAAA,QACd,WAAW,KAAK,aAAa,GAAG,QAAQ,SAAS,EAAE;AAAA,MACrD,CAAC;AAED,aAAO,KAAK;AAAA,IACd,SAAS,KAAU;AACjB,WAAK;AAAA,QACH;AAAA,QACA,KAAK,WAAW;AAAA,QAChB;AAAA,QACA;AAAA,MACF;AAEA,WAAK,kBAAkB;AACvB,WAAK,eAAe;AACpB,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,kBAAkB;AAChB,QAAI,CAAC,KAAK,aAAc;AAExB,SAAK,aAAa,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;AAGrD,WAAO,QAAQ,KAAK,KAAK,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,MAAM;AACnD,YAAM,UAAU,KAAK,cAAc,MAAM,KAAK,CAAC;AAC/C,cAAQ,QAAQ,CAAC,WAAW;AAC1B,YAAI;AACF,aAAG,YAAY,MAAM;AAAA,QACvB,SAAS,KAAK;AACZ,kBAAQ,KAAK,GAAG;AAAA,QAClB;AAAA,MACF,CAAC;AACD,aAAO,KAAK,cAAc,MAAM;AAGhC,WAAK,YAAY,QAAQ,IAAI;AAAA,IAC/B,CAAC;AAED,SAAK,eAAe;AACpB,SAAK,kBAAkB;AAEvB,SAAK,MAAM,uBAAuB;AAAA,MAChC,OAAO;AAAA,QACL,iBAAiB;AAAA,QACjB,cAAc;AAAA,QACd,aAAa;AAAA,MACf;AAAA,IACF,CAAC;AAED,QAAI,KAAK,MAAM,gBAAgB,KAAK,MAAM;AACxC,WAAK,MAAM,eAAe,IAAI;AAAA,IAChC;AAEA,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EAEA,gBAAgB,SAAoB;AAClC,QAAI,CAAC,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,MAAM;AACrD,cAAQ,KAAK,kBAAkB;AAC/B;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,QAAQ;AAChB,cAAQ,KAAK,eAAe;AAC5B;AAAA,IACF;AAEA,UAAM,YAAY,CAAC,CAAC,SAAS;AAE7B,UAAM,aAAa,KAAK,MAAM,kBAAkB,QAAQ;AAExD,UAAM,MAAmB;AAAA,MACvB,IAAI,OAAO,WAAW;AAAA,MACtB,WAAW,KAAK;AAAA,MAChB,aAAa;AAAA,MACb,MAAM,QAAQ,QAAQ,KAAK;AAAA,MAC3B,WAAW,KAAK,IAAI;AAAA,MACpB,UAAU,QAAQ,YAAY;AAAA,MAC9B,QAAQ,QAAQ,UAAU;AAAA,IAC5B;AAGA,SAAK,MAAM,eAAe,GAAG;AAG7B,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,SAAS,QAAQ,QAAQ,KAAK;AAAA,MAC9B,SAAS,KAAK;AAAA,MACd,aAAa;AAAA,MACb,SAAS,KAAK;AAAA,MACd,QAAQ,YAAa,QAAQ,UAAU,OAAQ;AAAA,MAC/C,UAAU,QAAQ,YAAY;AAAA,MAE9B,WAAW,KAAK,IAAI;AAAA,IACtB,CAAC;AAAA,EACH;AAAA,EAEA,aAAa;AACX,SAAK,gBAAgB;AAErB,WAAO,OAAO,KAAK,KAAK,EAAE,QAAQ,CAAC,OAAO,GAAG,MAAM,CAAC;AACpD,SAAK,QAAQ,CAAC;AACd,SAAK,WAAW,MAAM;AAEtB,SAAK,cAAc;AAEnB,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM;AACd,WAAK,KAAK;AAAA,IACZ;AAEA,QAAI,KAAK,aAAa;AACpB,WAAK,YAAY,UAAU,EAAE,QAAQ,CAAC,UAAU,MAAM,KAAK,CAAC;AAC5D,WAAK,cAAc;AAAA,IACrB;AAEA,SAAK,SAAS;AAGd,SAAK,MAAM,mBAAmB;AAC9B,SAAK,MAAM,OAAO,kBAAkB;AAEpC,SAAK,MAAM,aAAa,MAAM;AAC9B,SAAK,MAAM,OAAO,cAAc;AAEhC,SAAK,MAAM,UAAU;AACrB,SAAK,MAAM,eAAe,IAAI;AAAA,EAChC;AAAA,EAEA,MAAc,SAAS,IAAY,IAAuB;AACxD,UAAM,UAAU,KAAK,qBAAqB,EAAE;AAC5C,QAAI,CAAC,SAAS,OAAQ;AAEtB,eAAW,aAAa,SAAS;AAC/B,UAAI;AACF,cAAM,GAAG,gBAAgB,SAAS;AAAA,MACpC,SAAS,GAAG;AACV,gBAAQ,KAAK,mBAAmB,CAAC;AAAA,MACnC;AAAA,IACF;AAEA,WAAO,KAAK,qBAAqB,EAAE;AAAA,EACrC;AAAA,EAEQ,KAAK,KAAU;AACrB,SAAK,IAAI,KAAK,KAAK,UAAU,GAAG,CAAC;AAAA,EACnC;AACF;;;AC77BA,mBAAoC;AAI7B,SAAS,gBACd,cACA,OACA,UACG;AACH,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,MAAM,SAAS,YAAY,CAAC;AAE/D,8BAAU,MAAM;AAEd,UAAM,cAAc,aAAa,UAAU,OAAO,MAAM;AACtD,eAAS,SAAS,YAAY,CAAC;AAAA,IACjC,CAAC;AAED,WAAO;AAAA,EACT,GAAG,CAAC,cAAc,OAAO,QAAQ,CAAC;AAElC,SAAO;AACT;;;AJ0GI;AAxFJ,IAAM,qBAAiB,6BAA0C,IAAI;AAE9D,IAAM,kBAAkB,CAAC;AAAA,EAC9B;AAAA,EACA;AACF,MAGM;AACJ,QAAM,aAAS,sBAA4B,IAAI;AAC/C,QAAM,qBAAiB,sBAAO,oBAAI,IAAwB,CAAC;AAC3D,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,UAAU,IAAI,aAAa;AAAA,MAChC,SAAS,CAAC,QAAQ;AAChB,uBAAe,QAAQ,QAAQ,CAAC,OAAO,GAAG,GAAG,CAAC;AAAA,MAChD;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,MAAM,OAAO;AACnB,QAAM,cAAc;AAAA,IAClB,IAAI;AAAA,IACJ;AAAA,IACA,CAAC,MAAM,EAAE;AAAA,EACX;AACA,QAAM,eAAe;AAAA,IACnB,IAAI;AAAA,IACJ;AAAA,IACA,CAAC,MAAM,EAAE;AAAA,EACX;AACA,QAAM,mBAAmB;AAAA,IACvB,IAAI;AAAA,IACJ;AAAA,IACA,CAAC,MAAM,EAAE;AAAA,EACX;AACA,QAAM,WAAW;AAAA,IAAgB,IAAI;AAAA,IAAO;AAAA,IAAQ,CAAC,MACnD,EAAE,gBAAgB;AAAA,EACpB;AAEA,QAAM,YAAQ,uBAA6B,MAAM;AAC/C,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO,UAAU,IAAI,aAAa;AAAA,QAChC,SAAS,CAAC,QAAQ;AAChB,yBAAe,QAAQ,QAAQ,CAAC,OAAO,GAAG,GAAG,CAAC;AAAA,QAChD;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL;AAAA,MAEA,MAAM,CAAC,eACL,IAAI,YAAY;AAAA,QACd,GAAG;AAAA,QACH,GAAG;AAAA,MACL,CAAC;AAAA,MACH,OAAO,MAAM,IAAI,WAAW;AAAA,MAC5B,WAAW,IAAI,UAAU,KAAK,GAAG;AAAA,MACjC,WAAW,IAAI,UAAU,KAAK,GAAG;AAAA,MACjC,kBAAkB,IAAI,iBAAiB,KAAK,GAAG;AAAA,MAC/C,iBAAiB,IAAI,gBAAgB,KAAK,GAAG;AAAA,MAC7C,aAAa,IAAI,gBAAgB,KAAK,GAAG;AAAA,MAEzC,WAAW,IAAI,aAAa;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,CAAC,UAAuB;AACjC,YAAI,UAAU,eAAe;AAC3B,gBAAM,IAAI,MAAM,iCAAiC,KAAK,GAAG;AAAA,QAC3D;AACA,eAAO;AAAA,UACL,UAAU,IAAI,MAAM,gBAAgB;AAAA,UACpC,SAAS,IAAI,gBAAgB,KAAK,GAAG;AAAA,QACvC;AAAA,MACF;AAAA,MACA,SAAS,CAAC,OAA2B;AACnC,uBAAe,QAAQ,IAAI,EAAE;AAE7B,eAAO,MAAM;AACX,yBAAe,QAAQ,OAAO,EAAE;AAAA,QAClC;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,QAAQ,KAAK,kBAAkB,cAAc,UAAU,WAAW,CAAC;AAEvE,SACE,4CAAC,eAAe,UAAf,EAAwB,OAAe,UAAS;AAErD;AAEO,IAAM,oBAAoB,MAAM;AACrC,QAAM,UAAM,0BAAW,cAAc;AACrC,MAAI,CAAC;AACH,UAAM,IAAI,MAAM,yDAAyD;AAC3E,SAAO;AACT;;;ADpIO,IAAM,sBAAsB,MAAM;AACvC,QAAM,EAAE,IAAI,IAAI,kBAAkB;AAGlC,QAAM,CAAC,kBAAkB,mBAAmB,QAAI;AAAA,IAC9C,MAAM;AACJ,YAAM,UAAU,IAAI,MAAM;AAC1B,aAAO,WAAW,QAAQ,KAAM,UAA0B;AAAA,IAC5D;AAAA,EACF;AAEA,+BAAU,MAAM;AACd,UAAM,cAAc,IAAI,MAAM,UAAU,oBAAoB,MAAM;AAChE,YAAM,UAAU,IAAI,MAAM;AAG1B,UAAI,WAAW,QAAQ,IAAI;AACzB,4BAAoB,EAAE,GAAG,QAAQ,CAAgB;AAAA,MACnD,OAAO;AACL,4BAAoB,IAAI;AAAA,MAC1B;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,oBAAgB,sBAA2B,IAAI;AAErD,QAAM,eAAW;AAAA,IACf,CAAC,UAAmC;AAClC,UAAI,CAAC,MAAO;AAEZ,YAAM,SAAS,kBAAkB,OAAO;AACxC,UAAI,CAAC,OAAQ;AAEb,UAAI,cAAc,YAAY,OAAQ;AACtC,oBAAc,UAAU;AAExB,YAAM,YAAY;AAClB,YAAM,WAAW;AACjB,YAAM,cAAc;AAEpB,YAAM,KAAK,EAAE,MAAM,CAAC,QAAQ;AAC1B,gBAAQ,KAAK,mCAAmC,GAAG;AAAA,MACrD,CAAC;AAAA,IACH;AAAA,IACA,CAAC,kBAAkB,OAAO,MAAM;AAAA,EAClC;AAEA,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,EACF;AACF;;;AMzDA,IAAAC,gBAA0B;AAGnB,IAAM,aAAa,CAAC,aAAgD;AACzE,QAAM,MAAM,kBAAkB;AAE9B,+BAAU,MAAM;AACd,QAAI,CAAC,UAAU,QAAS;AAExB,UAAM,cAAc,IAAI,QAAQ,SAAS,OAAO;AAChD,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,OAAO,CAAC;AAEtB,SAAO;AACT;;;ACdA,IAAAC,gBAAoC;AAI7B,IAAM,kBAAkB,MAAM;AACnC,QAAM,EAAE,IAAI,IAAI,kBAAkB;AAElC,QAAM,CAAC,cAAc,eAAe,QAAI;AAAA,IAAwB,MAC9D,IAAI,MAAM,gBAAgB;AAAA,EAC5B;AAEA,+BAAU,MAAM;AACd,UAAM,SAAS,MAAM;AACnB,sBAAgB,IAAI,MAAM,gBAAgB,CAAC;AAAA,IAC7C;AAEA,WAAO;AAEP,UAAM,QAAQ,IAAI,MAAM,UAAU,gBAAgB,MAAM;AAExD,WAAO;AAAA,EACT,GAAG,CAAC,GAAG,CAAC;AAER,SAAO;AACT;;;ACxBA,IAAAC,gBAAyD;AAIlD,IAAM,iBAAiB,CAAC,kBAA0B;AACvD,QAAM,EAAE,IAAI,IAAI,kBAAkB;AAClC,QAAM,CAAC,aAAa,cAAc,QAAI;AAAA,IACpC,MAAM,IAAI,MAAM,eAAe,aAAa,KAAK;AAAA,EACnD;AAEA,QAAM,mBAAmB,CAACC,iBAAoC;AAC5D,QAAI,CAACA,cAAa,MAAO,QAAO;AAEhC,UAAM,SAAS,IAAI,YAAY;AAE/B,UAAM,aAAaA,aAAY,MAAM,QAAQ,iBAAiB,IAAI,CAAC;AACnE,UAAM,aAAaA,aAAY,MAAM,QAAQ,iBAAiB,IAAI,CAAC;AAEnE,QAAI,WAAY,QAAO,SAAS,UAAU;AAC1C,QAAI,WAAY,QAAO,SAAS,UAAU;AAE1C,WAAO;AAAA,EACT;AAEA,+BAAU,MAAM;AACd,UAAM,QAAQ,IAAI,MAAM,UAAU,eAAe,aAAa,IAAI,MAAM;AACtE,YAAM,IAAI,IAAI,MAAM,eAAe,aAAa;AAChD,qBAAe,IAAI,EAAE,GAAG,EAAE,IAAI,IAAI;AAAA,IACpC,CAAC;AACD,WAAO;AAAA,EACT,GAAG,CAAC,eAAe,GAAG,CAAC;AAGvB,QAAM,eAAW;AAAA,IACf,CAAC,SAAkC;AACjC,UAAI,CAAC,KAAM;AAEX,YAAM,SAAS,aAAa,OAAO;AACnC,UAAI,CAAC,OAAQ;AAGb,WAAK,YAAY;AAEjB,WAAK,QAAQ;AACb,WAAK,cAAc;AACnB,WAAK,WAAW;AAEhB,WAAK,KAAK,EAAE,MAAM,CAAC,QAAQ;AAEzB,gBAAQ,IAAI,qBAAqB,GAAG;AAAA,MACtC,CAAC;AAAA,IACH;AAAA,IACA,CAAC,aAAa,OAAO,MAAM;AAAA,EAC7B;AAGA,QAAM,eAAW;AAAA,IACf,CAAC,SAAkC;AACjC,UAAI,CAAC,KAAM;AAEX,YAAM,SAAS,aAAa,OAAO;AACnC,UAAI,CAAC,OAAQ;AAEb,WAAK,YAAY;AAEjB,WAAK,QAAQ;AAEb,WAAK,KAAK,EAAE,MAAM,CAAC,QAAQ;AACzB,gBAAQ,IAAI,qBAAqB,GAAG;AAAA,MACtC,CAAC;AAAA,IACH;AAAA,IACA,CAAC,aAAa,OAAO,MAAM;AAAA,EAC7B;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa,CAAC,CAAC,aAAa,OAAO;AAAA,IACnC,cAAc,CAAC,CAAC,aAAa,OAAO;AAAA,EACtC;AACF;","names":["import_react","import_react","peerId","pc","import_react","import_react","import_react","participant"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/react/useLocalParticipant.tsx","../src/react/MeetingProvider.tsx","../src/config/ws.ts","../src/core/MeetingState.ts","../src/core/VideoCore.ts","../src/react/useMeetingStore.ts","../src/react/useMeeting.ts","../src/react/useParticipants.ts","../src/react/useRemoteMedia.ts"],"sourcesContent":["export { useLocalParticipant } from \"./react/useLocalParticipant\";\r\n\r\n// Core exports\r\nexport { VideoSDKCore } from \"./core/VideoCore\";\r\nexport { MeetingState } from \"./core/MeetingState\";\r\nexport type { Participant, ChatInput } from \"./types/meeting\";\r\n\r\n// React hooks and components\r\nexport { MeetingProvider, useMeetingContext } from \"./react/MeetingProvider\";\r\nexport { useMeeting } from \"./react/useMeeting\";\r\nexport { useParticipants } from \"./react/useParticipants\";\r\nexport { useRemoteMedia } from \"./react/useRemoteMedia\";\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useLocalParticipant = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n // Guard the initial state to ensure it matches Participant | null\r\n const [localParticipant, setLocalParticipant] = useState<Participant | null>(\r\n () => {\r\n const current = sdk.state.localParticipant;\r\n return current && current.id ? (current as Participant) : null;\r\n },\r\n );\r\n\r\n useEffect(() => {\r\n const unsubscribe = sdk.state.subscribe(\"localParticipant\", () => {\r\n const current = sdk.state.localParticipant;\r\n\r\n // Safe Type-Guard: Only update if the object has a finalized id\r\n if (current && current.id) {\r\n setLocalParticipant({ ...current } as Participant);\r\n } else {\r\n setLocalParticipant(null);\r\n }\r\n });\r\n\r\n return unsubscribe;\r\n }, [sdk]);\r\n\r\n const lastStreamRef = useRef<MediaStream | null>(null);\r\n\r\n const videoRef = useCallback(\r\n (video: HTMLVideoElement | null) => {\r\n if (!video) return;\r\n\r\n const stream = localParticipant?.media?.stream;\r\n if (!stream) return;\r\n\r\n if (lastStreamRef.current === stream) return;\r\n lastStreamRef.current = stream;\r\n\r\n video.srcObject = stream;\r\n video.autoplay = true;\r\n video.playsInline = true;\r\n\r\n video.play().catch((err) => {\r\n console.warn(`Autoplay failed for local view:`, err);\r\n });\r\n },\r\n [localParticipant?.media?.stream],\r\n );\r\n\r\n return {\r\n participant: localParticipant,\r\n videoRef,\r\n };\r\n};\r\n","import React, { createContext, useContext, useMemo, useRef } from \"react\";\r\nimport { VideoSDKCore } from \"../core/VideoCore\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n MeetingConfig,\r\n Participant,\r\n PubSubTopic,\r\n} from \"../types/meeting\";\r\nimport { useMeetingStore } from \"./useMeetingStore\";\r\n\r\ntype PubSubHandle = {\r\n messages: ChatMessage[];\r\n publish: (input: ChatInput) => void;\r\n};\r\n\r\ntype MeetingContextValue = {\r\n sdk: VideoSDKCore;\r\n\r\n join: (config: MeetingConfig) => Promise<void>;\r\n leave: () => void;\r\n\r\n toggleMic: () => void;\r\n toggleCam: () => void;\r\n\r\n startScreenShare: () => Promise<MediaStream>;\r\n stopScreenShare: () => void;\r\n\r\n sendMessage: (input: ChatInput) => void;\r\n\r\n meetingId: string | null;\r\n localParticipant: Participant | null;\r\n participants: Map<string, Participant>;\r\n messages: ChatMessage[];\r\n presenterId: string | null;\r\n usePubSub: (topic: PubSubTopic) => PubSubHandle;\r\n onError: (cb: (err: any) => void) => () => void;\r\n};\r\n\r\nconst MeetingContext = createContext<MeetingContextValue | null>(null);\r\n\r\nexport const MeetingProvider = ({\r\n config,\r\n children,\r\n}: {\r\n config: MeetingConfig;\r\n children: React.ReactNode;\r\n}) => {\r\n const sdkRef = useRef<VideoSDKCore | null>(null);\r\n const errorListeners = useRef(new Set<(err: any) => void>());\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n const sdk = sdkRef.current;\r\n const presenterId = useMeetingStore(\r\n sdk.state,\r\n \"presenter\",\r\n (s) => s.presenterId,\r\n );\r\n const participants = useMeetingStore(\r\n sdk.state,\r\n \"participants\",\r\n (s) => s.participants,\r\n );\r\n const localParticipant = useMeetingStore(\r\n sdk.state,\r\n \"localParticipant\",\r\n (s) => s.localParticipant,\r\n );\r\n const messages = useMeetingStore(sdk.state, \"chat\", (s) =>\r\n s.getChatMessages(),\r\n );\r\n\r\n const value = useMemo<MeetingContextValue>(() => {\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n return {\r\n sdk,\r\n\r\n join: (joinConfig: MeetingConfig) =>\r\n sdk.joinMeeting({\r\n ...config,\r\n ...joinConfig,\r\n }),\r\n leave: () => sdk.disconnect(),\r\n toggleMic: sdk.toggleMic.bind(sdk),\r\n toggleCam: sdk.toggleCam.bind(sdk),\r\n startScreenShare: sdk.startScreenShare.bind(sdk),\r\n stopScreenShare: sdk.stopScreenShare.bind(sdk),\r\n sendMessage: sdk.sendChatMessage.bind(sdk),\r\n\r\n meetingId: sdk.getMeetingId(),\r\n localParticipant,\r\n participants,\r\n messages,\r\n presenterId,\r\n usePubSub: (topic: PubSubTopic) => {\r\n if (topic !== \"SECURE_CHAT\") {\r\n throw new Error(`Unsupported PubSub argument: \"${topic}\"`);\r\n }\r\n return {\r\n messages: sdk.state.getChatMessages(),\r\n publish: sdk.sendChatMessage.bind(sdk),\r\n };\r\n },\r\n onError: (cb: (err: any) => void) => {\r\n errorListeners.current.add(cb);\r\n\r\n return () => {\r\n errorListeners.current.delete(cb);\r\n };\r\n },\r\n };\r\n }, [config, sdk, localParticipant, participants, messages, presenterId]);\r\n\r\n return (\r\n <MeetingContext.Provider value={value}>{children}</MeetingContext.Provider>\r\n );\r\n};\r\n\r\nexport const useMeetingContext = () => {\r\n const ctx = useContext(MeetingContext);\r\n if (!ctx)\r\n throw new Error(\"useMeetingContext must be used inside <MeetingProvider>\");\r\n return ctx;\r\n};\r\n","export const SDK_CONFIG = {\r\n wsUrl: \"wss://rust-video-server-sfyf.onrender.com/ws\",\r\n};\r\n","import {\r\n ChatMessage,\r\n Listener,\r\n Participant,\r\n ParticipantMedia,\r\n StateScope,\r\n} from \"../types/meeting\";\r\n\r\ntype LocalParticipantPatch = {\r\n id?: string;\r\n name?: string;\r\n media?: Partial<ParticipantMedia>;\r\n};\r\n\r\nexport class MeetingState {\r\n participants = new Map<string, Participant>();\r\n localParticipant: Participant | null = null;\r\n localStream: MediaStream | null = null;\r\n chatMessages = new Map<string, ChatMessage>();\r\n presenterId: string | null = null;\r\n\r\n private listeners = new Map<StateScope, Set<Listener>>();\r\n\r\n // ---- reactive system ----\r\n\r\n subscribe(scope: StateScope, fn: Listener): () => void {\r\n if (!this.listeners.has(scope)) {\r\n this.listeners.set(scope, new Set());\r\n }\r\n this.listeners.get(scope)!.add(fn);\r\n\r\n return () => {\r\n this.listeners.get(scope)?.delete(fn);\r\n };\r\n }\r\n\r\n notify(scope: StateScope) {\r\n this.listeners.get(scope)?.forEach((fn) => fn());\r\n }\r\n\r\n setPresenterId(id: string | null) {\r\n if (this.presenterId === id) return;\r\n this.presenterId = id;\r\n this.notify(\"presenter\");\r\n this.notify(\"participants\");\r\n }\r\n\r\n // ---- participants ----\r\n\r\n addParticipant(p: Participant) {\r\n if (this.participants.has(p.id)) return false;\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.set(p.id, p);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n return true;\r\n }\r\n\r\n removeParticipant(id: string) {\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.delete(id);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateParticipantMedia(\r\n id: string,\r\n patch: Partial<NonNullable<Participant[\"media\"]>>,\r\n ) {\r\n const p = this.participants.get(id);\r\n if (!p) return;\r\n\r\n const updated: Participant = {\r\n ...p,\r\n media: {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n ...p.media, // preserve existing media items if they happen to exist\r\n ...patch, // apply the incoming stream updates\r\n },\r\n };\r\n\r\n // Keep your clean immutable map update\r\n const next = new Map(this.participants);\r\n next.set(id, updated);\r\n this.participants = next;\r\n\r\n this.notify(`participant:${id}`);\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateLocalParticipant(patch: LocalParticipantPatch) {\r\n const prev = this.localParticipant;\r\n\r\n if (!prev) {\r\n this.localParticipant = {\r\n id: patch.id ?? \"\",\r\n name: patch.name ?? \"\",\r\n media: {\r\n stream: patch.media?.stream ?? null, // ◄ FIX: Capture the stream from the patch here\r\n screenStream: patch.media?.screenStream,\r\n cameraTrack: patch.media?.cameraTrack,\r\n screenTrack: patch.media?.screenTrack,\r\n audioTrack: patch.media?.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? true,\r\n camEnabled: patch.media?.camEnabled ?? true,\r\n isScreenSharing: patch.media?.isScreenSharing ?? false,\r\n },\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n return;\r\n }\r\n\r\n const prevMedia = prev.media ?? {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n };\r\n\r\n const nextMedia: ParticipantMedia = {\r\n stream: patch.media?.stream ?? prevMedia.stream,\r\n screenStream: patch.media?.screenStream ?? prevMedia.screenStream,\r\n cameraTrack: patch.media?.cameraTrack ?? prevMedia.cameraTrack,\r\n screenTrack: patch.media?.screenTrack ?? prevMedia.screenTrack,\r\n audioTrack: patch.media?.audioTrack ?? prevMedia.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? prevMedia.micEnabled,\r\n camEnabled: patch.media?.camEnabled ?? prevMedia.camEnabled,\r\n isScreenSharing:\r\n patch.media?.isScreenSharing ?? prevMedia.isScreenSharing,\r\n };\r\n\r\n this.localParticipant = {\r\n ...prev,\r\n id: patch.id ?? prev.id,\r\n name: patch.name ?? prev.name,\r\n media: nextMedia,\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n }\r\n\r\n // ---- chat ----\r\n\r\n addChatMessage(msg: ChatMessage) {\r\n this.chatMessages.set(msg.id, msg);\r\n this.notify(\"chat\");\r\n }\r\n\r\n getChatMessages() {\r\n return Array.from(this.chatMessages.values()).sort(\r\n (a, b) => a.timestamp - b.timestamp,\r\n );\r\n }\r\n\r\n clearChat() {\r\n this.chatMessages.clear();\r\n this.notify(\"chat\");\r\n }\r\n\r\n // ---- helpers ----\r\n\r\n getParticipants() {\r\n return Array.from(this.participants.values());\r\n }\r\n\r\n getParticipant(id: string) {\r\n return this.participants.get(id) ?? null;\r\n }\r\n\r\n resetRemoteState() {\r\n this.participants.clear();\r\n this.chatMessages.clear();\r\n this.presenterId = null;\r\n this.notify(\"participants\");\r\n this.notify(\"chat\");\r\n this.notify(\"presenter\");\r\n }\r\n}\r\n","import { SDK_CONFIG } from \"../config/ws\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n Events,\r\n MeetingConfig,\r\n SDKError,\r\n} from \"../types/meeting\";\r\nimport { MeetingState } from \"./MeetingState\";\r\n\r\nexport class VideoSDKCore {\r\n private ws: WebSocket | null = null;\r\n private peers: Record<string, RTCPeerConnection> = {};\r\n private initiators = new Set<string>();\r\n private lastPong = Date.now();\r\n private intentionalDisconnect = false;\r\n\r\n private myId: string;\r\n private roomId: string | null = null;\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n private screenSenders: Record<string, RTCRtpSender[]> = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private reconnectAttempts = 0;\r\n private reconnectTimer?: number;\r\n private participantName = \"\";\r\n public readonly state: MeetingState;\r\n private joinResolver?: () => void;\r\n private joinRejecter?: (e: any) => void;\r\n private emitError(\r\n code: string,\r\n message: string,\r\n raw?: any,\r\n recoverable = true,\r\n ) {\r\n const err: SDKError = {\r\n code,\r\n message,\r\n raw,\r\n roomId: this.roomId,\r\n userId: this.myId,\r\n recoverable,\r\n };\r\n\r\n this.events.onError?.(err);\r\n\r\n this.joinRejecter?.(err);\r\n this.joinRejecter = undefined;\r\n\r\n console.error(\"[MeetingSDK Error]\", err);\r\n }\r\n\r\n constructor(\r\n private events: Events = {},\r\n private url: string = SDK_CONFIG.wsUrl,\r\n ) {\r\n this.state = new MeetingState();\r\n this.events = events;\r\n this.url = url;\r\n\r\n this.myId = localStorage.getItem(\"vsdk_id\") || crypto.randomUUID();\r\n\r\n localStorage.setItem(\"vsdk_id\", this.myId);\r\n }\r\n\r\n // ---------------- STREAM ----------------\r\n async initLocal(video: HTMLVideoElement, name: string) {\r\n this.participantName = name;\r\n\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n video.srcObject = this.localStream;\r\n\r\n // Fix: Supply mandatory fields to satisfy the Participant type constraint\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n }\r\n\r\n // ---------------- CONNECT ----------------\r\n async connect(roomId: string, name: string) {\r\n this.roomId = roomId;\r\n\r\n this.reset();\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n this.joinResolver = resolve;\r\n this.joinRejecter = reject;\r\n this.ws = new WebSocket(this.url);\r\n\r\n this.ws.onopen = () => {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: roomId,\r\n user_id: this.myId,\r\n sender_name: name,\r\n });\r\n };\r\n\r\n this.ws.onerror = (err) => {\r\n this.emitError(\"WS_ERROR\", \"WebSocket encountered an error\", err, true);\r\n };\r\n\r\n this.ws.onclose = (e) => {\r\n this.joinRejecter?.({\r\n code: \"WS_CLOSED\",\r\n message: \"Connection closed before join completed\",\r\n raw: e,\r\n });\r\n\r\n this.joinRejecter = undefined;\r\n\r\n if (this.intentionalDisconnect) {\r\n return; // do NOT reconnect\r\n }\r\n\r\n if (e.code === 1000 || e.code === 1001) {\r\n return;\r\n }\r\n\r\n this.scheduleReconnect();\r\n };\r\n\r\n this.ws.onmessage = async (e) => {\r\n await this.handle(JSON.parse(e.data));\r\n };\r\n });\r\n }\r\n\r\n async joinMeeting(config: MeetingConfig) {\r\n const { roomId, name, audioMuted = false, videoMuted = false } = config;\r\n\r\n if (!roomId || !name) {\r\n throw new Error(\"roomId and name are required to join meeting\");\r\n }\r\n\r\n this.participantName = name;\r\n\r\n // Reuse existing stream if initLocal already configured it\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n this.localStream.getAudioTracks().forEach((t) => {\r\n t.enabled = !audioMuted;\r\n });\r\n this.localStream.getVideoTracks().forEach((t) => {\r\n t.enabled = !videoMuted;\r\n });\r\n\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: !audioMuted,\r\n camEnabled: !videoMuted,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n\r\n await this.connect(roomId, name);\r\n }\r\n\r\n /** Expose the roomId without making it fully public */\r\n getMeetingId(): string | null {\r\n return this.roomId;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n const nextEnabled = !mediaState.micEnabled;\r\n\r\n this.localStream\r\n ?.getAudioTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n micEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"audio\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n toggleCam() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n const nextEnabled = !mediaState.camEnabled;\r\n\r\n this.localStream\r\n ?.getVideoTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n camEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"video\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n private scheduleReconnect() {\r\n if (!this.roomId) return;\r\n\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\r\n\r\n clearTimeout(this.reconnectTimer);\r\n\r\n this.reconnectTimer = window.setTimeout(async () => {\r\n try {\r\n await this.connect(this.roomId!, this.participantName);\r\n\r\n this.reconnectAttempts = 0;\r\n } catch {\r\n this.reconnectAttempts++;\r\n this.scheduleReconnect();\r\n }\r\n }, delay);\r\n }\r\n\r\n private startHeartbeat() {\r\n this.stopHeartbeat();\r\n\r\n this.pingInterval = setInterval(() => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\r\n\r\n this.send({\r\n type: \"PING\",\r\n client_ts: Date.now(),\r\n });\r\n }, 20000); // every 20s\r\n }\r\n private stopHeartbeat() {\r\n if (this.pingInterval) {\r\n clearInterval(this.pingInterval);\r\n this.pingInterval = null;\r\n }\r\n }\r\n\r\n // ---------------- RESET ----------------\r\n private reset() {\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n\r\n this.peers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n // ---------------- HANDLE SIGNALS ----------------\r\n private async handle(msg: any) {\r\n if (msg.sender === this.myId) return;\r\n\r\n switch (msg.type) {\r\n case \"PONG\":\r\n this.lastPong = Date.now();\r\n break;\r\n case \"OFFER\":\r\n await this.handleOffer(msg.payload, msg.sender);\r\n break;\r\n\r\n case \"ANSWER\": {\r\n const pc = this.peers[msg.sender];\r\n\r\n if (!pc) return;\r\n\r\n if (pc.signalingState !== \"have-local-offer\") {\r\n console.warn(\r\n `[Signaling] Unexpected ANSWER in state \"${pc.signalingState}\", ignoring`,\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n await this.flushIce(msg.sender, pc);\r\n } catch (err) {\r\n console.error(\"[Signaling] Failed to apply answer:\", err);\r\n this.emitError(\r\n \"ANSWER_FAILED\",\r\n `Failed to apply answer from ${msg.sender}`,\r\n err,\r\n true,\r\n );\r\n }\r\n break;\r\n }\r\n case \"ICE\": {\r\n const candidate = JSON.parse(msg.payload);\r\n\r\n let pc = this.peers[msg.sender];\r\n\r\n if (!pc) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n if (!pc.remoteDescription) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"ICE error:\", err);\r\n }\r\n\r\n break;\r\n }\r\n case \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\r\n\r\n // Trigger your event so the UI knows to render the stage\r\n this.events.onScreenShareStarted?.(msg.presenterId, null!);\r\n }\r\n\r\n for (const p of msg.participants || []) {\r\n if (!p?.id || p.id === this.myId) continue;\r\n this.state.addParticipant(p);\r\n this.events.onUserJoined?.(p);\r\n await this.createOffer(p.id);\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n this.intentionalDisconnect = false;\r\n this.reconnectAttempts = 0;\r\n this.startHeartbeat();\r\n this.joinResolver?.();\r\n this.joinResolver = undefined;\r\n this.joinRejecter = undefined;\r\n break;\r\n }\r\n case \"USER_JOINED\": {\r\n const p = msg.participant;\r\n\r\n if (!p?.id || p.id === this.myId) return;\r\n\r\n this.state.addParticipant(p);\r\n\r\n this.events.onUserJoined?.(p);\r\n await this.createOffer(p.id);\r\n\r\n break;\r\n }\r\n\r\n case \"USER_LEFT\":\r\n const peerId = msg.participant.id;\r\n this.closePeer(peerId);\r\n\r\n this.state.removeParticipant(peerId);\r\n\r\n this.events.onUserLeft?.(peerId);\r\n\r\n break;\r\n\r\n case \"MEDIA_STATE_CHANGE\": {\r\n const peerId = msg.peerId;\r\n const { kind, enabled } = msg;\r\n\r\n // 1. Sync the app state layer for UI rendering components\r\n if (kind === \"audio\") {\r\n this.state.updateParticipantMedia(peerId, { micEnabled: enabled });\r\n this.events.onMicToggled?.(peerId, enabled);\r\n } else if (kind === \"video\") {\r\n this.state.updateParticipantMedia(peerId, { camEnabled: enabled });\r\n this.events.onCamToggled?.(peerId, enabled);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"CHAT_MESSAGE\": {\r\n const newMsg = msg.data;\r\n\r\n if (newMsg.sender_id === this.myId) break; // already added optimistically\r\n this.state.addChatMessage({\r\n id: newMsg.id,\r\n text: newMsg.message,\r\n sender_id: newMsg.sender_id,\r\n sender_name: newMsg.sender_name,\r\n timestamp: new Date(newMsg.timestamp).getTime(),\r\n target: newMsg.target,\r\n });\r\n\r\n this.events.onChatMessage?.(msg);\r\n break;\r\n }\r\n case \"SCREEN_SHARE_START\": {\r\n const peerId = msg.peerId;\r\n\r\n this.state.updateParticipantMedia(peerId, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: msg.stream_id,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(peerId);\r\n }\r\n\r\n // Fix: Use screenStream instead of the regular camera stream\r\n const screenStream =\r\n this.state.getParticipant(peerId)?.media?.screenStream;\r\n\r\n this.events.onScreenShareStarted?.(peerId, screenStream || null!);\r\n break;\r\n }\r\n\r\n case \"SCREEN_SHARE_STOP\": {\r\n const peerId = msg.peerId;\r\n this.state.updateParticipantMedia(peerId, { isScreenSharing: false });\r\n if (this.state.presenterId === peerId) {\r\n this.state.setPresenterId(null);\r\n }\r\n this.events.onScreenShareStopped?.(peerId);\r\n break;\r\n }\r\n case \"ERROR\": {\r\n const fatal = msg?.fatal === true;\r\n\r\n this.emitError(\r\n \"WS_ERROR\",\r\n msg?.message || \"Unknown error\",\r\n msg,\r\n !fatal,\r\n );\r\n\r\n if (fatal) {\r\n this.disconnect();\r\n }\r\n\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // ---------------- PEER ----------------\r\n private createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n console.log(\r\n \"Adding tracks\",\r\n this.localStream.getTracks().map((t) => ({\r\n kind: t.kind,\r\n enabled: t.enabled,\r\n state: t.readyState,\r\n })),\r\n );\r\n\r\n const pc = new RTCPeerConnection({\r\n iceServers: [\r\n {\r\n urls: \"stun:stun.relay.metered.ca:80\",\r\n },\r\n {\r\n urls: \"turn:global.relay.metered.ca:80?transport=tcp\",\r\n username: \"25aed888d2d360e9fae0e812\",\r\n credential: \"WPYstojO9Wf3+HsQ\",\r\n },\r\n ],\r\n iceTransportPolicy: \"relay\",\r\n });\r\n\r\n pc.ontrack = (event) => {\r\n console.log(`Track received: ${event.track.kind}`, {\r\n trackId: event.track.id,\r\n streamCount: event.streams.length,\r\n streamTrackCount: event.streams[0]?.getTracks().length,\r\n });\r\n const incomingStream =\r\n event.streams?.[0] || new MediaStream([event.track]);\r\n const participant = this.state.getParticipant(id);\r\n\r\n const isScreenStream =\r\n incomingStream.id === participant?.media?.remoteScreenStreamId;\r\n\r\n if (event.track.kind === \"video\") {\r\n event.track.onunmute = () => {\r\n console.log(\"Video track unmuted for\", id);\r\n };\r\n }\r\n\r\n if (isScreenStream) {\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0] ||\r\n participant?.media?.screenTrack;\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\r\n\r\n isScreenSharing: true,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(id);\r\n }\r\n\r\n this.events.onScreenShareStarted?.(id, incomingStream);\r\n } else {\r\n this.state.updateParticipantMedia(id, {\r\n stream: incomingStream,\r\n cameraTrack: incomingStream.getVideoTracks()[0],\r\n audioTrack: incomingStream.getAudioTracks()[0],\r\n });\r\n this.events.onTrack?.(incomingStream, id);\r\n }\r\n };\r\n\r\n pc.onicecandidate = (e) => {\r\n if (!e.candidate) return;\r\n this.send({\r\n type: \"ICE\",\r\n payload: JSON.stringify(e.candidate),\r\n sender: this.myId,\r\n target: id,\r\n });\r\n };\r\n\r\n pc.oniceconnectionstatechange = () => {\r\n console.log(`ICE Connection State: ${pc.iceConnectionState}`);\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch {}\r\n }\r\n };\r\n\r\n this.localStream.getTracks().forEach((track) => {\r\n pc.addTrack(track, this.localStream!);\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n this.screenSenders[id] = [];\r\n this.screenStream.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[id].push(sender);\r\n });\r\n }\r\n\r\n return pc;\r\n }\r\n\r\n // ---------------- OFFER ----------------\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n if (!isRenegotiation && this.initiators.has(id)) {\r\n console.debug(\r\n `[Offer] Already initiating with ${id}, skipping duplicate`,\r\n );\r\n }\r\n\r\n if (isRenegotiation && this.peers[id]) {\r\n const pc = this.peers[id];\r\n if (pc.signalingState !== \"stable\") {\r\n console.warn(\r\n `[Offer] Cannot renegotiate: peer in state \"${pc.signalingState}\"`,\r\n );\r\n }\r\n }\r\n\r\n if (!isRenegotiation) {\r\n this.initiators.add(id);\r\n }\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n this.send({\r\n type: \"OFFER\",\r\n payload: offer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n\r\n console.debug(`[Offer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Offer] Failed for ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_FAILED\",\r\n `Failed to create offer for ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n // ---------------- ANSWER ----------------\r\n private async handleOffer(sdp: string, id: string) {\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n // ✅ Only set remote description if we're not already in negotiation\r\n if (\r\n pc.signalingState !== \"stable\" &&\r\n pc.signalingState !== \"have-local-offer\"\r\n ) {\r\n console.warn(\r\n `[Signaling] Cannot accept OFFER in state \"${pc.signalingState}\"`,\r\n );\r\n return;\r\n }\r\n\r\n await pc.setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"[ICE] Failed to add candidate:\", err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await pc.createAnswer();\r\n\r\n await pc.setLocalDescription(answer);\r\n await this.flushIce(id, pc);\r\n\r\n this.send({\r\n type: \"ANSWER\",\r\n payload: answer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n\r\n console.debug(`[Answer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Signaling] Failed to handle OFFER from ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_HANDLING_FAILED\",\r\n `Failed to handle offer from ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n // ---------------- CLEANUP ----------------\r\n private closePeer(id: string) {\r\n const pc = this.peers[id];\r\n\r\n if (!pc) return;\r\n\r\n pc.ontrack = null;\r\n pc.onicecandidate = null;\r\n pc.onconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n async startScreenShare() {\r\n try {\r\n if (this.state.presenterId && this.state.presenterId !== this.myId) {\r\n throw new Error(\"Another user is already sharing their screen.\");\r\n }\r\n if (!navigator.mediaDevices?.getDisplayMedia) {\r\n throw new Error(\"Screen sharing not supported on this device\");\r\n }\r\n\r\n this.screenStream = await navigator.mediaDevices.getDisplayMedia({\r\n video: true,\r\n // audio: true,\r\n });\r\n\r\n this.isScreenSharing = true;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: true,\r\n screenStream: this.screenStream,\r\n screenTrack: this.screenStream.getVideoTracks()[0],\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n // Handle the user clicking browser's built-in \"Stop Sharing\" button\r\n this.screenStream.getVideoTracks()[0].onended = () => {\r\n this.stopScreenShare();\r\n };\r\n\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n this.screenSenders[peerId] = [];\r\n this.screenStream!.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[peerId].push(sender);\r\n });\r\n\r\n // Renegotiate peer connection descriptors to notify remote side of new track footprint\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n return this.screenStream;\r\n } catch (err: any) {\r\n this.emitError(\r\n \"SCREEN_SHARE_FAILED\",\r\n err?.message || \"Failed to start screen sharing\",\r\n err,\r\n true,\r\n );\r\n\r\n this.isScreenSharing = false;\r\n this.screenStream = null;\r\n throw err;\r\n }\r\n }\r\n\r\n stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Remove tracks cleanly from WebRTC channel pathways across your peers\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n const senders = this.screenSenders[peerId] || [];\r\n senders.forEach((sender) => {\r\n try {\r\n pc.removeTrack(sender);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n });\r\n delete this.screenSenders[peerId];\r\n\r\n // Renegotiate layout expectations to scale down stream bounds\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.screenStream = null;\r\n this.isScreenSharing = false;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: false,\r\n screenStream: null,\r\n screenTrack: undefined,\r\n },\r\n });\r\n\r\n if (this.state.presenterId === this.myId) {\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_STOP\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n });\r\n }\r\n\r\n sendChatMessage(payload: ChatInput) {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n console.warn(\"WS not connected\");\r\n return;\r\n }\r\n\r\n if (!this.roomId) {\r\n console.warn(\"No roomId set\");\r\n return;\r\n }\r\n\r\n const isPrivate = !!payload?.target;\r\n\r\n const senderName = this.state.localParticipant?.name || \"Anonymous\";\r\n\r\n const msg: ChatMessage = {\r\n id: crypto.randomUUID(),\r\n sender_id: this.myId,\r\n sender_name: senderName,\r\n text: payload.message.trim(),\r\n timestamp: Date.now(),\r\n reply_to: payload.reply_to ?? null,\r\n target: payload.target ?? null,\r\n };\r\n\r\n // optimistic UI update\r\n this.state.addChatMessage(msg);\r\n\r\n // send protocol payload (clean + consistent)\r\n this.send({\r\n type: \"CHAT_MESSAGE\",\r\n message: payload.message.trim(),\r\n user_id: this.myId,\r\n sender_name: senderName,\r\n room_id: this.roomId,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n disconnect() {\r\n this.intentionalDisconnect = true;\r\n this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n\r\n if (this.localStream) {\r\n this.localStream.getTracks().forEach((track) => track.stop());\r\n this.localStream = null;\r\n }\r\n\r\n this.roomId = null;\r\n\r\n // Clear and notify\r\n this.state.localParticipant = null;\r\n this.state.notify(\"localParticipant\");\r\n\r\n this.state.participants.clear();\r\n this.state.notify(\"participants\");\r\n\r\n this.state.clearChat();\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n private async flushIce(id: string, pc: RTCPeerConnection) {\r\n const pending = this.pendingIceCandidates[id];\r\n if (!pending?.length) return;\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (e) {\r\n console.warn(\"ICE flush error\", e);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n }\r\n\r\n private send(msg: any) {\r\n this.ws?.send(JSON.stringify(msg));\r\n }\r\n}\r\n","import { useEffect, useState } from \"react\";\r\nimport { StateScope } from \"../types/meeting\";\r\nimport { MeetingState } from \"../core/MeetingState\";\r\n\r\nexport function useMeetingStore<T>(\r\n stateManager: MeetingState,\r\n scope: StateScope,\r\n selector: (state: MeetingState) => T,\r\n): T {\r\n const [state, setState] = useState(() => selector(stateManager));\r\n\r\n useEffect(() => {\r\n // Update local react state whenever the SDK notifies this scope\r\n const unsubscribe = stateManager.subscribe(scope, () => {\r\n setState(selector(stateManager));\r\n });\r\n\r\n return unsubscribe;\r\n }, [stateManager, scope, selector]);\r\n\r\n return state;\r\n}\r\n","import { useEffect } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\n\r\nexport const useMeeting = (handlers?: { onError?: (err: any) => void }) => {\r\n const ctx = useMeetingContext();\r\n\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n\r\n const unsubscribe = ctx.onError(handlers.onError);\r\n return unsubscribe;\r\n }, [handlers?.onError]);\r\n\r\n return ctx;\r\n};\r\n","import { useEffect, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useParticipants = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n const [participants, setParticipants] = useState<Participant[]>(() =>\r\n sdk.state.getParticipants(),\r\n );\r\n\r\n useEffect(() => {\r\n const update = () => {\r\n setParticipants(sdk.state.getParticipants());\r\n };\r\n\r\n update();\r\n\r\n const unsub = sdk.state.subscribe(\"participants\", update);\r\n\r\n return unsub;\r\n }, [sdk]);\r\n\r\n return participants;\r\n};\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useRemoteMedia = (participantId: string) => {\r\n const { sdk } = useMeetingContext();\r\n const videoRef = useRef<HTMLVideoElement | null>(null);\r\n const audioRef = useRef<HTMLAudioElement | null>(null);\r\n\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\r\n );\r\n\r\n useEffect(() => {\r\n const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {\r\n const p = sdk.state.getParticipant(participantId);\r\n setParticipant(p ? { ...p } : null);\r\n });\r\n\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n useEffect(() => {\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n // VIDEO\r\n if (videoRef.current) {\r\n videoRef.current.srcObject = stream;\r\n videoRef.current.muted = true;\r\n videoRef.current.playsInline = true;\r\n videoRef.current.play().catch(() => {});\r\n }\r\n\r\n // AUDIO\r\n if (audioRef.current) {\r\n audioRef.current.srcObject = stream;\r\n audioRef.current.play().catch(() => {});\r\n }\r\n }, [participant?.media?.stream]);\r\n\r\n return {\r\n videoRef,\r\n audioRef,\r\n isCamActive: !!participant?.media?.camEnabled,\r\n isMicEnabled: !!participant?.media?.micEnabled,\r\n };\r\n};\r\n"],"mappings":"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","names":["import_react","import_react","peerId","pc","import_react","import_react","import_react"]}
package/dist/index.mjs CHANGED
@@ -167,6 +167,8 @@ var VideoSDKCore = class {
167
167
  this.ws = null;
168
168
  this.peers = {};
169
169
  this.initiators = /* @__PURE__ */ new Set();
170
+ this.lastPong = Date.now();
171
+ this.intentionalDisconnect = false;
170
172
  this.roomId = null;
171
173
  this.localStream = null;
172
174
  this.screenStream = null;
@@ -238,18 +240,18 @@ var VideoSDKCore = class {
238
240
  this.emitError("WS_ERROR", "WebSocket encountered an error", err, true);
239
241
  };
240
242
  this.ws.onclose = (e) => {
241
- this.emitError(
242
- "WS_CLOSED",
243
- `Connection closed (${e.code}) ${e.reason || ""}`,
244
- e,
245
- true
246
- );
247
243
  this.joinRejecter?.({
248
244
  code: "WS_CLOSED",
249
245
  message: "Connection closed before join completed",
250
246
  raw: e
251
247
  });
252
248
  this.joinRejecter = void 0;
249
+ if (this.intentionalDisconnect) {
250
+ return;
251
+ }
252
+ if (e.code === 1e3 || e.code === 1001) {
253
+ return;
254
+ }
253
255
  this.scheduleReconnect();
254
256
  };
255
257
  this.ws.onmessage = async (e) => {
@@ -373,33 +375,9 @@ var VideoSDKCore = class {
373
375
  var _a, _b, _c, _d;
374
376
  if (msg.sender === this.myId) return;
375
377
  switch (msg.type) {
376
- case "EXISTING_USERS":
377
- if (msg.presenterId) {
378
- this.state.setPresenterId(msg.presenterId);
379
- this.events.onScreenShareStarted?.(msg.presenterId, null);
380
- }
381
- for (const p of msg.participants || []) {
382
- if (!p?.id || p.id === this.myId) continue;
383
- this.state.addParticipant(p);
384
- this.events.onUserJoined?.(p);
385
- await this.createOffer(p.id);
386
- }
387
- break;
388
- case "JOINED": {
389
- this.startHeartbeat();
390
- this.joinResolver?.();
391
- this.joinResolver = void 0;
392
- this.joinRejecter = void 0;
393
- break;
394
- }
395
- case "USER_JOINED": {
396
- const p = msg.participant;
397
- if (!p?.id || p.id === this.myId) return;
398
- this.state.addParticipant(p);
399
- this.events.onUserJoined?.(p);
400
- await this.createOffer(p.id);
378
+ case "PONG":
379
+ this.lastPong = Date.now();
401
380
  break;
402
- }
403
381
  case "OFFER":
404
382
  await this.handleOffer(msg.payload, msg.sender);
405
383
  break;
@@ -449,6 +427,35 @@ var VideoSDKCore = class {
449
427
  }
450
428
  break;
451
429
  }
430
+ case "EXISTING_USERS":
431
+ if (msg.presenterId) {
432
+ this.state.setPresenterId(msg.presenterId);
433
+ this.events.onScreenShareStarted?.(msg.presenterId, null);
434
+ }
435
+ for (const p of msg.participants || []) {
436
+ if (!p?.id || p.id === this.myId) continue;
437
+ this.state.addParticipant(p);
438
+ this.events.onUserJoined?.(p);
439
+ await this.createOffer(p.id);
440
+ }
441
+ break;
442
+ case "JOINED": {
443
+ this.intentionalDisconnect = false;
444
+ this.reconnectAttempts = 0;
445
+ this.startHeartbeat();
446
+ this.joinResolver?.();
447
+ this.joinResolver = void 0;
448
+ this.joinRejecter = void 0;
449
+ break;
450
+ }
451
+ case "USER_JOINED": {
452
+ const p = msg.participant;
453
+ if (!p?.id || p.id === this.myId) return;
454
+ this.state.addParticipant(p);
455
+ this.events.onUserJoined?.(p);
456
+ await this.createOffer(p.id);
457
+ break;
458
+ }
452
459
  case "USER_LEFT":
453
460
  const peerId = msg.participant.id;
454
461
  this.closePeer(peerId);
@@ -534,27 +541,13 @@ var VideoSDKCore = class {
534
541
  {
535
542
  urls: "stun:stun.relay.metered.ca:80"
536
543
  },
537
- {
538
- urls: "turn:global.relay.metered.ca:80",
539
- username: "25aed888d2d360e9fae0e812",
540
- credential: "WPYstojO9Wf3+HsQ"
541
- },
542
544
  {
543
545
  urls: "turn:global.relay.metered.ca:80?transport=tcp",
544
546
  username: "25aed888d2d360e9fae0e812",
545
547
  credential: "WPYstojO9Wf3+HsQ"
546
- },
547
- {
548
- urls: "turn:global.relay.metered.ca:443",
549
- username: "25aed888d2d360e9fae0e812",
550
- credential: "WPYstojO9Wf3+HsQ"
551
- },
552
- {
553
- urls: "turns:global.relay.metered.ca:443?transport=tcp",
554
- username: "25aed888d2d360e9fae0e812",
555
- credential: "WPYstojO9Wf3+HsQ"
556
548
  }
557
- ]
549
+ ],
550
+ iceTransportPolicy: "relay"
558
551
  });
559
552
  pc.ontrack = (event) => {
560
553
  console.log(`Track received: ${event.track.kind}`, {
@@ -562,9 +555,14 @@ var VideoSDKCore = class {
562
555
  streamCount: event.streams.length,
563
556
  streamTrackCount: event.streams[0]?.getTracks().length
564
557
  });
565
- const incomingStream = event.streams[0];
558
+ const incomingStream = event.streams?.[0] || new MediaStream([event.track]);
566
559
  const participant = this.state.getParticipant(id);
567
560
  const isScreenStream = incomingStream.id === participant?.media?.remoteScreenStreamId;
561
+ if (event.track.kind === "video") {
562
+ event.track.onunmute = () => {
563
+ console.log("Video track unmuted for", id);
564
+ };
565
+ }
568
566
  if (isScreenStream) {
569
567
  const videoTrack = event.track.kind === "video" ? event.track : incomingStream.getVideoTracks()[0] || participant?.media?.screenTrack;
570
568
  this.state.updateParticipantMedia(id, {
@@ -596,12 +594,6 @@ var VideoSDKCore = class {
596
594
  };
597
595
  pc.oniceconnectionstatechange = () => {
598
596
  console.log(`ICE Connection State: ${pc.iceConnectionState}`);
599
- if (pc.iceConnectionState === "failed" || pc.iceConnectionState === "disconnected") {
600
- this.emitError(
601
- "ICE_FAILED",
602
- "Connection failed. Please check your network or refresh."
603
- );
604
- }
605
597
  };
606
598
  pc.onconnectionstatechange = () => {
607
599
  if (pc.connectionState === "failed") {
@@ -629,7 +621,6 @@ var VideoSDKCore = class {
629
621
  console.debug(
630
622
  `[Offer] Already initiating with ${id}, skipping duplicate`
631
623
  );
632
- return;
633
624
  }
634
625
  if (isRenegotiation && this.peers[id]) {
635
626
  const pc2 = this.peers[id];
@@ -637,7 +628,6 @@ var VideoSDKCore = class {
637
628
  console.warn(
638
629
  `[Offer] Cannot renegotiate: peer in state "${pc2.signalingState}"`
639
630
  );
640
- return;
641
631
  }
642
632
  }
643
633
  if (!isRenegotiation) {
@@ -669,15 +659,6 @@ var VideoSDKCore = class {
669
659
  }
670
660
  // ---------------- ANSWER ----------------
671
661
  async handleOffer(sdp, id) {
672
- if (this.initiators.has(id) && this.peers[id]) {
673
- const pc2 = this.peers[id];
674
- if (pc2.signalingState !== "stable") {
675
- console.warn(
676
- `[Signaling] Offer collision with ${id}. We initiated, ignoring their offer.`
677
- );
678
- return;
679
- }
680
- }
681
662
  if (!this.peers[id]) {
682
663
  this.peers[id] = this.createPeer(id);
683
664
  }
@@ -851,6 +832,7 @@ var VideoSDKCore = class {
851
832
  });
852
833
  }
853
834
  disconnect() {
835
+ this.intentionalDisconnect = true;
854
836
  this.stopScreenShare();
855
837
  Object.values(this.peers).forEach((pc) => pc.close());
856
838
  this.peers = {};
@@ -1064,21 +1046,14 @@ var useParticipants = () => {
1064
1046
  };
1065
1047
 
1066
1048
  // src/react/useRemoteMedia.ts
1067
- import { useCallback as useCallback2, useEffect as useEffect5, useState as useState4 } from "react";
1049
+ import { useEffect as useEffect5, useRef as useRef3, useState as useState4 } from "react";
1068
1050
  var useRemoteMedia = (participantId) => {
1069
1051
  const { sdk } = useMeetingContext();
1052
+ const videoRef = useRef3(null);
1053
+ const audioRef = useRef3(null);
1070
1054
  const [participant, setParticipant] = useState4(
1071
1055
  () => sdk.state.getParticipant(participantId) || null
1072
1056
  );
1073
- const buildMediaStream = (participant2) => {
1074
- if (!participant2?.media) return null;
1075
- const stream = new MediaStream();
1076
- const videoTrack = participant2.media.stream?.getVideoTracks?.()?.[0];
1077
- const audioTrack = participant2.media.stream?.getAudioTracks?.()?.[0];
1078
- if (videoTrack) stream.addTrack(videoTrack);
1079
- if (audioTrack) stream.addTrack(audioTrack);
1080
- return stream;
1081
- };
1082
1057
  useEffect5(() => {
1083
1058
  const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {
1084
1059
  const p = sdk.state.getParticipant(participantId);
@@ -1086,34 +1061,22 @@ var useRemoteMedia = (participantId) => {
1086
1061
  });
1087
1062
  return unsub;
1088
1063
  }, [participantId, sdk]);
1089
- const videoRef = useCallback2(
1090
- (node) => {
1091
- if (!node) return;
1092
- const stream = participant?.media?.stream;
1093
- if (!stream) return;
1094
- node.srcObject = stream;
1095
- node.muted = true;
1096
- node.playsInline = true;
1097
- node.autoplay = true;
1098
- node.play().catch((err) => {
1099
- console.log("can't play video:", err);
1064
+ useEffect5(() => {
1065
+ const stream = participant?.media?.stream;
1066
+ if (!stream) return;
1067
+ if (videoRef.current) {
1068
+ videoRef.current.srcObject = stream;
1069
+ videoRef.current.muted = true;
1070
+ videoRef.current.playsInline = true;
1071
+ videoRef.current.play().catch(() => {
1100
1072
  });
1101
- },
1102
- [participant?.media?.stream]
1103
- );
1104
- const audioRef = useCallback2(
1105
- (node) => {
1106
- if (!node) return;
1107
- const stream = participant?.media?.stream;
1108
- if (!stream) return;
1109
- node.srcObject = stream;
1110
- node.muted = false;
1111
- node.play().catch((err) => {
1112
- console.log("can't play Audio:", err);
1073
+ }
1074
+ if (audioRef.current) {
1075
+ audioRef.current.srcObject = stream;
1076
+ audioRef.current.play().catch(() => {
1113
1077
  });
1114
- },
1115
- [participant?.media?.stream]
1116
- );
1078
+ }
1079
+ }, [participant?.media?.stream]);
1117
1080
  return {
1118
1081
  videoRef,
1119
1082
  audioRef,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react/useLocalParticipant.tsx","../src/react/MeetingProvider.tsx","../src/config/ws.ts","../src/core/MeetingState.ts","../src/core/VideoCore.ts","../src/react/useMeetingStore.ts","../src/react/useMeeting.ts","../src/react/useParticipants.ts","../src/react/useRemoteMedia.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useLocalParticipant = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n // Guard the initial state to ensure it matches Participant | null\r\n const [localParticipant, setLocalParticipant] = useState<Participant | null>(\r\n () => {\r\n const current = sdk.state.localParticipant;\r\n return current && current.id ? (current as Participant) : null;\r\n },\r\n );\r\n\r\n useEffect(() => {\r\n const unsubscribe = sdk.state.subscribe(\"localParticipant\", () => {\r\n const current = sdk.state.localParticipant;\r\n\r\n // Safe Type-Guard: Only update if the object has a finalized id\r\n if (current && current.id) {\r\n setLocalParticipant({ ...current } as Participant);\r\n } else {\r\n setLocalParticipant(null);\r\n }\r\n });\r\n\r\n return unsubscribe;\r\n }, [sdk]);\r\n\r\n const lastStreamRef = useRef<MediaStream | null>(null);\r\n\r\n const videoRef = useCallback(\r\n (video: HTMLVideoElement | null) => {\r\n if (!video) return;\r\n\r\n const stream = localParticipant?.media?.stream;\r\n if (!stream) return;\r\n\r\n if (lastStreamRef.current === stream) return;\r\n lastStreamRef.current = stream;\r\n\r\n video.srcObject = stream;\r\n video.autoplay = true;\r\n video.playsInline = true;\r\n\r\n video.play().catch((err) => {\r\n console.warn(`Autoplay failed for local view:`, err);\r\n });\r\n },\r\n [localParticipant?.media?.stream],\r\n );\r\n\r\n return {\r\n participant: localParticipant,\r\n videoRef,\r\n };\r\n};\r\n","import React, { createContext, useContext, useMemo, useRef } from \"react\";\r\nimport { VideoSDKCore } from \"../core/VideoCore\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n MeetingConfig,\r\n Participant,\r\n PubSubTopic,\r\n} from \"../types/meeting\";\r\nimport { useMeetingStore } from \"./useMeetingStore\";\r\n\r\ntype PubSubHandle = {\r\n messages: ChatMessage[];\r\n publish: (input: ChatInput) => void;\r\n};\r\n\r\ntype MeetingContextValue = {\r\n sdk: VideoSDKCore;\r\n\r\n join: (config: MeetingConfig) => Promise<void>;\r\n leave: () => void;\r\n\r\n toggleMic: () => void;\r\n toggleCam: () => void;\r\n\r\n startScreenShare: () => Promise<MediaStream>;\r\n stopScreenShare: () => void;\r\n\r\n sendMessage: (input: ChatInput) => void;\r\n\r\n meetingId: string | null;\r\n localParticipant: Participant | null;\r\n participants: Map<string, Participant>;\r\n messages: ChatMessage[];\r\n presenterId: string | null;\r\n usePubSub: (topic: PubSubTopic) => PubSubHandle;\r\n onError: (cb: (err: any) => void) => () => void;\r\n};\r\n\r\nconst MeetingContext = createContext<MeetingContextValue | null>(null);\r\n\r\nexport const MeetingProvider = ({\r\n config,\r\n children,\r\n}: {\r\n config: MeetingConfig;\r\n children: React.ReactNode;\r\n}) => {\r\n const sdkRef = useRef<VideoSDKCore | null>(null);\r\n const errorListeners = useRef(new Set<(err: any) => void>());\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n const sdk = sdkRef.current;\r\n const presenterId = useMeetingStore(\r\n sdk.state,\r\n \"presenter\",\r\n (s) => s.presenterId,\r\n );\r\n const participants = useMeetingStore(\r\n sdk.state,\r\n \"participants\",\r\n (s) => s.participants,\r\n );\r\n const localParticipant = useMeetingStore(\r\n sdk.state,\r\n \"localParticipant\",\r\n (s) => s.localParticipant,\r\n );\r\n const messages = useMeetingStore(sdk.state, \"chat\", (s) =>\r\n s.getChatMessages(),\r\n );\r\n\r\n const value = useMemo<MeetingContextValue>(() => {\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n return {\r\n sdk,\r\n\r\n join: (joinConfig: MeetingConfig) =>\r\n sdk.joinMeeting({\r\n ...config,\r\n ...joinConfig,\r\n }),\r\n leave: () => sdk.disconnect(),\r\n toggleMic: sdk.toggleMic.bind(sdk),\r\n toggleCam: sdk.toggleCam.bind(sdk),\r\n startScreenShare: sdk.startScreenShare.bind(sdk),\r\n stopScreenShare: sdk.stopScreenShare.bind(sdk),\r\n sendMessage: sdk.sendChatMessage.bind(sdk),\r\n\r\n meetingId: sdk.getMeetingId(),\r\n localParticipant,\r\n participants,\r\n messages,\r\n presenterId,\r\n usePubSub: (topic: PubSubTopic) => {\r\n if (topic !== \"SECURE_CHAT\") {\r\n throw new Error(`Unsupported PubSub argument: \"${topic}\"`);\r\n }\r\n return {\r\n messages: sdk.state.getChatMessages(),\r\n publish: sdk.sendChatMessage.bind(sdk),\r\n };\r\n },\r\n onError: (cb: (err: any) => void) => {\r\n errorListeners.current.add(cb);\r\n\r\n return () => {\r\n errorListeners.current.delete(cb);\r\n };\r\n },\r\n };\r\n }, [config, sdk, localParticipant, participants, messages, presenterId]);\r\n\r\n return (\r\n <MeetingContext.Provider value={value}>{children}</MeetingContext.Provider>\r\n );\r\n};\r\n\r\nexport const useMeetingContext = () => {\r\n const ctx = useContext(MeetingContext);\r\n if (!ctx)\r\n throw new Error(\"useMeetingContext must be used inside <MeetingProvider>\");\r\n return ctx;\r\n};\r\n","export const SDK_CONFIG = {\r\n wsUrl: \"wss://rust-video-server-sfyf.onrender.com/ws\",\r\n};\r\n","import {\r\n ChatMessage,\r\n Listener,\r\n Participant,\r\n ParticipantMedia,\r\n StateScope,\r\n} from \"../types/meeting\";\r\n\r\ntype LocalParticipantPatch = {\r\n id?: string;\r\n name?: string;\r\n media?: Partial<ParticipantMedia>;\r\n};\r\n\r\nexport class MeetingState {\r\n participants = new Map<string, Participant>();\r\n localParticipant: Participant | null = null;\r\n localStream: MediaStream | null = null;\r\n chatMessages = new Map<string, ChatMessage>();\r\n presenterId: string | null = null;\r\n\r\n private listeners = new Map<StateScope, Set<Listener>>();\r\n\r\n // ---- reactive system ----\r\n\r\n subscribe(scope: StateScope, fn: Listener): () => void {\r\n if (!this.listeners.has(scope)) {\r\n this.listeners.set(scope, new Set());\r\n }\r\n this.listeners.get(scope)!.add(fn);\r\n\r\n return () => {\r\n this.listeners.get(scope)?.delete(fn);\r\n };\r\n }\r\n\r\n notify(scope: StateScope) {\r\n this.listeners.get(scope)?.forEach((fn) => fn());\r\n }\r\n\r\n setPresenterId(id: string | null) {\r\n if (this.presenterId === id) return;\r\n this.presenterId = id;\r\n this.notify(\"presenter\");\r\n this.notify(\"participants\");\r\n }\r\n\r\n // ---- participants ----\r\n\r\n addParticipant(p: Participant) {\r\n if (this.participants.has(p.id)) return false;\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.set(p.id, p);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n return true;\r\n }\r\n\r\n removeParticipant(id: string) {\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.delete(id);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateParticipantMedia(\r\n id: string,\r\n patch: Partial<NonNullable<Participant[\"media\"]>>,\r\n ) {\r\n const p = this.participants.get(id);\r\n if (!p) return;\r\n\r\n const updated: Participant = {\r\n ...p,\r\n media: {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n ...p.media, // preserve existing media items if they happen to exist\r\n ...patch, // apply the incoming stream updates\r\n },\r\n };\r\n\r\n // Keep your clean immutable map update\r\n const next = new Map(this.participants);\r\n next.set(id, updated);\r\n this.participants = next;\r\n\r\n this.notify(`participant:${id}`);\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateLocalParticipant(patch: LocalParticipantPatch) {\r\n const prev = this.localParticipant;\r\n\r\n if (!prev) {\r\n this.localParticipant = {\r\n id: patch.id ?? \"\",\r\n name: patch.name ?? \"\",\r\n media: {\r\n stream: patch.media?.stream ?? null, // ◄ FIX: Capture the stream from the patch here\r\n screenStream: patch.media?.screenStream,\r\n cameraTrack: patch.media?.cameraTrack,\r\n screenTrack: patch.media?.screenTrack,\r\n audioTrack: patch.media?.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? true,\r\n camEnabled: patch.media?.camEnabled ?? true,\r\n isScreenSharing: patch.media?.isScreenSharing ?? false,\r\n },\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n return;\r\n }\r\n\r\n const prevMedia = prev.media ?? {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n };\r\n\r\n const nextMedia: ParticipantMedia = {\r\n stream: patch.media?.stream ?? prevMedia.stream,\r\n screenStream: patch.media?.screenStream ?? prevMedia.screenStream,\r\n cameraTrack: patch.media?.cameraTrack ?? prevMedia.cameraTrack,\r\n screenTrack: patch.media?.screenTrack ?? prevMedia.screenTrack,\r\n audioTrack: patch.media?.audioTrack ?? prevMedia.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? prevMedia.micEnabled,\r\n camEnabled: patch.media?.camEnabled ?? prevMedia.camEnabled,\r\n isScreenSharing:\r\n patch.media?.isScreenSharing ?? prevMedia.isScreenSharing,\r\n };\r\n\r\n this.localParticipant = {\r\n ...prev,\r\n id: patch.id ?? prev.id,\r\n name: patch.name ?? prev.name,\r\n media: nextMedia,\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n }\r\n\r\n // ---- chat ----\r\n\r\n addChatMessage(msg: ChatMessage) {\r\n this.chatMessages.set(msg.id, msg);\r\n this.notify(\"chat\");\r\n }\r\n\r\n getChatMessages() {\r\n return Array.from(this.chatMessages.values()).sort(\r\n (a, b) => a.timestamp - b.timestamp,\r\n );\r\n }\r\n\r\n clearChat() {\r\n this.chatMessages.clear();\r\n this.notify(\"chat\");\r\n }\r\n\r\n // ---- helpers ----\r\n\r\n getParticipants() {\r\n return Array.from(this.participants.values());\r\n }\r\n\r\n getParticipant(id: string) {\r\n return this.participants.get(id) ?? null;\r\n }\r\n\r\n resetRemoteState() {\r\n this.participants.clear();\r\n this.chatMessages.clear();\r\n this.presenterId = null;\r\n this.notify(\"participants\");\r\n this.notify(\"chat\");\r\n this.notify(\"presenter\");\r\n }\r\n}\r\n","import { SDK_CONFIG } from \"../config/ws\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n Events,\r\n MeetingConfig,\r\n SDKError,\r\n} from \"../types/meeting\";\r\nimport { MeetingState } from \"./MeetingState\";\r\n\r\nexport class VideoSDKCore {\r\n private ws: WebSocket | null = null;\r\n private peers: Record<string, RTCPeerConnection> = {};\r\n private initiators = new Set<string>();\r\n\r\n private myId: string;\r\n private roomId: string | null = null;\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n private screenSenders: Record<string, RTCRtpSender[]> = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private reconnectAttempts = 0;\r\n private reconnectTimer?: number;\r\n private participantName = \"\";\r\n public readonly state: MeetingState;\r\n private joinResolver?: () => void;\r\n private joinRejecter?: (e: any) => void;\r\n private emitError(\r\n code: string,\r\n message: string,\r\n raw?: any,\r\n recoverable = true,\r\n ) {\r\n const err: SDKError = {\r\n code,\r\n message,\r\n raw,\r\n roomId: this.roomId,\r\n userId: this.myId,\r\n recoverable,\r\n };\r\n\r\n this.events.onError?.(err);\r\n\r\n this.joinRejecter?.(err);\r\n this.joinRejecter = undefined;\r\n\r\n console.error(\"[MeetingSDK Error]\", err);\r\n }\r\n\r\n constructor(\r\n private events: Events = {},\r\n private url: string = SDK_CONFIG.wsUrl,\r\n ) {\r\n this.state = new MeetingState();\r\n this.events = events;\r\n this.url = url;\r\n\r\n this.myId = localStorage.getItem(\"vsdk_id\") || crypto.randomUUID();\r\n\r\n localStorage.setItem(\"vsdk_id\", this.myId);\r\n }\r\n\r\n // ---------------- STREAM ----------------\r\n async initLocal(video: HTMLVideoElement, name: string) {\r\n this.participantName = name;\r\n\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n video.srcObject = this.localStream;\r\n\r\n // Fix: Supply mandatory fields to satisfy the Participant type constraint\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n }\r\n\r\n // ---------------- CONNECT ----------------\r\n async connect(roomId: string, name: string) {\r\n this.roomId = roomId;\r\n\r\n this.reset();\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n this.joinResolver = resolve;\r\n this.joinRejecter = reject;\r\n this.ws = new WebSocket(this.url);\r\n\r\n this.ws.onopen = () => {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: roomId,\r\n user_id: this.myId,\r\n sender_name: name,\r\n });\r\n };\r\n\r\n this.ws.onerror = (err) => {\r\n this.emitError(\"WS_ERROR\", \"WebSocket encountered an error\", err, true);\r\n };\r\n\r\n this.ws.onclose = (e) => {\r\n this.emitError(\r\n \"WS_CLOSED\",\r\n `Connection closed (${e.code}) ${e.reason || \"\"}`,\r\n e,\r\n true,\r\n );\r\n\r\n // If join never resolved, fail the promise\r\n this.joinRejecter?.({\r\n code: \"WS_CLOSED\",\r\n message: \"Connection closed before join completed\",\r\n raw: e,\r\n });\r\n\r\n this.joinRejecter = undefined;\r\n\r\n this.scheduleReconnect();\r\n };\r\n\r\n this.ws.onmessage = async (e) => {\r\n await this.handle(JSON.parse(e.data));\r\n };\r\n });\r\n }\r\n\r\n async joinMeeting(config: MeetingConfig) {\r\n const { roomId, name, audioMuted = false, videoMuted = false } = config;\r\n\r\n if (!roomId || !name) {\r\n throw new Error(\"roomId and name are required to join meeting\");\r\n }\r\n\r\n this.participantName = name;\r\n\r\n // Reuse existing stream if initLocal already configured it\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n this.localStream.getAudioTracks().forEach((t) => {\r\n t.enabled = !audioMuted;\r\n });\r\n this.localStream.getVideoTracks().forEach((t) => {\r\n t.enabled = !videoMuted;\r\n });\r\n\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: !audioMuted,\r\n camEnabled: !videoMuted,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n\r\n await this.connect(roomId, name);\r\n }\r\n\r\n /** Expose the roomId without making it fully public */\r\n getMeetingId(): string | null {\r\n return this.roomId;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.micEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getAudioTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n micEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"audio\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n toggleCam() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.camEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getVideoTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n camEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"video\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n private scheduleReconnect() {\r\n if (!this.roomId) return;\r\n\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\r\n\r\n clearTimeout(this.reconnectTimer);\r\n\r\n this.reconnectTimer = window.setTimeout(async () => {\r\n try {\r\n await this.connect(this.roomId!, this.participantName);\r\n\r\n this.reconnectAttempts = 0;\r\n } catch {\r\n this.reconnectAttempts++;\r\n this.scheduleReconnect();\r\n }\r\n }, delay);\r\n }\r\n\r\n private startHeartbeat() {\r\n this.stopHeartbeat();\r\n\r\n this.pingInterval = setInterval(() => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\r\n\r\n this.send({\r\n type: \"PING\",\r\n client_ts: Date.now(),\r\n });\r\n }, 20000); // every 20s\r\n }\r\n private stopHeartbeat() {\r\n if (this.pingInterval) {\r\n clearInterval(this.pingInterval);\r\n this.pingInterval = null;\r\n }\r\n }\r\n\r\n // ---------------- RESET ----------------\r\n private reset() {\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n\r\n this.peers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n // ---------------- HANDLE SIGNALS ----------------\r\n private async handle(msg: any) {\r\n if (msg.sender === this.myId) return;\r\n\r\n switch (msg.type) {\r\n case \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\r\n\r\n // Trigger your event so the UI knows to render the stage\r\n this.events.onScreenShareStarted?.(msg.presenterId, null!);\r\n }\r\n\r\n for (const p of msg.participants || []) {\r\n if (!p?.id || p.id === this.myId) continue;\r\n this.state.addParticipant(p);\r\n this.events.onUserJoined?.(p);\r\n await this.createOffer(p.id);\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n this.startHeartbeat();\r\n this.joinResolver?.();\r\n this.joinResolver = undefined;\r\n this.joinRejecter = undefined;\r\n break;\r\n }\r\n case \"USER_JOINED\": {\r\n const p = msg.participant;\r\n\r\n if (!p?.id || p.id === this.myId) return;\r\n\r\n this.state.addParticipant(p);\r\n\r\n this.events.onUserJoined?.(p);\r\n await this.createOffer(p.id);\r\n\r\n break;\r\n }\r\n\r\n case \"OFFER\":\r\n await this.handleOffer(msg.payload, msg.sender);\r\n break;\r\n\r\n case \"ANSWER\": {\r\n const pc = this.peers[msg.sender];\r\n\r\n if (!pc) return;\r\n\r\n if (pc.signalingState !== \"have-local-offer\") {\r\n console.warn(\r\n `[Signaling] Unexpected ANSWER in state \"${pc.signalingState}\", ignoring`,\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n await this.flushIce(msg.sender, pc);\r\n } catch (err) {\r\n console.error(\"[Signaling] Failed to apply answer:\", err);\r\n this.emitError(\r\n \"ANSWER_FAILED\",\r\n `Failed to apply answer from ${msg.sender}`,\r\n err,\r\n true,\r\n );\r\n }\r\n break;\r\n }\r\n\r\n case \"ICE\": {\r\n const candidate = JSON.parse(msg.payload);\r\n\r\n let pc = this.peers[msg.sender];\r\n\r\n if (!pc) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n if (!pc.remoteDescription) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"ICE error:\", err);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"USER_LEFT\":\r\n const peerId = msg.participant.id;\r\n this.closePeer(peerId);\r\n\r\n this.state.removeParticipant(peerId);\r\n\r\n this.events.onUserLeft?.(peerId);\r\n\r\n break;\r\n\r\n case \"MEDIA_STATE_CHANGE\": {\r\n const peerId = msg.peerId;\r\n const { kind, enabled } = msg;\r\n\r\n // 1. Sync the app state layer for UI rendering components\r\n if (kind === \"audio\") {\r\n this.state.updateParticipantMedia(peerId, { micEnabled: enabled });\r\n this.events.onMicToggled?.(peerId, enabled);\r\n } else if (kind === \"video\") {\r\n this.state.updateParticipantMedia(peerId, { camEnabled: enabled });\r\n this.events.onCamToggled?.(peerId, enabled);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"CHAT_MESSAGE\": {\r\n const newMsg = msg.data;\r\n\r\n if (newMsg.sender_id === this.myId) break; // already added optimistically\r\n this.state.addChatMessage({\r\n id: newMsg.id,\r\n text: newMsg.message,\r\n sender_id: newMsg.sender_id,\r\n sender_name: newMsg.sender_name,\r\n timestamp: new Date(newMsg.timestamp).getTime(),\r\n target: newMsg.target,\r\n });\r\n\r\n this.events.onChatMessage?.(msg);\r\n break;\r\n }\r\n case \"SCREEN_SHARE_START\": {\r\n const peerId = msg.peerId;\r\n\r\n this.state.updateParticipantMedia(peerId, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: msg.stream_id,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(peerId);\r\n }\r\n\r\n // Fix: Use screenStream instead of the regular camera stream\r\n const screenStream =\r\n this.state.getParticipant(peerId)?.media?.screenStream;\r\n\r\n this.events.onScreenShareStarted?.(peerId, screenStream || null!);\r\n break;\r\n }\r\n\r\n case \"SCREEN_SHARE_STOP\": {\r\n const peerId = msg.peerId;\r\n this.state.updateParticipantMedia(peerId, { isScreenSharing: false });\r\n if (this.state.presenterId === peerId) {\r\n this.state.setPresenterId(null);\r\n }\r\n this.events.onScreenShareStopped?.(peerId);\r\n break;\r\n }\r\n case \"ERROR\": {\r\n const fatal = msg?.fatal === true;\r\n\r\n this.emitError(\r\n \"WS_ERROR\",\r\n msg?.message || \"Unknown error\",\r\n msg,\r\n !fatal,\r\n );\r\n\r\n if (fatal) {\r\n this.disconnect();\r\n }\r\n\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // ---------------- PEER ----------------\r\n private createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n console.log(\r\n \"Adding tracks\",\r\n this.localStream.getTracks().map((t) => ({\r\n kind: t.kind,\r\n enabled: t.enabled,\r\n state: t.readyState,\r\n })),\r\n );\r\n\r\n const pc = new RTCPeerConnection({\r\n iceServers: [\r\n {\r\n urls: \"stun:stun.relay.metered.ca:80\",\r\n },\r\n {\r\n urls: \"turn:global.relay.metered.ca:80\",\r\n username: \"25aed888d2d360e9fae0e812\",\r\n credential: \"WPYstojO9Wf3+HsQ\",\r\n },\r\n {\r\n urls: \"turn:global.relay.metered.ca:80?transport=tcp\",\r\n username: \"25aed888d2d360e9fae0e812\",\r\n credential: \"WPYstojO9Wf3+HsQ\",\r\n },\r\n {\r\n urls: \"turn:global.relay.metered.ca:443\",\r\n username: \"25aed888d2d360e9fae0e812\",\r\n credential: \"WPYstojO9Wf3+HsQ\",\r\n },\r\n {\r\n urls: \"turns:global.relay.metered.ca:443?transport=tcp\",\r\n username: \"25aed888d2d360e9fae0e812\",\r\n credential: \"WPYstojO9Wf3+HsQ\",\r\n },\r\n ],\r\n });\r\n\r\n pc.ontrack = (event) => {\r\n console.log(`Track received: ${event.track.kind}`, {\r\n trackId: event.track.id,\r\n streamCount: event.streams.length,\r\n streamTrackCount: event.streams[0]?.getTracks().length,\r\n });\r\n const incomingStream = event.streams[0];\r\n const participant = this.state.getParticipant(id);\r\n\r\n const isScreenStream =\r\n incomingStream.id === participant?.media?.remoteScreenStreamId;\r\n\r\n if (isScreenStream) {\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0] ||\r\n participant?.media?.screenTrack;\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\r\n\r\n isScreenSharing: true,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(id);\r\n }\r\n\r\n this.events.onScreenShareStarted?.(id, incomingStream);\r\n } else {\r\n this.state.updateParticipantMedia(id, {\r\n stream: incomingStream,\r\n cameraTrack: incomingStream.getVideoTracks()[0],\r\n audioTrack: incomingStream.getAudioTracks()[0],\r\n });\r\n this.events.onTrack?.(incomingStream, id);\r\n }\r\n };\r\n\r\n pc.onicecandidate = (e) => {\r\n if (!e.candidate) return;\r\n this.send({\r\n type: \"ICE\",\r\n payload: JSON.stringify(e.candidate),\r\n sender: this.myId,\r\n target: id,\r\n });\r\n };\r\n\r\n pc.oniceconnectionstatechange = () => {\r\n console.log(`ICE Connection State: ${pc.iceConnectionState}`);\r\n if (\r\n pc.iceConnectionState === \"failed\" ||\r\n pc.iceConnectionState === \"disconnected\"\r\n ) {\r\n // Trigger a UI notification to the user that the connection is unstable\r\n this.emitError(\r\n \"ICE_FAILED\",\r\n \"Connection failed. Please check your network or refresh.\",\r\n );\r\n }\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch {}\r\n }\r\n };\r\n\r\n this.localStream.getTracks().forEach((track) => {\r\n pc.addTrack(track, this.localStream!);\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n this.screenSenders[id] = [];\r\n this.screenStream.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[id].push(sender);\r\n });\r\n }\r\n\r\n return pc;\r\n }\r\n\r\n // ---------------- OFFER ----------------\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n if (!isRenegotiation && this.initiators.has(id)) {\r\n console.debug(\r\n `[Offer] Already initiating with ${id}, skipping duplicate`,\r\n );\r\n return;\r\n }\r\n\r\n if (isRenegotiation && this.peers[id]) {\r\n const pc = this.peers[id];\r\n if (pc.signalingState !== \"stable\") {\r\n console.warn(\r\n `[Offer] Cannot renegotiate: peer in state \"${pc.signalingState}\"`,\r\n );\r\n return;\r\n }\r\n }\r\n\r\n if (!isRenegotiation) {\r\n this.initiators.add(id);\r\n }\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n this.send({\r\n type: \"OFFER\",\r\n payload: offer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n\r\n console.debug(`[Offer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Offer] Failed for ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_FAILED\",\r\n `Failed to create offer for ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n // ---------------- ANSWER ----------------\r\n private async handleOffer(sdp: string, id: string) {\r\n if (this.initiators.has(id) && this.peers[id]) {\r\n const pc = this.peers[id];\r\n if (pc.signalingState !== \"stable\") {\r\n console.warn(\r\n `[Signaling] Offer collision with ${id}. We initiated, ignoring their offer.`,\r\n );\r\n return; // We initiated, so ignore their offer\r\n }\r\n }\r\n\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n // ✅ Only set remote description if we're not already in negotiation\r\n if (\r\n pc.signalingState !== \"stable\" &&\r\n pc.signalingState !== \"have-local-offer\"\r\n ) {\r\n console.warn(\r\n `[Signaling] Cannot accept OFFER in state \"${pc.signalingState}\"`,\r\n );\r\n return;\r\n }\r\n\r\n await pc.setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"[ICE] Failed to add candidate:\", err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await pc.createAnswer();\r\n\r\n await pc.setLocalDescription(answer);\r\n await this.flushIce(id, pc);\r\n\r\n this.send({\r\n type: \"ANSWER\",\r\n payload: answer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n\r\n console.debug(`[Answer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Signaling] Failed to handle OFFER from ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_HANDLING_FAILED\",\r\n `Failed to handle offer from ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n // ---------------- CLEANUP ----------------\r\n private closePeer(id: string) {\r\n const pc = this.peers[id];\r\n\r\n if (!pc) return;\r\n\r\n pc.ontrack = null;\r\n pc.onicecandidate = null;\r\n pc.onconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n async startScreenShare() {\r\n try {\r\n if (this.state.presenterId && this.state.presenterId !== this.myId) {\r\n throw new Error(\"Another user is already sharing their screen.\");\r\n }\r\n if (!navigator.mediaDevices?.getDisplayMedia) {\r\n throw new Error(\"Screen sharing not supported on this device\");\r\n }\r\n\r\n this.screenStream = await navigator.mediaDevices.getDisplayMedia({\r\n video: true,\r\n // audio: true,\r\n });\r\n\r\n this.isScreenSharing = true;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: true,\r\n screenStream: this.screenStream,\r\n screenTrack: this.screenStream.getVideoTracks()[0],\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n // Handle the user clicking browser's built-in \"Stop Sharing\" button\r\n this.screenStream.getVideoTracks()[0].onended = () => {\r\n this.stopScreenShare();\r\n };\r\n\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n this.screenSenders[peerId] = [];\r\n this.screenStream!.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[peerId].push(sender);\r\n });\r\n\r\n // Renegotiate peer connection descriptors to notify remote side of new track footprint\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n return this.screenStream;\r\n } catch (err: any) {\r\n this.emitError(\r\n \"SCREEN_SHARE_FAILED\",\r\n err?.message || \"Failed to start screen sharing\",\r\n err,\r\n true,\r\n );\r\n\r\n this.isScreenSharing = false;\r\n this.screenStream = null;\r\n throw err;\r\n }\r\n }\r\n\r\n stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Remove tracks cleanly from WebRTC channel pathways across your peers\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n const senders = this.screenSenders[peerId] || [];\r\n senders.forEach((sender) => {\r\n try {\r\n pc.removeTrack(sender);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n });\r\n delete this.screenSenders[peerId];\r\n\r\n // Renegotiate layout expectations to scale down stream bounds\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.screenStream = null;\r\n this.isScreenSharing = false;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: false,\r\n screenStream: null,\r\n screenTrack: undefined,\r\n },\r\n });\r\n\r\n if (this.state.presenterId === this.myId) {\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_STOP\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n });\r\n }\r\n\r\n sendChatMessage(payload: ChatInput) {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n console.warn(\"WS not connected\");\r\n return;\r\n }\r\n\r\n if (!this.roomId) {\r\n console.warn(\"No roomId set\");\r\n return;\r\n }\r\n\r\n const isPrivate = !!payload?.target;\r\n\r\n const senderName = this.state.localParticipant?.name || \"Anonymous\";\r\n\r\n const msg: ChatMessage = {\r\n id: crypto.randomUUID(),\r\n sender_id: this.myId,\r\n sender_name: senderName,\r\n text: payload.message.trim(),\r\n timestamp: Date.now(),\r\n reply_to: payload.reply_to ?? null,\r\n target: payload.target ?? null,\r\n };\r\n\r\n // optimistic UI update\r\n this.state.addChatMessage(msg);\r\n\r\n // send protocol payload (clean + consistent)\r\n this.send({\r\n type: \"CHAT_MESSAGE\",\r\n message: payload.message.trim(),\r\n user_id: this.myId,\r\n sender_name: senderName,\r\n room_id: this.roomId,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n disconnect() {\r\n this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n\r\n if (this.localStream) {\r\n this.localStream.getTracks().forEach((track) => track.stop());\r\n this.localStream = null;\r\n }\r\n\r\n this.roomId = null;\r\n\r\n // Clear and notify\r\n this.state.localParticipant = null;\r\n this.state.notify(\"localParticipant\");\r\n\r\n this.state.participants.clear();\r\n this.state.notify(\"participants\");\r\n\r\n this.state.clearChat();\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n private async flushIce(id: string, pc: RTCPeerConnection) {\r\n const pending = this.pendingIceCandidates[id];\r\n if (!pending?.length) return;\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (e) {\r\n console.warn(\"ICE flush error\", e);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n }\r\n\r\n private send(msg: any) {\r\n this.ws?.send(JSON.stringify(msg));\r\n }\r\n}\r\n","import { useEffect, useState } from \"react\";\r\nimport { StateScope } from \"../types/meeting\";\r\nimport { MeetingState } from \"../core/MeetingState\";\r\n\r\nexport function useMeetingStore<T>(\r\n stateManager: MeetingState,\r\n scope: StateScope,\r\n selector: (state: MeetingState) => T,\r\n): T {\r\n const [state, setState] = useState(() => selector(stateManager));\r\n\r\n useEffect(() => {\r\n // Update local react state whenever the SDK notifies this scope\r\n const unsubscribe = stateManager.subscribe(scope, () => {\r\n setState(selector(stateManager));\r\n });\r\n\r\n return unsubscribe;\r\n }, [stateManager, scope, selector]);\r\n\r\n return state;\r\n}\r\n","import { useEffect } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\n\r\nexport const useMeeting = (handlers?: { onError?: (err: any) => void }) => {\r\n const ctx = useMeetingContext();\r\n\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n\r\n const unsubscribe = ctx.onError(handlers.onError);\r\n return unsubscribe;\r\n }, [handlers?.onError]);\r\n\r\n return ctx;\r\n};\r\n","import { useEffect, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useParticipants = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n const [participants, setParticipants] = useState<Participant[]>(() =>\r\n sdk.state.getParticipants(),\r\n );\r\n\r\n useEffect(() => {\r\n const update = () => {\r\n setParticipants(sdk.state.getParticipants());\r\n };\r\n\r\n update();\r\n\r\n const unsub = sdk.state.subscribe(\"participants\", update);\r\n\r\n return unsub;\r\n }, [sdk]);\r\n\r\n return participants;\r\n};\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useRemoteMedia = (participantId: string) => {\r\n const { sdk } = useMeetingContext();\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\r\n );\r\n\r\n const buildMediaStream = (participant: Participant | null) => {\r\n if (!participant?.media) return null;\r\n\r\n const stream = new MediaStream();\r\n\r\n const videoTrack = participant.media.stream?.getVideoTracks?.()?.[0];\r\n const audioTrack = participant.media.stream?.getAudioTracks?.()?.[0];\r\n\r\n if (videoTrack) stream.addTrack(videoTrack);\r\n if (audioTrack) stream.addTrack(audioTrack);\r\n\r\n return stream;\r\n };\r\n\r\n useEffect(() => {\r\n const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {\r\n const p = sdk.state.getParticipant(participantId);\r\n setParticipant(p ? { ...p } : null);\r\n });\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n // Video Callback Ref\r\n const videoRef = useCallback(\r\n (node: HTMLVideoElement | null) => {\r\n if (!node) return;\r\n\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n // IMPORTANT: always force rebind (no conditions)\r\n node.srcObject = stream;\r\n\r\n node.muted = true;\r\n node.playsInline = true;\r\n node.autoplay = true;\r\n\r\n node.play().catch((err) => {\r\n // ignore autoplay restrictions\r\n console.log(\"can't play video:\", err);\r\n });\r\n },\r\n [participant?.media?.stream],\r\n );\r\n\r\n // Audio Callback Ref\r\n const audioRef = useCallback(\r\n (node: HTMLAudioElement | null) => {\r\n if (!node) return;\r\n\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n node.srcObject = stream;\r\n\r\n node.muted = false;\r\n\r\n node.play().catch((err) => {\r\n console.log(\"can't play Audio:\", err);\r\n });\r\n },\r\n [participant?.media?.stream],\r\n );\r\n\r\n return {\r\n videoRef,\r\n audioRef,\r\n isCamActive: !!participant?.media?.camEnabled,\r\n isMicEnabled: !!participant?.media?.micEnabled,\r\n };\r\n};\r\n"],"mappings":"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","names":["useEffect","useRef","useState","peerId","pc","useState","useEffect","useRef","useEffect","useEffect","useEffect","useState","useState","useEffect","useCallback","useEffect","useState","useState","participant","useEffect","useCallback"]}
1
+ {"version":3,"sources":["../src/react/useLocalParticipant.tsx","../src/react/MeetingProvider.tsx","../src/config/ws.ts","../src/core/MeetingState.ts","../src/core/VideoCore.ts","../src/react/useMeetingStore.ts","../src/react/useMeeting.ts","../src/react/useParticipants.ts","../src/react/useRemoteMedia.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useLocalParticipant = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n // Guard the initial state to ensure it matches Participant | null\r\n const [localParticipant, setLocalParticipant] = useState<Participant | null>(\r\n () => {\r\n const current = sdk.state.localParticipant;\r\n return current && current.id ? (current as Participant) : null;\r\n },\r\n );\r\n\r\n useEffect(() => {\r\n const unsubscribe = sdk.state.subscribe(\"localParticipant\", () => {\r\n const current = sdk.state.localParticipant;\r\n\r\n // Safe Type-Guard: Only update if the object has a finalized id\r\n if (current && current.id) {\r\n setLocalParticipant({ ...current } as Participant);\r\n } else {\r\n setLocalParticipant(null);\r\n }\r\n });\r\n\r\n return unsubscribe;\r\n }, [sdk]);\r\n\r\n const lastStreamRef = useRef<MediaStream | null>(null);\r\n\r\n const videoRef = useCallback(\r\n (video: HTMLVideoElement | null) => {\r\n if (!video) return;\r\n\r\n const stream = localParticipant?.media?.stream;\r\n if (!stream) return;\r\n\r\n if (lastStreamRef.current === stream) return;\r\n lastStreamRef.current = stream;\r\n\r\n video.srcObject = stream;\r\n video.autoplay = true;\r\n video.playsInline = true;\r\n\r\n video.play().catch((err) => {\r\n console.warn(`Autoplay failed for local view:`, err);\r\n });\r\n },\r\n [localParticipant?.media?.stream],\r\n );\r\n\r\n return {\r\n participant: localParticipant,\r\n videoRef,\r\n };\r\n};\r\n","import React, { createContext, useContext, useMemo, useRef } from \"react\";\r\nimport { VideoSDKCore } from \"../core/VideoCore\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n MeetingConfig,\r\n Participant,\r\n PubSubTopic,\r\n} from \"../types/meeting\";\r\nimport { useMeetingStore } from \"./useMeetingStore\";\r\n\r\ntype PubSubHandle = {\r\n messages: ChatMessage[];\r\n publish: (input: ChatInput) => void;\r\n};\r\n\r\ntype MeetingContextValue = {\r\n sdk: VideoSDKCore;\r\n\r\n join: (config: MeetingConfig) => Promise<void>;\r\n leave: () => void;\r\n\r\n toggleMic: () => void;\r\n toggleCam: () => void;\r\n\r\n startScreenShare: () => Promise<MediaStream>;\r\n stopScreenShare: () => void;\r\n\r\n sendMessage: (input: ChatInput) => void;\r\n\r\n meetingId: string | null;\r\n localParticipant: Participant | null;\r\n participants: Map<string, Participant>;\r\n messages: ChatMessage[];\r\n presenterId: string | null;\r\n usePubSub: (topic: PubSubTopic) => PubSubHandle;\r\n onError: (cb: (err: any) => void) => () => void;\r\n};\r\n\r\nconst MeetingContext = createContext<MeetingContextValue | null>(null);\r\n\r\nexport const MeetingProvider = ({\r\n config,\r\n children,\r\n}: {\r\n config: MeetingConfig;\r\n children: React.ReactNode;\r\n}) => {\r\n const sdkRef = useRef<VideoSDKCore | null>(null);\r\n const errorListeners = useRef(new Set<(err: any) => void>());\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n const sdk = sdkRef.current;\r\n const presenterId = useMeetingStore(\r\n sdk.state,\r\n \"presenter\",\r\n (s) => s.presenterId,\r\n );\r\n const participants = useMeetingStore(\r\n sdk.state,\r\n \"participants\",\r\n (s) => s.participants,\r\n );\r\n const localParticipant = useMeetingStore(\r\n sdk.state,\r\n \"localParticipant\",\r\n (s) => s.localParticipant,\r\n );\r\n const messages = useMeetingStore(sdk.state, \"chat\", (s) =>\r\n s.getChatMessages(),\r\n );\r\n\r\n const value = useMemo<MeetingContextValue>(() => {\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n return {\r\n sdk,\r\n\r\n join: (joinConfig: MeetingConfig) =>\r\n sdk.joinMeeting({\r\n ...config,\r\n ...joinConfig,\r\n }),\r\n leave: () => sdk.disconnect(),\r\n toggleMic: sdk.toggleMic.bind(sdk),\r\n toggleCam: sdk.toggleCam.bind(sdk),\r\n startScreenShare: sdk.startScreenShare.bind(sdk),\r\n stopScreenShare: sdk.stopScreenShare.bind(sdk),\r\n sendMessage: sdk.sendChatMessage.bind(sdk),\r\n\r\n meetingId: sdk.getMeetingId(),\r\n localParticipant,\r\n participants,\r\n messages,\r\n presenterId,\r\n usePubSub: (topic: PubSubTopic) => {\r\n if (topic !== \"SECURE_CHAT\") {\r\n throw new Error(`Unsupported PubSub argument: \"${topic}\"`);\r\n }\r\n return {\r\n messages: sdk.state.getChatMessages(),\r\n publish: sdk.sendChatMessage.bind(sdk),\r\n };\r\n },\r\n onError: (cb: (err: any) => void) => {\r\n errorListeners.current.add(cb);\r\n\r\n return () => {\r\n errorListeners.current.delete(cb);\r\n };\r\n },\r\n };\r\n }, [config, sdk, localParticipant, participants, messages, presenterId]);\r\n\r\n return (\r\n <MeetingContext.Provider value={value}>{children}</MeetingContext.Provider>\r\n );\r\n};\r\n\r\nexport const useMeetingContext = () => {\r\n const ctx = useContext(MeetingContext);\r\n if (!ctx)\r\n throw new Error(\"useMeetingContext must be used inside <MeetingProvider>\");\r\n return ctx;\r\n};\r\n","export const SDK_CONFIG = {\r\n wsUrl: \"wss://rust-video-server-sfyf.onrender.com/ws\",\r\n};\r\n","import {\r\n ChatMessage,\r\n Listener,\r\n Participant,\r\n ParticipantMedia,\r\n StateScope,\r\n} from \"../types/meeting\";\r\n\r\ntype LocalParticipantPatch = {\r\n id?: string;\r\n name?: string;\r\n media?: Partial<ParticipantMedia>;\r\n};\r\n\r\nexport class MeetingState {\r\n participants = new Map<string, Participant>();\r\n localParticipant: Participant | null = null;\r\n localStream: MediaStream | null = null;\r\n chatMessages = new Map<string, ChatMessage>();\r\n presenterId: string | null = null;\r\n\r\n private listeners = new Map<StateScope, Set<Listener>>();\r\n\r\n // ---- reactive system ----\r\n\r\n subscribe(scope: StateScope, fn: Listener): () => void {\r\n if (!this.listeners.has(scope)) {\r\n this.listeners.set(scope, new Set());\r\n }\r\n this.listeners.get(scope)!.add(fn);\r\n\r\n return () => {\r\n this.listeners.get(scope)?.delete(fn);\r\n };\r\n }\r\n\r\n notify(scope: StateScope) {\r\n this.listeners.get(scope)?.forEach((fn) => fn());\r\n }\r\n\r\n setPresenterId(id: string | null) {\r\n if (this.presenterId === id) return;\r\n this.presenterId = id;\r\n this.notify(\"presenter\");\r\n this.notify(\"participants\");\r\n }\r\n\r\n // ---- participants ----\r\n\r\n addParticipant(p: Participant) {\r\n if (this.participants.has(p.id)) return false;\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.set(p.id, p);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n return true;\r\n }\r\n\r\n removeParticipant(id: string) {\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.delete(id);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateParticipantMedia(\r\n id: string,\r\n patch: Partial<NonNullable<Participant[\"media\"]>>,\r\n ) {\r\n const p = this.participants.get(id);\r\n if (!p) return;\r\n\r\n const updated: Participant = {\r\n ...p,\r\n media: {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n ...p.media, // preserve existing media items if they happen to exist\r\n ...patch, // apply the incoming stream updates\r\n },\r\n };\r\n\r\n // Keep your clean immutable map update\r\n const next = new Map(this.participants);\r\n next.set(id, updated);\r\n this.participants = next;\r\n\r\n this.notify(`participant:${id}`);\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateLocalParticipant(patch: LocalParticipantPatch) {\r\n const prev = this.localParticipant;\r\n\r\n if (!prev) {\r\n this.localParticipant = {\r\n id: patch.id ?? \"\",\r\n name: patch.name ?? \"\",\r\n media: {\r\n stream: patch.media?.stream ?? null, // ◄ FIX: Capture the stream from the patch here\r\n screenStream: patch.media?.screenStream,\r\n cameraTrack: patch.media?.cameraTrack,\r\n screenTrack: patch.media?.screenTrack,\r\n audioTrack: patch.media?.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? true,\r\n camEnabled: patch.media?.camEnabled ?? true,\r\n isScreenSharing: patch.media?.isScreenSharing ?? false,\r\n },\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n return;\r\n }\r\n\r\n const prevMedia = prev.media ?? {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n };\r\n\r\n const nextMedia: ParticipantMedia = {\r\n stream: patch.media?.stream ?? prevMedia.stream,\r\n screenStream: patch.media?.screenStream ?? prevMedia.screenStream,\r\n cameraTrack: patch.media?.cameraTrack ?? prevMedia.cameraTrack,\r\n screenTrack: patch.media?.screenTrack ?? prevMedia.screenTrack,\r\n audioTrack: patch.media?.audioTrack ?? prevMedia.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? prevMedia.micEnabled,\r\n camEnabled: patch.media?.camEnabled ?? prevMedia.camEnabled,\r\n isScreenSharing:\r\n patch.media?.isScreenSharing ?? prevMedia.isScreenSharing,\r\n };\r\n\r\n this.localParticipant = {\r\n ...prev,\r\n id: patch.id ?? prev.id,\r\n name: patch.name ?? prev.name,\r\n media: nextMedia,\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n }\r\n\r\n // ---- chat ----\r\n\r\n addChatMessage(msg: ChatMessage) {\r\n this.chatMessages.set(msg.id, msg);\r\n this.notify(\"chat\");\r\n }\r\n\r\n getChatMessages() {\r\n return Array.from(this.chatMessages.values()).sort(\r\n (a, b) => a.timestamp - b.timestamp,\r\n );\r\n }\r\n\r\n clearChat() {\r\n this.chatMessages.clear();\r\n this.notify(\"chat\");\r\n }\r\n\r\n // ---- helpers ----\r\n\r\n getParticipants() {\r\n return Array.from(this.participants.values());\r\n }\r\n\r\n getParticipant(id: string) {\r\n return this.participants.get(id) ?? null;\r\n }\r\n\r\n resetRemoteState() {\r\n this.participants.clear();\r\n this.chatMessages.clear();\r\n this.presenterId = null;\r\n this.notify(\"participants\");\r\n this.notify(\"chat\");\r\n this.notify(\"presenter\");\r\n }\r\n}\r\n","import { SDK_CONFIG } from \"../config/ws\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n Events,\r\n MeetingConfig,\r\n SDKError,\r\n} from \"../types/meeting\";\r\nimport { MeetingState } from \"./MeetingState\";\r\n\r\nexport class VideoSDKCore {\r\n private ws: WebSocket | null = null;\r\n private peers: Record<string, RTCPeerConnection> = {};\r\n private initiators = new Set<string>();\r\n private lastPong = Date.now();\r\n private intentionalDisconnect = false;\r\n\r\n private myId: string;\r\n private roomId: string | null = null;\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n private screenSenders: Record<string, RTCRtpSender[]> = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private reconnectAttempts = 0;\r\n private reconnectTimer?: number;\r\n private participantName = \"\";\r\n public readonly state: MeetingState;\r\n private joinResolver?: () => void;\r\n private joinRejecter?: (e: any) => void;\r\n private emitError(\r\n code: string,\r\n message: string,\r\n raw?: any,\r\n recoverable = true,\r\n ) {\r\n const err: SDKError = {\r\n code,\r\n message,\r\n raw,\r\n roomId: this.roomId,\r\n userId: this.myId,\r\n recoverable,\r\n };\r\n\r\n this.events.onError?.(err);\r\n\r\n this.joinRejecter?.(err);\r\n this.joinRejecter = undefined;\r\n\r\n console.error(\"[MeetingSDK Error]\", err);\r\n }\r\n\r\n constructor(\r\n private events: Events = {},\r\n private url: string = SDK_CONFIG.wsUrl,\r\n ) {\r\n this.state = new MeetingState();\r\n this.events = events;\r\n this.url = url;\r\n\r\n this.myId = localStorage.getItem(\"vsdk_id\") || crypto.randomUUID();\r\n\r\n localStorage.setItem(\"vsdk_id\", this.myId);\r\n }\r\n\r\n // ---------------- STREAM ----------------\r\n async initLocal(video: HTMLVideoElement, name: string) {\r\n this.participantName = name;\r\n\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n video.srcObject = this.localStream;\r\n\r\n // Fix: Supply mandatory fields to satisfy the Participant type constraint\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n }\r\n\r\n // ---------------- CONNECT ----------------\r\n async connect(roomId: string, name: string) {\r\n this.roomId = roomId;\r\n\r\n this.reset();\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n this.joinResolver = resolve;\r\n this.joinRejecter = reject;\r\n this.ws = new WebSocket(this.url);\r\n\r\n this.ws.onopen = () => {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: roomId,\r\n user_id: this.myId,\r\n sender_name: name,\r\n });\r\n };\r\n\r\n this.ws.onerror = (err) => {\r\n this.emitError(\"WS_ERROR\", \"WebSocket encountered an error\", err, true);\r\n };\r\n\r\n this.ws.onclose = (e) => {\r\n this.joinRejecter?.({\r\n code: \"WS_CLOSED\",\r\n message: \"Connection closed before join completed\",\r\n raw: e,\r\n });\r\n\r\n this.joinRejecter = undefined;\r\n\r\n if (this.intentionalDisconnect) {\r\n return; // do NOT reconnect\r\n }\r\n\r\n if (e.code === 1000 || e.code === 1001) {\r\n return;\r\n }\r\n\r\n this.scheduleReconnect();\r\n };\r\n\r\n this.ws.onmessage = async (e) => {\r\n await this.handle(JSON.parse(e.data));\r\n };\r\n });\r\n }\r\n\r\n async joinMeeting(config: MeetingConfig) {\r\n const { roomId, name, audioMuted = false, videoMuted = false } = config;\r\n\r\n if (!roomId || !name) {\r\n throw new Error(\"roomId and name are required to join meeting\");\r\n }\r\n\r\n this.participantName = name;\r\n\r\n // Reuse existing stream if initLocal already configured it\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n this.localStream.getAudioTracks().forEach((t) => {\r\n t.enabled = !audioMuted;\r\n });\r\n this.localStream.getVideoTracks().forEach((t) => {\r\n t.enabled = !videoMuted;\r\n });\r\n\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: !audioMuted,\r\n camEnabled: !videoMuted,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n\r\n await this.connect(roomId, name);\r\n }\r\n\r\n /** Expose the roomId without making it fully public */\r\n getMeetingId(): string | null {\r\n return this.roomId;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n const nextEnabled = !mediaState.micEnabled;\r\n\r\n this.localStream\r\n ?.getAudioTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n micEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"audio\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n toggleCam() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n const nextEnabled = !mediaState.camEnabled;\r\n\r\n this.localStream\r\n ?.getVideoTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n camEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"video\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n private scheduleReconnect() {\r\n if (!this.roomId) return;\r\n\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\r\n\r\n clearTimeout(this.reconnectTimer);\r\n\r\n this.reconnectTimer = window.setTimeout(async () => {\r\n try {\r\n await this.connect(this.roomId!, this.participantName);\r\n\r\n this.reconnectAttempts = 0;\r\n } catch {\r\n this.reconnectAttempts++;\r\n this.scheduleReconnect();\r\n }\r\n }, delay);\r\n }\r\n\r\n private startHeartbeat() {\r\n this.stopHeartbeat();\r\n\r\n this.pingInterval = setInterval(() => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\r\n\r\n this.send({\r\n type: \"PING\",\r\n client_ts: Date.now(),\r\n });\r\n }, 20000); // every 20s\r\n }\r\n private stopHeartbeat() {\r\n if (this.pingInterval) {\r\n clearInterval(this.pingInterval);\r\n this.pingInterval = null;\r\n }\r\n }\r\n\r\n // ---------------- RESET ----------------\r\n private reset() {\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n\r\n this.peers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n // ---------------- HANDLE SIGNALS ----------------\r\n private async handle(msg: any) {\r\n if (msg.sender === this.myId) return;\r\n\r\n switch (msg.type) {\r\n case \"PONG\":\r\n this.lastPong = Date.now();\r\n break;\r\n case \"OFFER\":\r\n await this.handleOffer(msg.payload, msg.sender);\r\n break;\r\n\r\n case \"ANSWER\": {\r\n const pc = this.peers[msg.sender];\r\n\r\n if (!pc) return;\r\n\r\n if (pc.signalingState !== \"have-local-offer\") {\r\n console.warn(\r\n `[Signaling] Unexpected ANSWER in state \"${pc.signalingState}\", ignoring`,\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n await this.flushIce(msg.sender, pc);\r\n } catch (err) {\r\n console.error(\"[Signaling] Failed to apply answer:\", err);\r\n this.emitError(\r\n \"ANSWER_FAILED\",\r\n `Failed to apply answer from ${msg.sender}`,\r\n err,\r\n true,\r\n );\r\n }\r\n break;\r\n }\r\n case \"ICE\": {\r\n const candidate = JSON.parse(msg.payload);\r\n\r\n let pc = this.peers[msg.sender];\r\n\r\n if (!pc) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n if (!pc.remoteDescription) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"ICE error:\", err);\r\n }\r\n\r\n break;\r\n }\r\n case \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\r\n\r\n // Trigger your event so the UI knows to render the stage\r\n this.events.onScreenShareStarted?.(msg.presenterId, null!);\r\n }\r\n\r\n for (const p of msg.participants || []) {\r\n if (!p?.id || p.id === this.myId) continue;\r\n this.state.addParticipant(p);\r\n this.events.onUserJoined?.(p);\r\n await this.createOffer(p.id);\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n this.intentionalDisconnect = false;\r\n this.reconnectAttempts = 0;\r\n this.startHeartbeat();\r\n this.joinResolver?.();\r\n this.joinResolver = undefined;\r\n this.joinRejecter = undefined;\r\n break;\r\n }\r\n case \"USER_JOINED\": {\r\n const p = msg.participant;\r\n\r\n if (!p?.id || p.id === this.myId) return;\r\n\r\n this.state.addParticipant(p);\r\n\r\n this.events.onUserJoined?.(p);\r\n await this.createOffer(p.id);\r\n\r\n break;\r\n }\r\n\r\n case \"USER_LEFT\":\r\n const peerId = msg.participant.id;\r\n this.closePeer(peerId);\r\n\r\n this.state.removeParticipant(peerId);\r\n\r\n this.events.onUserLeft?.(peerId);\r\n\r\n break;\r\n\r\n case \"MEDIA_STATE_CHANGE\": {\r\n const peerId = msg.peerId;\r\n const { kind, enabled } = msg;\r\n\r\n // 1. Sync the app state layer for UI rendering components\r\n if (kind === \"audio\") {\r\n this.state.updateParticipantMedia(peerId, { micEnabled: enabled });\r\n this.events.onMicToggled?.(peerId, enabled);\r\n } else if (kind === \"video\") {\r\n this.state.updateParticipantMedia(peerId, { camEnabled: enabled });\r\n this.events.onCamToggled?.(peerId, enabled);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"CHAT_MESSAGE\": {\r\n const newMsg = msg.data;\r\n\r\n if (newMsg.sender_id === this.myId) break; // already added optimistically\r\n this.state.addChatMessage({\r\n id: newMsg.id,\r\n text: newMsg.message,\r\n sender_id: newMsg.sender_id,\r\n sender_name: newMsg.sender_name,\r\n timestamp: new Date(newMsg.timestamp).getTime(),\r\n target: newMsg.target,\r\n });\r\n\r\n this.events.onChatMessage?.(msg);\r\n break;\r\n }\r\n case \"SCREEN_SHARE_START\": {\r\n const peerId = msg.peerId;\r\n\r\n this.state.updateParticipantMedia(peerId, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: msg.stream_id,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(peerId);\r\n }\r\n\r\n // Fix: Use screenStream instead of the regular camera stream\r\n const screenStream =\r\n this.state.getParticipant(peerId)?.media?.screenStream;\r\n\r\n this.events.onScreenShareStarted?.(peerId, screenStream || null!);\r\n break;\r\n }\r\n\r\n case \"SCREEN_SHARE_STOP\": {\r\n const peerId = msg.peerId;\r\n this.state.updateParticipantMedia(peerId, { isScreenSharing: false });\r\n if (this.state.presenterId === peerId) {\r\n this.state.setPresenterId(null);\r\n }\r\n this.events.onScreenShareStopped?.(peerId);\r\n break;\r\n }\r\n case \"ERROR\": {\r\n const fatal = msg?.fatal === true;\r\n\r\n this.emitError(\r\n \"WS_ERROR\",\r\n msg?.message || \"Unknown error\",\r\n msg,\r\n !fatal,\r\n );\r\n\r\n if (fatal) {\r\n this.disconnect();\r\n }\r\n\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // ---------------- PEER ----------------\r\n private createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n console.log(\r\n \"Adding tracks\",\r\n this.localStream.getTracks().map((t) => ({\r\n kind: t.kind,\r\n enabled: t.enabled,\r\n state: t.readyState,\r\n })),\r\n );\r\n\r\n const pc = new RTCPeerConnection({\r\n iceServers: [\r\n {\r\n urls: \"stun:stun.relay.metered.ca:80\",\r\n },\r\n {\r\n urls: \"turn:global.relay.metered.ca:80?transport=tcp\",\r\n username: \"25aed888d2d360e9fae0e812\",\r\n credential: \"WPYstojO9Wf3+HsQ\",\r\n },\r\n ],\r\n iceTransportPolicy: \"relay\",\r\n });\r\n\r\n pc.ontrack = (event) => {\r\n console.log(`Track received: ${event.track.kind}`, {\r\n trackId: event.track.id,\r\n streamCount: event.streams.length,\r\n streamTrackCount: event.streams[0]?.getTracks().length,\r\n });\r\n const incomingStream =\r\n event.streams?.[0] || new MediaStream([event.track]);\r\n const participant = this.state.getParticipant(id);\r\n\r\n const isScreenStream =\r\n incomingStream.id === participant?.media?.remoteScreenStreamId;\r\n\r\n if (event.track.kind === \"video\") {\r\n event.track.onunmute = () => {\r\n console.log(\"Video track unmuted for\", id);\r\n };\r\n }\r\n\r\n if (isScreenStream) {\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0] ||\r\n participant?.media?.screenTrack;\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\r\n\r\n isScreenSharing: true,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(id);\r\n }\r\n\r\n this.events.onScreenShareStarted?.(id, incomingStream);\r\n } else {\r\n this.state.updateParticipantMedia(id, {\r\n stream: incomingStream,\r\n cameraTrack: incomingStream.getVideoTracks()[0],\r\n audioTrack: incomingStream.getAudioTracks()[0],\r\n });\r\n this.events.onTrack?.(incomingStream, id);\r\n }\r\n };\r\n\r\n pc.onicecandidate = (e) => {\r\n if (!e.candidate) return;\r\n this.send({\r\n type: \"ICE\",\r\n payload: JSON.stringify(e.candidate),\r\n sender: this.myId,\r\n target: id,\r\n });\r\n };\r\n\r\n pc.oniceconnectionstatechange = () => {\r\n console.log(`ICE Connection State: ${pc.iceConnectionState}`);\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch {}\r\n }\r\n };\r\n\r\n this.localStream.getTracks().forEach((track) => {\r\n pc.addTrack(track, this.localStream!);\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n this.screenSenders[id] = [];\r\n this.screenStream.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[id].push(sender);\r\n });\r\n }\r\n\r\n return pc;\r\n }\r\n\r\n // ---------------- OFFER ----------------\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n if (!isRenegotiation && this.initiators.has(id)) {\r\n console.debug(\r\n `[Offer] Already initiating with ${id}, skipping duplicate`,\r\n );\r\n }\r\n\r\n if (isRenegotiation && this.peers[id]) {\r\n const pc = this.peers[id];\r\n if (pc.signalingState !== \"stable\") {\r\n console.warn(\r\n `[Offer] Cannot renegotiate: peer in state \"${pc.signalingState}\"`,\r\n );\r\n }\r\n }\r\n\r\n if (!isRenegotiation) {\r\n this.initiators.add(id);\r\n }\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n this.send({\r\n type: \"OFFER\",\r\n payload: offer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n\r\n console.debug(`[Offer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Offer] Failed for ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_FAILED\",\r\n `Failed to create offer for ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n // ---------------- ANSWER ----------------\r\n private async handleOffer(sdp: string, id: string) {\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n // ✅ Only set remote description if we're not already in negotiation\r\n if (\r\n pc.signalingState !== \"stable\" &&\r\n pc.signalingState !== \"have-local-offer\"\r\n ) {\r\n console.warn(\r\n `[Signaling] Cannot accept OFFER in state \"${pc.signalingState}\"`,\r\n );\r\n return;\r\n }\r\n\r\n await pc.setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"[ICE] Failed to add candidate:\", err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await pc.createAnswer();\r\n\r\n await pc.setLocalDescription(answer);\r\n await this.flushIce(id, pc);\r\n\r\n this.send({\r\n type: \"ANSWER\",\r\n payload: answer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n\r\n console.debug(`[Answer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Signaling] Failed to handle OFFER from ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_HANDLING_FAILED\",\r\n `Failed to handle offer from ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n // ---------------- CLEANUP ----------------\r\n private closePeer(id: string) {\r\n const pc = this.peers[id];\r\n\r\n if (!pc) return;\r\n\r\n pc.ontrack = null;\r\n pc.onicecandidate = null;\r\n pc.onconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n async startScreenShare() {\r\n try {\r\n if (this.state.presenterId && this.state.presenterId !== this.myId) {\r\n throw new Error(\"Another user is already sharing their screen.\");\r\n }\r\n if (!navigator.mediaDevices?.getDisplayMedia) {\r\n throw new Error(\"Screen sharing not supported on this device\");\r\n }\r\n\r\n this.screenStream = await navigator.mediaDevices.getDisplayMedia({\r\n video: true,\r\n // audio: true,\r\n });\r\n\r\n this.isScreenSharing = true;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: true,\r\n screenStream: this.screenStream,\r\n screenTrack: this.screenStream.getVideoTracks()[0],\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n // Handle the user clicking browser's built-in \"Stop Sharing\" button\r\n this.screenStream.getVideoTracks()[0].onended = () => {\r\n this.stopScreenShare();\r\n };\r\n\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n this.screenSenders[peerId] = [];\r\n this.screenStream!.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[peerId].push(sender);\r\n });\r\n\r\n // Renegotiate peer connection descriptors to notify remote side of new track footprint\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n return this.screenStream;\r\n } catch (err: any) {\r\n this.emitError(\r\n \"SCREEN_SHARE_FAILED\",\r\n err?.message || \"Failed to start screen sharing\",\r\n err,\r\n true,\r\n );\r\n\r\n this.isScreenSharing = false;\r\n this.screenStream = null;\r\n throw err;\r\n }\r\n }\r\n\r\n stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Remove tracks cleanly from WebRTC channel pathways across your peers\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n const senders = this.screenSenders[peerId] || [];\r\n senders.forEach((sender) => {\r\n try {\r\n pc.removeTrack(sender);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n });\r\n delete this.screenSenders[peerId];\r\n\r\n // Renegotiate layout expectations to scale down stream bounds\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.screenStream = null;\r\n this.isScreenSharing = false;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: false,\r\n screenStream: null,\r\n screenTrack: undefined,\r\n },\r\n });\r\n\r\n if (this.state.presenterId === this.myId) {\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_STOP\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n });\r\n }\r\n\r\n sendChatMessage(payload: ChatInput) {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n console.warn(\"WS not connected\");\r\n return;\r\n }\r\n\r\n if (!this.roomId) {\r\n console.warn(\"No roomId set\");\r\n return;\r\n }\r\n\r\n const isPrivate = !!payload?.target;\r\n\r\n const senderName = this.state.localParticipant?.name || \"Anonymous\";\r\n\r\n const msg: ChatMessage = {\r\n id: crypto.randomUUID(),\r\n sender_id: this.myId,\r\n sender_name: senderName,\r\n text: payload.message.trim(),\r\n timestamp: Date.now(),\r\n reply_to: payload.reply_to ?? null,\r\n target: payload.target ?? null,\r\n };\r\n\r\n // optimistic UI update\r\n this.state.addChatMessage(msg);\r\n\r\n // send protocol payload (clean + consistent)\r\n this.send({\r\n type: \"CHAT_MESSAGE\",\r\n message: payload.message.trim(),\r\n user_id: this.myId,\r\n sender_name: senderName,\r\n room_id: this.roomId,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n disconnect() {\r\n this.intentionalDisconnect = true;\r\n this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n\r\n if (this.localStream) {\r\n this.localStream.getTracks().forEach((track) => track.stop());\r\n this.localStream = null;\r\n }\r\n\r\n this.roomId = null;\r\n\r\n // Clear and notify\r\n this.state.localParticipant = null;\r\n this.state.notify(\"localParticipant\");\r\n\r\n this.state.participants.clear();\r\n this.state.notify(\"participants\");\r\n\r\n this.state.clearChat();\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n private async flushIce(id: string, pc: RTCPeerConnection) {\r\n const pending = this.pendingIceCandidates[id];\r\n if (!pending?.length) return;\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (e) {\r\n console.warn(\"ICE flush error\", e);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n }\r\n\r\n private send(msg: any) {\r\n this.ws?.send(JSON.stringify(msg));\r\n }\r\n}\r\n","import { useEffect, useState } from \"react\";\r\nimport { StateScope } from \"../types/meeting\";\r\nimport { MeetingState } from \"../core/MeetingState\";\r\n\r\nexport function useMeetingStore<T>(\r\n stateManager: MeetingState,\r\n scope: StateScope,\r\n selector: (state: MeetingState) => T,\r\n): T {\r\n const [state, setState] = useState(() => selector(stateManager));\r\n\r\n useEffect(() => {\r\n // Update local react state whenever the SDK notifies this scope\r\n const unsubscribe = stateManager.subscribe(scope, () => {\r\n setState(selector(stateManager));\r\n });\r\n\r\n return unsubscribe;\r\n }, [stateManager, scope, selector]);\r\n\r\n return state;\r\n}\r\n","import { useEffect } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\n\r\nexport const useMeeting = (handlers?: { onError?: (err: any) => void }) => {\r\n const ctx = useMeetingContext();\r\n\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n\r\n const unsubscribe = ctx.onError(handlers.onError);\r\n return unsubscribe;\r\n }, [handlers?.onError]);\r\n\r\n return ctx;\r\n};\r\n","import { useEffect, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useParticipants = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n const [participants, setParticipants] = useState<Participant[]>(() =>\r\n sdk.state.getParticipants(),\r\n );\r\n\r\n useEffect(() => {\r\n const update = () => {\r\n setParticipants(sdk.state.getParticipants());\r\n };\r\n\r\n update();\r\n\r\n const unsub = sdk.state.subscribe(\"participants\", update);\r\n\r\n return unsub;\r\n }, [sdk]);\r\n\r\n return participants;\r\n};\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useRemoteMedia = (participantId: string) => {\r\n const { sdk } = useMeetingContext();\r\n const videoRef = useRef<HTMLVideoElement | null>(null);\r\n const audioRef = useRef<HTMLAudioElement | null>(null);\r\n\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\r\n );\r\n\r\n useEffect(() => {\r\n const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {\r\n const p = sdk.state.getParticipant(participantId);\r\n setParticipant(p ? { ...p } : null);\r\n });\r\n\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n useEffect(() => {\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n // VIDEO\r\n if (videoRef.current) {\r\n videoRef.current.srcObject = stream;\r\n videoRef.current.muted = true;\r\n videoRef.current.playsInline = true;\r\n videoRef.current.play().catch(() => {});\r\n }\r\n\r\n // AUDIO\r\n if (audioRef.current) {\r\n audioRef.current.srcObject = stream;\r\n audioRef.current.play().catch(() => {});\r\n }\r\n }, [participant?.media?.stream]);\r\n\r\n return {\r\n videoRef,\r\n audioRef,\r\n isCamActive: !!participant?.media?.camEnabled,\r\n isMicEnabled: !!participant?.media?.micEnabled,\r\n };\r\n};\r\n"],"mappings":"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","names":["useEffect","useRef","useState","peerId","pc","useState","useEffect","useRef","useEffect","useEffect","useEffect","useState","useState","useEffect","useEffect","useRef","useState","useRef","useState","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afosecure/meetingsdk",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "A modern, lightweight React SDK for building peer-to-peer video communication applications. Built on WebRTC with a clean, composable rust API.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",