@streaming-cdn/rtc-web 1.3.21 → 1.3.23

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.
@@ -1 +1 @@
1
- (function(b,B){typeof exports=="object"&&typeof module<"u"?B(exports):typeof define=="function"&&define.amd?define(["exports"],B):(b=typeof globalThis<"u"?globalThis:b||self,B(b.StreamingCdnRtc={}))})(this,(function(b){"use strict";function H(a,e,t=.02,i=1.4){const n=a.filter(r=>r.level>=t);if(n.length===0)return null;const s=n.reduce((r,c)=>c.level>r.level?c:r),o=n.find(r=>r.participantId===e);return o?s.participantId===e?e:s.level>=o.level*i?s.participantId:e:s.participantId}function ee(a){const e=[];for(const t of a){if(String(t.type??"")!=="inbound-rtp"||String(t.kind??t.mediaType??"")!=="audio")continue;const i=Number(t.audioLevel??NaN);Number.isFinite(i)&&e.push({mid:typeof t.mid=="string"?t.mid:null,level:i})}return e}const V={audio:{active:!1,maxBitrate:0,maxFramerate:0,scaleResolutionDownBy:4},low:{active:!0,maxBitrate:35e4,maxFramerate:15,scaleResolutionDownBy:4},medium:{active:!0,maxBitrate:11e5,maxFramerate:24,scaleResolutionDownBy:2},high:{active:!0,maxBitrate:28e5,maxFramerate:30,scaleResolutionDownBy:1}},Te={warmupSamples:3,poorSamplesToStepDown:2,goodSamplesToStepUp:6,criticalLossPct:8,criticalRttMs:800,poorLossPct:3,poorRttMs:350,goodLossPct:1,goodRttMs:250,sustainRatio:.8,bandwidthHeadroomRatio:1.25};function be(a){const e=String(a??"").trim().toLowerCase();return!e||e==="none"?"":e==="cpu"||e==="bandwidth"?e:"other"}function Ce(a,e,t,i,n={}){const s={...Te,...n.thresholds},o=["low","medium","high"],r=n.ceiling&&n.ceiling!=="audio"?n.ceiling:"high",c=o.indexOf(r),l=o.indexOf(a),d=(n.samplesSeen??s.warmupSamples)+1,u=d<=s.warmupSamples,k=be(e.qualityLimitationReason),f=e.packetLossPct??0,v=e.rttMs??0,R=e.availableOutgoingBitrateBps,y=V[a].maxBitrate,w=f>=s.criticalLossPct||v>=s.criticalRttMs||k==="cpu",p=k==="bandwidth"&&(R==null||R<y*s.bandwidthHeadroomRatio),I=w||f>=s.poorLossPct||v>=s.poorRttMs||p,A=R==null||R>=y*s.sustainRatio,D=f<s.goodLossPct&&v<s.goodRttMs&&!k&&A,E=I?t+1:0,L=D?i+1:0,S={quality:a,poorSamples:E,goodSamples:L,samplesSeen:d,changed:!1};return l>c?{quality:o[c],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:u?S:w&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:E>=s.poorSamplesToStepDown&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:L>=s.goodSamplesToStepUp&&l<c?{quality:o[l+1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:S}function Re(a,e){const t=a instanceof Map?a:new Map(a);let i=0,n=0,s=null,o=null,r=null;for(const d of t.values()){const u=String(d.type??"");if(u==="outbound-rtp"&&String(d.kind??"")==="video"&&(i+=Number(d.packetsSent??0),s=d.qualityLimitationReason??s),u==="remote-inbound-rtp"&&String(d.kind??"")==="video"){n+=Number(d.packetsLost??0);const k=Number(d.roundTripTime??NaN);Number.isFinite(k)&&(o=k*1e3)}if(u==="candidate-pair"&&(d.nominated===!0||String(d.state??"")==="succeeded")){const k=Number(d.currentRoundTripTime??NaN);o==null&&Number.isFinite(k)&&(o=k*1e3);const f=Number(d.availableOutgoingBitrate??NaN);Number.isFinite(f)&&(r=f)}}const c={packetsSent:i,packetsLost:n,timestamp:Date.now()};let l=null;if(e){const d=i-e.packetsSent,u=n-e.packetsLost;d>0&&u>=0&&(l=u/(d+u)*100)}return{sample:{packetLossPct:l,rttMs:o,availableOutgoingBitrateBps:r,qualityLimitationReason:s},counters:c}}const Ie={audio:{active:!1,maxBitrate:0,maxFramerate:0,scaleResolutionDownBy:4}},Ee={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1};class x{options;socket=null;localStream=null;screenStream=null;peers=new Map;joinPromise=null;resolveJoin=null;rejectJoin=null;joined=!1;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;qualityTimer=null;speakerTimer=null;activeSpeaker=null;heartbeatDeadline=0;quality;preferredVideoCodec;signalQueue=Promise.resolve();socketGeneration=0;constructor(e){this.options=e,this.localStream=e.localStream??null,this.quality=e.quality??"high",this.preferredVideoCodec=e.preferredVideoCodec??"auto"}async initialize(){if(!globalThis.RTCPeerConnection||!globalThis.WebSocket)throw new Error("This browser does not support WebRTC")}async join(){if(!this.joined){if(this.joinPromise)return this.joinPromise;this.joinPromise=this.connect();try{await this.joinPromise}finally{this.joinPromise=null}}}async leave(){this.stopped=!0,this.clearConnectionTimers(),this.send({type:"rtc.leave"}),this.joined=!1,this.socket?.close(1e3,"Participant left"),this.socket=null;for(const e of this.peers.values())e.connection.close();this.peers.clear(),this.stopStream(this.localStream),this.stopStream(this.screenStream),this.localStream=null,this.screenStream=null}setMuted(e){for(const t of this.localStream?.getAudioTracks()??[])t.enabled=!e}async setCameraEnabled(e){if(e&&!this.localStream?.getVideoTracks().length){const i=(await navigator.mediaDevices.getUserMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}})).getVideoTracks()[0];if(!i)throw new Error("Camera is unavailable");this.localStream||(this.localStream=new MediaStream),this.localStream.addTrack(i),this.screenStream?.getVideoTracks().some(n=>n.readyState==="live")||await this.replaceVideoTrack(i);return}for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e}async setScreenShareEnabled(e){if(!e){const n=this.screenStream;this.screenStream=null,this.stopStream(n);const s=this.localStream?.getVideoTracks()[0]??null;await this.replaceVideoTrack(s),this.options.onEvent("screensharechange",{enabled:!1,stream:null});return}if(this.screenStream?.getVideoTracks()[0]?.readyState==="live")return;const t=await navigator.mediaDevices.getDisplayMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}},audio:!1}),i=t.getVideoTracks()[0];if(!i)throw new Error("Screen sharing is unavailable");i.contentHint="detail",this.screenStream=t,i.addEventListener("ended",()=>{this.screenStream?.getVideoTracks()[0]?.id===i.id&&this.setScreenShareEnabled(!1)});try{await this.replaceVideoTrack(i),this.options.onEvent("screensharechange",{enabled:!0,stream:t})}catch(n){throw this.screenStream=null,this.stopStream(t),n}}async setDevice(e){const t={deviceId:{exact:e.deviceId}},i=await navigator.mediaDevices.getUserMedia(e.kind==="audioinput"?{audio:t}:{video:{...t,width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}}),n=e.kind==="audioinput"?i.getAudioTracks()[0]:i.getVideoTracks()[0];if(!n)throw new Error("Selected media device is unavailable");const s=e.kind==="audioinput"?"audio":"video",o=this.localStream?.getTracks().find(r=>r.kind===s);if(this.localStream||(this.localStream=new MediaStream),o&&(this.localStream.removeTrack(o),o.stop()),this.localStream.addTrack(n),s==="video"){this.screenStream?.getVideoTracks().some(r=>r.readyState==="live")||await this.replaceVideoTrack(n);return}for(const r of this.peers.values()){const c=r.connection.getSenders().find(l=>l.track?.kind===s);c?await c.replaceTrack(n):r.connection.addTrack(n,this.localStream)}await this.applyVideoQuality()}async setQuality(e){this.quality=e;for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e!=="audio";for(const t of this.peers.values())t.quality=x.clampToCeiling(t.quality,e),t.poorSamples=0,t.goodSamples=0;await this.applyVideoQuality()}static clampToCeiling(e,t){if(t==="audio")return e;const i=["low","medium","high"];return i.indexOf(e)>i.indexOf(t)?t:e}async setPreferredVideoCodec(e){this.preferredVideoCodec=e;for(const t of this.peers.values())this.applyCodecPreference(t.connection);await this.renegotiateAll()}getParticipants(){return[...this.peers.values()].map(e=>e.identity)}getLocalStream(){return this.localStream}async setVideoTrack(e){if(e.kind!=="video")throw new Error("A video track is required");this.localStream||(this.localStream=new MediaStream);const t=this.localStream.getVideoTracks()[0];t?.id!==e.id&&(t&&this.localStream.removeTrack(t),this.localStream.addTrack(e)),await this.replaceVideoTrack(e),this.options.onEvent("localstream",{stream:this.localStream,track:e})}getRemoteStreams(){return[...this.peers.values()].map(e=>({participant:e.identity,stream:e.stream}))}getPrimaryPeerConnection(){return this.peers.values().next().value?.connection??null}sendChat(e,t){this.send({type:"rtc.chat",message:e,participantIds:t??[]})}sendControl(e,t={}){this.send({type:"rtc.control",action:e,detail:t})}async connect(){const{credential:e}=this.options;if(!e.signalingToken||!e.signalingUrl||!e.roomId||!e.participantId)throw new Error("A room signaling credential is required");this.stopped=!1,!this.localStream&&(this.options.audio||this.options.video)&&(this.localStream=await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})),this.localStream&&this.options.onEvent("localstream",{stream:this.localStream}),await this.openSocket(!1)}async openSocket(e){const{credential:t}=this.options,i=new URL(t.signalingUrl,globalThis.location?.href);i.protocol=i.protocol==="https:"?"wss:":"ws:";const n=new Promise((c,l)=>{this.resolveJoin=c,this.rejectJoin=l}),s=new WebSocket(i,["rtc-client",t.signalingToken]),o=++this.socketGeneration;this.socket=s;const r=setTimeout(()=>this.rejectJoin?.(new Error("RTC signaling timed out")),15e3);s.addEventListener("open",()=>{this.heartbeatDeadline=Date.now()+45e3,this.startHeartbeat(s),this.startQualitySampling(),this.send({type:"rtc.room.join"})}),s.addEventListener("message",c=>{const l=String(c.data);this.signalQueue=this.signalQueue.then(async()=>{this.socket!==s||this.socketGeneration!==o||await this.handleSignal(l)}).catch(d=>{const u=d instanceof Error?d.message:"RTC signaling processing failed";this.options.onEvent("signalingerror",{message:u}),this.joined||this.rejectJoin?.(new Error(u))})}),s.addEventListener("error",()=>this.rejectJoin?.(new Error("RTC signaling connection failed"))),s.addEventListener("close",c=>{if(this.socket!==s)return;this.socket=null,this.stopHeartbeat(),this.stopQualitySampling(),!this.joined&&!e&&this.rejectJoin?.(new Error(c.reason||"RTC signaling connection closed"));const l=!this.stopped&&(this.joined||e);this.joined=!1,l?(this.resetPeers(),this.options.onEvent("reconnecting",{reason:c.reason,code:c.code,attempt:this.reconnectAttempt+1}),this.scheduleReconnect()):this.stopped||this.options.onEvent("disconnected",{reason:c.reason,code:c.code})});try{await n,this.joined=!0,this.options.onEvent("connected",{participantId:t.participantId,resumed:e})}finally{clearTimeout(r),this.resolveJoin=null,this.rejectJoin=null}}async handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i!=="ready"){if(i==="pong"){this.heartbeatDeadline=Date.now()+45e3;return}if(i==="rtc.room.peers"){this.reconnectAttempt=0;const n=Array.isArray(t.peers)?t.peers:[];for(const s of n)this.ensurePeer(s);this.resolveJoin?.();for(const s of n)this.shouldInitiateOffer(s)&&await this.createOffer(s);return}if(i==="rtc.room.join"&&t.sender){this.options.onEvent("participantjoined",t.sender),this.ensurePeer(t.sender),this.shouldInitiateOffer(t.sender)&&await this.createOffer(t.sender);return}if(i==="rtc.offer"&&t.sender&&t.description){const n=this.ensurePeer(t.sender);n.offerTask&&await n.offerTask;const s=n.connection.signalingState!=="stable",o=!this.shouldInitiateOffer(t.sender);if(s&&!o)return;s&&await n.connection.setLocalDescription({type:"rollback"}),await n.connection.setRemoteDescription(t.description);const r=await n.connection.createAnswer();await n.connection.setLocalDescription(r),this.send({type:"rtc.answer",targetParticipantId:t.sender.participantId,negotiationId:t.negotiationId,description:n.connection.localDescription});return}if(i==="rtc.answer"&&t.sender&&t.description){const n=this.peers.get(t.sender.participantId);if(!n||n.connection.signalingState!=="have-local-offer"||t.negotiationId&&n.activeNegotiationId&&t.negotiationId!==n.activeNegotiationId)return;await n.connection.setRemoteDescription(t.description),n.activeNegotiationId=null;return}if(i==="rtc.ice"&&t.sender&&t.candidate){await this.ensurePeer(t.sender).connection.addIceCandidate(t.candidate).catch(()=>{});return}if((i==="rtc.peer.left"||i==="rtc.leave")&&t.sender){this.removePeer(t.sender.participantId);return}i==="rtc.chat"?this.options.onEvent("chatupdate",t):i==="rtc.control"&&this.options.onEvent("control",t)}}ensurePeer(e){const t=this.peers.get(e.participantId);if(t)return t;const i=new RTCPeerConnection({iceServers:this.options.credential.iceServers??[],iceTransportPolicy:this.options.iceTransportPolicy??"all"}),n=new MediaStream,s={identity:e,connection:i,stream:n,makingOffer:!1,offerTask:null,activeNegotiationId:null,quality:x.startingQuality(this.quality),poorSamples:0,goodSamples:0,samplesSeen:0,counters:null};this.peers.set(e.participantId,s);for(const c of this.localStream?.getAudioTracks()??[])i.addTrack(c,this.localStream);const o=this.screenStream?.getVideoTracks().some(c=>c.readyState==="live")?this.screenStream:this.localStream,r=o?.getVideoTracks()[0];return r&&o&&(s.videoSender=i.addTrack(r,o)),!this.localStream?.getAudioTracks().length&&this.options.receiveAudio&&i.addTransceiver("audio",{direction:"recvonly"}),!r&&this.options.receiveVideo&&(s.videoTransceiver=i.addTransceiver("video",{direction:"recvonly"}),s.videoSender=s.videoTransceiver.sender),this.applyCodecPreference(i),this.applyVideoQuality(i),i.addEventListener("icecandidate",c=>{c.candidate&&this.send({type:"rtc.ice",targetParticipantId:e.participantId,candidate:c.candidate.toJSON()})}),i.addEventListener("track",c=>{n.getTracks().some(l=>l.id===c.track.id)||n.addTrack(c.track),this.options.onEvent("remotestream",{participant:e,stream:n,track:c.track})}),i.addEventListener("connectionstatechange",()=>{["failed","closed"].includes(i.connectionState)&&this.removePeer(e.participantId),this.options.onEvent("peerconnectionstate",{participant:e,state:i.connectionState})}),s}async createOffer(e){const t=this.ensurePeer(e);if(t.offerTask)return t.offerTask;if(t.connection.signalingState!=="stable")return;const i=this.performOffer(t);t.offerTask=i;try{await i}finally{t.offerTask===i&&(t.offerTask=null)}}async performOffer(e){e.makingOffer=!0;try{const t=await e.connection.createOffer({offerToReceiveAudio:this.options.receiveAudio,offerToReceiveVideo:this.options.receiveVideo});if(e.connection.signalingState!=="stable")return;await e.connection.setLocalDescription(t);const i=te();e.activeNegotiationId=i,this.send({type:"rtc.offer",targetParticipantId:e.identity.participantId,negotiationId:i,description:e.connection.localDescription})}finally{e.makingOffer=!1}}shouldInitiateOffer(e){return this.options.credential.participantId.localeCompare(e.participantId)<0}async renegotiateAll(){for(const e of this.peers.values())await this.createOffer(e.identity)}async replaceVideoTrack(e){let t=!1;for(const i of this.peers.values()){const n=i.videoSender??i.connection.getSenders().find(o=>o.track?.kind==="video")??i.connection.getTransceivers().find(o=>o.receiver.track.kind==="video")?.sender,s=i.videoTransceiver??i.connection.getTransceivers().find(o=>o.sender===n);if(n){const o=e?this.options.receiveVideo?"sendrecv":"sendonly":this.options.receiveVideo?"recvonly":"inactive";s&&s.direction!==o&&(s.direction=o,t=!0),await n.replaceTrack(e),i.videoSender=n,s&&(i.videoTransceiver=s)}else e&&(i.videoSender=i.connection.addTrack(e,this.screenStream??this.localStream??new MediaStream([e])),t=!0)}t&&await this.renegotiateAll(),await this.applyVideoQuality()}async applyVideoQuality(e){const t=e?[...this.peers.values()].filter(i=>i.connection===e):[...this.peers.values()];for(const i of t)await this.applyPeerVideoQuality(i)}async applyPeerVideoQuality(e){const t=this.screenStream?.getVideoTracks()[0]?.id;for(const i of e.connection.getSenders().filter(n=>n.track?.kind==="video")){const n=!!(t&&i.track?.id===t),s=n?Ee:this.quality==="audio"?Ie.audio:V[e.quality],o=i.getParameters(),r=o.encodings?.length?o.encodings:[{}];o.encodings=r.map(c=>({...c,...s})),o.degradationPreference=n?"maintain-resolution":"maintain-framerate",await i.setParameters(o)}}startQualitySampling(){this.stopQualitySampling(),this.qualityTimer=setInterval(()=>{this.samplePeerQuality()},5e3),this.speakerTimer=setInterval(()=>{this.sampleActiveSpeaker()},1e3)}stopQualitySampling(){this.qualityTimer&&clearInterval(this.qualityTimer),this.qualityTimer=null,this.speakerTimer&&clearInterval(this.speakerTimer),this.speakerTimer=null}async sampleActiveSpeaker(){const e=[];for(const i of[...this.peers.values()])if(i.connection.connectionState==="connected")try{const n=await i.connection.getStats();for(const s of ee(n.values()))e.push({participantId:i.identity.participantId,level:s.level})}catch{}const t=H(e,this.activeSpeaker);t!==this.activeSpeaker&&(this.activeSpeaker=t,this.options.onEvent("activespeaker",{participantId:t}))}async samplePeerQuality(){if(this.quality!=="audio")for(const e of[...this.peers.values()]){if(e.connection.connectionState!=="connected")continue;let t;try{t=await e.connection.getStats()}catch{continue}const{sample:i,counters:n}=Re(t,e.counters);e.counters=n;const s=Ce(e.quality,i,e.poorSamples,e.goodSamples,{samplesSeen:e.samplesSeen,ceiling:this.quality});e.poorSamples=s.poorSamples,e.goodSamples=s.goodSamples,e.samplesSeen=s.samplesSeen,s.changed&&(e.quality=s.quality,await this.applyPeerVideoQuality(e).catch(()=>{}),this.options.onEvent("peerquality",{participantId:e.identity.participantId,quality:s.quality}))}}static startingQuality(e){return e==="low"?"low":"medium"}applyCodecPreference(e){if(this.preferredVideoCodec==="auto"||!globalThis.RTCRtpReceiver?.getCapabilities)return;const t=RTCRtpReceiver.getCapabilities("video")?.codecs??[],i=`video/${this.preferredVideoCodec}`,n=[...t.filter(s=>s.mimeType.toLowerCase()===i),...t.filter(s=>s.mimeType.toLowerCase()!==i)];for(const s of e.getTransceivers())s.receiver.track.kind==="video"&&typeof s.setCodecPreferences=="function"&&s.setCodecPreferences(n)}removePeer(e){const t=this.peers.get(e);t&&(t.connection.close(),this.peers.delete(e),this.options.onEvent("participantleft",t.identity))}resetPeers(){for(const e of this.peers.values())e.connection.close();this.peers.clear()}startHeartbeat(e){this.stopHeartbeat(),this.stopQualitySampling(),this.heartbeatTimer=setInterval(()=>{if(!(this.socket!==e||e.readyState!==WebSocket.OPEN)){if(Date.now()>this.heartbeatDeadline){e.close(4e3,"Signaling heartbeat timed out");return}e.send("ping")}},2e4)}stopHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null}clearConnectionTimers(){this.stopHeartbeat(),this.stopQualitySampling(),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=null}scheduleReconnect(){if(this.reconnectTimer||this.stopped)return;const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.openSocket(!0).catch(t=>{this.options.onEvent("signalingerror",{message:t instanceof Error?t.message:"RTC signaling connection failed"}),this.scheduleReconnect()})},e)}send(e){this.socket?.readyState===WebSocket.OPEN&&this.socket.send(JSON.stringify({clientMessageId:e.clientMessageId??te(),...e}))}stopStream(e){for(const t of e?.getTracks()??[])t.stop()}}function te(){return globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}const z={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1},ie=[{rid:"a",maxBitrate:V.high.maxBitrate,maxFramerate:V.high.maxFramerate},{rid:"b",maxBitrate:V.medium.maxBitrate,maxFramerate:V.medium.maxFramerate,scaleResolutionDownBy:2},{rid:"c",maxBitrate:V.low.maxBitrate,maxFramerate:V.low.maxFramerate,scaleResolutionDownBy:4}],ne={high:"a",medium:"b",low:"c"};class Me{options;apiBase;socket=null;connection=null;sessionId=null;localStream=null;screenStream=null;remotes=new Map;midToParticipant=new Map;pendingAnnouncements=new Map;videoSender=null;joined=!1;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;heartbeatDeadline=0;speakerTimer=null;activeSpeaker=null;quality;resolveJoin=null;rejectJoin=null;signalQueue=Promise.resolve();negotiationQueue=Promise.resolve();socketGeneration=0;constructor(e){this.options=e,this.localStream=e.localStream??null,this.quality=e.quality??"high",this.apiBase=new URL(e.credential.signalingUrl,globalThis.location?.href).origin}async initialize(){if(!globalThis.RTCPeerConnection||!globalThis.WebSocket)throw new Error("This browser does not support WebRTC")}async join(){if(this.joined)return;const{credential:e}=this.options;if(!e.signalingToken||!e.signalingUrl||!e.roomId||!e.participantId)throw new Error("A room signaling credential is required");this.stopped=!1,!this.localStream&&(this.options.audio||this.options.video)&&(this.localStream=await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})),this.localStream&&this.options.onEvent("localstream",{stream:this.localStream}),await this.openSocket(!1),await this.publish(),this.startSpeakerSampling(),this.announceTracks();const t=[...this.pendingAnnouncements.values()];this.pendingAnnouncements.clear();for(const i of t)await this.handleTrackAnnouncement(i.sender,i.detail)}async leave(){this.stopped=!0,this.clearTimers(),this.send({type:"rtc.leave"}),this.joined=!1,this.socket?.close(1e3,"Participant left"),this.socket=null,await this.closePublishedTracks().catch(()=>{}),this.connection?.close(),this.connection=null,this.sessionId=null,this.remotes.clear(),this.midToParticipant.clear(),this.stopStream(this.localStream),this.stopStream(this.screenStream),this.localStream=null,this.screenStream=null}setMuted(e){for(const t of this.localStream?.getAudioTracks()??[])t.enabled=!e}async setCameraEnabled(e){if(e&&!this.localStream?.getVideoTracks().length){const i=(await navigator.mediaDevices.getUserMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}})).getVideoTracks()[0];if(!i)throw new Error("Camera is unavailable");this.localStream||(this.localStream=new MediaStream),this.localStream.addTrack(i),this.screenStream?.getVideoTracks().some(n=>n.readyState==="live")||await this.replaceVideoTrack(i);return}for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e}async setScreenShareEnabled(e){if(!e){const n=this.screenStream;this.screenStream=null,this.stopStream(n);const s=this.localStream?.getVideoTracks()[0]??null;await this.replaceVideoTrack(s),this.options.onEvent("screensharechange",{enabled:!1,stream:null});return}if(this.screenStream?.getVideoTracks()[0]?.readyState==="live")return;const t=await navigator.mediaDevices.getDisplayMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}},audio:!1}),i=t.getVideoTracks()[0];if(!i)throw new Error("Screen sharing is unavailable");i.contentHint="detail",this.screenStream=t,i.addEventListener("ended",()=>{this.screenStream?.getVideoTracks()[0]?.id===i.id&&this.setScreenShareEnabled(!1)});try{await this.replaceVideoTrack(i),this.options.onEvent("screensharechange",{enabled:!0,stream:t})}catch(n){throw this.screenStream=null,this.stopStream(t),n}}async setDevice(e){const t={deviceId:{exact:e.deviceId}},i=await navigator.mediaDevices.getUserMedia(e.kind==="audioinput"?{audio:t}:{video:{...t,width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}}),n=e.kind==="audioinput"?i.getAudioTracks()[0]:i.getVideoTracks()[0];if(!n)throw new Error("Selected media device is unavailable");const s=e.kind==="audioinput"?"audio":"video",o=this.localStream?.getTracks().find(c=>c.kind===s);if(this.localStream||(this.localStream=new MediaStream),o&&(this.localStream.removeTrack(o),o.stop()),this.localStream.addTrack(n),s==="video"){this.screenStream?.getVideoTracks().some(c=>c.readyState==="live")||await this.replaceVideoTrack(n);return}const r=this.connection?.getSenders().find(c=>c.track?.kind==="audio");r&&await r.replaceTrack(n)}async setQuality(e){this.quality=e;for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e!=="audio";await this.applyVideoQuality()}async setPreferredVideoCodec(e){}async setVideoTrack(e){if(e.kind!=="video")throw new Error("A video track is required");this.localStream||(this.localStream=new MediaStream);const t=this.localStream.getVideoTracks()[0];t?.id!==e.id&&(t&&this.localStream.removeTrack(t),this.localStream.addTrack(e)),await this.replaceVideoTrack(e),this.options.onEvent("localstream",{stream:this.localStream,track:e})}getParticipants(){return[...this.remotes.values()].map(e=>e.identity)}getLocalStream(){return this.localStream}getRemoteStreams(){return[...this.remotes.values()].map(e=>({participant:e.identity,stream:e.stream}))}getPrimaryPeerConnection(){return this.connection}sendChat(e,t){this.send({type:"rtc.chat",message:e,participantIds:t??[]})}sendControl(e,t={}){this.send({type:"rtc.control",action:e,detail:t})}async openSocket(e){const{credential:t}=this.options,i=new URL(t.signalingUrl,globalThis.location?.href);i.protocol=i.protocol==="https:"?"wss:":"ws:";const n=new Promise((c,l)=>{this.resolveJoin=c,this.rejectJoin=l}),s=new WebSocket(i,["rtc-client",t.signalingToken]),o=++this.socketGeneration;this.socket=s;const r=setTimeout(()=>this.rejectJoin?.(new Error("RTC signaling timed out")),15e3);s.addEventListener("open",()=>{this.heartbeatDeadline=Date.now()+45e3,this.startHeartbeat(s),this.send({type:"rtc.room.join"})}),s.addEventListener("message",c=>{const l=String(c.data);this.signalQueue=this.signalQueue.then(async()=>{this.socket!==s||this.socketGeneration!==o||await this.handleSignal(l)}).catch(d=>{const u=d instanceof Error?d.message:"RTC signaling processing failed";this.options.onEvent("signalingerror",{message:u}),this.joined||this.rejectJoin?.(new Error(u))})}),s.addEventListener("error",()=>this.rejectJoin?.(new Error("RTC signaling connection failed"))),s.addEventListener("close",c=>{if(this.socket!==s)return;this.socket=null,this.stopHeartbeat(),!this.joined&&!e&&this.rejectJoin?.(new Error(c.reason||"RTC signaling connection closed"));const l=!this.stopped&&(this.joined||e);this.joined=!1,l?(this.options.onEvent("reconnecting",{reason:c.reason,code:c.code,attempt:this.reconnectAttempt+1}),this.scheduleReconnect()):this.stopped||this.options.onEvent("disconnected",{reason:c.reason,code:c.code})});try{await n,this.joined=!0,this.options.onEvent("connected",{participantId:t.participantId,resumed:e})}finally{clearTimeout(r),this.resolveJoin=null,this.rejectJoin=null}}async handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i!=="ready"){if(i==="pong"){this.heartbeatDeadline=Date.now()+45e3;return}if(i==="rtc.room.peers"){this.reconnectAttempt=0,this.resolveJoin?.();return}if(i==="rtc.room.join"&&t.sender){this.options.onEvent("participantjoined",t.sender),this.sessionId&&this.announceTracks();return}if((i==="rtc.peer.left"||i==="rtc.leave")&&t.sender){this.removeRemote(t.sender.participantId);return}if(i==="rtc.control"){if(String(t.action??"")==="sfu.tracks"&&t.sender){await this.handleTrackAnnouncement(t.sender,t.detail);return}this.options.onEvent("control",t);return}i==="rtc.chat"&&this.options.onEvent("chatupdate",t)}}async publish(){const e=[...this.localStream?.getAudioTracks()??[],...this.localStream?.getVideoTracks()??[]],t=new RTCPeerConnection({iceServers:this.options.credential.iceServers??[],iceTransportPolicy:this.options.iceTransportPolicy??"all"});this.connection=t,t.addEventListener("track",r=>this.handleRemoteTrack(r)),t.addEventListener("connectionstatechange",()=>{this.options.onEvent("peerconnectionstate",{participant:this.selfIdentity(),state:t.connectionState})});const i=await this.sfuFetch("POST","/v1/rtc/sfu/sessions");if(this.sessionId=i.sessionId,e.length===0)return;const n=e.map(r=>t.addTransceiver(r,{direction:"sendonly",streams:this.localStream?[this.localStream]:[],...r.kind==="video"&&this.simulcastEnabled()?{sendEncodings:ie.map(c=>({...c}))}:{}})),s=await t.createOffer();await t.setLocalDescription(s);const o=await this.sfuFetch("POST",`/v1/rtc/sfu/sessions/${encodeURIComponent(i.sessionId)}/tracks`,{sessionDescription:t.localDescription,tracks:n.map(r=>({location:"local",mid:r.mid,trackName:this.trackName(r.sender.track)}))});o.sessionDescription&&await t.setRemoteDescription(o.sessionDescription),this.videoSender=t.getSenders().find(r=>r.track?.kind==="video")??null,await this.applyVideoQuality()}trackName(e){return`${this.options.credential.participantId}/${e.kind}`}announceTracks(){if(!this.sessionId)return;const e=[...(this.localStream?.getAudioTracks()??[]).map(t=>({trackName:this.trackName(t),kind:t.kind,simulcast:!1})),...(this.localStream?.getVideoTracks()??[]).map(t=>({trackName:this.trackName(t),kind:t.kind,simulcast:this.simulcastEnabled()}))];e.length!==0&&this.sendControl("sfu.tracks",{sessionId:this.sessionId,tracks:e})}async handleTrackAnnouncement(e,t){const i=String(t?.sessionId??""),n=(t?.tracks??[]).map(r=>({trackName:String(r.trackName??""),simulcast:r.simulcast===!0})).filter(r=>!!r.trackName),s=n.map(r=>r.trackName);if(!i||s.length===0||e.participantId===this.options.credential.participantId)return;if(!this.connection||!this.sessionId){this.pendingAnnouncements.set(e.participantId,{sender:e,detail:t??{}});return}const o=this.remotes.get(e.participantId)??{identity:e,stream:new MediaStream,sessionId:i,trackNames:[],simulcastNames:new Set,mids:new Map};o.sessionId=i;for(const r of n)r.simulcast&&o.simulcastNames.add(r.trackName);this.remotes.set(e.participantId,o),this.negotiationQueue=this.negotiationQueue.then(async()=>{const r=s.filter(c=>!o.trackNames.includes(c));r.length!==0&&await this.subscribe(o,r)}).catch(r=>{this.options.onEvent("signalingerror",{message:r instanceof Error?r.message:"SFU subscribe failed"})}),await this.negotiationQueue}async subscribe(e,t){const i=this.connection;if(!i||!this.sessionId||t.length===0)return;const n=await this.sfuFetch("POST",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks`,{tracks:t.map(s=>({location:"remote",sessionId:e.sessionId,trackName:s,...e.simulcastNames.has(s)?{simulcast:{preferredRid:"a",priorityOrdering:"asciibetical",ridNotAvailable:"asciibetical"}}:{}}))});for(const s of n.tracks??[])s.mid&&(e.mids.set(s.mid,String(s.trackName??"")),this.midToParticipant.set(s.mid,e.identity.participantId));if(e.trackNames=[...new Set([...e.trackNames,...t])],n.requiresImmediateRenegotiation&&n.sessionDescription){await i.setRemoteDescription(n.sessionDescription);const s=await i.createAnswer();await i.setLocalDescription(s),await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/renegotiate`,{sessionDescription:i.localDescription})}}handleRemoteTrack(e){const t=e.transceiver?.mid??null,i=t?this.midToParticipant.get(t):void 0,n=i?this.remotes.get(i):void 0;n&&(n.stream.getTracks().some(s=>s.id===e.track.id)||n.stream.addTrack(e.track),this.options.onEvent("remotestream",{participant:n.identity,stream:n.stream,track:e.track}))}removeRemote(e){const t=this.remotes.get(e);if(t){for(const i of t.mids.keys())this.midToParticipant.delete(i);this.remotes.delete(e),this.options.onEvent("participantleft",t.identity)}}async setRemoteQuality(e,t){const i=this.remotes.get(e);if(!i||!this.sessionId)return;const n=[...i.mids.entries()].find(([,s])=>i.simulcastNames.has(s))?.[0];n&&await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks/update`,{tracks:[{mid:n,simulcast:{preferredRid:ne[t],priorityOrdering:"asciibetical",ridNotAvailable:"asciibetical"}}]})}async replaceVideoTrack(e){this.videoSender&&(await this.videoSender.replaceTrack(e),await this.applyVideoQuality())}simulcastEnabled(){return this.options.simulcast!==!1}async applyVideoQuality(){const e=this.videoSender;if(!e)return;const t=this.screenStream?.getVideoTracks()[0]?.id,i=!!(t&&e.track?.id===t),n=e.getParameters(),s=n.encodings?.length?n.encodings:[{}];if(s.length>1){const o=this.quality==="audio"?null:ne[this.quality];n.encodings=s.map(r=>{if(i)return{...r,active:r.rid==="a",...r.rid==="a"?{maxBitrate:z.maxBitrate,maxFramerate:z.maxFramerate}:{}};const c=ie.find(d=>d.rid===r.rid),l=o!=null&&String(r.rid??"")>=o;return{...r,...c??{},active:l}})}else{const o=this.quality==="audio"?V.audio:V[this.quality],r=i?z:o;n.encodings=s.map(c=>({...c,...r}))}n.degradationPreference=i?"maintain-resolution":"maintain-framerate",await e.setParameters(n)}async closePublishedTracks(){const e=this.connection;if(!e||!this.sessionId)return;const t=e.getTransceivers().filter(i=>i.sender.track&&i.mid).map(i=>({mid:i.mid}));t.length!==0&&await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks/close`,{tracks:t,force:!0})}selfIdentity(){return{participantId:this.options.credential.participantId,externalUserId:this.options.credential.externalUserId??this.options.credential.participantId,displayName:this.options.credential.externalUserId??this.options.credential.participantId,platform:"web"}}async sfuFetch(e,t,i){const n=await fetch(`${this.apiBase}${t}`,{method:e,headers:{authorization:`Bearer ${this.options.credential.signalingToken}`,"content-type":"application/json"},body:i===void 0?void 0:JSON.stringify(i)});if(!n.ok)throw new Error(`SFU request failed with HTTP ${n.status}`);return n.json()}startHeartbeat(e){this.stopHeartbeat(),this.heartbeatTimer=setInterval(()=>{if(!(this.socket!==e||e.readyState!==WebSocket.OPEN)){if(Date.now()>this.heartbeatDeadline){e.close(4e3,"Signaling heartbeat timed out");return}e.send("ping")}},2e4)}stopHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null}startSpeakerSampling(){this.stopSpeakerSampling(),this.speakerTimer=setInterval(()=>{this.sampleActiveSpeaker()},1e3)}stopSpeakerSampling(){this.speakerTimer&&clearInterval(this.speakerTimer),this.speakerTimer=null}async sampleActiveSpeaker(){const e=this.connection;if(!e||e.connectionState!=="connected")return;let t;try{t=await e.getStats()}catch{return}const i=[];for(const s of ee(t.values())){const o=s.mid?this.midToParticipant.get(s.mid):void 0;o&&i.push({participantId:o,level:s.level})}const n=H(i,this.activeSpeaker);n!==this.activeSpeaker&&(this.activeSpeaker=n,this.options.onEvent("activespeaker",{participantId:n}))}clearTimers(){this.stopHeartbeat(),this.stopSpeakerSampling(),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=null}scheduleReconnect(){if(this.reconnectTimer||this.stopped)return;const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.openSocket(!0).then(()=>this.announceTracks()).catch(t=>{this.options.onEvent("signalingerror",{message:t instanceof Error?t.message:"RTC signaling connection failed"}),this.scheduleReconnect()})},e)}send(e){this.socket?.readyState===WebSocket.OPEN&&this.socket.send(JSON.stringify({clientMessageId:e.clientMessageId??Pe(),...e}))}stopStream(e){for(const t of e?.getTracks()??[])t.stop()}}function Pe(){return globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}function Ae(a,e={}){const t=e.document??globalThis.document,i=!!(t&&t.pictureInPictureEnabled);let n=null;function s(){return n||(n=t.createElement("video"),n.muted=!0,n.playsInline=!0,n.style.position="fixed",n.style.width="1px",n.style.height="1px",n.style.opacity="0",n.style.pointerEvents="none",n.addEventListener("leavepictureinpicture",()=>e.onLeave?.()),t.body.appendChild(n),n)}function o(r){const c=a.getRemoteStreams();return r?c.find(l=>l.participant.participantId===r)?.stream??null:c[0]?.stream??a.getLocalStream()}return{supported:i,async enter(r){if(!i)throw new Error("This browser does not support picture-in-picture");const c=o(r);if(!c)throw new Error("No video stream is available for picture-in-picture");const l=s();l.srcObject!==c&&(l.srcObject=c),await l.play().catch(()=>{}),await l.requestPictureInPicture()},async exit(){t.pictureInPictureElement&&typeof t.exitPictureInPicture=="function"&&await t.exitPictureInPicture()},dispose(){n?.remove(),n=null}}}function Le(a,e){const t=e.document??globalThis.document,i=e.fetchImplementation??globalThis.fetch.bind(globalThis),n=new URL(e.credential.signalingUrl,globalThis.location?.href).origin,s=e.mimeType??"video/webm",o=e.height??720,r=Math.round(o*16/9);let c=null,l=null,d=null,u=null,k=0,f=[],v=!1;async function R(y,w,p,I="application/json"){const A=await i(`${n}${w}`,{method:y,headers:{authorization:`Bearer ${e.credential.signalingToken}`,"content-type":I},body:p===void 0?void 0:I==="application/json"?JSON.stringify(p):p});if(!A.ok)throw new Error(`Recording request failed with HTTP ${A.status}`);return A.json()}return{get recording(){return v},async start(){if(v)return;u=(await R("POST","/v1/rtc/recordings")).recordingId;const w=t.createElement("canvas");w.width=r,w.height=o;const p=w.getContext("2d"),I=new Map,A=(T,P)=>{let C=I.get(T);return C||(C=t.createElement("video"),C.muted=!0,C.playsInline=!0,C.srcObject=P,C.play().catch(()=>{}),I.set(T,C)),C};l=setInterval(()=>{const T=a.getRemoteStreams(),P=a.getLocalStream(),C=[...T.map(q=>({key:q.participant.participantId,stream:q.stream,label:q.participant.displayName})),...P?[{key:"local",stream:P,label:"me"}]:[]];p.fillStyle="#101418",p.fillRect(0,0,r,o);const N=Math.max(1,Math.ceil(Math.sqrt(C.length))),Q=Math.max(1,Math.ceil(C.length/N)),j=r/N,g=o/Q;C.forEach((q,ke)=>{const we=A(q.key,q.stream),X=ke%N*j,Z=Math.floor(ke/N)*g;we.videoWidth>0&&p.drawImage(we,X+2,Z+2,j-4,g-4),p.fillStyle="rgba(0,0,0,.6)",p.fillRect(X+8,Z+g-30,Math.min(j-16,12+q.label.length*9),22),p.fillStyle="#fff",p.font="14px system-ui",p.fillText(q.label,X+14,Z+g-14)})},66);const D=w.captureStream(15);d=new AudioContext;const E=d.createMediaStreamDestination(),L=new Set,S=()=>{const T=[...a.getRemoteStreams().map(P=>P.stream),...a.getLocalStream()?[a.getLocalStream()]:[]];for(const P of T)for(const C of P.getAudioTracks())L.has(C.id)||(L.add(C.id),d.createMediaStreamSource(new MediaStream([C])).connect(E))};S();const M=setInterval(S,1e3),m=()=>clearInterval(M);for(const T of E.stream.getAudioTracks())D.addTrack(T);c=(e.mediaRecorderFactory??((T,P)=>new MediaRecorder(T,P)))(D,{mimeType:s}),f=[],c.addEventListener("dataavailable",T=>{T.data&&T.data.size>0&&f.push(T.data)}),c.addEventListener("stop",m),c.start(1e3),k=Date.now(),v=!0},async stop(){if(!v||!c||!u)throw new Error("The recorder is not running");v=!1;const y=c,w=new Promise(E=>y.addEventListener("stop",()=>E(),{once:!0}));y.stop(),await w,l&&clearInterval(l),l=null,await d?.close().catch(()=>{}),d=null;const p=new Blob(f,{type:s});f=[];const I=Math.max(1,Math.round((Date.now()-k)/1e3)),A=await R("PUT",`/v1/rtc/recordings/${encodeURIComponent(u)}/media`,p,s);await R("POST",`/v1/rtc/recordings/${encodeURIComponent(u)}/complete`,{durationSeconds:I});const D={recordingId:u,sizeBytes:A.sizeBytes,durationSeconds:I};return u=null,c=null,D},dispose(){l&&clearInterval(l),l=null;try{c?.stop()}catch{}c=null,d?.close().catch(()=>{}),d=null,v=!1}}}const ae=["none","blur","aurora","cybergrid","bokeh","sunset","image"],se=["none","snow","confetti","vignette"],_={aurora(a,e,t,i){const n=a.createLinearGradient(0,0,0,t);n.addColorStop(0,"#050b1e"),n.addColorStop(1,"#0b2540"),a.fillStyle=n,a.fillRect(0,0,e,t);for(let s=0;s<3;s+=1){a.beginPath();for(let o=0;o<=e;o+=16){const r=t*.35+s*40+Math.sin(o/140+i/900+s)*60;o===0?a.moveTo(o,r):a.lineTo(o,r)}a.lineTo(e,0),a.lineTo(0,0),a.closePath(),a.fillStyle=`hsla(${140+s*40+Math.sin(i/1300)*20}, 80%, 55%, 0.14)`,a.fill()}},cybergrid(a,e,t,i){a.fillStyle="#0a0118",a.fillRect(0,0,e,t),a.strokeStyle="rgba(255,0,180,0.5)",a.lineWidth=1.5;const n=t*.45;for(let o=0;o<12;o+=1){const r=n+(o*34+i/18)%(t-n);a.beginPath(),a.moveTo(0,r),a.lineTo(e,r),a.stroke()}for(let o=-8;o<=8;o+=1)a.beginPath(),a.moveTo(e/2+o*24,n),a.lineTo(e/2+o*e*.12,t),a.stroke();const s=a.createLinearGradient(0,t*.1,0,n);s.addColorStop(0,"#ffd34d"),s.addColorStop(1,"#ff2d78"),a.fillStyle=s,a.beginPath(),a.arc(e/2,n,t*.22,Math.PI,0),a.fill()},bokeh(a,e,t,i){a.fillStyle="#14100c",a.fillRect(0,0,e,t);for(let n=0;n<26;n+=1){const s=(Math.sin(n*12.9898)*.5+.5)*e,o=(Math.sin(n*78.233)*.5+.5)*t+Math.sin(i/1500+n)*12,r=14+n%5*12;a.fillStyle=`hsla(${35+n%4*12}, 90%, 60%, ${.1+n%3*.05})`,a.beginPath(),a.arc(s,o,r,0,Math.PI*2),a.fill()}},sunset(a,e,t,i){const n=a.createLinearGradient(0,0,0,t);n.addColorStop(0,"#2b1055"),n.addColorStop(.55,"#d1548a"),n.addColorStop(1,"#ffb36b"),a.fillStyle=n,a.fillRect(0,0,e,t),a.fillStyle="#ffdf8e",a.beginPath(),a.arc(e*.5,t*.62+Math.sin(i/4e3)*4,t*.13,0,Math.PI*2),a.fill(),a.fillStyle="rgba(20,8,40,0.85)",a.fillRect(0,t*.78,e,t*.22)}};function De(a,e={}){const t=e.document??globalThis.document;let i=e.width??1280,n=e.height??720;const s=!!(e.width||e.height),o=e.frameRate??24,r=a instanceof MediaStreamTrack?a:a.getVideoTracks()[0];if(!r)throw new Error("A video track is required");let c=e.background??"none",l=e.overlay??"none",d=e.backgroundImage??null,u=e.segmenter??null,k=null,f=!1;const v=t.createElement("video");v.muted=!0,v.playsInline=!0,v.srcObject=new MediaStream([r]),v.play().catch(()=>{});const R=t.createElement("canvas");R.width=i,R.height=n;const y=R.getContext("2d"),w=t.createElement("canvas");w.width=i,w.height=n;const p=w.getContext("2d"),I=()=>Array.from({length:80},(m,h)=>({x:(Math.sin(h*12.9898)*.5+.5)*i,y:(Math.sin(h*78.233)*.5+.5)*n,speed:.6+h%5*.5,drift:Math.sin(h)*.6,size:2+h%4,hue:h*47%360}));let A=I();const D=()=>{if(s)return;const m=v.videoWidth,h=v.videoHeight;if(!m||!h)return;const T=Math.min(1,1280/Math.max(m,h)),P=Math.round(m*T),C=Math.round(h*T);P===i&&C===n||(i=P,n=C,R.width=i,R.height=n,w.width=i,w.height=n,A=I())};v.addEventListener?.("loadedmetadata",D),v.addEventListener?.("resize",D);function E(){if(k){p.clearRect(0,0,i,n),p.drawImage(k,0,0,i,n),p.globalCompositeOperation="source-in",p.drawImage(v,0,0,i,n),p.globalCompositeOperation="source-over",y.drawImage(w,0,0);return}const m=i*.42,h=m*9/16,T=i-m-24,P=n-h-24;y.save(),y.shadowColor="rgba(0,0,0,.5)",y.shadowBlur=24,y.beginPath(),y.roundRect(T,P,m,h,14),y.clip(),y.drawImage(v,T,P,m,h),y.restore()}function L(m){if(c==="none"?y.drawImage(v,0,0,i,n):c==="blur"?(y.filter="blur(14px)",y.drawImage(v,-16,-16,i+32,n+32),y.filter="none",k&&E()):c==="image"&&d?(y.drawImage(d,0,0,i,n),E()):((_[c]??_.aurora)(y,i,n,m),E()),l==="snow"||l==="confetti")for(const h of A)h.y+=h.speed*(l==="confetti"?1.6:1),h.x+=h.drift,h.y>n&&(h.y=-6,h.x=Math.random()*i),y.fillStyle=l==="snow"?"rgba(255,255,255,.85)":`hsl(${h.hue}, 90%, 60%)`,l==="confetti"?y.fillRect(h.x,h.y,h.size+2,h.size):(y.beginPath(),y.arc(h.x,h.y,h.size/2,0,Math.PI*2),y.fill());else if(l==="vignette"){const h=y.createRadialGradient(i/2,n/2,n*.4,i/2,n/2,n*.85);h.addColorStop(0,"rgba(0,0,0,0)"),h.addColorStop(1,"rgba(0,0,0,0.55)"),y.fillStyle=h,y.fillRect(0,0,i,n)}}const S=setInterval(()=>{L(Date.now()),u&&!f&&c!=="none"&&(f=!0,u.segment(v).then(m=>{k=m}).catch(()=>{k=null}).finally(()=>{f=!1}))},Math.round(1e3/o)),M=R.captureStream(o);return{track:M.getVideoTracks()[0],stream:M,setBackground(m,h){c=ae.includes(m)?m:"none",h!==void 0&&(d=h),c==="none"&&(k=null)},setOverlay(m){l=se.includes(m)?m:"none"},setSegmenter(m){u=m,m||(k=null)},dispose(){clearInterval(S),u?.close?.();for(const m of M.getTracks())m.stop();v.srcObject=null}}}function W(){return{blinkLeft:0,blinkRight:0,jawOpen:0,smile:0,browUp:0,pupilX:0,pupilY:0,headYaw:0,headPitch:0,headRoll:0,tracked:!1}}function O(a,e){const t=a.find(i=>i.categoryName===e);return t?Math.max(0,Math.min(1,t.score)):0}function re(a){if(!a||a.length<16)return{yaw:0,pitch:0,roll:0};a[0],a[4];const e=a[8],t=a[1],i=a[5],n=a[9],s=a[10],o=Math.atan2(e,s),r=Math.asin(Math.max(-1,Math.min(1,-n))),c=Math.atan2(t,i);return{yaw:o,pitch:r,roll:c}}function oe(a,e,t=1){if(!a||a.length===0)return W();const i=re(e),n=(O(a,"eyeLookOutRight")+O(a,"eyeLookInLeft"))/2,s=(O(a,"eyeLookOutLeft")+O(a,"eyeLookInRight"))/2,o=(O(a,"eyeLookUpLeft")+O(a,"eyeLookUpRight"))/2,r=(O(a,"eyeLookDownLeft")+O(a,"eyeLookDownRight"))/2,c=(O(a,"mouthSmileLeft")+O(a,"mouthSmileRight"))/2,l=(O(a,"mouthFrownLeft")+O(a,"mouthFrownRight"))/2,d=O(a,"eyeBlinkLeft"),u=O(a,"eyeBlinkRight"),k=Math.min(1,Math.abs(i.yaw)/.35)*Math.max(0,Math.min(1,t)),f=(d+u)/2;return{blinkLeft:d+(f-d)*k,blinkRight:u+(f-u)*k,jawOpen:O(a,"jawOpen"),smile:c-l,browUp:(O(a,"browInnerUp")+O(a,"browOuterUpLeft")+O(a,"browOuterUpRight"))/3,pupilX:n-s,pupilY:r-o,headYaw:i.yaw,headPitch:i.pitch,headRoll:i.roll,tracked:!0}}function $(a,e,t=.4){const i=(n,s)=>n+(s-n)*t;return{blinkLeft:i(a.blinkLeft,e.blinkLeft),blinkRight:i(a.blinkRight,e.blinkRight),jawOpen:i(a.jawOpen,e.jawOpen),smile:i(a.smile,e.smile),browUp:i(a.browUp,e.browUp),pupilX:i(a.pupilX,e.pupilX),pupilY:i(a.pupilY,e.pupilY),headYaw:i(a.headYaw,e.headYaw),headPitch:i(a.headPitch,e.headPitch),headRoll:i(a.headRoll,e.headRoll),tracked:e.tracked}}function ce(a,e,t,i,n){n||(a.fillStyle="#101822",a.fillRect(0,0,t,i));const s=n?n.x+e.headYaw*n.radius*.1:t/2+e.headYaw*t*.18,o=n?n.y+e.headPitch*n.radius*.1:i/2+e.headPitch*i*.18,r=n?n.radius:Math.min(t,i)*.28;a.save(),a.translate(s,o),a.rotate(e.headRoll*.6);const c=Math.max(-1.35,Math.min(1.35,e.headYaw)),l=Math.max(-.9,Math.min(.9,e.headPitch)),d=Math.sin(c),u=L=>Math.sin(L+c)*r,k=L=>Math.cos(L+c),f=Math.sin(l)*r*.22;a.fillStyle="#3a2a1c",a.beginPath(),a.ellipse(-d*r*.35,-r*.25,r*.98,r*.95,0,0,Math.PI*2),a.fill(),a.fillStyle=e.tracked?"#f2c9a0":"#5a6470",a.beginPath(),a.ellipse(0,0,r*(1-Math.abs(d)*.12),r*1.12,0,0,Math.PI*2),a.fill(),a.fillStyle="#3a2a1c",a.beginPath(),a.ellipse(-d*r*.28,-r*.72+f*.4,r*.92,r*.5,0,Math.PI,Math.PI*2),a.fill();const v=0;a.fillStyle="#e5b58a",a.beginPath();const R=u(v)*.9,y=r*.12+f;a.ellipse(R+d*r*.14,y,r*.11,r*.14,0,0,Math.PI*2),a.fill();for(const L of[-1,1]){const S=L*.42,M=k(S);if(M<=.12)continue;const m=u(S)*.82,h=Math.max(.2,M),T=L<0?e.blinkLeft:e.blinkRight,P=Math.max(.06,1-T),C=e.browUp*r*.14;a.strokeStyle="#4a2e18",a.lineWidth=r*.07,a.lineCap="round",a.beginPath(),a.moveTo(m-r*.2*h,-r*.42-C+f),a.quadraticCurveTo(m,-r*.52-C+f,m+r*.2*h,-r*.44-C+f),a.stroke();const N=-r*.18+f;a.fillStyle="#ffffff",a.beginPath(),a.ellipse(m,N,r*.16*h,r*.12*P,0,0,Math.PI*2),a.fill(),a.fillStyle="#22303c",a.beginPath(),a.ellipse(m+e.pupilX*r*.07*h,N+e.pupilY*r*.05,r*.06*h,r*.06*P,0,0,Math.PI*2),a.fill()}const w=Math.max(.25,k(0)),p=u(0)*.7,I=r*.55*w,A=r*(.04+e.jawOpen*.34),D=e.smile*r*.12,E=r*.42+f;a.fillStyle="#8c2f2f",a.beginPath(),a.moveTo(p-I/2,E-D),a.quadraticCurveTo(p,E+A,p+I/2,E-D),a.quadraticCurveTo(p,E-A*.25,p-I/2,E-D),a.closePath(),a.fill(),a.restore(),!e.tracked&&!n&&(a.fillStyle="#9fb0bd",a.font=`${Math.round(i*.045)}px system-ui`,a.textAlign="center",a.fillText("No face detected",t/2,i*.9),a.textAlign="left")}const J={mouthGain:1.7,blinkGain:1,eyeSync:1,smoothing:.4,avatarScale:1,avatarLift:0,lostHoldMs:400};function G(){return{render(a,e,t,i,n){ce(a,e,t,i,n??void 0)}}}const Y=a=>Math.max(0,Math.min(1,a));function Oe(a,e={}){const t=e.document??globalThis.document;let i=e.width??1280,n=e.height??720;const s=!!(e.width||e.height),o=e.frameRate??24,r=a instanceof MediaStreamTrack?a:a.getVideoTracks()[0];if(!r)throw new Error("A video track is required");let c=e.tracker??null,l=e.renderer??G(),d=e.mode??"replace";const u={...J,...e.tuning},k=e.backdrop??"#101822",f=t.createElement("video");f.muted=!0,f.playsInline=!0,f.srcObject=new MediaStream([r]),f.play().catch(()=>{});const v=t.createElement("canvas");v.width=i,v.height=n;const R=v.getContext("2d"),y=()=>{if(s)return;const S=f.videoWidth,M=f.videoHeight;if(!S||!M)return;const m=Math.min(1,1280/Math.max(S,M)),h=Math.round(S*m),T=Math.round(M*m);(!(h===i||T===n)||h!==i||T!==n)&&(i=h,n=T,v.width=i,v.height=n)};f.addEventListener?.("loadedmetadata",y),f.addEventListener?.("resize",y);let w=W(),p=null,I=0;function A(S){if(S.length<=454)return;const M=S[10],m=S[152],h=S[234],T=S[454],P=(h.x+T.x)/2*i,C=(h.y+T.y)/2*n,N=Math.hypot((m.x-M.x)*i,(m.y-M.y)*n),Q=Math.max(.6,Math.cos(w.headPitch)),j={x:P,y:C,radius:Math.max(24,N/Q*.8)};p=p?{x:p.x+(j.x-p.x)*.4,y:p.y+(j.y-p.y)*.4,radius:p.radius+(j.radius-p.radius)*.15}:j}function D(S){if(c&&f.readyState>=2){let m=null;try{m=c.detect(f,S)}catch{}const h=oe(m?.categories??null,m?.matrix??null,u.eyeSync);h.jawOpen=Y(h.jawOpen*u.mouthGain),h.blinkLeft=Y(h.blinkLeft*u.blinkGain),h.blinkRight=Y(h.blinkRight*u.blinkGain),h.tracked?(I=S,w=$(w,h,u.smoothing),m?.landmarks&&A(m.landmarks)):w.tracked&&S-I>u.lostHoldMs&&(w=$(w,{...w,jawOpen:0,browUp:0,smile:w.smile*.5,blinkLeft:.1,blinkRight:.1},.05))}d==="overlay"&&f.readyState>=2?R.drawImage(f,0,0,i,n):(R.fillStyle=k,R.fillRect(0,0,i,n));const M=p?{x:p.x,y:p.y+u.avatarLift*p.radius,radius:p.radius*u.avatarScale}:null;l?.render(R,w,i,n,M)}const E=setInterval(()=>D(Date.now()),Math.round(1e3/o)),L=v.captureStream(o);return{track:L.getVideoTracks()[0],stream:L,getRig:()=>({...w}),setMode(S){d=S==="overlay"?"overlay":"replace"},setTracker(S){c?.close?.(),c=S},setRenderer(S){l?.dispose?.(),l=S??G()},setTuning(S){for(const M of Object.keys(J)){const m=S[M];typeof m=="number"&&Number.isFinite(m)&&(u[M]=m)}},getTuning:()=>({...u}),dispose(){clearInterval(E),c?.close?.(),l?.dispose?.();for(const S of L.getTracks())S.stop();f.srcObject=null}}}class le extends EventTarget{mode;options;client=null;peerConnection;state="initialized";latestMediaScore=null;latestQuality=null;statsTimer=null;statsRunning=!1;previousCounters=null;lastTelemetryAt=0;joinPromise=null;requestedQuality;effectiveQuality;poorQualitySamples=0;goodQualitySamples=0;constructor(e){if(super(),!e.credential?.signalingToken||!e.credential.signalingUrl||!e.credential.roomId||!e.credential.participantId)throw new Error("A valid room signaling credential is required");this.mode=e.mode,this.requestedQuality=e.mode==="voice"?"audio":e.quality??"auto",this.effectiveQuality=this.requestedQuality==="auto"?"medium":this.requestedQuality,this.peerConnection=e.peerConnection??null,this.options={...e,audio:e.audio??!0,video:e.mode==="voice"?!1:e.video??!0,receiveAudio:e.receiveAudio??!0,receiveVideo:e.mode==="voice"?!1:e.receiveVideo??!0,statsInterval:Math.max(1e3,e.statsInterval??5e3),telemetryInterval:Math.max(1e4,e.telemetryInterval??3e4),autoReportStats:e.autoReportStats??!0}}async initialize(){if(this.client)return this;try{const t=(this.options.transport==="auto"?this.mode==="meeting"?"sfu":"mesh":this.options.transport??"mesh")==="sfu"?Me:x;return this.client=new t({credential:{signalingToken:this.options.credential.signalingToken,signalingUrl:this.options.credential.signalingUrl,roomId:this.options.credential.roomId,participantId:this.options.credential.participantId,externalUserId:this.options.credential.externalUserId,iceServers:this.options.credential.iceServers},audio:this.options.audio,video:this.options.video,receiveAudio:this.options.receiveAudio,receiveVideo:this.options.receiveVideo,quality:this.effectiveQuality,preferredVideoCodec:this.options.preferredVideoCodec??"auto",iceTransportPolicy:this.options.iceTransportPolicy??"all",localStream:this.options.localStream,simulcast:this.options.simulcast??!0,onEvent:(i,n)=>this.handleNativeEvent(i,n)}),await this.client.initialize(),this.startStats(),this.emit("initialized",{mode:this.mode,sdkVersion:Se}),this.options.autoJoin&&await this.join(),this}catch(e){this.state="failed";const t=ve(e,"RTC initialization failed");throw this.emit("error",U(t)),t}}async join(){const e=this.requireClient();return this.joinPromise?this.joinPromise:(this.state="joining",this.emit("joining",{}),this.joinPromise=e.join().catch(t=>{this.state="failed";const i=ve(t,"RTC connection failed");throw this.emit("error",U(i)),i}).finally(()=>{this.joinPromise=null}),this.joinPromise)}async leave(){this.client&&(await this.joinPromise?.catch(()=>{}),await this.sampleAndReport("final").catch(()=>{}),await this.client.leave(),this.state="disconnected",this.stopStats())}async setMuted(e){this.requireClient().setMuted(e),this.emit("mediachange",{kind:"audio",enabled:!e})}async setCameraEnabled(e){if(this.mode==="voice"&&e)throw new Error("Camera is unavailable in voice mode");await this.requireClient().setCameraEnabled(e),this.emit("mediachange",{kind:"video",enabled:e})}async setVideoTrack(e){if(this.mode==="voice")throw new Error("Camera is unavailable in voice mode");await this.requireClient().setVideoTrack(e),this.emit("mediachange",{kind:"video",enabled:e.enabled,trackId:e.id})}async setScreenShareEnabled(e){await this.requireClient().setScreenShareEnabled(e),this.emit("mediachange",{kind:"screenshare",enabled:e})}async setDevice(e){await this.requireClient().setDevice(e),this.emit("devicechange",{deviceId:e.deviceId,kind:e.kind})}async setQuality(e){if(this.mode==="voice"&&e!=="audio")throw new Error("Voice calls only support audio quality");this.requestedQuality=e,this.poorQualitySamples=0,this.goodQualitySamples=0,await this.applyEffectiveQuality(e==="auto"?"medium":e,"manual")}async setPreferredVideoCodec(e){await this.requireClient().setPreferredVideoCodec(e),this.emit("codecchange",{preferredVideoCodec:e})}async getDevices(){return navigator.mediaDevices?.enumerateDevices?navigator.mediaDevices.enumerateDevices():[]}getParticipants(){return this.requireClient().getParticipants().map(fe)}getLocalStream(){return this.client?.getLocalStream()??null}getRemoteStreams(){return(this.client?.getRemoteStreams()??[]).map(e=>({participant:fe(e.participant),stream:e.stream}))}setPeerConnection(e){this.peerConnection=e,this.previousCounters=null}async sendChatMessage(e,t){const i=e.trim();if(!i)throw new Error("message is required");this.requireClient().sendChat(i,t)}async sendControlMessage(e,t={}){const i=e.trim();if(!i)throw new Error("action is required");this.requireClient().sendControl(i,t)}async createPoll(e,t,i={}){const n=t.map(s=>s.trim()).filter(Boolean);if(!e.trim()||n.length<2)throw new Error("A poll requires a question and at least two options");this.requireClient().sendControl("poll.create",{poll:{id:`poll-${crypto.randomUUID()}`,question:e.trim(),options:n,votes:{},...i}})}async votePoll(e,t){this.requireClient().sendControl("poll.vote",{pollId:e,optionIndex:t})}async requestStageAccess(){this.requireClient().sendControl("stage.request")}async cancelStageAccess(){this.requireClient().sendControl("stage.cancel")}async joinStage(){this.requireClient().sendControl("stage.join")}async leaveStage(){this.requireClient().sendControl("stage.leave")}async grantStageAccess(e){this.requireClient().sendControl("stage.grant",{participantIds:e})}async denyStageAccess(e){this.requireClient().sendControl("stage.deny",{participantIds:e})}async startRecording(e=!1){this.requireClient().sendControl("recording.start",{allowMultiple:e})}async stopRecording(e){this.requireClient().sendControl("recording.stop",{recordingId:e})}async pauseRecording(e){this.requireClient().sendControl("recording.pause",{recordingId:e})}async resumeRecording(e){this.requireClient().sendControl("recording.resume",{recordingId:e})}async runPreflight(e={}){const t=[],i=globalThis.isSecureContext===!0;i||t.push("secure_context_required");let n="unavailable";if(e.requestMedia!==!1&&navigator.mediaDevices?.getUserMedia)try{(await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})).getTracks().forEach(k=>k.stop()),n="granted"}catch{n="denied",t.push("media_permission_denied")}const s=navigator.mediaDevices?.enumerateDevices?await navigator.mediaDevices.enumerateDevices().catch(()=>[]):[],o=s.filter(u=>u.kind==="audioinput").length,r=s.filter(u=>u.kind==="videoinput").length;this.options.audio&&o===0&&t.push("microphone_unavailable"),this.options.video&&r===0&&t.push("camera_unavailable");const c=e.probeUrl??this.options.preflightProbeUrl;let l=null;if(c){const u=performance.now();try{const k=await fetch(c,{method:"HEAD",cache:"no-store",credentials:"omit"});l=Math.round(performance.now()-u),k.ok||t.push("network_probe_failed")}catch{t.push("network_probe_failed")}}const d={passed:t.length===0,sampledAt:new Date().toISOString(),secureContext:i,mediaPermission:n,microphoneCount:o,cameraCount:r,probeLatencyMs:l,effectiveConnectionType:ge()?.effectiveType??null,issues:t};return this.emit("preflight",d),this.options.autoReportStats&&await this.reportQuality({sampleType:"preflight",sampledAt:d.sampledAt,connectionState:d.passed?"ready":"failed",rttMs:d.probeLatencyMs,effectiveConnectionType:d.effectiveConnectionType}).catch(()=>{}),d}async collectQualitySample(e="runtime"){const t=this.options.statsProvider?await this.options.statsProvider():{},i=this.peerConnection?await Ue(this.peerConnection,this.previousCounters):null;i?.counters&&(this.previousCounters=i.counters);const s={...{sampleType:e,sampledAt:new Date().toISOString(),connectionState:this.peerConnection?.connectionState??this.state,mediaScore:this.latestMediaScore,networkScore:this.latestMediaScore==null?5:Math.max(0,Math.min(5,Math.round(this.latestMediaScore))),estimatedMos:4.5,rttMs:null,jitterMs:null,packetLossPct:null,packetLossCumulativePct:null,packetsLost:null,packetsReceived:null,inboundBitrateBps:null,outboundBitrateBps:null,availableOutgoingBitrateBps:null,framesPerSecond:null,frameWidth:null,frameHeight:null,inboundFrameWidth:null,inboundFrameHeight:null,outboundFrameWidth:null,outboundFrameHeight:null,framesDropped:null,freezeCount:null,freezeDurationMs:null,concealedSamples:null,codec:null,transportProtocol:null,candidateType:null,qualityLimitationReason:null,deviceMemoryGb:Be(),effectiveConnectionType:ge()?.effectiveType??null},...i?.sample??{},...t,sampleType:e,sampledAt:new Date().toISOString()},o=Qe(s);return s.networkScore=o.networkScore,s.estimatedMos=o.estimatedMos,this.latestQuality=s,s}async reportQuality(e){const t=this.options.credential.telemetryToken,i=this.options.telemetryEndpoint??this.options.credential.telemetryEndpoint;if(!t||!i)return;const n=await fetch(i,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify(e),keepalive:e.sampleType==="final"});if(!n.ok)throw new Error(`RTC telemetry failed with HTTP ${n.status}`)}getStats(){const e=this.client,t=e?.getLocalStream();return{state:this.state,participantCount:e?e.getParticipants().length+1:0,audioEnabled:!!t?.getAudioTracks().some(i=>i.enabled),videoEnabled:!!t?.getVideoTracks().some(i=>i.enabled),screenShareEnabled:!1,latestMediaScore:this.latestMediaScore,latestQuality:this.latestQuality,sampledAt:new Date().toISOString()}}getNativeClient(){return this.client}destroy(){this.stopStats(),this.client&&this.client.leave(),this.client=null,this.joinPromise=null,this.peerConnection=null,this.state="disconnected"}handleNativeEvent(e,t){e==="connected"?(this.state="connected",this.peerConnection=this.client?.getPrimaryPeerConnection()??null):e==="disconnected"?this.state="disconnected":e==="peerconnectionstate"&&(this.peerConnection=this.client?.getPrimaryPeerConnection()??null),this.emit(e,t)}startStats(){this.stopStats(),this.statsTimer=setInterval(()=>{this.sampleAndReport("runtime")},this.options.statsInterval)}async sampleAndReport(e){if(!this.statsRunning){this.statsRunning=!0;try{const t=await this.collectQualitySample(e);await this.adaptQuality(t),this.emit("stats",{...this.getStats(),latestQuality:t});const i=Date.now(),n=e!=="runtime"||i-this.lastTelemetryAt>=this.options.telemetryInterval;this.options.autoReportStats&&n&&(await this.reportQuality(t),this.lastTelemetryAt=i)}catch(t){this.emit("telemetryerror",U(t))}finally{this.statsRunning=!1}}}stopStats(){this.statsTimer!==null&&clearInterval(this.statsTimer),this.statsTimer=null}async adaptQuality(e){if(this.requestedQuality!=="auto"||this.options.adaptiveQuality===!1||this.mode==="voice")return;if((this.client?.getParticipants().length??0)>1){this.poorQualitySamples=0,this.goodQualitySamples=0,this.effectiveQuality!=="high"&&await this.applyEffectiveQuality("high","network");return}const t=String(e.qualityLimitationReason??"").toLowerCase(),i=(e.packetLossPct??0)>=8||(e.rttMs??0)>=800||t==="cpu"||t==="bandwidth",n=i||(e.packetLossPct??0)>=3||(e.rttMs??0)>=350||e.availableOutgoingBitrateBps!=null&&e.availableOutgoingBitrateBps<35e4,s=(e.packetLossPct??100)<1&&(e.rttMs??1e3)<180&&!t&&(e.availableOutgoingBitrateBps==null||e.availableOutgoingBitrateBps>1e6);this.poorQualitySamples=n?this.poorQualitySamples+1:0,this.goodQualitySamples=s?this.goodQualitySamples+1:0;const o=["low","medium","high"],r=o.indexOf(this.effectiveQuality);i&&r>0?(this.poorQualitySamples=0,await this.applyEffectiveQuality("low","network")):this.poorQualitySamples>=2&&r>0?(this.poorQualitySamples=0,await this.applyEffectiveQuality(o[r-1],"network")):this.goodQualitySamples>=6&&r>=0&&r<o.length-1&&(this.goodQualitySamples=0,await this.applyEffectiveQuality(o[r+1],"network"))}async applyEffectiveQuality(e,t){if(this.effectiveQuality===e&&this.client)return;const i=this.effectiveQuality;this.effectiveQuality=e,this.client&&await this.client.setQuality(e),this.emit("qualitychange",{requestedQuality:this.requestedQuality,quality:e,previous:i,reason:t})}requireClient(){if(!this.client)throw new Error("Call initialize() before using the RTC client");return this.client}emit(e,t){const i={type:e,detail:t,timestamp:new Date().toISOString()};this.dispatchEvent(new CustomEvent(e,{detail:i})),this.options.onEvent?.(i)}}async function qe(a){return new le(a).initialize()}let F=null;function de(){if(F)return F;const a=globalThis,e=a.AudioContext??a.webkitAudioContext;return e?(F=new e,F):null}async function he(){const a=de();return a?(a.state==="suspended"&&await a.resume().catch(()=>{}),a.state==="running"):!1}class Ne{options;media=null;cadenceTimer=null;oscillators=new Set;constructor(e){this.options=e}unlock(){return he()}startIncoming(){this.start("incoming",this.options.ringtoneUrl)}startRingback(){this.start("ringback",this.options.ringbackUrl)}stop(){this.cadenceTimer&&clearInterval(this.cadenceTimer),this.cadenceTimer=null,this.media?.pause(),this.media&&(this.media.currentTime=0),this.media=null;for(const e of this.oscillators)try{e.stop()}catch{}this.oscillators.clear()}start(e,t){if(this.stop(),this.options.sounds===!1)return;if(t&&typeof Audio<"u"){this.media=new Audio(t),this.media.loop=!0,this.media.play().catch(()=>{});return}const i=()=>this.playBuiltInTone(e);i(),this.cadenceTimer=setInterval(i,e==="incoming"?3e3:3500)}playBuiltInTone(e){const t=de();if(!t||t.state!=="running")return;const i=e==="incoming"?1.25:1,n=e==="incoming"?[440,480]:[425],s=t.createGain(),o=t.currentTime;s.gain.setValueAtTime(1e-4,o),s.gain.exponentialRampToValueAtTime(.045,o+.025),s.gain.setValueAtTime(.045,o+i-.06),s.gain.exponentialRampToValueAtTime(1e-4,o+i),s.connect(t.destination);for(const r of n){const c=t.createOscillator();c.type="sine",c.frequency.value=r,c.connect(s),this.oscillators.add(c),c.onended=()=>this.oscillators.delete(c),c.start(o),c.stop(o+i+.02)}}}class ue extends EventTarget{options;socket=null;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;heartbeatDeadline=0;currentCall=null;outgoingCallId=null;mount=null;terminalTimer=null;tokenRefreshTimer=null;tokenRequest=null;currentToken;currentTokenExpiresAt;callAudio;mediaClients=new Map;activeCallIds=new Set;seenInvitationIds=new Set;startCallRequest=null;lastDisconnect=null;constructor(e){if(super(),!e.token&&!e.tokenProvider)throw new Error("A client token or tokenProvider is required");if(e.token&&!e.token.startsWith("rtcc_"))throw new Error("A valid RTC client token is required");if(!e.signalingUrl&&!e.callActionsUrl&&!e.apiBaseUrl&&!globalThis.location?.origin)throw new Error("signalingUrl or callActionsUrl is required outside a browser");this.options=e,this.currentToken=e.token??null,this.currentTokenExpiresAt=e.tokenExpiresAt,this.callAudio=new Ne(e),this.mount=me(e.mount),this.scheduleTokenRefresh()}connect(){if(!this.options.signalingUrl)throw new Error("signalingUrl is required to connect signaling");return this.stopped=!1,this.scheduleTokenRefresh(),this.openSocket("initial"),this}setMount(e){this.clearUi(),this.mount=me(e),this.currentCall&&this.renderIncoming(this.currentCall)}attachMediaClient(e,t){if(!e.trim())throw new Error("callId is required");return this.mediaClients.set(e,t),()=>{this.mediaClients.get(e)===t&&this.mediaClients.delete(e)}}async startCall(e){if(this.startCallRequest)return this.startCallRequest;if(this.activeCallIds.size>0)throw new Error("Another call is already active");const t=e.targets?.length?e.targets:e.target?[e.target]:[];if(t.length===0)throw new Error("startCall requires at least one target");const i={mode:e.mode,targets:t,metadata:e.metadata,idempotencyKey:e.idempotencyKey},n=(async()=>{await this.callAudio.unlock(),this.callAudio.startRingback();let s;try{s=await this.request("/v1/rtc/client/calls",{method:"POST",headers:e.idempotencyKey?{"idempotency-key":e.idempotencyKey}:void 0,body:JSON.stringify(i)})}catch(o){throw this.callAudio.stop(),o}return!s.credential&&s.callerCredential&&(s.credential=s.callerCredential),this.outgoingCallId=String(s.call?.id??"")||null,this.outgoingCallId&&this.activeCallIds.add(this.outgoingCallId),this.emit("outgoingcall",s),s})();this.startCallRequest=n;try{return await n}finally{this.startCallRequest===n&&(this.startCallRequest=null)}}async accept(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"accept");return this.activeCallIds.add(e),this.callAudio.stop(),this.dismiss(e),this.emit("callaccepted",t),t}async reject(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"reject");return this.releaseMedia(e),this.showTerminal(e,"Call declined"),this.emit("callrejected",t),t}async busy(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"busy");return this.releaseMedia(e),this.showTerminal(e,"Line busy"),t}async end(e){this.callAudio.stop();const t=await this.callAction(e,"end");return this.releaseMedia(e),this.showTerminal(e,"Call ended"),t}async cancel(e){this.callAudio.stop();const t=await this.callAction(e,"cancel");return this.releaseMedia(e),this.showTerminal(e,"Call cancelled"),t}handleIncomingCall(e){if(!e?.id||!e.caller?.id||!e.caller.name||e.mode!=="voice"&&e.mode!=="video")throw new Error("Incoming call payload is invalid");if(e.expiresAt&&Date.parse(e.expiresAt)<=Date.now()){this.emit("callexpired",{id:e.id,expiresAt:e.expiresAt});return}if(this.currentCall?.id===e.id){this.currentCall=e,this.emit("callrestored",e);return}if(this.seenInvitationIds.has(e.id)){this.emit("callrestored",e);return}this.seenInvitationIds.add(e.id),this.activeCallIds.add(e.id),this.currentCall=e,this.callAudio.startIncoming(),this.renderIncoming(e),this.emit("incomingcall",e)}handleCallUpdate(e){const t=e.id===this.currentCall?.id||e.id===this.outgoingCallId;t&&e.status==="accepted"?(this.activeCallIds.add(e.id),this.callAudio.stop(),this.dismiss(e.id)):t&&["rejected","busy","cancelled","ended","failed","missed"].includes(e.status)&&this.showTerminal(e.id,Ve(e.status)),["rejected","busy","cancelled","ended","failed","missed"].includes(e.status)&&this.releaseMedia(e.id),this.emit("callupdated",e)}disconnect(){this.stopped=!0,this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.tokenRefreshTimer&&clearTimeout(this.tokenRefreshTimer),this.reconnectTimer=null,this.heartbeatTimer=null,this.tokenRefreshTimer=null,this.socket?.close(1e3,"Client disconnected"),this.socket=null,this.callAudio.stop(),this.dismiss();for(const e of this.mediaClients.keys())this.releaseMedia(e);this.activeCallIds.clear(),this.seenInvitationIds.clear()}destroy(){this.disconnect(),this.mount=null}async openSocket(e){if(!this.options.signalingUrl||this.stopped||this.socket?.readyState===WebSocket.OPEN||this.socket?.readyState===WebSocket.CONNECTING)return;let t;try{t=await this.resolveToken(e,e==="reconnect")}catch(s){this.emit("tokenerror",U(s)),!this.stopped&&this.options.reconnect!==!1&&this.scheduleReconnect();return}if(this.stopped||this.socket)return;const i=new URL(this.options.signalingUrl,globalThis.location?.href);i.protocol=i.protocol==="https:"?"wss:":"ws:";const n=new WebSocket(i,["rtc-client",t]);this.socket=n,n.addEventListener("open",()=>{const s=this.reconnectAttempt>0,o=this.lastDisconnect;this.reconnectAttempt=0,this.heartbeatDeadline=Date.now()+45e3,this.emit("signalingconnected",{resumed:s}),o&&(this.reportSignalingDiagnostic({event:"recovered",code:o.code,reason:o.reason,wasClean:o.wasClean,outageMs:Date.now()-o.at}),this.lastDisconnect=null),this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=setInterval(()=>{if(n.readyState===WebSocket.OPEN){if(Date.now()>this.heartbeatDeadline){n.close(4e3,"Signaling heartbeat timed out");return}n.send("ping")}},2e4)}),n.addEventListener("message",s=>{this.heartbeatDeadline=Date.now()+45e3,this.handleSignal(String(s.data))}),n.addEventListener("close",s=>{if(this.socket!==n)return;this.socket=null,this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null;const o=!this.stopped&&this.options.reconnect!==!1,r={code:s.code,reason:s.reason,wasClean:s.wasClean,willReconnect:o,attempt:this.reconnectAttempt+1};o&&(this.lastDisconnect={code:s.code,reason:s.reason,wasClean:s.wasClean,at:Date.now()}),this.emit("signalingdisconnected",r),this.reportSignalingDiagnostic({event:o?"reconnecting":"disconnected",...r}),o&&this.scheduleReconnect()}),n.addEventListener("error",()=>{const s={message:"RTC signaling connection failed",reconnecting:!this.stopped&&this.options.reconnect!==!1,attempt:this.reconnectAttempt+1};this.emit("signalingerror",s),this.reportSignalingDiagnostic({event:"error",attempt:s.attempt})})}handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i==="call.incoming"&&t.call&&typeof t.call=="object"){this.handleIncomingCall(t.call);return}if(i==="call.updated"){const n=t.call;n?.id&&n.status&&this.handleCallUpdate({id:n.id,status:n.status});return}i==="presence"?this.emit("presence",{users:t.users??[]}):i!=="pong"&&this.emit(i||"signal",t)}async callAction(e,t){return this.request(`/v1/rtc/client/calls/${encodeURIComponent(e)}/${t}`,{method:"POST",body:"{}"})}async request(e,t,i=!1){const n=this.options.callActionsUrl??this.options.apiBaseUrl??this.options.signalingUrl??globalThis.location?.origin;if(!n)throw new Error("callActionsUrl is required for call actions");const s=new URL(n,globalThis.location?.href),o=await this.resolveToken("initial"),r=new Headers(t.headers);r.set("authorization",`Bearer ${o}`),r.set("content-type","application/json");const c=await fetch(new URL(e,s.origin),{...t,headers:r}),l=await c.json().catch(()=>({}));if(c.status===401&&this.options.tokenProvider&&!i)return await this.resolveToken("unauthorized",!0),this.request(e,t,!0);if(!c.ok){const d=l.error;throw new Error(typeof d=="string"?d:d?.message||`RTC API failed with HTTP ${c.status}`)}return l}renderIncoming(e){if(!this.mount)return;this.clearUi();const t=document.createElement("section");t.dataset.rtcIncomingCall=e.id,t.setAttribute("role","dialog"),t.setAttribute("aria-label",`Incoming ${e.mode} call from ${e.caller.name}`),Object.assign(t.style,{display:"grid",gap:"14px",padding:"20px",border:"1px solid #cbd8dc",borderRadius:"8px",background:"#fff",color:"#102027",boxShadow:"0 14px 38px #07101429",fontFamily:"system-ui, sans-serif"});const i=document.createElement("small");i.textContent=e.mode==="video"?"Incoming video call":"Incoming voice call",i.style.color="#5d7079";const n=document.createElement("strong");n.textContent=e.caller.name,n.style.fontSize="20px";const s=document.createElement("div");Object.assign(s.style,{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"10px"});const o=pe("Decline","#fff","#a32920","#d9a09b"),r=pe("Answer","#fff","#08745f","#08745f");o.addEventListener("click",()=>{this.reject(e.id).catch(c=>this.emit("error",U(c)))}),r.addEventListener("click",()=>{this.accept(e.id).catch(c=>this.emit("error",U(c)))}),s.appendChild(o),s.appendChild(r),t.appendChild(i),t.appendChild(n),t.appendChild(s),this.mount.replaceChildren(t)}dismiss(e){e&&this.currentCall?.id!==e&&this.outgoingCallId!==e||(this.currentCall=null,(!e||this.outgoingCallId===e)&&(this.outgoingCallId=null),this.callAudio.stop(),this.clearUi())}showTerminal(e,t){if(this.callAudio.stop(),this.currentCall?.id===e&&(this.currentCall=null),this.outgoingCallId===e&&(this.outgoingCallId=null),!this.mount)return;this.clearUi();const i=document.createElement("section");i.dataset.rtcCallTerminal=e,i.setAttribute("role","status"),Object.assign(i.style,{display:"grid",placeItems:"center",gap:"10px",minHeight:"150px",padding:"20px",border:"1px solid #cbd8dc",borderRadius:"8px",background:"#fff",color:"#102027",boxShadow:"0 14px 38px #07101429",fontFamily:"system-ui, sans-serif"});const n=document.createElement("strong");n.textContent=t,n.style.fontSize="18px",i.appendChild(n),this.mount.replaceChildren(i);const s=Math.max(0,this.options.terminalStateDurationMs??1600);this.terminalTimer=setTimeout(()=>this.clearUi(),s)}clearUi(){this.terminalTimer&&clearTimeout(this.terminalTimer),this.terminalTimer=null,this.mount&&this.mount.replaceChildren()}scheduleReconnect(){this.reconnectTimer&&clearTimeout(this.reconnectTimer);const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.openSocket("reconnect")},e)}async resolveToken(e,t=!1){if(!t&&this.currentToken&&!this.tokenNeedsRefresh())return this.currentToken;if(!this.options.tokenProvider){if(!this.currentToken)throw new Error("A valid RTC client token is required");return this.currentToken}return this.tokenRequest?this.tokenRequest:(this.tokenRequest=(async()=>{const i=await this.options.tokenProvider?.({reason:e,previousExpiresAt:this.currentTokenExpiresAt}),n=typeof i=="string"?{token:i}:i;if(!n?.token?.startsWith("rtcc_"))throw new Error("tokenProvider returned an invalid RTC client token");return this.currentToken=n.token,this.currentTokenExpiresAt=n.expiresAt,this.scheduleTokenRefresh(),this.emit("tokenrefreshed",{reason:e,expiresAt:n.expiresAt}),n.token})().finally(()=>{this.tokenRequest=null}),this.tokenRequest)}tokenNeedsRefresh(){if(!this.currentTokenExpiresAt)return!1;const e=Date.parse(this.currentTokenExpiresAt)-Date.now();return Number.isFinite(e)?e<=this.refreshSkew(e):!0}refreshSkew(e){const t=Math.max(5e3,this.options.tokenRefreshSkewMs??3e5);return Math.min(t,Math.max(5e3,e*.2))}scheduleTokenRefresh(){if(this.tokenRefreshTimer&&clearTimeout(this.tokenRefreshTimer),this.tokenRefreshTimer=null,this.stopped||!this.options.tokenProvider||!this.currentTokenExpiresAt)return;const e=Date.parse(this.currentTokenExpiresAt)-Date.now();if(!Number.isFinite(e))return;const t=Math.max(0,e-this.refreshSkew(e));this.tokenRefreshTimer=setTimeout(()=>{this.tokenRefreshTimer=null,this.resolveToken("expiring",!0).catch(i=>{this.emit("tokenerror",U(i)),this.stopped||(this.tokenRefreshTimer=setTimeout(()=>this.scheduleTokenRefresh(),3e4))})},Math.min(t,2147e6))}releaseMedia(e){this.activeCallIds.delete(e),this.seenInvitationIds.delete(e);const t=this.mediaClients.get(e);t&&(this.mediaClients.delete(e),t.leave().catch(()=>{}).finally(()=>t.destroy()))}reportSignalingDiagnostic(e){this.options.reportSignalingDiagnostics===!1||this.stopped||this.request("/v1/rtc/client/signaling-events",{method:"POST",body:JSON.stringify({...e,occurredAt:new Date().toISOString()})}).catch(t=>this.emit("diagnosticserror",U(t)))}emit(e,t){const i={type:e,detail:t,timestamp:new Date().toISOString()};this.dispatchEvent(new CustomEvent(e,{detail:i})),this.options.onEvent?.(i)}}function je(a){const e=new ue(a);return a.autoConnect!==!1&&a.signalingUrl&&e.connect(),e}function me(a){return a?typeof a=="string"?document.querySelector(a):a:null}function pe(a,e,t,i){const n=document.createElement("button");return n.type="button",n.textContent=a,Object.assign(n.style,{minHeight:"44px",border:`1px solid ${i}`,borderRadius:"6px",background:t,color:e,cursor:"pointer",font:"inherit",fontWeight:"700"}),n}function Ve(a){return{rejected:"Call declined",busy:"Line busy",cancelled:"Call cancelled",ended:"Call ended",failed:"Call failed",missed:"No answer"}[a]??"Call ended"}function fe(a){return{id:a.participantId,name:a.displayName,role:"participant"}}async function Ue(a,e){const t=await a.getStats(),i=new Map,n=new Map;let s=0,o=0,r=0,c=0,l=0,d=0,u=0,k=0,f=0,v=0,R=0,y=0,w=0,p=0,I=0,A=null,D=null,E=null,L=null,S=null,M=null;t.forEach(g=>{n.set(g.id,g),g.type==="codec"&&i.set(g.id,String(g.mimeType??""))}),t.forEach(g=>{if(g.type==="inbound-rtp"&&!g.isRemote&&(s+=Number(g.bytesReceived??0),r+=Math.max(0,Number(g.packetsLost??0)),c+=Math.max(0,Number(g.packetsReceived??0)),Number.isFinite(g.jitter)&&(l+=Number(g.jitter),d+=1),u=Math.max(u,Number(g.framesPerSecond??0)),k=Math.max(k,Number(g.frameWidth??0)),R=Math.max(R,Number(g.frameHeight??0)),y+=Number(g.framesDropped??0),w+=Number(g.freezeCount??0),p+=Number(g.totalFreezesDuration??0),I+=Number(g.concealedSamples??0),S||=i.get(String(g.codecId??""))??null),g.type==="outbound-rtp"&&!g.isRemote&&(f=Math.max(f,Number(g.frameWidth??0)),v=Math.max(v,Number(g.frameHeight??0)),o+=Number(g.bytesSent??0),M||=g.qualityLimitationReason?String(g.qualityLimitationReason):null,S||=i.get(String(g.codecId??""))??null),g.type==="candidate-pair"&&(g.selected||g.nominated)&&g.state==="succeeded"){A=Number.isFinite(g.currentRoundTripTime)?Number(g.currentRoundTripTime):A,D=Number.isFinite(g.availableOutgoingBitrate)?Number(g.availableOutgoingBitrate):D;const q=n.get(g.localCandidateId);E=q?.candidateType?String(q.candidateType):E,L=q?.protocol?String(q.protocol):L}});const m=Date.now(),h=e?Math.max(.001,(m-e.timestamp)/1e3):0,T=e?Math.max(0,Math.round((s-e.inboundBytes)*8/h)):null,P=e?Math.max(0,Math.round((o-e.outboundBytes)*8/h)):null,C=r+c,N=e?Math.max(0,r-e.packetsLost):r,Q=e?Math.max(0,c-e.packetsReceived):c,j=N+Q;return{counters:{timestamp:m,inboundBytes:s,outboundBytes:o,packetsLost:r,packetsReceived:c},sample:{rttMs:A===null?null:A*1e3,jitterMs:d?l/d*1e3:null,packetLossPct:j?N/j*100:null,packetLossCumulativePct:C?r/C*100:null,packetsLost:r,packetsReceived:c,inboundBitrateBps:T,outboundBitrateBps:P,availableOutgoingBitrateBps:D,framesPerSecond:u||null,frameWidth:k||null,frameHeight:R||null,inboundFrameWidth:k||null,inboundFrameHeight:R||null,outboundFrameWidth:f||null,outboundFrameHeight:v||null,framesDropped:y,freezeCount:w,freezeDurationMs:Math.round(p*1e3),concealedSamples:I,codec:S,transportProtocol:L,candidateType:E,qualityLimitationReason:M}}}function Qe(a){let e=a.mediaScore==null?5:Math.round(ye(Number(a.mediaScore),0,5));const t=K(a.packetLossPct)??0,i=K(a.rttMs)??0,n=K(a.jitterMs)??0,s=String(a.connectionState??"unknown").toLowerCase();["failed","closed","disconnected"].includes(s)?e=0:["reconnecting","checking"].includes(s)&&(e=Math.min(e,2)),t>=15?e=Math.min(e,0):t>=8?e=Math.min(e,1):t>=3?e=Math.min(e,2):t>=1&&(e=Math.min(e,3)),i>=1200?e=Math.min(e,0):i>=800?e=Math.min(e,1):i>=400?e=Math.min(e,2):i>=250&&(e=Math.min(e,3)),n>=150?e=Math.min(e,1):n>=80?e=Math.min(e,2):n>=40&&(e=Math.min(e,3));const o=Number(ye(4.5-t*.09-i*.0011-n*.006,1,4.5).toFixed(2));return{networkScore:e,estimatedMos:o}}function ge(){return navigator.connection??null}function Be(){const a=Number(navigator.deviceMemory);return Number.isFinite(a)?a:null}function K(a){const e=Number(a);return Number.isFinite(e)?e:null}function ye(a,e,t){return Math.min(t,Math.max(e,a))}function U(a){return a instanceof Error?{name:a.name,message:a.message}:{name:"ServiceError",message:String(a??"Service request failed")}}function ve(a,e){return a instanceof Error?a:new Error(e)}const Se="1.3.21";b.AVATAR_TUNING_DEFAULTS=J,b.BACKGROUND_PRESETS=ae,b.OVERLAY_EFFECTS=se,b.RtcClient=le,b.RtcIncomingClient=ue,b.SPEAKER_NOISE_FLOOR=.02,b.SPEAKER_TAKEOVER_RATIO=1.4,b.createAvatarPipeline=Oe,b.createCallRecorder=Le,b.createPictureInPictureController=Ae,b.createRtcClient=qe,b.createRtcIncomingClient=je,b.createVideoEffectsPipeline=De,b.drawAvatar=ce,b.headPoseFromMatrix=re,b.neutralRig=W,b.pickActiveSpeaker=H,b.rigFromBlendshapes=oe,b.scenePainters=_,b.smoothRig=$,b.stylizedAvatarRenderer=G,b.unlockRtcCallAudio=he,b.version=Se,Object.defineProperty(b,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(b,Q){typeof exports=="object"&&typeof module<"u"?Q(exports):typeof define=="function"&&define.amd?define(["exports"],Q):(b=typeof globalThis<"u"?globalThis:b||self,Q(b.StreamingCdnRtc={}))})(this,(function(b){"use strict";function H(a,e,t=.02,i=1.4){const n=a.filter(r=>r.level>=t);if(n.length===0)return null;const s=n.reduce((r,c)=>c.level>r.level?c:r),o=n.find(r=>r.participantId===e);return o?s.participantId===e?e:s.level>=o.level*i?s.participantId:e:s.participantId}function ie(a){const e=[];for(const t of a){if(String(t.type??"")!=="inbound-rtp"||String(t.kind??t.mediaType??"")!=="audio")continue;const i=Number(t.audioLevel??NaN);Number.isFinite(i)&&e.push({mid:typeof t.mid=="string"?t.mid:null,level:i})}return e}const V={audio:{active:!1,maxBitrate:0,maxFramerate:0,scaleResolutionDownBy:4},low:{active:!0,maxBitrate:35e4,maxFramerate:15,scaleResolutionDownBy:4},medium:{active:!0,maxBitrate:11e5,maxFramerate:24,scaleResolutionDownBy:2},high:{active:!0,maxBitrate:28e5,maxFramerate:30,scaleResolutionDownBy:1}},Ce={warmupSamples:3,poorSamplesToStepDown:2,goodSamplesToStepUp:6,criticalLossPct:8,criticalRttMs:800,poorLossPct:3,poorRttMs:350,goodLossPct:1,goodRttMs:250,sustainRatio:.8,bandwidthHeadroomRatio:1.25};function Re(a){const e=String(a??"").trim().toLowerCase();return!e||e==="none"?"":e==="cpu"||e==="bandwidth"?e:"other"}function Ee(a,e,t,i,n={}){const s={...Ce,...n.thresholds},o=["low","medium","high"],r=n.ceiling&&n.ceiling!=="audio"?n.ceiling:"high",c=o.indexOf(r),l=o.indexOf(a),d=(n.samplesSeen??s.warmupSamples)+1,u=d<=s.warmupSamples,k=Re(e.qualityLimitationReason),f=e.packetLossPct??0,v=e.rttMs??0,R=e.availableOutgoingBitrateBps,y=V[a].maxBitrate,w=f>=s.criticalLossPct||v>=s.criticalRttMs||k==="cpu",p=k==="bandwidth"&&(R==null||R<y*s.bandwidthHeadroomRatio),E=w||f>=s.poorLossPct||v>=s.poorRttMs||p,A=R==null||R>=y*s.sustainRatio,D=f<s.goodLossPct&&v<s.goodRttMs&&!k&&A,I=E?t+1:0,L=D?i+1:0,S={quality:a,poorSamples:I,goodSamples:L,samplesSeen:d,changed:!1};return l>c?{quality:o[c],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:u?S:w&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:I>=s.poorSamplesToStepDown&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:L>=s.goodSamplesToStepUp&&l<c?{quality:o[l+1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:S}function Ie(a,e){const t=a instanceof Map?a:new Map(a);let i=0,n=0,s=null,o=null,r=null;for(const d of t.values()){const u=String(d.type??"");if(u==="outbound-rtp"&&String(d.kind??"")==="video"&&(i+=Number(d.packetsSent??0),s=d.qualityLimitationReason??s),u==="remote-inbound-rtp"&&String(d.kind??"")==="video"){n+=Number(d.packetsLost??0);const k=Number(d.roundTripTime??NaN);Number.isFinite(k)&&(o=k*1e3)}if(u==="candidate-pair"&&(d.nominated===!0||String(d.state??"")==="succeeded")){const k=Number(d.currentRoundTripTime??NaN);o==null&&Number.isFinite(k)&&(o=k*1e3);const f=Number(d.availableOutgoingBitrate??NaN);Number.isFinite(f)&&(r=f)}}const c={packetsSent:i,packetsLost:n,timestamp:Date.now()};let l=null;if(e){const d=i-e.packetsSent,u=n-e.packetsLost;d>0&&u>=0&&(l=u/(d+u)*100)}return{sample:{packetLossPct:l,rttMs:o,availableOutgoingBitrateBps:r,qualityLimitationReason:s},counters:c}}function z(a,e){const t=new URL(String(a),e);return t.protocol==="https:"?t.protocol="wss:":t.protocol==="http:"&&(t.protocol="ws:"),t}function _(a,e){const t=new URL(String(a),e);return t.protocol==="wss:"?t.protocol="https:":t.protocol==="ws:"&&(t.protocol="http:"),t}const Me={audio:{active:!1,maxBitrate:0,maxFramerate:0,scaleResolutionDownBy:4}},Pe={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1};class B{options;socket=null;localStream=null;screenStream=null;peers=new Map;joinPromise=null;resolveJoin=null;rejectJoin=null;joined=!1;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;qualityTimer=null;speakerTimer=null;activeSpeaker=null;heartbeatDeadline=0;quality;preferredVideoCodec;signalQueue=Promise.resolve();socketGeneration=0;constructor(e){this.options=e,this.localStream=e.localStream??null,this.quality=e.quality??"high",this.preferredVideoCodec=e.preferredVideoCodec??"auto"}async initialize(){if(!globalThis.RTCPeerConnection||!globalThis.WebSocket)throw new Error("This browser does not support WebRTC")}async join(){if(!this.joined){if(this.joinPromise)return this.joinPromise;this.joinPromise=this.connect();try{await this.joinPromise}finally{this.joinPromise=null}}}async leave(){this.stopped=!0,this.clearConnectionTimers(),this.send({type:"rtc.leave"}),this.joined=!1,this.socket?.close(1e3,"Participant left"),this.socket=null;for(const e of this.peers.values())e.connection.close();this.peers.clear(),this.stopStream(this.localStream),this.stopStream(this.screenStream),this.localStream=null,this.screenStream=null}setMuted(e){for(const t of this.localStream?.getAudioTracks()??[])t.enabled=!e}async setCameraEnabled(e){if(e&&!this.localStream?.getVideoTracks().length){const i=(await navigator.mediaDevices.getUserMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}})).getVideoTracks()[0];if(!i)throw new Error("Camera is unavailable");this.localStream||(this.localStream=new MediaStream),this.localStream.addTrack(i),this.screenStream?.getVideoTracks().some(n=>n.readyState==="live")||await this.replaceVideoTrack(i);return}for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e}async setScreenShareEnabled(e){if(!e){const n=this.screenStream;this.screenStream=null,this.stopStream(n);const s=this.localStream?.getVideoTracks()[0]??null;await this.replaceVideoTrack(s),this.options.onEvent("screensharechange",{enabled:!1,stream:null});return}if(this.screenStream?.getVideoTracks()[0]?.readyState==="live")return;const t=await navigator.mediaDevices.getDisplayMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}},audio:!1}),i=t.getVideoTracks()[0];if(!i)throw new Error("Screen sharing is unavailable");i.contentHint="detail",this.screenStream=t,i.addEventListener("ended",()=>{this.screenStream?.getVideoTracks()[0]?.id===i.id&&this.setScreenShareEnabled(!1)});try{await this.replaceVideoTrack(i),this.options.onEvent("screensharechange",{enabled:!0,stream:t})}catch(n){throw this.screenStream=null,this.stopStream(t),n}}async setDevice(e){const t={deviceId:{exact:e.deviceId}},i=await navigator.mediaDevices.getUserMedia(e.kind==="audioinput"?{audio:t}:{video:{...t,width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}}),n=e.kind==="audioinput"?i.getAudioTracks()[0]:i.getVideoTracks()[0];if(!n)throw new Error("Selected media device is unavailable");const s=e.kind==="audioinput"?"audio":"video",o=this.localStream?.getTracks().find(r=>r.kind===s);if(this.localStream||(this.localStream=new MediaStream),o&&(this.localStream.removeTrack(o),o.stop()),this.localStream.addTrack(n),s==="video"){this.screenStream?.getVideoTracks().some(r=>r.readyState==="live")||await this.replaceVideoTrack(n);return}for(const r of this.peers.values()){const c=r.connection.getSenders().find(l=>l.track?.kind===s);c?await c.replaceTrack(n):r.connection.addTrack(n,this.localStream)}await this.applyVideoQuality()}async setQuality(e){this.quality=e;for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e!=="audio";for(const t of this.peers.values())t.quality=B.clampToCeiling(t.quality,e),t.poorSamples=0,t.goodSamples=0;await this.applyVideoQuality()}static clampToCeiling(e,t){if(t==="audio")return e;const i=["low","medium","high"];return i.indexOf(e)>i.indexOf(t)?t:e}async setPreferredVideoCodec(e){this.preferredVideoCodec=e;for(const t of this.peers.values())this.applyCodecPreference(t.connection);await this.renegotiateAll()}getParticipants(){return[...this.peers.values()].map(e=>e.identity)}getLocalStream(){return this.localStream}async setVideoTrack(e){if(e.kind!=="video")throw new Error("A video track is required");this.localStream||(this.localStream=new MediaStream);const t=this.localStream.getVideoTracks()[0];t?.id!==e.id&&(t&&this.localStream.removeTrack(t),this.localStream.addTrack(e)),await this.replaceVideoTrack(e),this.options.onEvent("localstream",{stream:this.localStream,track:e})}getRemoteStreams(){return[...this.peers.values()].map(e=>({participant:e.identity,stream:e.stream}))}getPrimaryPeerConnection(){return this.peers.values().next().value?.connection??null}sendChat(e,t){this.send({type:"rtc.chat",message:e,participantIds:t??[]})}sendControl(e,t={}){this.send({type:"rtc.control",action:e,detail:t})}async connect(){const{credential:e}=this.options;if(!e.signalingToken||!e.signalingUrl||!e.roomId||!e.participantId)throw new Error("A room signaling credential is required");this.stopped=!1,!this.localStream&&(this.options.audio||this.options.video)&&(this.localStream=await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})),this.localStream&&this.options.onEvent("localstream",{stream:this.localStream}),await this.openSocket(!1)}async openSocket(e){const{credential:t}=this.options,i=z(t.signalingUrl,globalThis.location?.href),n=new Promise((c,l)=>{this.resolveJoin=c,this.rejectJoin=l}),s=new WebSocket(i,["rtc-client",t.signalingToken]),o=++this.socketGeneration;this.socket=s;const r=setTimeout(()=>this.rejectJoin?.(new Error("RTC signaling timed out")),15e3);s.addEventListener("open",()=>{this.heartbeatDeadline=Date.now()+45e3,this.startHeartbeat(s),this.startQualitySampling(),this.send({type:"rtc.room.join"})}),s.addEventListener("message",c=>{const l=String(c.data);this.signalQueue=this.signalQueue.then(async()=>{this.socket!==s||this.socketGeneration!==o||await this.handleSignal(l)}).catch(d=>{const u=d instanceof Error?d.message:"RTC signaling processing failed";this.options.onEvent("signalingerror",{message:u}),this.joined||this.rejectJoin?.(new Error(u))})}),s.addEventListener("error",()=>this.rejectJoin?.(new Error("RTC signaling connection failed"))),s.addEventListener("close",c=>{if(this.socket!==s)return;this.socket=null,this.stopHeartbeat(),this.stopQualitySampling(),!this.joined&&!e&&this.rejectJoin?.(new Error(c.reason||"RTC signaling connection closed"));const l=!this.stopped&&(this.joined||e);this.joined=!1,l?(this.resetPeers(),this.options.onEvent("reconnecting",{reason:c.reason,code:c.code,attempt:this.reconnectAttempt+1}),this.scheduleReconnect()):this.stopped||this.options.onEvent("disconnected",{reason:c.reason,code:c.code})});try{await n,this.joined=!0,this.options.onEvent("connected",{participantId:t.participantId,resumed:e})}finally{clearTimeout(r),this.resolveJoin=null,this.rejectJoin=null}}async handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i!=="ready"){if(i==="pong"){this.heartbeatDeadline=Date.now()+45e3;return}if(i==="rtc.room.peers"){this.reconnectAttempt=0;const n=Array.isArray(t.peers)?t.peers:[];for(const s of n)this.ensurePeer(s);this.resolveJoin?.();for(const s of n)this.shouldInitiateOffer(s)&&await this.createOffer(s);return}if(i==="rtc.room.join"&&t.sender){this.options.onEvent("participantjoined",t.sender),this.ensurePeer(t.sender),this.shouldInitiateOffer(t.sender)&&await this.createOffer(t.sender);return}if(i==="rtc.offer"&&t.sender&&t.description){const n=this.ensurePeer(t.sender);n.offerTask&&await n.offerTask;const s=n.connection.signalingState!=="stable",o=!this.shouldInitiateOffer(t.sender);if(s&&!o)return;s&&await n.connection.setLocalDescription({type:"rollback"}),await n.connection.setRemoteDescription(t.description);const r=await n.connection.createAnswer();await n.connection.setLocalDescription(r),this.send({type:"rtc.answer",targetParticipantId:t.sender.participantId,negotiationId:t.negotiationId,description:n.connection.localDescription});return}if(i==="rtc.answer"&&t.sender&&t.description){const n=this.peers.get(t.sender.participantId);if(!n||n.connection.signalingState!=="have-local-offer"||t.negotiationId&&n.activeNegotiationId&&t.negotiationId!==n.activeNegotiationId)return;await n.connection.setRemoteDescription(t.description),n.activeNegotiationId=null;return}if(i==="rtc.ice"&&t.sender&&t.candidate){await this.ensurePeer(t.sender).connection.addIceCandidate(t.candidate).catch(()=>{});return}if((i==="rtc.peer.left"||i==="rtc.leave")&&t.sender){this.removePeer(t.sender.participantId);return}i==="rtc.chat"?this.options.onEvent("chatupdate",t):i==="rtc.control"&&this.options.onEvent("control",t)}}ensurePeer(e){const t=this.peers.get(e.participantId);if(t)return t;const i=new RTCPeerConnection({iceServers:this.options.credential.iceServers??[],iceTransportPolicy:this.options.iceTransportPolicy??"all"}),n=new MediaStream,s={identity:e,connection:i,stream:n,makingOffer:!1,offerTask:null,activeNegotiationId:null,quality:B.startingQuality(this.quality),poorSamples:0,goodSamples:0,samplesSeen:0,counters:null};this.peers.set(e.participantId,s);for(const c of this.localStream?.getAudioTracks()??[])i.addTrack(c,this.localStream);const o=this.screenStream?.getVideoTracks().some(c=>c.readyState==="live")?this.screenStream:this.localStream,r=o?.getVideoTracks()[0];return r&&o&&(s.videoSender=i.addTrack(r,o)),!this.localStream?.getAudioTracks().length&&this.options.receiveAudio&&i.addTransceiver("audio",{direction:"recvonly"}),!r&&this.options.receiveVideo&&(s.videoTransceiver=i.addTransceiver("video",{direction:"recvonly"}),s.videoSender=s.videoTransceiver.sender),this.applyCodecPreference(i),this.applyVideoQuality(i),i.addEventListener("icecandidate",c=>{c.candidate&&this.send({type:"rtc.ice",targetParticipantId:e.participantId,candidate:c.candidate.toJSON()})}),i.addEventListener("track",c=>{n.getTracks().some(l=>l.id===c.track.id)||n.addTrack(c.track),this.options.onEvent("remotestream",{participant:e,stream:n,track:c.track})}),i.addEventListener("connectionstatechange",()=>{["failed","closed"].includes(i.connectionState)&&this.removePeer(e.participantId),this.options.onEvent("peerconnectionstate",{participant:e,state:i.connectionState})}),s}async createOffer(e){const t=this.ensurePeer(e);if(t.offerTask)return t.offerTask;if(t.connection.signalingState!=="stable")return;const i=this.performOffer(t);t.offerTask=i;try{await i}finally{t.offerTask===i&&(t.offerTask=null)}}async performOffer(e){e.makingOffer=!0;try{const t=await e.connection.createOffer({offerToReceiveAudio:this.options.receiveAudio,offerToReceiveVideo:this.options.receiveVideo});if(e.connection.signalingState!=="stable")return;await e.connection.setLocalDescription(t);const i=ne();e.activeNegotiationId=i,this.send({type:"rtc.offer",targetParticipantId:e.identity.participantId,negotiationId:i,description:e.connection.localDescription})}finally{e.makingOffer=!1}}shouldInitiateOffer(e){return this.options.credential.participantId.localeCompare(e.participantId)<0}async renegotiateAll(){for(const e of this.peers.values())await this.createOffer(e.identity)}async replaceVideoTrack(e){let t=!1;for(const i of this.peers.values()){const n=i.videoSender??i.connection.getSenders().find(o=>o.track?.kind==="video")??i.connection.getTransceivers().find(o=>o.receiver.track.kind==="video")?.sender,s=i.videoTransceiver??i.connection.getTransceivers().find(o=>o.sender===n);if(n){const o=e?this.options.receiveVideo?"sendrecv":"sendonly":this.options.receiveVideo?"recvonly":"inactive";s&&s.direction!==o&&(s.direction=o,t=!0),await n.replaceTrack(e),i.videoSender=n,s&&(i.videoTransceiver=s)}else e&&(i.videoSender=i.connection.addTrack(e,this.screenStream??this.localStream??new MediaStream([e])),t=!0)}t&&await this.renegotiateAll(),await this.applyVideoQuality()}async applyVideoQuality(e){const t=e?[...this.peers.values()].filter(i=>i.connection===e):[...this.peers.values()];for(const i of t)await this.applyPeerVideoQuality(i)}async applyPeerVideoQuality(e){const t=this.screenStream?.getVideoTracks()[0]?.id;for(const i of e.connection.getSenders().filter(n=>n.track?.kind==="video")){const n=!!(t&&i.track?.id===t),s=n?Pe:this.quality==="audio"?Me.audio:V[e.quality],o=i.getParameters(),r=o.encodings?.length?o.encodings:[{}];o.encodings=r.map(c=>({...c,...s})),o.degradationPreference=n?"maintain-resolution":"maintain-framerate",await i.setParameters(o)}}startQualitySampling(){this.stopQualitySampling(),this.qualityTimer=setInterval(()=>{this.samplePeerQuality()},5e3),this.speakerTimer=setInterval(()=>{this.sampleActiveSpeaker()},1e3)}stopQualitySampling(){this.qualityTimer&&clearInterval(this.qualityTimer),this.qualityTimer=null,this.speakerTimer&&clearInterval(this.speakerTimer),this.speakerTimer=null}async sampleActiveSpeaker(){const e=[];for(const i of[...this.peers.values()])if(i.connection.connectionState==="connected")try{const n=await i.connection.getStats();for(const s of ie(n.values()))e.push({participantId:i.identity.participantId,level:s.level})}catch{}const t=H(e,this.activeSpeaker);t!==this.activeSpeaker&&(this.activeSpeaker=t,this.options.onEvent("activespeaker",{participantId:t}))}async samplePeerQuality(){if(this.quality!=="audio")for(const e of[...this.peers.values()]){if(e.connection.connectionState!=="connected")continue;let t;try{t=await e.connection.getStats()}catch{continue}const{sample:i,counters:n}=Ie(t,e.counters);e.counters=n;const s=Ee(e.quality,i,e.poorSamples,e.goodSamples,{samplesSeen:e.samplesSeen,ceiling:this.quality});e.poorSamples=s.poorSamples,e.goodSamples=s.goodSamples,e.samplesSeen=s.samplesSeen,s.changed&&(e.quality=s.quality,await this.applyPeerVideoQuality(e).catch(()=>{}),this.options.onEvent("peerquality",{participantId:e.identity.participantId,quality:s.quality}))}}static startingQuality(e){return e==="low"?"low":"medium"}applyCodecPreference(e){if(this.preferredVideoCodec==="auto"||!globalThis.RTCRtpReceiver?.getCapabilities)return;const t=RTCRtpReceiver.getCapabilities("video")?.codecs??[],i=`video/${this.preferredVideoCodec}`,n=[...t.filter(s=>s.mimeType.toLowerCase()===i),...t.filter(s=>s.mimeType.toLowerCase()!==i)];for(const s of e.getTransceivers())s.receiver.track.kind==="video"&&typeof s.setCodecPreferences=="function"&&s.setCodecPreferences(n)}removePeer(e){const t=this.peers.get(e);t&&(t.connection.close(),this.peers.delete(e),this.options.onEvent("participantleft",t.identity))}resetPeers(){for(const e of this.peers.values())e.connection.close();this.peers.clear()}startHeartbeat(e){this.stopHeartbeat(),this.stopQualitySampling(),this.heartbeatTimer=setInterval(()=>{if(!(this.socket!==e||e.readyState!==WebSocket.OPEN)){if(Date.now()>this.heartbeatDeadline){e.close(4e3,"Signaling heartbeat timed out");return}e.send("ping")}},2e4)}stopHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null}clearConnectionTimers(){this.stopHeartbeat(),this.stopQualitySampling(),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=null}scheduleReconnect(){if(this.reconnectTimer||this.stopped)return;const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.openSocket(!0).catch(t=>{this.options.onEvent("signalingerror",{message:t instanceof Error?t.message:"RTC signaling connection failed"}),this.scheduleReconnect()})},e)}send(e){this.socket?.readyState===WebSocket.OPEN&&this.socket.send(JSON.stringify({clientMessageId:e.clientMessageId??ne(),...e}))}stopStream(e){for(const t of e?.getTracks()??[])t.stop()}}function ne(){return globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}const W={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1},ae=[{rid:"a",maxBitrate:V.high.maxBitrate,maxFramerate:V.high.maxFramerate},{rid:"b",maxBitrate:V.medium.maxBitrate,maxFramerate:V.medium.maxFramerate,scaleResolutionDownBy:2},{rid:"c",maxBitrate:V.low.maxBitrate,maxFramerate:V.low.maxFramerate,scaleResolutionDownBy:4}],se={high:"a",medium:"b",low:"c"};class Ae{options;apiBase;socket=null;connection=null;sessionId=null;localStream=null;screenStream=null;remotes=new Map;midToParticipant=new Map;pendingAnnouncements=new Map;videoSender=null;joined=!1;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;heartbeatDeadline=0;speakerTimer=null;activeSpeaker=null;quality;resolveJoin=null;rejectJoin=null;signalQueue=Promise.resolve();negotiationQueue=Promise.resolve();socketGeneration=0;constructor(e){this.options=e,this.localStream=e.localStream??null,this.quality=e.quality??"high",this.apiBase=_(e.credential.signalingUrl,globalThis.location?.href).origin}async initialize(){if(!globalThis.RTCPeerConnection||!globalThis.WebSocket)throw new Error("This browser does not support WebRTC")}async join(){if(this.joined)return;const{credential:e}=this.options;if(!e.signalingToken||!e.signalingUrl||!e.roomId||!e.participantId)throw new Error("A room signaling credential is required");this.stopped=!1,!this.localStream&&(this.options.audio||this.options.video)&&(this.localStream=await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})),this.localStream&&this.options.onEvent("localstream",{stream:this.localStream}),await this.openSocket(!1),await this.publish(),this.startSpeakerSampling(),this.announceTracks();const t=[...this.pendingAnnouncements.values()];this.pendingAnnouncements.clear();for(const i of t)await this.handleTrackAnnouncement(i.sender,i.detail)}async leave(){this.stopped=!0,this.clearTimers(),this.send({type:"rtc.leave"}),this.joined=!1,this.socket?.close(1e3,"Participant left"),this.socket=null,await this.closePublishedTracks().catch(()=>{}),this.connection?.close(),this.connection=null,this.sessionId=null,this.remotes.clear(),this.midToParticipant.clear(),this.stopStream(this.localStream),this.stopStream(this.screenStream),this.localStream=null,this.screenStream=null}setMuted(e){for(const t of this.localStream?.getAudioTracks()??[])t.enabled=!e}async setCameraEnabled(e){if(e&&!this.localStream?.getVideoTracks().length){const i=(await navigator.mediaDevices.getUserMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}})).getVideoTracks()[0];if(!i)throw new Error("Camera is unavailable");this.localStream||(this.localStream=new MediaStream),this.localStream.addTrack(i),this.screenStream?.getVideoTracks().some(n=>n.readyState==="live")||await this.replaceVideoTrack(i);return}for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e}async setScreenShareEnabled(e){if(!e){const n=this.screenStream;this.screenStream=null,this.stopStream(n);const s=this.localStream?.getVideoTracks()[0]??null;await this.replaceVideoTrack(s),this.options.onEvent("screensharechange",{enabled:!1,stream:null});return}if(this.screenStream?.getVideoTracks()[0]?.readyState==="live")return;const t=await navigator.mediaDevices.getDisplayMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}},audio:!1}),i=t.getVideoTracks()[0];if(!i)throw new Error("Screen sharing is unavailable");i.contentHint="detail",this.screenStream=t,i.addEventListener("ended",()=>{this.screenStream?.getVideoTracks()[0]?.id===i.id&&this.setScreenShareEnabled(!1)});try{await this.replaceVideoTrack(i),this.options.onEvent("screensharechange",{enabled:!0,stream:t})}catch(n){throw this.screenStream=null,this.stopStream(t),n}}async setDevice(e){const t={deviceId:{exact:e.deviceId}},i=await navigator.mediaDevices.getUserMedia(e.kind==="audioinput"?{audio:t}:{video:{...t,width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}}),n=e.kind==="audioinput"?i.getAudioTracks()[0]:i.getVideoTracks()[0];if(!n)throw new Error("Selected media device is unavailable");const s=e.kind==="audioinput"?"audio":"video",o=this.localStream?.getTracks().find(c=>c.kind===s);if(this.localStream||(this.localStream=new MediaStream),o&&(this.localStream.removeTrack(o),o.stop()),this.localStream.addTrack(n),s==="video"){this.screenStream?.getVideoTracks().some(c=>c.readyState==="live")||await this.replaceVideoTrack(n);return}const r=this.connection?.getSenders().find(c=>c.track?.kind==="audio");r&&await r.replaceTrack(n)}async setQuality(e){this.quality=e;for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e!=="audio";await this.applyVideoQuality()}async setPreferredVideoCodec(e){}async setVideoTrack(e){if(e.kind!=="video")throw new Error("A video track is required");this.localStream||(this.localStream=new MediaStream);const t=this.localStream.getVideoTracks()[0];t?.id!==e.id&&(t&&this.localStream.removeTrack(t),this.localStream.addTrack(e)),await this.replaceVideoTrack(e),this.options.onEvent("localstream",{stream:this.localStream,track:e})}getParticipants(){return[...this.remotes.values()].map(e=>e.identity)}getLocalStream(){return this.localStream}getRemoteStreams(){return[...this.remotes.values()].map(e=>({participant:e.identity,stream:e.stream}))}getPrimaryPeerConnection(){return this.connection}sendChat(e,t){this.send({type:"rtc.chat",message:e,participantIds:t??[]})}sendControl(e,t={}){this.send({type:"rtc.control",action:e,detail:t})}async openSocket(e){const{credential:t}=this.options,i=z(t.signalingUrl,globalThis.location?.href),n=new Promise((c,l)=>{this.resolveJoin=c,this.rejectJoin=l}),s=new WebSocket(i,["rtc-client",t.signalingToken]),o=++this.socketGeneration;this.socket=s;const r=setTimeout(()=>this.rejectJoin?.(new Error("RTC signaling timed out")),15e3);s.addEventListener("open",()=>{this.heartbeatDeadline=Date.now()+45e3,this.startHeartbeat(s),this.send({type:"rtc.room.join"})}),s.addEventListener("message",c=>{const l=String(c.data);this.signalQueue=this.signalQueue.then(async()=>{this.socket!==s||this.socketGeneration!==o||await this.handleSignal(l)}).catch(d=>{const u=d instanceof Error?d.message:"RTC signaling processing failed";this.options.onEvent("signalingerror",{message:u}),this.joined||this.rejectJoin?.(new Error(u))})}),s.addEventListener("error",()=>this.rejectJoin?.(new Error("RTC signaling connection failed"))),s.addEventListener("close",c=>{if(this.socket!==s)return;this.socket=null,this.stopHeartbeat(),!this.joined&&!e&&this.rejectJoin?.(new Error(c.reason||"RTC signaling connection closed"));const l=!this.stopped&&(this.joined||e);this.joined=!1,l?(this.options.onEvent("reconnecting",{reason:c.reason,code:c.code,attempt:this.reconnectAttempt+1}),this.scheduleReconnect()):this.stopped||this.options.onEvent("disconnected",{reason:c.reason,code:c.code})});try{await n,this.joined=!0,this.options.onEvent("connected",{participantId:t.participantId,resumed:e})}finally{clearTimeout(r),this.resolveJoin=null,this.rejectJoin=null}}async handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i!=="ready"){if(i==="pong"){this.heartbeatDeadline=Date.now()+45e3;return}if(i==="rtc.room.peers"){this.reconnectAttempt=0,this.resolveJoin?.();return}if(i==="rtc.room.join"&&t.sender){this.options.onEvent("participantjoined",t.sender),this.sessionId&&this.announceTracks();return}if((i==="rtc.peer.left"||i==="rtc.leave")&&t.sender){this.removeRemote(t.sender.participantId);return}if(i==="rtc.control"){if(String(t.action??"")==="sfu.tracks"&&t.sender){await this.handleTrackAnnouncement(t.sender,t.detail);return}this.options.onEvent("control",t);return}i==="rtc.chat"&&this.options.onEvent("chatupdate",t)}}async publish(){const e=[...this.localStream?.getAudioTracks()??[],...this.localStream?.getVideoTracks()??[]],t=new RTCPeerConnection({iceServers:this.options.credential.iceServers??[],iceTransportPolicy:this.options.iceTransportPolicy??"all"});this.connection=t,t.addEventListener("track",r=>this.handleRemoteTrack(r)),t.addEventListener("connectionstatechange",()=>{this.options.onEvent("peerconnectionstate",{participant:this.selfIdentity(),state:t.connectionState})});const i=await this.sfuFetch("POST","/v1/rtc/sfu/sessions");if(this.sessionId=i.sessionId,e.length===0)return;const n=e.map(r=>t.addTransceiver(r,{direction:"sendonly",streams:this.localStream?[this.localStream]:[],...r.kind==="video"&&this.simulcastEnabled()?{sendEncodings:ae.map(c=>({...c}))}:{}})),s=await t.createOffer();await t.setLocalDescription(s);const o=await this.sfuFetch("POST",`/v1/rtc/sfu/sessions/${encodeURIComponent(i.sessionId)}/tracks`,{sessionDescription:t.localDescription,tracks:n.map(r=>({location:"local",mid:r.mid,trackName:this.trackName(r.sender.track)}))});o.sessionDescription&&await t.setRemoteDescription(o.sessionDescription),this.videoSender=t.getSenders().find(r=>r.track?.kind==="video")??null,await this.applyVideoQuality()}trackName(e){return`${this.options.credential.participantId}/${e.kind}`}announceTracks(){if(!this.sessionId)return;const e=[...(this.localStream?.getAudioTracks()??[]).map(t=>({trackName:this.trackName(t),kind:t.kind,simulcast:!1})),...(this.localStream?.getVideoTracks()??[]).map(t=>({trackName:this.trackName(t),kind:t.kind,simulcast:this.simulcastEnabled()}))];e.length!==0&&this.sendControl("sfu.tracks",{sessionId:this.sessionId,tracks:e})}async handleTrackAnnouncement(e,t){const i=String(t?.sessionId??""),n=(t?.tracks??[]).map(r=>({trackName:String(r.trackName??""),simulcast:r.simulcast===!0})).filter(r=>!!r.trackName),s=n.map(r=>r.trackName);if(!i||s.length===0||e.participantId===this.options.credential.participantId)return;if(!this.connection||!this.sessionId){this.pendingAnnouncements.set(e.participantId,{sender:e,detail:t??{}});return}const o=this.remotes.get(e.participantId)??{identity:e,stream:new MediaStream,sessionId:i,trackNames:[],simulcastNames:new Set,mids:new Map};o.sessionId=i;for(const r of n)r.simulcast&&o.simulcastNames.add(r.trackName);this.remotes.set(e.participantId,o),this.negotiationQueue=this.negotiationQueue.then(async()=>{const r=s.filter(c=>!o.trackNames.includes(c));r.length!==0&&await this.subscribe(o,r)}).catch(r=>{this.options.onEvent("signalingerror",{message:r instanceof Error?r.message:"SFU subscribe failed"})}),await this.negotiationQueue}async subscribe(e,t){const i=this.connection;if(!i||!this.sessionId||t.length===0)return;const n=await this.sfuFetch("POST",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks`,{tracks:t.map(s=>({location:"remote",sessionId:e.sessionId,trackName:s,...e.simulcastNames.has(s)?{simulcast:{preferredRid:"a",priorityOrdering:"asciibetical",ridNotAvailable:"asciibetical"}}:{}}))});for(const s of n.tracks??[])s.mid&&(e.mids.set(s.mid,String(s.trackName??"")),this.midToParticipant.set(s.mid,e.identity.participantId));if(e.trackNames=[...new Set([...e.trackNames,...t])],n.requiresImmediateRenegotiation&&n.sessionDescription){await i.setRemoteDescription(n.sessionDescription);const s=await i.createAnswer();await i.setLocalDescription(s),await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/renegotiate`,{sessionDescription:i.localDescription})}}handleRemoteTrack(e){const t=e.transceiver?.mid??null,i=t?this.midToParticipant.get(t):void 0,n=i?this.remotes.get(i):void 0;n&&(n.stream.getTracks().some(s=>s.id===e.track.id)||n.stream.addTrack(e.track),this.options.onEvent("remotestream",{participant:n.identity,stream:n.stream,track:e.track}))}removeRemote(e){const t=this.remotes.get(e);if(t){for(const i of t.mids.keys())this.midToParticipant.delete(i);this.remotes.delete(e),this.options.onEvent("participantleft",t.identity)}}async setRemoteQuality(e,t){const i=this.remotes.get(e);if(!i||!this.sessionId)return;const n=[...i.mids.entries()].find(([,s])=>i.simulcastNames.has(s))?.[0];n&&await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks/update`,{tracks:[{mid:n,simulcast:{preferredRid:se[t],priorityOrdering:"asciibetical",ridNotAvailable:"asciibetical"}}]})}async replaceVideoTrack(e){this.videoSender&&(await this.videoSender.replaceTrack(e),await this.applyVideoQuality())}simulcastEnabled(){return this.options.simulcast!==!1}async applyVideoQuality(){const e=this.videoSender;if(!e)return;const t=this.screenStream?.getVideoTracks()[0]?.id,i=!!(t&&e.track?.id===t),n=e.getParameters(),s=n.encodings?.length?n.encodings:[{}];if(s.length>1){const o=this.quality==="audio"?null:se[this.quality];n.encodings=s.map(r=>{if(i)return{...r,active:r.rid==="a",...r.rid==="a"?{maxBitrate:W.maxBitrate,maxFramerate:W.maxFramerate}:{}};const c=ae.find(d=>d.rid===r.rid),l=o!=null&&String(r.rid??"")>=o;return{...r,...c??{},active:l}})}else{const o=this.quality==="audio"?V.audio:V[this.quality],r=i?W:o;n.encodings=s.map(c=>({...c,...r}))}n.degradationPreference=i?"maintain-resolution":"maintain-framerate",await e.setParameters(n)}async closePublishedTracks(){const e=this.connection;if(!e||!this.sessionId)return;const t=e.getTransceivers().filter(i=>i.sender.track&&i.mid).map(i=>({mid:i.mid}));t.length!==0&&await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks/close`,{tracks:t,force:!0})}selfIdentity(){return{participantId:this.options.credential.participantId,externalUserId:this.options.credential.externalUserId??this.options.credential.participantId,displayName:this.options.credential.externalUserId??this.options.credential.participantId,platform:"web"}}async sfuFetch(e,t,i){const n=await fetch(`${this.apiBase}${t}`,{method:e,headers:{authorization:`Bearer ${this.options.credential.signalingToken}`,"content-type":"application/json"},body:i===void 0?void 0:JSON.stringify(i)});if(!n.ok)throw new Error(`SFU request failed with HTTP ${n.status}`);return n.json()}startHeartbeat(e){this.stopHeartbeat(),this.heartbeatTimer=setInterval(()=>{if(!(this.socket!==e||e.readyState!==WebSocket.OPEN)){if(Date.now()>this.heartbeatDeadline){e.close(4e3,"Signaling heartbeat timed out");return}e.send("ping")}},2e4)}stopHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null}startSpeakerSampling(){this.stopSpeakerSampling(),this.speakerTimer=setInterval(()=>{this.sampleActiveSpeaker()},1e3)}stopSpeakerSampling(){this.speakerTimer&&clearInterval(this.speakerTimer),this.speakerTimer=null}async sampleActiveSpeaker(){const e=this.connection;if(!e||e.connectionState!=="connected")return;let t;try{t=await e.getStats()}catch{return}const i=[];for(const s of ie(t.values())){const o=s.mid?this.midToParticipant.get(s.mid):void 0;o&&i.push({participantId:o,level:s.level})}const n=H(i,this.activeSpeaker);n!==this.activeSpeaker&&(this.activeSpeaker=n,this.options.onEvent("activespeaker",{participantId:n}))}clearTimers(){this.stopHeartbeat(),this.stopSpeakerSampling(),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=null}scheduleReconnect(){if(this.reconnectTimer||this.stopped)return;const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.openSocket(!0).then(()=>this.announceTracks()).catch(t=>{this.options.onEvent("signalingerror",{message:t instanceof Error?t.message:"RTC signaling connection failed"}),this.scheduleReconnect()})},e)}send(e){this.socket?.readyState===WebSocket.OPEN&&this.socket.send(JSON.stringify({clientMessageId:e.clientMessageId??Le(),...e}))}stopStream(e){for(const t of e?.getTracks()??[])t.stop()}}function Le(){return globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}function De(a,e={}){const t=e.document??globalThis.document,i=!!(t&&t.pictureInPictureEnabled);let n=null;function s(){return n||(n=t.createElement("video"),n.muted=!0,n.playsInline=!0,n.style.position="fixed",n.style.width="1px",n.style.height="1px",n.style.opacity="0",n.style.pointerEvents="none",n.addEventListener("leavepictureinpicture",()=>e.onLeave?.()),t.body.appendChild(n),n)}function o(r){const c=a.getRemoteStreams();return r?c.find(l=>l.participant.participantId===r)?.stream??null:c[0]?.stream??a.getLocalStream()}return{supported:i,async enter(r){if(!i)throw new Error("This browser does not support picture-in-picture");const c=o(r);if(!c)throw new Error("No video stream is available for picture-in-picture");const l=s();l.srcObject!==c&&(l.srcObject=c),await l.play().catch(()=>{}),await l.requestPictureInPicture()},async exit(){t.pictureInPictureElement&&typeof t.exitPictureInPicture=="function"&&await t.exitPictureInPicture()},dispose(){n?.remove(),n=null}}}function Oe(a,e){const t=e.document??globalThis.document,i=e.fetchImplementation??globalThis.fetch.bind(globalThis),n=_(e.credential.signalingUrl,globalThis.location?.href).origin,s=e.mimeType??"video/webm",o=e.height??720,r=Math.round(o*16/9);let c=null,l=null,d=null,u=null,k=0,f=[],v=!1;async function R(y,w,p,E="application/json"){const A=await i(`${n}${w}`,{method:y,headers:{authorization:`Bearer ${e.credential.signalingToken}`,"content-type":E},body:p===void 0?void 0:E==="application/json"?JSON.stringify(p):p});if(!A.ok)throw new Error(`Recording request failed with HTTP ${A.status}`);return A.json()}return{get recording(){return v},async start(){if(v)return;u=(await R("POST","/v1/rtc/recordings")).recordingId;const w=t.createElement("canvas");w.width=r,w.height=o;const p=w.getContext("2d"),E=new Map,A=(T,P)=>{let C=E.get(T);return C||(C=t.createElement("video"),C.muted=!0,C.playsInline=!0,C.srcObject=P,C.play().catch(()=>{}),E.set(T,C)),C};l=setInterval(()=>{const T=a.getRemoteStreams(),P=a.getLocalStream(),C=[...T.map(q=>({key:q.participant.participantId,stream:q.stream,label:q.participant.displayName})),...P?[{key:"local",stream:P,label:"me"}]:[]];p.fillStyle="#101418",p.fillRect(0,0,r,o);const N=Math.max(1,Math.ceil(Math.sqrt(C.length))),U=Math.max(1,Math.ceil(C.length/N)),j=r/N,g=o/U;C.forEach((q,Te)=>{const be=A(q.key,q.stream),ee=Te%N*j,te=Math.floor(Te/N)*g;be.videoWidth>0&&p.drawImage(be,ee+2,te+2,j-4,g-4),p.fillStyle="rgba(0,0,0,.6)",p.fillRect(ee+8,te+g-30,Math.min(j-16,12+q.label.length*9),22),p.fillStyle="#fff",p.font="14px system-ui",p.fillText(q.label,ee+14,te+g-14)})},66);const D=w.captureStream(15);d=new AudioContext;const I=d.createMediaStreamDestination(),L=new Set,S=()=>{const T=[...a.getRemoteStreams().map(P=>P.stream),...a.getLocalStream()?[a.getLocalStream()]:[]];for(const P of T)for(const C of P.getAudioTracks())L.has(C.id)||(L.add(C.id),d.createMediaStreamSource(new MediaStream([C])).connect(I))};S();const M=setInterval(S,1e3),m=()=>clearInterval(M);for(const T of I.stream.getAudioTracks())D.addTrack(T);c=(e.mediaRecorderFactory??((T,P)=>new MediaRecorder(T,P)))(D,{mimeType:s}),f=[],c.addEventListener("dataavailable",T=>{T.data&&T.data.size>0&&f.push(T.data)}),c.addEventListener("stop",m),c.start(1e3),k=Date.now(),v=!0},async stop(){if(!v||!c||!u)throw new Error("The recorder is not running");v=!1;const y=c,w=new Promise(I=>y.addEventListener("stop",()=>I(),{once:!0}));y.stop(),await w,l&&clearInterval(l),l=null,await d?.close().catch(()=>{}),d=null;const p=new Blob(f,{type:s});f=[];const E=Math.max(1,Math.round((Date.now()-k)/1e3)),A=await R("PUT",`/v1/rtc/recordings/${encodeURIComponent(u)}/media`,p,s);await R("POST",`/v1/rtc/recordings/${encodeURIComponent(u)}/complete`,{durationSeconds:E});const D={recordingId:u,sizeBytes:A.sizeBytes,durationSeconds:E};return u=null,c=null,D},dispose(){l&&clearInterval(l),l=null;try{c?.stop()}catch{}c=null,d?.close().catch(()=>{}),d=null,v=!1}}}const re=["none","blur","aurora","cybergrid","bokeh","sunset","image"],oe=["none","snow","confetti","vignette"],$={aurora(a,e,t,i){const n=a.createLinearGradient(0,0,0,t);n.addColorStop(0,"#050b1e"),n.addColorStop(1,"#0b2540"),a.fillStyle=n,a.fillRect(0,0,e,t);for(let s=0;s<3;s+=1){a.beginPath();for(let o=0;o<=e;o+=16){const r=t*.35+s*40+Math.sin(o/140+i/900+s)*60;o===0?a.moveTo(o,r):a.lineTo(o,r)}a.lineTo(e,0),a.lineTo(0,0),a.closePath(),a.fillStyle=`hsla(${140+s*40+Math.sin(i/1300)*20}, 80%, 55%, 0.14)`,a.fill()}},cybergrid(a,e,t,i){a.fillStyle="#0a0118",a.fillRect(0,0,e,t),a.strokeStyle="rgba(255,0,180,0.5)",a.lineWidth=1.5;const n=t*.45;for(let o=0;o<12;o+=1){const r=n+(o*34+i/18)%(t-n);a.beginPath(),a.moveTo(0,r),a.lineTo(e,r),a.stroke()}for(let o=-8;o<=8;o+=1)a.beginPath(),a.moveTo(e/2+o*24,n),a.lineTo(e/2+o*e*.12,t),a.stroke();const s=a.createLinearGradient(0,t*.1,0,n);s.addColorStop(0,"#ffd34d"),s.addColorStop(1,"#ff2d78"),a.fillStyle=s,a.beginPath(),a.arc(e/2,n,t*.22,Math.PI,0),a.fill()},bokeh(a,e,t,i){a.fillStyle="#14100c",a.fillRect(0,0,e,t);for(let n=0;n<26;n+=1){const s=(Math.sin(n*12.9898)*.5+.5)*e,o=(Math.sin(n*78.233)*.5+.5)*t+Math.sin(i/1500+n)*12,r=14+n%5*12;a.fillStyle=`hsla(${35+n%4*12}, 90%, 60%, ${.1+n%3*.05})`,a.beginPath(),a.arc(s,o,r,0,Math.PI*2),a.fill()}},sunset(a,e,t,i){const n=a.createLinearGradient(0,0,0,t);n.addColorStop(0,"#2b1055"),n.addColorStop(.55,"#d1548a"),n.addColorStop(1,"#ffb36b"),a.fillStyle=n,a.fillRect(0,0,e,t),a.fillStyle="#ffdf8e",a.beginPath(),a.arc(e*.5,t*.62+Math.sin(i/4e3)*4,t*.13,0,Math.PI*2),a.fill(),a.fillStyle="rgba(20,8,40,0.85)",a.fillRect(0,t*.78,e,t*.22)}};function qe(a,e={}){const t=e.document??globalThis.document;let i=e.width??1280,n=e.height??720;const s=!!(e.width||e.height),o=e.frameRate??24,r=a instanceof MediaStreamTrack?a:a.getVideoTracks()[0];if(!r)throw new Error("A video track is required");let c=e.background??"none",l=e.overlay??"none",d=e.backgroundImage??null,u=e.segmenter??null,k=null,f=!1;const v=t.createElement("video");v.muted=!0,v.playsInline=!0,v.srcObject=new MediaStream([r]),v.play().catch(()=>{});const R=t.createElement("canvas");R.width=i,R.height=n;const y=R.getContext("2d"),w=t.createElement("canvas");w.width=i,w.height=n;const p=w.getContext("2d"),E=()=>Array.from({length:80},(m,h)=>({x:(Math.sin(h*12.9898)*.5+.5)*i,y:(Math.sin(h*78.233)*.5+.5)*n,speed:.6+h%5*.5,drift:Math.sin(h)*.6,size:2+h%4,hue:h*47%360}));let A=E();const D=()=>{if(s)return;const m=v.videoWidth,h=v.videoHeight;if(!m||!h)return;const T=Math.min(1,1280/Math.max(m,h)),P=Math.round(m*T),C=Math.round(h*T);P===i&&C===n||(i=P,n=C,R.width=i,R.height=n,w.width=i,w.height=n,A=E())};v.addEventListener?.("loadedmetadata",D),v.addEventListener?.("resize",D);function I(){if(k){p.clearRect(0,0,i,n),p.drawImage(k,0,0,i,n),p.globalCompositeOperation="source-in",p.drawImage(v,0,0,i,n),p.globalCompositeOperation="source-over",y.drawImage(w,0,0);return}const m=i*.42,h=m*9/16,T=i-m-24,P=n-h-24;y.save(),y.shadowColor="rgba(0,0,0,.5)",y.shadowBlur=24,y.beginPath(),y.roundRect(T,P,m,h,14),y.clip(),y.drawImage(v,T,P,m,h),y.restore()}function L(m){if(c==="none"?y.drawImage(v,0,0,i,n):c==="blur"?(y.filter="blur(14px)",y.drawImage(v,-16,-16,i+32,n+32),y.filter="none",k&&I()):c==="image"&&d?(y.drawImage(d,0,0,i,n),I()):(($[c]??$.aurora)(y,i,n,m),I()),l==="snow"||l==="confetti")for(const h of A)h.y+=h.speed*(l==="confetti"?1.6:1),h.x+=h.drift,h.y>n&&(h.y=-6,h.x=Math.random()*i),y.fillStyle=l==="snow"?"rgba(255,255,255,.85)":`hsl(${h.hue}, 90%, 60%)`,l==="confetti"?y.fillRect(h.x,h.y,h.size+2,h.size):(y.beginPath(),y.arc(h.x,h.y,h.size/2,0,Math.PI*2),y.fill());else if(l==="vignette"){const h=y.createRadialGradient(i/2,n/2,n*.4,i/2,n/2,n*.85);h.addColorStop(0,"rgba(0,0,0,0)"),h.addColorStop(1,"rgba(0,0,0,0.55)"),y.fillStyle=h,y.fillRect(0,0,i,n)}}const S=setInterval(()=>{L(Date.now()),u&&!f&&c!=="none"&&(f=!0,u.segment(v).then(m=>{k=m}).catch(()=>{k=null}).finally(()=>{f=!1}))},Math.round(1e3/o)),M=R.captureStream(o);return{track:M.getVideoTracks()[0],stream:M,setBackground(m,h){c=re.includes(m)?m:"none",h!==void 0&&(d=h),c==="none"&&(k=null)},setOverlay(m){l=oe.includes(m)?m:"none"},setSegmenter(m){u=m,m||(k=null)},dispose(){clearInterval(S),u?.close?.();for(const m of M.getTracks())m.stop();v.srcObject=null}}}function J(){return{blinkLeft:0,blinkRight:0,jawOpen:0,smile:0,browUp:0,pupilX:0,pupilY:0,headYaw:0,headPitch:0,headRoll:0,tracked:!1}}function O(a,e){const t=a.find(i=>i.categoryName===e);return t?Math.max(0,Math.min(1,t.score)):0}function ce(a){if(!a||a.length<16)return{yaw:0,pitch:0,roll:0};a[0],a[4];const e=a[8],t=a[1],i=a[5],n=a[9],s=a[10],o=Math.atan2(e,s),r=Math.asin(Math.max(-1,Math.min(1,-n))),c=Math.atan2(t,i);return{yaw:o,pitch:r,roll:c}}function le(a,e,t=1){if(!a||a.length===0)return J();const i=ce(e),n=(O(a,"eyeLookOutRight")+O(a,"eyeLookInLeft"))/2,s=(O(a,"eyeLookOutLeft")+O(a,"eyeLookInRight"))/2,o=(O(a,"eyeLookUpLeft")+O(a,"eyeLookUpRight"))/2,r=(O(a,"eyeLookDownLeft")+O(a,"eyeLookDownRight"))/2,c=(O(a,"mouthSmileLeft")+O(a,"mouthSmileRight"))/2,l=(O(a,"mouthFrownLeft")+O(a,"mouthFrownRight"))/2,d=O(a,"eyeBlinkLeft"),u=O(a,"eyeBlinkRight"),k=Math.min(1,Math.abs(i.yaw)/.35)*Math.max(0,Math.min(1,t)),f=(d+u)/2;return{blinkLeft:d+(f-d)*k,blinkRight:u+(f-u)*k,jawOpen:O(a,"jawOpen"),smile:c-l,browUp:(O(a,"browInnerUp")+O(a,"browOuterUpLeft")+O(a,"browOuterUpRight"))/3,pupilX:n-s,pupilY:r-o,headYaw:i.yaw,headPitch:i.pitch,headRoll:i.roll,tracked:!0}}function G(a,e,t=.4){const i=(n,s)=>n+(s-n)*t;return{blinkLeft:i(a.blinkLeft,e.blinkLeft),blinkRight:i(a.blinkRight,e.blinkRight),jawOpen:i(a.jawOpen,e.jawOpen),smile:i(a.smile,e.smile),browUp:i(a.browUp,e.browUp),pupilX:i(a.pupilX,e.pupilX),pupilY:i(a.pupilY,e.pupilY),headYaw:i(a.headYaw,e.headYaw),headPitch:i(a.headPitch,e.headPitch),headRoll:i(a.headRoll,e.headRoll),tracked:e.tracked}}function de(a,e,t,i,n){n||(a.fillStyle="#101822",a.fillRect(0,0,t,i));const s=n?n.x+e.headYaw*n.radius*.1:t/2+e.headYaw*t*.18,o=n?n.y+e.headPitch*n.radius*.1:i/2+e.headPitch*i*.18,r=n?n.radius:Math.min(t,i)*.28;a.save(),a.translate(s,o),a.rotate(e.headRoll*.6);const c=Math.max(-1.35,Math.min(1.35,e.headYaw)),l=Math.max(-.9,Math.min(.9,e.headPitch)),d=Math.sin(c),u=L=>Math.sin(L+c)*r,k=L=>Math.cos(L+c),f=Math.sin(l)*r*.22;a.fillStyle="#3a2a1c",a.beginPath(),a.ellipse(-d*r*.35,-r*.25,r*.98,r*.95,0,0,Math.PI*2),a.fill(),a.fillStyle=e.tracked?"#f2c9a0":"#5a6470",a.beginPath(),a.ellipse(0,0,r*(1-Math.abs(d)*.12),r*1.12,0,0,Math.PI*2),a.fill(),a.fillStyle="#3a2a1c",a.beginPath(),a.ellipse(-d*r*.28,-r*.72+f*.4,r*.92,r*.5,0,Math.PI,Math.PI*2),a.fill();const v=0;a.fillStyle="#e5b58a",a.beginPath();const R=u(v)*.9,y=r*.12+f;a.ellipse(R+d*r*.14,y,r*.11,r*.14,0,0,Math.PI*2),a.fill();for(const L of[-1,1]){const S=L*.42,M=k(S);if(M<=.12)continue;const m=u(S)*.82,h=Math.max(.2,M),T=L<0?e.blinkLeft:e.blinkRight,P=Math.max(.06,1-T),C=e.browUp*r*.14;a.strokeStyle="#4a2e18",a.lineWidth=r*.07,a.lineCap="round",a.beginPath(),a.moveTo(m-r*.2*h,-r*.42-C+f),a.quadraticCurveTo(m,-r*.52-C+f,m+r*.2*h,-r*.44-C+f),a.stroke();const N=-r*.18+f;a.fillStyle="#ffffff",a.beginPath(),a.ellipse(m,N,r*.16*h,r*.12*P,0,0,Math.PI*2),a.fill(),a.fillStyle="#22303c",a.beginPath(),a.ellipse(m+e.pupilX*r*.07*h,N+e.pupilY*r*.05,r*.06*h,r*.06*P,0,0,Math.PI*2),a.fill()}const w=Math.max(.25,k(0)),p=u(0)*.7,E=r*.55*w,A=r*(.04+e.jawOpen*.34),D=e.smile*r*.12,I=r*.42+f;a.fillStyle="#8c2f2f",a.beginPath(),a.moveTo(p-E/2,I-D),a.quadraticCurveTo(p,I+A,p+E/2,I-D),a.quadraticCurveTo(p,I-A*.25,p-E/2,I-D),a.closePath(),a.fill(),a.restore(),!e.tracked&&!n&&(a.fillStyle="#9fb0bd",a.font=`${Math.round(i*.045)}px system-ui`,a.textAlign="center",a.fillText("No face detected",t/2,i*.9),a.textAlign="left")}const Y={mouthGain:1.7,blinkGain:1,eyeSync:1,smoothing:.4,avatarScale:1,avatarLift:0,lostHoldMs:400};function K(){return{render(a,e,t,i,n){de(a,e,t,i,n??void 0)}}}const X=a=>Math.max(0,Math.min(1,a));function Ne(a,e={}){const t=e.document??globalThis.document;let i=e.width??1280,n=e.height??720;const s=!!(e.width||e.height),o=e.frameRate??24,r=a instanceof MediaStreamTrack?a:a.getVideoTracks()[0];if(!r)throw new Error("A video track is required");let c=e.tracker??null,l=e.renderer??K(),d=e.mode??"replace";const u={...Y,...e.tuning},k=e.backdrop??"#101822",f=t.createElement("video");f.muted=!0,f.playsInline=!0,f.srcObject=new MediaStream([r]),f.play().catch(()=>{});const v=t.createElement("canvas");v.width=i,v.height=n;const R=v.getContext("2d"),y=()=>{if(s)return;const S=f.videoWidth,M=f.videoHeight;if(!S||!M)return;const m=Math.min(1,1280/Math.max(S,M)),h=Math.round(S*m),T=Math.round(M*m);(!(h===i||T===n)||h!==i||T!==n)&&(i=h,n=T,v.width=i,v.height=n)};f.addEventListener?.("loadedmetadata",y),f.addEventListener?.("resize",y);let w=J(),p=null,E=0;function A(S){if(S.length<=454)return;const M=S[10],m=S[152],h=S[234],T=S[454],P=(h.x+T.x)/2*i,C=(h.y+T.y)/2*n,N=Math.hypot((m.x-M.x)*i,(m.y-M.y)*n),U=Math.max(.6,Math.cos(w.headPitch)),j={x:P,y:C,radius:Math.max(24,N/U*.8)};p=p?{x:p.x+(j.x-p.x)*.4,y:p.y+(j.y-p.y)*.4,radius:p.radius+(j.radius-p.radius)*.15}:j}function D(S){if(c&&f.readyState>=2){let m=null;try{m=c.detect(f,S)}catch{}const h=le(m?.categories??null,m?.matrix??null,u.eyeSync);h.jawOpen=X(h.jawOpen*u.mouthGain),h.blinkLeft=X(h.blinkLeft*u.blinkGain),h.blinkRight=X(h.blinkRight*u.blinkGain),h.tracked?(E=S,w=G(w,h,u.smoothing),m?.landmarks&&A(m.landmarks)):w.tracked&&S-E>u.lostHoldMs&&(w=G(w,{...w,jawOpen:0,browUp:0,smile:w.smile*.5,blinkLeft:.1,blinkRight:.1},.05))}d==="overlay"&&f.readyState>=2?R.drawImage(f,0,0,i,n):(R.fillStyle=k,R.fillRect(0,0,i,n));const M=p?{x:p.x,y:p.y+u.avatarLift*p.radius,radius:p.radius*u.avatarScale}:null;l?.render(R,w,i,n,M)}const I=setInterval(()=>D(Date.now()),Math.round(1e3/o)),L=v.captureStream(o);return{track:L.getVideoTracks()[0],stream:L,getRig:()=>({...w}),setMode(S){d=S==="overlay"?"overlay":"replace"},setTracker(S){c?.close?.(),c=S},setRenderer(S){l?.dispose?.(),l=S??K()},setTuning(S){for(const M of Object.keys(Y)){const m=S[M];typeof m=="number"&&Number.isFinite(m)&&(u[M]=m)}},getTuning:()=>({...u}),dispose(){clearInterval(I),c?.close?.(),l?.dispose?.();for(const S of L.getTracks())S.stop();f.srcObject=null}}}class he extends EventTarget{mode;options;client=null;peerConnection;state="initialized";latestMediaScore=null;latestQuality=null;statsTimer=null;statsRunning=!1;previousCounters=null;lastTelemetryAt=0;joinPromise=null;requestedQuality;effectiveQuality;poorQualitySamples=0;goodQualitySamples=0;constructor(e){if(super(),!e.credential?.signalingToken||!e.credential.signalingUrl||!e.credential.roomId||!e.credential.participantId)throw new Error("A valid room signaling credential is required");this.mode=e.mode,this.requestedQuality=e.mode==="voice"?"audio":e.quality??"auto",this.effectiveQuality=this.requestedQuality==="auto"?"medium":this.requestedQuality,this.peerConnection=e.peerConnection??null,this.options={...e,audio:e.audio??!0,video:e.mode==="voice"?!1:e.video??!0,receiveAudio:e.receiveAudio??!0,receiveVideo:e.mode==="voice"?!1:e.receiveVideo??!0,statsInterval:Math.max(1e3,e.statsInterval??5e3),telemetryInterval:Math.max(1e4,e.telemetryInterval??3e4),autoReportStats:e.autoReportStats??!0}}async initialize(){if(this.client)return this;try{const t=(this.options.transport==="auto"?this.mode==="meeting"?"sfu":"mesh":this.options.transport??"mesh")==="sfu"?Ae:B;return this.client=new t({credential:{signalingToken:this.options.credential.signalingToken,signalingUrl:this.options.credential.signalingUrl,roomId:this.options.credential.roomId,participantId:this.options.credential.participantId,externalUserId:this.options.credential.externalUserId,iceServers:this.options.credential.iceServers},audio:this.options.audio,video:this.options.video,receiveAudio:this.options.receiveAudio,receiveVideo:this.options.receiveVideo,quality:this.effectiveQuality,preferredVideoCodec:this.options.preferredVideoCodec??"auto",iceTransportPolicy:this.options.iceTransportPolicy??"all",localStream:this.options.localStream,simulcast:this.options.simulcast??!0,onEvent:(i,n)=>this.handleNativeEvent(i,n)}),await this.client.initialize(),this.startStats(),this.emit("initialized",{mode:this.mode,sdkVersion:we}),this.options.autoJoin&&await this.join(),this}catch(e){this.state="failed";const t=ke(e,"RTC initialization failed");throw this.emit("error",x(t)),t}}async join(){const e=this.requireClient();return this.joinPromise?this.joinPromise:(this.state="joining",this.emit("joining",{}),this.joinPromise=e.join().catch(t=>{this.state="failed";const i=ke(t,"RTC connection failed");throw this.emit("error",x(i)),i}).finally(()=>{this.joinPromise=null}),this.joinPromise)}async leave(){this.client&&(await this.joinPromise?.catch(()=>{}),await this.sampleAndReport("final").catch(()=>{}),await this.client.leave(),this.state="disconnected",this.stopStats())}async setMuted(e){this.requireClient().setMuted(e),this.emit("mediachange",{kind:"audio",enabled:!e})}async setCameraEnabled(e){if(this.mode==="voice"&&e)throw new Error("Camera is unavailable in voice mode");await this.requireClient().setCameraEnabled(e),this.emit("mediachange",{kind:"video",enabled:e})}async setVideoTrack(e){if(this.mode==="voice")throw new Error("Camera is unavailable in voice mode");await this.requireClient().setVideoTrack(e),this.emit("mediachange",{kind:"video",enabled:e.enabled,trackId:e.id})}async setScreenShareEnabled(e){await this.requireClient().setScreenShareEnabled(e),this.emit("mediachange",{kind:"screenshare",enabled:e})}async setDevice(e){await this.requireClient().setDevice(e),this.emit("devicechange",{deviceId:e.deviceId,kind:e.kind})}async setQuality(e){if(this.mode==="voice"&&e!=="audio")throw new Error("Voice calls only support audio quality");this.requestedQuality=e,this.poorQualitySamples=0,this.goodQualitySamples=0,await this.applyEffectiveQuality(e==="auto"?"medium":e,"manual")}async setPreferredVideoCodec(e){await this.requireClient().setPreferredVideoCodec(e),this.emit("codecchange",{preferredVideoCodec:e})}async getDevices(){return navigator.mediaDevices?.enumerateDevices?navigator.mediaDevices.enumerateDevices():[]}getParticipants(){return this.requireClient().getParticipants().map(ye)}getLocalStream(){return this.client?.getLocalStream()??null}getRemoteStreams(){return(this.client?.getRemoteStreams()??[]).map(e=>({participant:ye(e.participant),stream:e.stream}))}setPeerConnection(e){this.peerConnection=e,this.previousCounters=null}async sendChatMessage(e,t){const i=e.trim();if(!i)throw new Error("message is required");this.requireClient().sendChat(i,t)}async sendControlMessage(e,t={}){const i=e.trim();if(!i)throw new Error("action is required");this.requireClient().sendControl(i,t)}async createPoll(e,t,i={}){const n=t.map(s=>s.trim()).filter(Boolean);if(!e.trim()||n.length<2)throw new Error("A poll requires a question and at least two options");this.requireClient().sendControl("poll.create",{poll:{id:`poll-${crypto.randomUUID()}`,question:e.trim(),options:n,votes:{},...i}})}async votePoll(e,t){this.requireClient().sendControl("poll.vote",{pollId:e,optionIndex:t})}async requestStageAccess(){this.requireClient().sendControl("stage.request")}async cancelStageAccess(){this.requireClient().sendControl("stage.cancel")}async joinStage(){this.requireClient().sendControl("stage.join")}async leaveStage(){this.requireClient().sendControl("stage.leave")}async grantStageAccess(e){this.requireClient().sendControl("stage.grant",{participantIds:e})}async denyStageAccess(e){this.requireClient().sendControl("stage.deny",{participantIds:e})}async startRecording(e=!1){this.requireClient().sendControl("recording.start",{allowMultiple:e})}async stopRecording(e){this.requireClient().sendControl("recording.stop",{recordingId:e})}async pauseRecording(e){this.requireClient().sendControl("recording.pause",{recordingId:e})}async resumeRecording(e){this.requireClient().sendControl("recording.resume",{recordingId:e})}async runPreflight(e={}){const t=[],i=globalThis.isSecureContext===!0;i||t.push("secure_context_required");let n="unavailable";if(e.requestMedia!==!1&&navigator.mediaDevices?.getUserMedia)try{(await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})).getTracks().forEach(k=>k.stop()),n="granted"}catch{n="denied",t.push("media_permission_denied")}const s=navigator.mediaDevices?.enumerateDevices?await navigator.mediaDevices.enumerateDevices().catch(()=>[]):[],o=s.filter(u=>u.kind==="audioinput").length,r=s.filter(u=>u.kind==="videoinput").length;this.options.audio&&o===0&&t.push("microphone_unavailable"),this.options.video&&r===0&&t.push("camera_unavailable");const c=e.probeUrl??this.options.preflightProbeUrl;let l=null;if(c){const u=performance.now();try{const k=await fetch(c,{method:"HEAD",cache:"no-store",credentials:"omit"});l=Math.round(performance.now()-u),k.ok||t.push("network_probe_failed")}catch{t.push("network_probe_failed")}}const d={passed:t.length===0,sampledAt:new Date().toISOString(),secureContext:i,mediaPermission:n,microphoneCount:o,cameraCount:r,probeLatencyMs:l,effectiveConnectionType:ve()?.effectiveType??null,issues:t};return this.emit("preflight",d),this.options.autoReportStats&&await this.reportQuality({sampleType:"preflight",sampledAt:d.sampledAt,connectionState:d.passed?"ready":"failed",rttMs:d.probeLatencyMs,effectiveConnectionType:d.effectiveConnectionType}).catch(()=>{}),d}async collectQualitySample(e="runtime"){const t=this.options.statsProvider?await this.options.statsProvider():{},i=this.peerConnection?await Qe(this.peerConnection,this.previousCounters):null;i?.counters&&(this.previousCounters=i.counters);const s={...{sampleType:e,sampledAt:new Date().toISOString(),connectionState:this.peerConnection?.connectionState??this.state,mediaScore:this.latestMediaScore,networkScore:this.latestMediaScore==null?5:Math.max(0,Math.min(5,Math.round(this.latestMediaScore))),estimatedMos:4.5,rttMs:null,jitterMs:null,packetLossPct:null,packetLossCumulativePct:null,packetsLost:null,packetsReceived:null,inboundBitrateBps:null,outboundBitrateBps:null,availableOutgoingBitrateBps:null,framesPerSecond:null,frameWidth:null,frameHeight:null,inboundFrameWidth:null,inboundFrameHeight:null,outboundFrameWidth:null,outboundFrameHeight:null,framesDropped:null,freezeCount:null,freezeDurationMs:null,concealedSamples:null,codec:null,transportProtocol:null,candidateType:null,qualityLimitationReason:null,deviceMemoryGb:Fe(),effectiveConnectionType:ve()?.effectiveType??null},...i?.sample??{},...t,sampleType:e,sampledAt:new Date().toISOString()},o=Be(s);return s.networkScore=o.networkScore,s.estimatedMos=o.estimatedMos,this.latestQuality=s,s}async reportQuality(e){const t=this.options.credential.telemetryToken,i=this.options.telemetryEndpoint??this.options.credential.telemetryEndpoint;if(!t||!i)return;const n=await fetch(i,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify(e),keepalive:e.sampleType==="final"});if(!n.ok)throw new Error(`RTC telemetry failed with HTTP ${n.status}`)}getStats(){const e=this.client,t=e?.getLocalStream();return{state:this.state,participantCount:e?e.getParticipants().length+1:0,audioEnabled:!!t?.getAudioTracks().some(i=>i.enabled),videoEnabled:!!t?.getVideoTracks().some(i=>i.enabled),screenShareEnabled:!1,latestMediaScore:this.latestMediaScore,latestQuality:this.latestQuality,sampledAt:new Date().toISOString()}}getNativeClient(){return this.client}destroy(){this.stopStats(),this.client&&this.client.leave(),this.client=null,this.joinPromise=null,this.peerConnection=null,this.state="disconnected"}handleNativeEvent(e,t){e==="connected"?(this.state="connected",this.peerConnection=this.client?.getPrimaryPeerConnection()??null):e==="disconnected"?this.state="disconnected":e==="peerconnectionstate"&&(this.peerConnection=this.client?.getPrimaryPeerConnection()??null),this.emit(e,t)}startStats(){this.stopStats(),this.statsTimer=setInterval(()=>{this.sampleAndReport("runtime")},this.options.statsInterval)}async sampleAndReport(e){if(!this.statsRunning){this.statsRunning=!0;try{const t=await this.collectQualitySample(e);await this.adaptQuality(t),this.emit("stats",{...this.getStats(),latestQuality:t});const i=Date.now(),n=e!=="runtime"||i-this.lastTelemetryAt>=this.options.telemetryInterval;this.options.autoReportStats&&n&&(await this.reportQuality(t),this.lastTelemetryAt=i)}catch(t){this.emit("telemetryerror",x(t))}finally{this.statsRunning=!1}}}stopStats(){this.statsTimer!==null&&clearInterval(this.statsTimer),this.statsTimer=null}async adaptQuality(e){if(this.requestedQuality!=="auto"||this.options.adaptiveQuality===!1||this.mode==="voice")return;if((this.client?.getParticipants().length??0)>1){this.poorQualitySamples=0,this.goodQualitySamples=0,this.effectiveQuality!=="high"&&await this.applyEffectiveQuality("high","network");return}const t=String(e.qualityLimitationReason??"").toLowerCase(),i=(e.packetLossPct??0)>=8||(e.rttMs??0)>=800||t==="cpu"||t==="bandwidth",n=i||(e.packetLossPct??0)>=3||(e.rttMs??0)>=350||e.availableOutgoingBitrateBps!=null&&e.availableOutgoingBitrateBps<35e4,s=(e.packetLossPct??100)<1&&(e.rttMs??1e3)<180&&!t&&(e.availableOutgoingBitrateBps==null||e.availableOutgoingBitrateBps>1e6);this.poorQualitySamples=n?this.poorQualitySamples+1:0,this.goodQualitySamples=s?this.goodQualitySamples+1:0;const o=["low","medium","high"],r=o.indexOf(this.effectiveQuality);i&&r>0?(this.poorQualitySamples=0,await this.applyEffectiveQuality("low","network")):this.poorQualitySamples>=2&&r>0?(this.poorQualitySamples=0,await this.applyEffectiveQuality(o[r-1],"network")):this.goodQualitySamples>=6&&r>=0&&r<o.length-1&&(this.goodQualitySamples=0,await this.applyEffectiveQuality(o[r+1],"network"))}async applyEffectiveQuality(e,t){if(this.effectiveQuality===e&&this.client)return;const i=this.effectiveQuality;this.effectiveQuality=e,this.client&&await this.client.setQuality(e),this.emit("qualitychange",{requestedQuality:this.requestedQuality,quality:e,previous:i,reason:t})}requireClient(){if(!this.client)throw new Error("Call initialize() before using the RTC client");return this.client}emit(e,t){const i={type:e,detail:t,timestamp:new Date().toISOString()};this.dispatchEvent(new CustomEvent(e,{detail:i})),this.options.onEvent?.(i)}}async function je(a){return new he(a).initialize()}let F=null;function ue(){if(F)return F;const a=globalThis,e=a.AudioContext??a.webkitAudioContext;return e?(F=new e,F):null}async function me(){const a=ue();return a?(a.state==="suspended"&&await a.resume().catch(()=>{}),a.state==="running"):!1}class Ve{options;media=null;cadenceTimer=null;oscillators=new Set;constructor(e){this.options=e}unlock(){return me()}startIncoming(){this.start("incoming",this.options.ringtoneUrl)}startRingback(){this.start("ringback",this.options.ringbackUrl)}stop(){this.cadenceTimer&&clearInterval(this.cadenceTimer),this.cadenceTimer=null,this.media?.pause(),this.media&&(this.media.currentTime=0),this.media=null;for(const e of this.oscillators)try{e.stop()}catch{}this.oscillators.clear()}start(e,t){if(this.stop(),this.options.sounds===!1)return;if(t&&typeof Audio<"u"){this.media=new Audio(t),this.media.loop=!0,this.media.play().catch(()=>{});return}const i=()=>this.playBuiltInTone(e);i(),this.cadenceTimer=setInterval(i,e==="incoming"?3e3:3500)}playBuiltInTone(e){const t=ue();if(!t||t.state!=="running")return;const i=e==="incoming"?1.25:1,n=e==="incoming"?[440,480]:[425],s=t.createGain(),o=t.currentTime;s.gain.setValueAtTime(1e-4,o),s.gain.exponentialRampToValueAtTime(.045,o+.025),s.gain.setValueAtTime(.045,o+i-.06),s.gain.exponentialRampToValueAtTime(1e-4,o+i),s.connect(t.destination);for(const r of n){const c=t.createOscillator();c.type="sine",c.frequency.value=r,c.connect(s),this.oscillators.add(c),c.onended=()=>this.oscillators.delete(c),c.start(o),c.stop(o+i+.02)}}}class pe extends EventTarget{options;socket=null;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;heartbeatDeadline=0;currentCall=null;outgoingCallId=null;mount=null;terminalTimer=null;tokenRefreshTimer=null;tokenRequest=null;currentToken;currentTokenExpiresAt;callAudio;mediaClients=new Map;activeCallIds=new Set;seenInvitationIds=new Set;invitationExpiryTimers=new Map;startCallRequest=null;lastDisconnect=null;constructor(e){if(super(),!e.token&&!e.tokenProvider)throw new Error("A client token or tokenProvider is required");if(e.token&&!e.token.startsWith("rtcc_"))throw new Error("A valid RTC client token is required");if(!e.signalingUrl&&!e.callActionsUrl&&!e.apiBaseUrl&&!globalThis.location?.origin)throw new Error("signalingUrl or callActionsUrl is required outside a browser");this.options=e,this.currentToken=e.token??null,this.currentTokenExpiresAt=e.tokenExpiresAt,this.callAudio=new Ve(e),this.mount=fe(e.mount),this.scheduleTokenRefresh()}connect(){if(!this.options.signalingUrl)throw new Error("signalingUrl is required to connect signaling");return this.stopped=!1,this.scheduleTokenRefresh(),this.openSocket("initial"),this}setMount(e){this.clearUi(),this.mount=fe(e),this.currentCall&&this.renderIncoming(this.currentCall)}attachMediaClient(e,t){if(!e.trim())throw new Error("callId is required");return this.mediaClients.set(e,t),()=>{this.mediaClients.get(e)===t&&this.mediaClients.delete(e)}}async startCall(e){if(this.startCallRequest)return this.startCallRequest;if(this.activeCallIds.size>0)throw new Error("Another call is already active");const t=e.targets?.length?e.targets:e.target?[e.target]:[];if(t.length===0)throw new Error("startCall requires at least one target");const i={mode:e.mode,targets:t,metadata:e.metadata,idempotencyKey:e.idempotencyKey},n=(async()=>{await this.callAudio.unlock(),this.callAudio.startRingback();let s;try{s=await this.request("/v1/rtc/client/calls",{method:"POST",headers:e.idempotencyKey?{"idempotency-key":e.idempotencyKey}:void 0,body:JSON.stringify(i)})}catch(o){throw this.callAudio.stop(),o}return!s.credential&&s.callerCredential&&(s.credential=s.callerCredential),this.outgoingCallId=String(s.call?.id??"")||null,this.outgoingCallId&&this.activeCallIds.add(this.outgoingCallId),this.emit("outgoingcall",s),s})();this.startCallRequest=n;try{return await n}finally{this.startCallRequest===n&&(this.startCallRequest=null)}}async accept(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"accept");return this.activeCallIds.add(e),this.clearInvitationExpiry(e),this.callAudio.stop(),this.dismiss(e),this.emit("callaccepted",t),t}async reject(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"reject");return this.releaseMedia(e),this.showTerminal(e,"Call declined"),this.emit("callrejected",t),t}async busy(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"busy");return this.releaseMedia(e),this.showTerminal(e,"Line busy"),t}async end(e){this.callAudio.stop();const t=await this.callAction(e,"end");return this.releaseMedia(e),this.showTerminal(e,"Call ended"),t}async cancel(e){this.callAudio.stop();const t=await this.callAction(e,"cancel");return this.releaseMedia(e),this.showTerminal(e,"Call cancelled"),t}handleIncomingCall(e){if(!e?.id||!e.caller?.id||!e.caller.name||e.mode!=="voice"&&e.mode!=="video")throw new Error("Incoming call payload is invalid");if(e.expiresAt&&Date.parse(e.expiresAt)<=Date.now()){this.emit("callexpired",{id:e.id,expiresAt:e.expiresAt});return}if(this.currentCall?.id===e.id){this.currentCall=e,this.emit("callrestored",e);return}if(this.seenInvitationIds.has(e.id)){this.emit("callrestored",e);return}this.seenInvitationIds.add(e.id),this.activeCallIds.add(e.id),this.scheduleInvitationExpiry(e.id,e.expiresAt),this.currentCall=e,this.callAudio.startIncoming(),this.renderIncoming(e),this.emit("incomingcall",e)}scheduleInvitationExpiry(e,t){const i=t?Date.parse(t):Date.now()+45e3,n=Math.min(18e4,Math.max(5e3,(Number.isFinite(i)?i:Date.now()+45e3)-Date.now()+1e4));this.clearInvitationExpiry(e),this.invitationExpiryTimers.set(e,setTimeout(()=>{this.invitationExpiryTimers.delete(e),!(!this.activeCallIds.has(e)||this.mediaClients.has(e))&&(this.emit("callexpired",{id:e,expiresAt:t??null}),this.release(e))},n))}clearInvitationExpiry(e){const t=this.invitationExpiryTimers.get(e);t&&clearTimeout(t),this.invitationExpiryTimers.delete(e)}release(e){if(!e.trim())throw new Error("callId is required");this.clearInvitationExpiry(e),this.currentCall?.id===e&&(this.callAudio.stop(),this.dismiss(e)),this.outgoingCallId===e&&(this.outgoingCallId=null),this.releaseMedia(e),this.emit("callreleased",{id:e})}handleCallUpdate(e){const t=e.id===this.currentCall?.id||e.id===this.outgoingCallId,i=["answered_elsewhere","rejected","busy","cancelled","ended","failed","missed"];(e.status==="accepted"||i.includes(e.status))&&this.clearInvitationExpiry(e.id),t&&e.status==="accepted"?(this.activeCallIds.add(e.id),this.callAudio.stop(),this.dismiss(e.id)):t&&i.includes(e.status)&&this.showTerminal(e.id,Ue(e.status)),i.includes(e.status)&&this.releaseMedia(e.id),this.emit("callupdated",e)}disconnect(){this.stopped=!0,this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.tokenRefreshTimer&&clearTimeout(this.tokenRefreshTimer),this.reconnectTimer=null,this.heartbeatTimer=null,this.tokenRefreshTimer=null,this.socket?.close(1e3,"Client disconnected"),this.socket=null,this.callAudio.stop(),this.dismiss();for(const e of this.invitationExpiryTimers.values())clearTimeout(e);this.invitationExpiryTimers.clear();for(const e of this.mediaClients.keys())this.releaseMedia(e);this.activeCallIds.clear(),this.seenInvitationIds.clear()}destroy(){this.disconnect(),this.mount=null}async openSocket(e){if(!this.options.signalingUrl||this.stopped||this.socket?.readyState===WebSocket.OPEN||this.socket?.readyState===WebSocket.CONNECTING)return;let t;try{t=await this.resolveToken(e,e==="reconnect")}catch(s){this.emit("tokenerror",x(s)),!this.stopped&&this.options.reconnect!==!1&&this.scheduleReconnect();return}if(this.stopped||this.socket)return;const i=z(this.options.signalingUrl,globalThis.location?.href),n=new WebSocket(i,["rtc-client",t]);this.socket=n,n.addEventListener("open",()=>{const s=this.reconnectAttempt>0,o=this.lastDisconnect;this.reconnectAttempt=0,this.heartbeatDeadline=Date.now()+45e3,this.emit("signalingconnected",{resumed:s}),o&&(this.reportSignalingDiagnostic({event:"recovered",code:o.code,reason:o.reason,wasClean:o.wasClean,outageMs:Date.now()-o.at}),this.lastDisconnect=null),this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=setInterval(()=>{if(n.readyState===WebSocket.OPEN){if(Date.now()>this.heartbeatDeadline){n.close(4e3,"Signaling heartbeat timed out");return}n.send("ping")}},2e4)}),n.addEventListener("message",s=>{this.heartbeatDeadline=Date.now()+45e3,this.handleSignal(String(s.data))}),n.addEventListener("close",s=>{if(this.socket!==n)return;this.socket=null,this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null;const o=!this.stopped&&this.options.reconnect!==!1,r={code:s.code,reason:s.reason,wasClean:s.wasClean,willReconnect:o,attempt:this.reconnectAttempt+1};o&&(this.lastDisconnect={code:s.code,reason:s.reason,wasClean:s.wasClean,at:Date.now()}),this.emit("signalingdisconnected",r),this.reportSignalingDiagnostic({event:o?"reconnecting":"disconnected",...r}),o&&this.scheduleReconnect()}),n.addEventListener("error",()=>{const s={message:"RTC signaling connection failed",reconnecting:!this.stopped&&this.options.reconnect!==!1,attempt:this.reconnectAttempt+1};this.emit("signalingerror",s),this.reportSignalingDiagnostic({event:"error",attempt:s.attempt})})}handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i==="call.incoming"&&t.call&&typeof t.call=="object"){this.handleIncomingCall(t.call);return}if(i==="call.updated"){const n=t.call;n?.id&&n.status&&this.handleCallUpdate({id:n.id,status:n.status});return}i==="presence"?this.emit("presence",{users:t.users??[]}):i!=="pong"&&this.emit(i||"signal",t)}async callAction(e,t){return this.request(`/v1/rtc/client/calls/${encodeURIComponent(e)}/${t}`,{method:"POST",body:"{}"})}async request(e,t,i=!1){const n=this.options.callActionsUrl??this.options.apiBaseUrl??this.options.signalingUrl??globalThis.location?.origin;if(!n)throw new Error("callActionsUrl is required for call actions");const s=_(n,globalThis.location?.href),o=await this.resolveToken("initial"),r=new Headers(t.headers);r.set("authorization",`Bearer ${o}`),r.set("content-type","application/json");const c=await fetch(new URL(e,s.origin),{...t,headers:r}),l=await c.json().catch(()=>({}));if(c.status===401&&this.options.tokenProvider&&!i)return await this.resolveToken("unauthorized",!0),this.request(e,t,!0);if(!c.ok){const d=l.error;throw new Error(typeof d=="string"?d:d?.message||`RTC API failed with HTTP ${c.status}`)}return l}renderIncoming(e){if(!this.mount)return;this.clearUi();const t=document.createElement("section");t.dataset.rtcIncomingCall=e.id,t.setAttribute("role","dialog"),t.setAttribute("aria-label",`Incoming ${e.mode} call from ${e.caller.name}`),Object.assign(t.style,{display:"grid",gap:"14px",padding:"20px",border:"1px solid #cbd8dc",borderRadius:"8px",background:"#fff",color:"#102027",boxShadow:"0 14px 38px #07101429",fontFamily:"system-ui, sans-serif"});const i=document.createElement("small");i.textContent=e.mode==="video"?"Incoming video call":"Incoming voice call",i.style.color="#5d7079";const n=document.createElement("strong");n.textContent=e.caller.name,n.style.fontSize="20px";const s=document.createElement("div");Object.assign(s.style,{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"10px"});const o=ge("Decline","#fff","#a32920","#d9a09b"),r=ge("Answer","#fff","#08745f","#08745f");o.addEventListener("click",()=>{this.reject(e.id).catch(c=>this.emit("error",x(c)))}),r.addEventListener("click",()=>{this.accept(e.id).catch(c=>this.emit("error",x(c)))}),s.appendChild(o),s.appendChild(r),t.appendChild(i),t.appendChild(n),t.appendChild(s),this.mount.replaceChildren(t)}dismiss(e){e&&this.currentCall?.id!==e&&this.outgoingCallId!==e||(this.currentCall=null,(!e||this.outgoingCallId===e)&&(this.outgoingCallId=null),this.callAudio.stop(),this.clearUi())}showTerminal(e,t){if(this.callAudio.stop(),this.currentCall?.id===e&&(this.currentCall=null),this.outgoingCallId===e&&(this.outgoingCallId=null),!this.mount)return;this.clearUi();const i=document.createElement("section");i.dataset.rtcCallTerminal=e,i.setAttribute("role","status"),Object.assign(i.style,{display:"grid",placeItems:"center",gap:"10px",minHeight:"150px",padding:"20px",border:"1px solid #cbd8dc",borderRadius:"8px",background:"#fff",color:"#102027",boxShadow:"0 14px 38px #07101429",fontFamily:"system-ui, sans-serif"});const n=document.createElement("strong");n.textContent=t,n.style.fontSize="18px",i.appendChild(n),this.mount.replaceChildren(i);const s=Math.max(0,this.options.terminalStateDurationMs??1600);this.terminalTimer=setTimeout(()=>this.clearUi(),s)}clearUi(){this.terminalTimer&&clearTimeout(this.terminalTimer),this.terminalTimer=null,this.mount&&this.mount.replaceChildren()}scheduleReconnect(){this.reconnectTimer&&clearTimeout(this.reconnectTimer);const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.openSocket("reconnect")},e)}async resolveToken(e,t=!1){if(!t&&this.currentToken&&!this.tokenNeedsRefresh())return this.currentToken;if(!this.options.tokenProvider){if(!this.currentToken)throw new Error("A valid RTC client token is required");return this.currentToken}return this.tokenRequest?this.tokenRequest:(this.tokenRequest=(async()=>{const i=await this.options.tokenProvider?.({reason:e,previousExpiresAt:this.currentTokenExpiresAt}),n=typeof i=="string"?{token:i}:i;if(!n?.token?.startsWith("rtcc_"))throw new Error("tokenProvider returned an invalid RTC client token");return this.currentToken=n.token,this.currentTokenExpiresAt=n.expiresAt,this.scheduleTokenRefresh(),this.emit("tokenrefreshed",{reason:e,expiresAt:n.expiresAt}),n.token})().finally(()=>{this.tokenRequest=null}),this.tokenRequest)}tokenNeedsRefresh(){if(!this.currentTokenExpiresAt)return!1;const e=Date.parse(this.currentTokenExpiresAt)-Date.now();return Number.isFinite(e)?e<=this.refreshSkew(e):!0}refreshSkew(e){const t=Math.max(5e3,this.options.tokenRefreshSkewMs??3e5);return Math.min(t,Math.max(5e3,e*.2))}scheduleTokenRefresh(){if(this.tokenRefreshTimer&&clearTimeout(this.tokenRefreshTimer),this.tokenRefreshTimer=null,this.stopped||!this.options.tokenProvider||!this.currentTokenExpiresAt)return;const e=Date.parse(this.currentTokenExpiresAt)-Date.now();if(!Number.isFinite(e))return;const t=Math.max(0,e-this.refreshSkew(e));this.tokenRefreshTimer=setTimeout(()=>{this.tokenRefreshTimer=null,this.resolveToken("expiring",!0).catch(i=>{this.emit("tokenerror",x(i)),this.stopped||(this.tokenRefreshTimer=setTimeout(()=>this.scheduleTokenRefresh(),3e4))})},Math.min(t,2147e6))}releaseMedia(e){this.activeCallIds.delete(e),this.seenInvitationIds.delete(e);const t=this.mediaClients.get(e);t&&(this.mediaClients.delete(e),t.leave().catch(()=>{}).finally(()=>t.destroy()))}reportSignalingDiagnostic(e){this.options.reportSignalingDiagnostics===!1||this.stopped||this.request("/v1/rtc/client/signaling-events",{method:"POST",body:JSON.stringify({...e,occurredAt:new Date().toISOString()})}).catch(t=>this.emit("diagnosticserror",x(t)))}emit(e,t){const i={type:e,detail:t,timestamp:new Date().toISOString()};this.dispatchEvent(new CustomEvent(e,{detail:i})),this.options.onEvent?.(i)}}function xe(a){const e=new pe(a);return a.autoConnect!==!1&&a.signalingUrl&&e.connect(),e}function fe(a){return a?typeof a=="string"?document.querySelector(a):a:null}function ge(a,e,t,i){const n=document.createElement("button");return n.type="button",n.textContent=a,Object.assign(n.style,{minHeight:"44px",border:`1px solid ${i}`,borderRadius:"6px",background:t,color:e,cursor:"pointer",font:"inherit",fontWeight:"700"}),n}function Ue(a){return{rejected:"Call declined",busy:"Line busy",cancelled:"Call cancelled",ended:"Call ended",failed:"Call failed",missed:"No answer",answered_elsewhere:"Answered on another device"}[a]??"Call ended"}function ye(a){return{id:a.participantId,name:a.displayName,role:"participant"}}async function Qe(a,e){const t=await a.getStats(),i=new Map,n=new Map;let s=0,o=0,r=0,c=0,l=0,d=0,u=0,k=0,f=0,v=0,R=0,y=0,w=0,p=0,E=0,A=null,D=null,I=null,L=null,S=null,M=null;t.forEach(g=>{n.set(g.id,g),g.type==="codec"&&i.set(g.id,String(g.mimeType??""))}),t.forEach(g=>{if(g.type==="inbound-rtp"&&!g.isRemote&&(s+=Number(g.bytesReceived??0),r+=Math.max(0,Number(g.packetsLost??0)),c+=Math.max(0,Number(g.packetsReceived??0)),Number.isFinite(g.jitter)&&(l+=Number(g.jitter),d+=1),u=Math.max(u,Number(g.framesPerSecond??0)),k=Math.max(k,Number(g.frameWidth??0)),R=Math.max(R,Number(g.frameHeight??0)),y+=Number(g.framesDropped??0),w+=Number(g.freezeCount??0),p+=Number(g.totalFreezesDuration??0),E+=Number(g.concealedSamples??0),S||=i.get(String(g.codecId??""))??null),g.type==="outbound-rtp"&&!g.isRemote&&(f=Math.max(f,Number(g.frameWidth??0)),v=Math.max(v,Number(g.frameHeight??0)),o+=Number(g.bytesSent??0),M||=g.qualityLimitationReason?String(g.qualityLimitationReason):null,S||=i.get(String(g.codecId??""))??null),g.type==="candidate-pair"&&(g.selected||g.nominated)&&g.state==="succeeded"){A=Number.isFinite(g.currentRoundTripTime)?Number(g.currentRoundTripTime):A,D=Number.isFinite(g.availableOutgoingBitrate)?Number(g.availableOutgoingBitrate):D;const q=n.get(g.localCandidateId);I=q?.candidateType?String(q.candidateType):I,L=q?.protocol?String(q.protocol):L}});const m=Date.now(),h=e?Math.max(.001,(m-e.timestamp)/1e3):0,T=e?Math.max(0,Math.round((s-e.inboundBytes)*8/h)):null,P=e?Math.max(0,Math.round((o-e.outboundBytes)*8/h)):null,C=r+c,N=e?Math.max(0,r-e.packetsLost):r,U=e?Math.max(0,c-e.packetsReceived):c,j=N+U;return{counters:{timestamp:m,inboundBytes:s,outboundBytes:o,packetsLost:r,packetsReceived:c},sample:{rttMs:A===null?null:A*1e3,jitterMs:d?l/d*1e3:null,packetLossPct:j?N/j*100:null,packetLossCumulativePct:C?r/C*100:null,packetsLost:r,packetsReceived:c,inboundBitrateBps:T,outboundBitrateBps:P,availableOutgoingBitrateBps:D,framesPerSecond:u||null,frameWidth:k||null,frameHeight:R||null,inboundFrameWidth:k||null,inboundFrameHeight:R||null,outboundFrameWidth:f||null,outboundFrameHeight:v||null,framesDropped:y,freezeCount:w,freezeDurationMs:Math.round(p*1e3),concealedSamples:E,codec:S,transportProtocol:L,candidateType:I,qualityLimitationReason:M}}}function Be(a){let e=a.mediaScore==null?5:Math.round(Se(Number(a.mediaScore),0,5));const t=Z(a.packetLossPct)??0,i=Z(a.rttMs)??0,n=Z(a.jitterMs)??0,s=String(a.connectionState??"unknown").toLowerCase();["failed","closed","disconnected"].includes(s)?e=0:["reconnecting","checking"].includes(s)&&(e=Math.min(e,2)),t>=15?e=Math.min(e,0):t>=8?e=Math.min(e,1):t>=3?e=Math.min(e,2):t>=1&&(e=Math.min(e,3)),i>=1200?e=Math.min(e,0):i>=800?e=Math.min(e,1):i>=400?e=Math.min(e,2):i>=250&&(e=Math.min(e,3)),n>=150?e=Math.min(e,1):n>=80?e=Math.min(e,2):n>=40&&(e=Math.min(e,3));const o=Number(Se(4.5-t*.09-i*.0011-n*.006,1,4.5).toFixed(2));return{networkScore:e,estimatedMos:o}}function ve(){return navigator.connection??null}function Fe(){const a=Number(navigator.deviceMemory);return Number.isFinite(a)?a:null}function Z(a){const e=Number(a);return Number.isFinite(e)?e:null}function Se(a,e,t){return Math.min(t,Math.max(e,a))}function x(a){return a instanceof Error?{name:a.name,message:a.message}:{name:"ServiceError",message:String(a??"Service request failed")}}function ke(a,e){return a instanceof Error?a:new Error(e)}const we="1.3.23";b.AVATAR_TUNING_DEFAULTS=Y,b.BACKGROUND_PRESETS=re,b.OVERLAY_EFFECTS=oe,b.RtcClient=he,b.RtcIncomingClient=pe,b.SPEAKER_NOISE_FLOOR=.02,b.SPEAKER_TAKEOVER_RATIO=1.4,b.createAvatarPipeline=Ne,b.createCallRecorder=Oe,b.createPictureInPictureController=De,b.createRtcClient=je,b.createRtcIncomingClient=xe,b.createVideoEffectsPipeline=qe,b.drawAvatar=de,b.headPoseFromMatrix=ce,b.neutralRig=J,b.pickActiveSpeaker=H,b.rigFromBlendshapes=le,b.scenePainters=$,b.smoothRig=G,b.stylizedAvatarRenderer=K,b.unlockRtcCallAudio=me,b.version=we,Object.defineProperty(b,Symbol.toStringTag,{value:"Module"})}));
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Scheme bridging between the REST and WebSocket faces of one endpoint.
3
+ *
4
+ * Platform-issued credentials carry https:// signaling URLs, but integrators
5
+ * writing their own token flows naturally hand the SDK wss:// — and the old
6
+ * inline ternary (`https: ? wss: : ws:`) silently DOWNGRADED wss to insecure
7
+ * ws, while REST fallbacks derived from a signaling URL tried to fetch
8
+ * wss:// outright. Both directions normalize here instead.
9
+ */
10
+ /** https→wss, http→ws; an already-websocket URL keeps its scheme. */
11
+ export declare function toWebSocketUrl(value: string | URL, base?: string): URL;
12
+ /** wss→https, ws→http; an already-HTTP URL keeps its scheme. */
13
+ export declare function toHttpUrl(value: string | URL, base?: string): URL;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streaming-cdn/rtc-web",
3
- "version": "1.3.21",
3
+ "version": "1.3.23",
4
4
  "type": "module",
5
5
  "main": "dist/rtc-web.umd.js",
6
6
  "module": "dist/rtc-web.es.js",