@vocantly/widget 0.1.10 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,70 +1,125 @@
1
- var Fe=Object.create;var be=Object.defineProperty;var Pe=Object.getOwnPropertyDescriptor;var $e=Object.getOwnPropertyNames;var Ve=Object.getPrototypeOf,We=Object.prototype.hasOwnProperty;var j=(e=>typeof require!="undefined"?require:typeof Proxy!="undefined"?new Proxy(e,{get:(t,s)=>(typeof require!="undefined"?require:t)[s]}):e)(function(e){if(typeof require!="undefined")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var ye=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(s){throw t=0,s}};var Ne=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of $e(t))!We.call(e,i)&&i!==s&&be(e,i,{get:()=>t[i],enumerable:!(n=Pe(t,i))||n.enumerable});return e};var Se=(e,t,s)=>(s=e!=null?Fe(Ve(e)):{},Ne(t||!e||!e.__esModule?be(s,"default",{value:e,enumerable:!0}):s,e));var _e=ye((mt,te)=>{"use strict";var qe=Object.prototype.hasOwnProperty,C="~";function P(){}Object.create&&(P.prototype=Object.create(null),new P().__proto__||(C=!1));function Be(e,t,s){this.fn=e,this.context=t,this.once=s||!1}function Ce(e,t,s,n,i){if(typeof s!="function")throw new TypeError("The listener must be a function");var a=new Be(s,n||e,i),r=C?C+t:t;return e._events[r]?e._events[r].fn?e._events[r]=[e._events[r],a]:e._events[r].push(a):(e._events[r]=a,e._eventsCount++),e}function K(e,t){--e._eventsCount===0?e._events=new P:delete e._events[t]}function S(){this._events=new P,this._eventsCount=0}S.prototype.eventNames=function(){var t=[],s,n;if(this._eventsCount===0)return t;for(n in s=this._events)qe.call(s,n)&&t.push(C?n.slice(1):n);return Object.getOwnPropertySymbols?t.concat(Object.getOwnPropertySymbols(s)):t};S.prototype.listeners=function(t){var s=C?C+t:t,n=this._events[s];if(!n)return[];if(n.fn)return[n.fn];for(var i=0,a=n.length,r=new Array(a);i<a;i++)r[i]=n[i].fn;return r};S.prototype.listenerCount=function(t){var s=C?C+t:t,n=this._events[s];return n?n.fn?1:n.length:0};S.prototype.emit=function(t,s,n,i,a,r){var l=C?C+t:t;if(!this._events[l])return!1;var c=this._events[l],h=arguments.length,f,d;if(c.fn){switch(c.once&&this.removeListener(t,c.fn,void 0,!0),h){case 1:return c.fn.call(c.context),!0;case 2:return c.fn.call(c.context,s),!0;case 3:return c.fn.call(c.context,s,n),!0;case 4:return c.fn.call(c.context,s,n,i),!0;case 5:return c.fn.call(c.context,s,n,i,a),!0;case 6:return c.fn.call(c.context,s,n,i,a,r),!0}for(d=1,f=new Array(h-1);d<h;d++)f[d-1]=arguments[d];c.fn.apply(c.context,f)}else{var y=c.length,m;for(d=0;d<y;d++)switch(c[d].once&&this.removeListener(t,c[d].fn,void 0,!0),h){case 1:c[d].fn.call(c[d].context);break;case 2:c[d].fn.call(c[d].context,s);break;case 3:c[d].fn.call(c[d].context,s,n);break;case 4:c[d].fn.call(c[d].context,s,n,i);break;default:if(!f)for(m=1,f=new Array(h-1);m<h;m++)f[m-1]=arguments[m];c[d].fn.apply(c[d].context,f)}}return!0};S.prototype.on=function(t,s,n){return Ce(this,t,s,n,!1)};S.prototype.once=function(t,s,n){return Ce(this,t,s,n,!0)};S.prototype.removeListener=function(t,s,n,i){var a=C?C+t:t;if(!this._events[a])return this;if(!s)return K(this,a),this;var r=this._events[a];if(r.fn)r.fn===s&&(!i||r.once)&&(!n||r.context===n)&&K(this,a);else{for(var l=0,c=[],h=r.length;l<h;l++)(r[l].fn!==s||i&&!r[l].once||n&&r[l].context!==n)&&c.push(r[l]);c.length?this._events[a]=c.length===1?c[0]:c:K(this,a)}return this};S.prototype.removeAllListeners=function(t){var s;return t?(s=C?C+t:t,this._events[s]&&K(this,s)):(this._events=new P,this._eventsCount=0),this};S.prototype.off=S.prototype.removeListener;S.prototype.addListener=S.prototype.on;S.prefixed=C;S.EventEmitter=S;typeof te!="undefined"&&(te.exports=S)});var xe=ye((Mt,Te)=>{"use strict";Te.exports=function(){throw new Error("ws does not work in the browser. Browser clients must use the native WebSocket object")}});var k=Se(_e(),1);var b=[];for(Q=0;Q<256;++Q)b.push((Q+256).toString(16).slice(1));var Q;function ke(e,t=0){return(b[e[t+0]]+b[e[t+1]]+b[e[t+2]]+b[e[t+3]]+"-"+b[e[t+4]]+b[e[t+5]]+"-"+b[e[t+6]]+b[e[t+7]]+"-"+b[e[t+8]]+b[e[t+9]]+"-"+b[e[t+10]]+b[e[t+11]]+b[e[t+12]]+b[e[t+13]]+b[e[t+14]]+b[e[t+15]]).toLowerCase()}var G,Je=new Uint8Array(16);function se(){if(!G&&(G=typeof crypto!="undefined"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!G))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return G(Je)}var ze=typeof crypto!="undefined"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),ne={randomUUID:ze};function Ke(e,t,s){if(ne.randomUUID&&!t&&!e)return ne.randomUUID();e=e||{};var n=e.random||(e.rng||se)();if(n[6]=n[6]&15|64,n[8]=n[8]&63|128,t){s=s||0;for(var i=0;i<16;++i)t[s+i]=n[i];return t}return ke(n)}var ie=Ke;var dt={};var Qe=(e=>typeof j!="undefined"?j:typeof Proxy!="undefined"?new Proxy(e,{get:(t,s)=>(typeof j!="undefined"?j:t)[s]}):e)(function(e){if(typeof j!="undefined")return j.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var M=class{constructor(e=!1){this.enabled=e}setEnabled(e){this.enabled=e}debug(...e){this.enabled&&console.debug("[Vocantly SDK]",...e)}info(...e){this.enabled&&console.info("[Vocantly SDK]",...e)}warn(...e){this.enabled&&console.warn("[Vocantly SDK]",...e)}error(...e){this.enabled&&console.error("[Vocantly SDK]",...e)}},re=typeof process!="undefined"&&process.versions!=null&&process.versions.node!=null,L=null,$=null;async function Ge(){return re?L||$||($=(async()=>{try{try{return L=await Promise.resolve().then(()=>Se(xe(),1)),L}catch(e){if(typeof Qe!="undefined"){let{createRequire:t}=await import("module");return L=t(dt.url||__filename)("ws"),L}throw e}}catch{return null}finally{$=null}})(),$):null}var Ye=class extends k.default{constructor(e,t={}){var s,n,i;super(),this.ws=null,this.token=null,this.reconnectAttempts=0,this.reconnectTimer=null,this.isManuallyDisconnected=!1,this.isTokenExpired=!1,this.url=e,this.autoReconnect=(s=t.autoReconnect)!=null?s:!0,this.maxReconnectAttempts=(n=t.maxReconnectAttempts)!=null?n:5,this.logger=new M((i=t.debug)!=null?i:!1)}connect(e){if(!e||e.length===0){let t=new Error("Token is required for connection");return this.emit("error",t),Promise.reject(t)}return this.token=e,this.isManuallyDisconnected=!1,this.isTokenExpired=!1,this.attemptConnection()}async attemptConnection(){var s;let e=new URL(this.url),t=(s=this.token)!=null?s:"";if(re){let n=await Ge();if(this.logger.debug("ws module loaded:",!!n),n&&n.WebSocket)return this.logger.debug("Using ws package with Authorization header"),new Promise((i,a)=>{try{let r=new n.WebSocket(e.toString(),{headers:{Authorization:`Bearer ${t}`}});this.ws=r;let l=!1,c=h=>{if(l)return;l=!0;let f=String((h==null?void 0:h.message)||h||"").toLowerCase();if(f.includes("401")||f.includes("unauthorized")||h!=null&&h.code&&String(h.code).includes("401")){this.handleTokenExpiration();let d=new Error("Token expired or invalid. Please refresh your token and reconnect.");d.name="TokenExpiredError",this.emit("error",d),a(d);return}};typeof r.addEventListener=="function"?(r.addEventListener("error",c),r.addEventListener("open",()=>{r.removeEventListener("error",c)})):typeof r.once=="function"&&(r.once("error",c),r.once("open",()=>{})),this.setupEventHandlers(i,a)}catch(r){this.logger.error("Failed to create ws.WebSocket:",r),a(r)}});this.logger.warn("ws package not available, falling back to native WebSocket")}return this.logger.debug("Using native WebSocket with query parameter",{url:e.toString(),hasToken:!!t}),new Promise((n,i)=>{try{if(!t||t.length===0){let l=new Error("Token is required for WebSocket connection");this.logger.error(l.message),i(l);return}let a=new URL(e.toString());a.searchParams.set("token",t),this.ws=new globalThis.WebSocket(a.toString()),this.logger.debug("WebSocket instance created",{readyState:this.ws.readyState,url:this.ws.url,protocol:this.ws.protocol});let r=this.ws;this.setupEventHandlers(n,i),setTimeout(()=>{(r.readyState===WebSocket.CLOSED||r.readyState===WebSocket.CLOSING)&&this.logger.warn("WebSocket closed immediately after creation",{readyState:r.readyState,url:r.url})},100)}catch(a){this.logger.error("Failed to create native WebSocket:",a),i(a)}})}setupEventHandlers(e,t){this.ws&&(this.ws.onopen=()=>{this.reconnectAttempts=0,this.isTokenExpired=!1;let s=this.ws;if(!s){this.logger.error("WebSocket is null in onopen handler"),t(new Error("WebSocket instance lost"));return}if(this.logger.debug("WebSocket connected",{readyState:s.readyState,url:s.url,protocol:s.protocol,extensions:s.extensions}),s.readyState!==WebSocket.OPEN){this.logger.error("WebSocket readyState is not OPEN after onopen",{readyState:s.readyState}),t(new Error(`WebSocket not open: readyState=${s.readyState}`));return}this.emit("connected"),e()},this.ws.onmessage=s=>{try{let n=JSON.parse(s.data);this.logger.debug("Received message:",n),this.emit("message",n)}catch(n){let i=n instanceof Error?n:new Error("Failed to parse message");this.logger.error("Failed to parse message:",i),this.emit("error",i)}},this.ws.onerror=s=>{this.logger.error("WebSocket error:",s);let n;if(s instanceof Error?n=s:s&&s.message?n=s instanceof Error?s:new Error(String(s.message)):n=new Error("WebSocket connection error"),re&&s&&s.message){let i=String(s.message).toLowerCase();if(i.includes("401")||i.includes("unauthorized")){this.handleTokenExpiration();let a=new Error("Token expired or invalid. Please refresh your token and reconnect.");a.name="TokenExpiredError",this.emit("error",a),t(a);return}}this.emit("error",n)},this.ws.onclose=s=>{if(this.logger.debug("WebSocket closed:",{code:s.code,reason:s.reason,wasClean:s.wasClean}),s.code===1006&&this.logger.warn("Abnormal closure (1006) - connection closed without proper handshake. Possible causes: network issue, server closed connection, CORS issue, or protocol mismatch."),s.code===1008||s.code===1002||s.reason&&s.reason.toLowerCase().includes("unauthorized")||s.reason&&s.reason.toLowerCase().includes("401")){this.handleTokenExpiration();let i=new Error("Token expired or invalid. Please refresh your token and reconnect.");i.name="TokenExpiredError",this.emit("error",i),this.emit("disconnected",{code:s.code,reason:s.reason,tokenExpired:!0});return}this.emit("disconnected",{code:s.code,reason:s.reason}),!this.isManuallyDisconnected&&!this.isTokenExpired&&this.autoReconnect?this.reconnectAttempts<this.maxReconnectAttempts?this.scheduleReconnect():(this.logger.warn("Max reconnection attempts reached"),this.emit("error",new Error("Max reconnection attempts reached"))):this.isTokenExpired&&this.logger.warn("Not reconnecting: token is expired")})}handleTokenExpiration(){this.isTokenExpired=!0,this.autoReconnect=!1,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}disconnect(){this.isManuallyDisconnected=!0,this.autoReconnect=!1,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.close(),this.ws=null),this.logger.debug("WebSocket disconnected")}send(e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN){let t=new Error("WebSocket is not connected");throw this.logger.error(t.message),this.emit("error",t),t}try{let t=JSON.stringify(e);this.logger.debug("Sending message:",e),this.ws.send(t)}catch(t){let s=t instanceof Error?t:new Error("Failed to send message");throw this.logger.error("Send error:",s),this.emit("error",s),s}}isConnected(){return this.ws!==null&&this.ws.readyState===WebSocket.OPEN}getReconnectAttempts(){return this.reconnectAttempts}scheduleReconnect(){if(this.reconnectTimer||!this.token)return;this.reconnectAttempts++;let e=[1e3,2e3,5e3,1e4],t=e[Math.min(this.reconnectAttempts-1,e.length-1)];this.logger.debug(`Scheduling reconnect attempt ${this.reconnectAttempts} in ${t}ms`),this.emit("reconnecting",{attempt:this.reconnectAttempts,delay:t}),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.attemptConnection().catch(()=>{})},t)}},Xe=class{constructor(){this.token=null}setToken(e){if(!this.isValidFormat(e))throw new Error("Invalid token format");this.token=e}getToken(){return this.token}hasToken(){return this.token!==null&&this.token.length>0}clearToken(){this.token=null}rotateToken(e){if(!this.isValidFormat(e))throw new Error("Invalid token format");this.token=e}isValidFormat(e){return typeof e=="string"&&e.length>0}validateTokenPresence(){if(!this.hasToken())throw new Error("Token is required but not set")}},V=class{static createOptimistic(e,t,s,n="text",i){return{id:t,conversationId:e,content:s,messageType:n,optimistic:!0,receiptState:"sent",createdAt:new Date,metadata:i}}static fromServerPayload(e,t){var i,a;let s=(i=t.sender_id)!=null?i:t.senderId,n=(a=t.sender_type)!=null?a:t.senderType;return{id:t.message_id,conversationId:e,content:t.content,senderId:s,senderName:t.sender_name,senderType:n,messageType:t.message_type==="system"?"system":"text",optimistic:!1,receiptState:"sent",createdAt:t.sent_at?new Date(t.sent_at):new Date}}static confirmOptimistic(e,t){var i,a,r;let s=(i=t.sender_id)!=null?i:t.senderId,n=(a=t.sender_type)!=null?a:t.senderType;return{...e,optimistic:!1,senderId:s,senderName:t.sender_name,senderType:n,receiptState:(r=e.receiptState)!=null?r:"sent",createdAt:t.sent_at?new Date(t.sent_at):e.createdAt}}static updateReceiptState(e,t){return{...e,receiptState:t}}},Ze=class extends k.default{constructor(e=!1){super(),this.receiptState=new Map,this.logger=new M(e)}getReceiptState(e){return this.receiptState.get(e)}handleDelivered(e,t){var n;if(!e)return this.logger.warn("Invalid delivered receipt: missing messageId"),!1;let s=(n=this.receiptState.get(e))!=null?n:"sent";return this.compareStates(s,"delivered")<0?(this.receiptState.set(e,"delivered"),this.emit("receipt",{messageId:e,state:"delivered",userId:t}),this.logger.debug("Receipt delivered:",e),!0):(this.logger.debug("Delivered receipt ignored (already delivered or read):",e),!1)}handleRead(e,t){var n;if(!e)return this.logger.warn("Invalid read receipt: missing messageId"),!1;let s=(n=this.receiptState.get(e))!=null?n:"sent";return this.compareStates(s,"read")<0?(this.receiptState.set(e,"read"),this.emit("receipt",{messageId:e,state:"read",userId:t}),this.logger.debug("Receipt read:",e),!0):(this.logger.debug("Read receipt ignored (already read):",e),!1)}compareStates(e,t){let s=["sent","delivered","read"],n=s.indexOf(e),i=s.indexOf(t);return n===-1||i===-1?0:n-i}clearConversation(e,t){let s=0;for(let n of t)this.receiptState.has(n)&&(this.receiptState.delete(n),s++);s>0&&this.logger.debug("Cleared receipt state for conversation:",e,`(${s} messages)`)}clearMessage(e){this.receiptState.delete(e)&&this.logger.debug("Cleared receipt state for message:",e)}clear(){this.receiptState.clear(),this.logger.debug("Cleared all receipt state")}},et=class extends k.default{constructor(e=!1){super(),this.seenMessageIds=new Map,this.optimisticMessages=new Map,this.confirmedMessages=new Map,this.warnedMissingSenderId=new Set,this.logger=new M(e),this.receiptManager=new Ze(e),this.receiptManager.on("receipt",t=>{this.updateMessageReceiptState(t)})}createOptimisticMessage(e,t,s,n="text",i){let a=V.createOptimistic(e,t,s,n,i);return this.trackOptimisticMessage(e,t,a),this.trackMessageId(e,t),this.emit("message",a),this.logger.debug("Created optimistic message:",t),a}handleIncomingMessage(e,t){let s=t.message_id,n=this.getOptimisticMessage(e,s);if(n){let a=this.reconcileOptimisticMessage(n,t);return a.senderId==null&&!this.warnedMissingSenderId.has(e)&&(this.warnedMissingSenderId.add(e),console.warn('[Vocantly SDK] Message payload missing sender_id (and sender_type). Clients cannot show "You" vs other participant. The realtime backend MUST include sender_id and sender_type in every type:"message" payload. See docs/REALTIME_WEBSOCKET_CONTRACT.md.')),this.removeOptimisticMessage(e,s),this.trackMessageId(e,s),this.trackConfirmedMessage(e,s,a),a}if(this.hasSeenMessage(e,s))return this.logger.debug("Duplicate message ignored:",s),null;this.trackMessageId(e,s);let i=V.fromServerPayload(e,t);return i.senderId==null&&!this.warnedMissingSenderId.has(e)&&(this.warnedMissingSenderId.add(e),console.warn('[Vocantly SDK] Message payload missing sender_id (and sender_type). Clients cannot show "You" vs other participant. The realtime backend MUST include sender_id and sender_type in every type:"message" payload. See docs/REALTIME_WEBSOCKET_CONTRACT.md.')),this.trackConfirmedMessage(e,s,i),this.emit("message",i),this.logger.debug("Handled incoming message:",s),i}handleReceipt(e,t,s,n){s==="delivered"?this.receiptManager.handleDelivered(t,n):s==="read"&&this.receiptManager.handleRead(t,n)}updateMessageReceiptState(e){for(let[,t]of this.confirmedMessages.entries()){let s=t.get(e.messageId);if(s){let n=V.updateReceiptState(s,e.state);t.set(e.messageId,n),this.emit("message",n),this.logger.debug("Updated message receipt state:",e.messageId,e.state);return}}for(let[,t]of this.optimisticMessages.entries()){let s=t.get(e.messageId);if(s){let n=V.updateReceiptState(s,e.state);t.set(e.messageId,n),this.emit("message",n),this.logger.debug("Updated optimistic message receipt state:",e.messageId,e.state);return}}this.logger.warn("Receipt for unknown message:",e.messageId)}reconcileOptimisticMessage(e,t){let s=V.confirmOptimistic(e,t);return this.logger.debug("Reconciled optimistic message (no second emit):",e.id),s}hasSeenMessage(e,t){var n;let s=this.seenMessageIds.get(e);return(n=s==null?void 0:s.has(t))!=null?n:!1}trackMessageIdsFromHistory(e,t){for(let s of t)this.trackMessageId(e,s)}trackMessageId(e,t){let s=this.seenMessageIds.get(e);s||(s=new Set,this.seenMessageIds.set(e,s)),s.add(t)}trackOptimisticMessage(e,t,s){let n=this.optimisticMessages.get(e);n||(n=new Map,this.optimisticMessages.set(e,n)),n.set(t,s)}getOptimisticMessage(e,t){let s=this.optimisticMessages.get(e);return s==null?void 0:s.get(t)}removeOptimisticMessage(e,t){let s=this.optimisticMessages.get(e);s&&(s.delete(t),s.size===0&&this.optimisticMessages.delete(e))}trackConfirmedMessage(e,t,s){let n=this.confirmedMessages.get(e);n||(n=new Map,this.confirmedMessages.set(e,n)),n.set(t,s)}getMessageIds(e){let t=this.confirmedMessages.get(e),s=this.optimisticMessages.get(e),n=new Set;if(t)for(let i of t.keys())n.add(i);if(s)for(let i of s.keys())n.add(i);return Array.from(n)}clearConversation(e){this.seenMessageIds.delete(e),this.optimisticMessages.delete(e);let t=this.getMessageIds(e);this.receiptManager.clearConversation(e,t),this.confirmedMessages.delete(e),this.logger.debug("Cleared message tracking for conversation:",e)}clear(){this.seenMessageIds.clear(),this.optimisticMessages.clear(),this.confirmedMessages.clear(),this.receiptManager.clear(),this.logger.debug("Cleared all message tracking")}},tt=class extends k.default{constructor(e=!1){super(),this.typingTimers=new Map,this.activeTyping=new Map,this.TYPING_TIMEOUT_MS=5e3,this.logger=new M(e)}handleTypingStart(e,t){if(!e||!t){this.logger.warn("Invalid typing_start: missing conversationId or userId");return}let s=this.activeTyping.get(e);s||(s=new Set,this.activeTyping.set(e,s));let n=this.typingTimers.get(e);n||(n=new Map,this.typingTimers.set(e,n));let i=s.has(t),a=n.get(t);a&&clearTimeout(a),s.add(t);let r=setTimeout(()=>{this.handleTypingStop(e,t)},this.TYPING_TIMEOUT_MS);n.set(t,r),i?this.logger.debug("Typing timer restarted:",e,t):(this.emit("typing",{conversationId:e,userId:t,state:"start"}),this.logger.debug("Typing started:",e,t))}handleTypingStop(e,t){if(!e||!t){this.logger.warn("Invalid typing_stop: missing conversationId or userId");return}let s=this.activeTyping.get(e),n=this.typingTimers.get(e);if(!s||!s.has(t)){this.logger.debug("Typing stop ignored (user not typing):",e,t);return}let i=n==null?void 0:n.get(t);i&&(clearTimeout(i),n==null||n.delete(t)),s.delete(t),s.size===0&&this.activeTyping.delete(e),n&&n.size===0&&this.typingTimers.delete(e),this.emit("typing",{conversationId:e,userId:t,state:"stop"}),this.logger.debug("Typing stopped:",e,t)}clearConversation(e){let t=this.activeTyping.get(e),s=this.typingTimers.get(e);if(t){for(let n of t)this.emit("typing",{conversationId:e,userId:n,state:"stop"});this.activeTyping.delete(e)}if(s){for(let n of s.values())clearTimeout(n);this.typingTimers.delete(e)}this.logger.debug("Cleared typing state for conversation:",e)}clear(){for(let e of this.typingTimers.values())for(let t of e.values())clearTimeout(t);this.typingTimers.clear(),this.activeTyping.clear(),this.logger.debug("Cleared all typing state")}getTypingUsers(e){let t=this.activeTyping.get(e);return t?Array.from(t):[]}},st=[{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun1.l.google.com:19302"}],Ee,Me=typeof RTCPeerConnection!="undefined"&&typeof navigator!="undefined"&&typeof((Ee=navigator.mediaDevices)==null?void 0:Ee.getUserMedia)=="function",W=class extends k.default{constructor(e={}){var t;super(),this.peers=new Map,this.localStream=null,this.mediaOptions={},this.screenTrack=null,this.cameraTrack=null,this.options=e,this.iceServers=(t=e.iceServers)!=null?t:st,this.getIceServers=e.getIceServers,this.iceServersResolved=!e.getIceServers}static isAvailable(){return Me}static async enumerateDevices(){var e;if(!((e=navigator.mediaDevices)!=null&&e.enumerateDevices))return[];try{return await navigator.mediaDevices.enumerateDevices()}catch{return[]}}setMediaOptions(e){this.mediaOptions={...this.mediaOptions,...e}}async getMedia(e){if(this.localStream)return this.localStream;let t=this.mediaOptions,s={echoCancellation:!0,noiseSuppression:t.noiseSuppression!==!1};t.audioDeviceId&&(s.deviceId={exact:t.audioDeviceId});let n=!1;if(e==="video"){let a={};t.videoDeviceId&&(a.deviceId={exact:t.videoDeviceId}),t.lowBandwidth&&(a.width={ideal:640},a.height={ideal:360},a.frameRate={ideal:15,max:20}),n=Object.keys(a).length?a:!0}let i=await navigator.mediaDevices.getUserMedia({audio:s,video:n});return t.startMuted&&i.getAudioTracks().forEach(a=>a.enabled=!1),t.startVideoOff&&i.getVideoTracks().forEach(a=>a.enabled=!1),this.localStream=i,i}async createPeerConnection(e,t){if(!Me)return null;if(!this.iceServersResolved&&this.getIceServers)try{this.iceServers=await this.getIceServers(),this.iceServersResolved=!0}catch(i){this.options.debug&&console.warn("Failed to fetch ICE servers, using defaults:",i),this.iceServersResolved=!0}let s=new RTCPeerConnection({iceServers:this.iceServers}),n={pc:s};return this.peers.set(e,n),s.ontrack=i=>{i.streams[0]&&(n.remoteStream=i.streams[0],this.emit("remote_stream",{userId:e,stream:i.streams[0]}))},s.onicecandidate=i=>{i.candidate&&this.emit("ice_candidate",{userId:e,candidate:i.candidate.toJSON()})},s.oniceconnectionstatechange=()=>{if(s.iceConnectionState==="connected"||s.iceConnectionState==="completed")this.mediaOptions.lowBandwidth&&this.setVideoBitrate(35e4),this.emit("peer_connected",{userId:e});else if(s.iceConnectionState==="failed"){let i=this.peers.get(e);i&&!i.hasRestartedIce&&typeof s.restartIce=="function"?(i.hasRestartedIce=!0,s.restartIce()):this.emit("ice_failed",{userId:e})}},s}async createOffer(e,t){let s=await this.getMedia(t),n=await this.createPeerConnection(e,t);if(!n)return null;s.getTracks().forEach(a=>n.addTrack(a,s));let i=await n.createOffer();return await n.setLocalDescription(i),i}async handleOffer(e,t,s){let n=await this.getMedia(s),i=await this.createPeerConnection(e,s);if(!i)return null;n.getTracks().forEach(r=>i.addTrack(r,n)),await i.setRemoteDescription(new RTCSessionDescription(t));let a=await i.createAnswer();return await i.setLocalDescription(a),a}async handleAnswer(e,t){let s=this.peers.get(e);s&&await s.pc.setRemoteDescription(new RTCSessionDescription(t))}async addIceCandidate(e,t){let s=this.peers.get(e);if(s)try{await s.pc.addIceCandidate(new RTCIceCandidate(t))}catch(n){this.options.debug&&console.warn("addIceCandidate failed:",n)}}getLocalStream(){return this.localStream}getRemoteStream(e){var t;return(t=this.peers.get(e))==null?void 0:t.remoteStream}getAllRemoteStreams(){let e=new Map;for(let[t,s]of this.peers)s.remoteStream&&e.set(t,s.remoteStream);return e}async applyVideoConstraints(e){var s;let t=(s=this.localStream)==null?void 0:s.getVideoTracks()[0];if(t)try{await t.applyConstraints(e)}catch{}}async applyAudioConstraints(e){var s;let t=(s=this.localStream)==null?void 0:s.getAudioTracks()[0];if(t)try{await t.applyConstraints(e)}catch{}}async setVideoBitrate(e){for(let[,t]of this.peers){let s=t.pc.getSenders().find(i=>{var a;return((a=i.track)==null?void 0:a.kind)==="video"});if(!s)continue;let n=s.getParameters();(!n.encodings||n.encodings.length===0)&&(n.encodings=[{}]);for(let i of n.encodings)e==null?delete i.maxBitrate:i.maxBitrate=e;try{await s.setParameters(n)}catch{}}}async setLowBandwidth(e){this.mediaOptions.lowBandwidth=e,await this.applyVideoConstraints(e?{width:{max:640},height:{max:360},frameRate:{max:20}}:{width:{ideal:1280},height:{ideal:720},frameRate:{ideal:30}}),await this.setVideoBitrate(e?35e4:null)}async switchDevice(e,t){if(!this.localStream)return;let s=e==="video"?{video:{deviceId:{exact:t}}}:{audio:{deviceId:{exact:t}}},n=await navigator.mediaDevices.getUserMedia(s),i=e==="video"?n.getVideoTracks()[0]:n.getAudioTracks()[0];if(!i)return;let a=e==="video"?this.localStream.getVideoTracks()[0]:this.localStream.getAudioTracks()[0];a&&(i.enabled=a.enabled);for(let[,r]of this.peers){let l=r.pc.getSenders().find(c=>{var h;return((h=c.track)==null?void 0:h.kind)===e});l&&await l.replaceTrack(i)}a&&(this.localStream.removeTrack(a),a.stop()),this.localStream.addTrack(i)}isScreenSharing(){return!!this.screenTrack}async startScreenShare(){var i,a;if(this.screenTrack)return!0;let e=navigator.mediaDevices;if(!e.getDisplayMedia)return!1;let t=await e.getDisplayMedia({video:!0,audio:!1}),s=t.getVideoTracks()[0];if(!s)return!1;this.cameraTrack=(a=(i=this.localStream)==null?void 0:i.getVideoTracks()[0])!=null?a:null;let n=!1;for(let[,r]of this.peers){let l=r.pc.getSenders().find(c=>{var h;return((h=c.track)==null?void 0:h.kind)==="video"});l&&(await l.replaceTrack(s),n=!0)}return n?(this.screenTrack=s,s.onended=()=>{this.stopScreenShare()},this.emit("screen_share_changed",{active:!0,stream:t}),!0):(s.stop(),!1)}async stopScreenShare(){var e;if(this.screenTrack){for(let[,t]of this.peers){let s=t.pc.getSenders().find(n=>{var i;return((i=n.track)==null?void 0:i.kind)==="video"||n.track===this.screenTrack});s&&await s.replaceTrack((e=this.cameraTrack)!=null?e:null)}this.screenTrack.stop(),this.screenTrack=null,this.emit("screen_share_changed",{active:!1})}}closePeer(e){let t=this.peers.get(e);t&&t.pc.signalingState!=="closed"&&t.pc.close(),this.peers.delete(e)}cleanup(){for(let[e]of this.peers)this.closePeer(e);this.localStream&&(this.localStream.getTracks().forEach(e=>e.stop()),this.localStream=null),this.removeAllListeners()}async getCallStats(){let e={};for(let[,t]of this.peers)try{(await t.pc.getStats()).forEach(n=>{var a,r,l,c,h,f,d,y,m,v;let i=n;if(n.type==="inbound-rtp")e.packets_lost_in=((a=e.packets_lost_in)!=null?a:0)+((r=i.packetsLost)!=null?r:0),e.packets_received=((l=e.packets_received)!=null?l:0)+((c=i.packetsReceived)!=null?c:0),e.bytes_received=((h=e.bytes_received)!=null?h:0)+((f=i.bytesReceived)!=null?f:0),typeof i.jitter=="number"&&(e.jitter_ms=i.jitter*1e3);else if(n.type==="outbound-rtp")e.packets_sent=((d=e.packets_sent)!=null?d:0)+((y=i.packetsSent)!=null?y:0),e.bytes_sent=((m=e.bytes_sent)!=null?m:0)+((v=i.bytesSent)!=null?v:0);else if(n.type==="candidate-pair"&&n.state==="succeeded"){let R=n.currentRoundTripTime;typeof R=="number"&&(e.rtt_ms=R*1e3)}})}catch{}return e}setLocalAudioEnabled(e){this.localStream&&this.localStream.getAudioTracks().forEach(t=>t.enabled=e)}setLocalVideoEnabled(e){this.localStream&&this.localStream.getVideoTracks().forEach(t=>t.enabled=e)}},nt=6e4,it=class extends k.default{constructor(e,t={}){var s,n;super(),this.ringTimeoutId=null,this.webRTC=null,this.callRole=null,this.currentCallType="video",this.participants=new Set,this.webRTCEventsSetup=!1,this.callStartedAt=null,this.callConnectedEmitted=!1,this.conversationId=e,this.logger=new M((s=t.debug)!=null?s:!1),this.ringTimeoutMs=(n=t.ringTimeout)!=null?n:nt,this.submitCallQuality=t.submitCallQuality,W.isAvailable()&&(this.webRTC=new W({debug:t.debug,getIceServers:t.getIceServers}))}emitCallConnected(e,t,s){this.callConnectedEmitted||(this.callConnectedEmitted=!0,this.callStartedAt==null&&(this.callStartedAt=Date.now()),this.emit("call_connected",{localStream:e,remoteStream:t,remoteUserId:s}))}setSendCallHandler(e){this.sendCallHandler=e}setCallOptions(e){var t;(t=this.webRTC)==null||t.setMediaOptions(e)}listDevices(){return W.enumerateDevices()}applyVideoConstraints(e){return this.webRTC?this.webRTC.applyVideoConstraints(e):Promise.resolve()}applyAudioConstraints(e){return this.webRTC?this.webRTC.applyAudioConstraints(e):Promise.resolve()}switchDevice(e,t){return this.webRTC?this.webRTC.switchDevice(e,t):Promise.resolve()}setLowBandwidth(e){return this.webRTC?this.webRTC.setLowBandwidth(e):Promise.resolve()}startScreenShare(){return this.webRTC?this.webRTC.startScreenShare():Promise.resolve(!1)}stopScreenShare(){return this.webRTC?this.webRTC.stopScreenShare():Promise.resolve()}isScreenSharing(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.isScreenSharing())!=null?t:!1}setSubmitCallQuality(e){this.submitCallQuality=e}send(e){if(!this.sendCallHandler){let t=new Error("Call not connected. Join conversation first.");throw this.emit("call_error",{reason:"websocket_lost",message:t.message}),t}this.sendCallHandler(e)}clearRingTimeout(){this.ringTimeoutId&&(clearTimeout(this.ringTimeoutId),this.ringTimeoutId=null)}startCall(e="video"){this.clearRingTimeout(),this.callConnectedEmitted=!1,this.callRole="caller",this.currentCallType=e;let t={type:"call_invite",conversation_id:this.conversationId,payload:{call_type:e}};this.send(t),this.logger.debug("call.invite sent",{conversation_id:this.conversationId,call_type:e}),this.ringTimeoutId=setTimeout(()=>{this.ringTimeoutId=null,this.logger.debug("call ring timeout - no answer"),this.endCall("no_answer"),this.emit("call_timeout",{reason:"no_answer"})},this.ringTimeoutMs)}acceptCall(){this.clearRingTimeout(),this.callConnectedEmitted=!1,this.callRole="callee";let e={type:"call_accept",conversation_id:this.conversationId};this.send(e),this.logger.debug("call.accept sent",{conversation_id:this.conversationId})}joinCall(e="video"){this.callRole="joiner",this.currentCallType=e;let t={type:"call_join",conversation_id:this.conversationId};this.send(t),this.logger.debug("call.join sent",{conversation_id:this.conversationId})}rejectCall(e){this.clearRingTimeout();let t={type:"call_reject",conversation_id:this.conversationId,payload:e?{reason:e}:void 0};this.send(t),this.logger.debug("call.reject sent",{conversation_id:this.conversationId,reason:e})}endCall(e){this.clearRingTimeout();let t={type:"call_hangup",conversation_id:this.conversationId,payload:e?{reason:e}:void 0};this.send(t),this.logger.debug("call.hangup sent",{conversation_id:this.conversationId,reason:e})}handleCallInvite(e){this.currentCallType=e.call_type||"video",this.emit("call_invite",{callType:this.currentCallType,callerId:e.caller_id,callerName:e.caller_name})}async handleCallAccept(e){this.clearRingTimeout();let t=e==null?void 0:e.sender_id;if(this.participants.add(t!=null?t:"unknown"),this.webRTC&&t&&this.callRole==="caller")try{let s=await this.webRTC.createOffer(t,this.currentCallType);s&&(this.sendOffer(t,s),this.setupWebRTCEvents())}catch(s){this.logger.debug("WebRTC createOffer failed:",s),this.emit("call_error",{reason:"permission_denied",message:String(s)})}this.emit("call_accept",{accepterId:t})}async handleCallJoin(e){let t=e.joiner_id;if(t&&this.participants.add(t),this.webRTC&&t&&(this.callRole==="caller"||this.callRole==="callee"))try{let s=await this.webRTC.createOffer(t,this.currentCallType);s&&(this.sendOffer(t,s),this.setupWebRTCEvents())}catch(s){this.logger.debug("WebRTC createOffer for joiner failed:",s)}this.emit("call_join",{joinerId:e.joiner_id,joinerName:e.joiner_name})}handleCallReject(e){this.clearRingTimeout(),this.emit("call_reject",{reason:e==null?void 0:e.reason})}async handleCallHangup(e){var s;this.clearRingTimeout();let t=(s=e==null?void 0:e.reason)!=null?s:"user_hangup";await this.submitQualityAndCleanup(t),this.callRole=null,this.participants.clear(),this.emit("call_hangup",{endedBy:e==null?void 0:e.ended_by,reason:t})}handleCallParticipantLeft(e){var s;let t=e.left_user_id;t&&(this.participants.delete(t),(s=this.webRTC)==null||s.closePeer(t)),this.emit("peer_left",{userId:t})}handleCallError(e){this.clearRingTimeout(),this.emit("call_error",{code:e.code,message:e.message,details:e.details})}async handleCallOffer(e){var n;let t=(n=e.from_user_id)!=null?n:e.sender_id,s=e.sdp;if(!(!t||!s)){if(this.participants.add(t),this.webRTC&&(this.callRole==="callee"||this.callRole==="joiner"))try{this.setupWebRTCEvents();let i=await this.webRTC.handleOffer(t,s,this.currentCallType);if(i){this.sendAnswer(t,i);let a=this.webRTC.getLocalStream(),r=this.webRTC.getRemoteStream(t);a&&r&&this.emitCallConnected(a,r,t)}}catch(i){this.logger.debug("WebRTC handleOffer failed:",i),this.emit("call_error",{reason:"permission_denied",message:String(i)})}this.emit("call_offer",{sdp:s,fromUserId:t})}}async handleCallAnswer(e){var n;let t=(n=e.from_user_id)!=null?n:e.sender_id,s=e.sdp;if(!(!t||!s)){if(this.webRTC){await this.webRTC.handleAnswer(t,s);let i=this.webRTC.getLocalStream(),a=this.webRTC.getRemoteStream(t);i&&a&&this.emitCallConnected(i,a,t)}this.emit("call_answer",{sdp:s,fromUserId:t})}}async handleCallICECandidate(e){var n;let t=(n=e.from_user_id)!=null?n:e.sender_id,s=e.candidate;!t||!s||(this.webRTC&&await this.webRTC.addIceCandidate(t,s),this.emit("call_ice_candidate",{candidate:s,fromUserId:t}))}handleCallMute(e){this.emit("peer_mute",{userId:e==null?void 0:e.user_id})}handleCallUnmute(e){this.emit("peer_unmute",{userId:e==null?void 0:e.user_id})}handleVideoOff(e){this.emit("peer_video_off",{userId:e==null?void 0:e.user_id})}handleVideoOn(e){this.emit("peer_video_on",{userId:e==null?void 0:e.user_id})}setMuted(e){var s;(s=this.webRTC)==null||s.setLocalAudioEnabled(!e);let t=e?{type:"call_mute",conversation_id:this.conversationId}:{type:"call_unmute",conversation_id:this.conversationId};this.send(t),this.emit("local_mute_changed",{muted:e})}setVideoEnabled(e){var s;(s=this.webRTC)==null||s.setLocalVideoEnabled(e);let t=e?{type:"video_on",conversation_id:this.conversationId}:{type:"video_off",conversation_id:this.conversationId};this.send(t),this.emit("local_video_changed",{enabled:e})}getLocalStream(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.getLocalStream())!=null?t:null}getRemoteStream(e){var t;return(t=this.webRTC)==null?void 0:t.getRemoteStream(e)}getAllRemoteStreams(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.getAllRemoteStreams())!=null?t:new Map}sendOffer(e,t){let s={sdp:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_offer",conversation_id:this.conversationId,payload:s})}sendAnswer(e,t){let s={sdp:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_answer",conversation_id:this.conversationId,payload:s})}setupWebRTCEvents(){!this.webRTC||this.webRTCEventsSetup||(this.webRTCEventsSetup=!0,this.webRTC.on("ice_candidate",({userId:e,candidate:t})=>{let s={candidate:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_ice_candidate",conversation_id:this.conversationId,payload:s})}),this.webRTC.on("remote_stream",({userId:e,stream:t})=>{var n;let s=(n=this.webRTC)==null?void 0:n.getLocalStream();s&&this.emitCallConnected(s,t,e)}),this.webRTC.on("ice_failed",async()=>{await this.submitQualityAndCleanup("ice_failed"),this.emit("call_error",{reason:"ice_failed",message:"ICE connection failed"})}),this.webRTC.on("screen_share_changed",e=>this.emit("screen_share_changed",e)))}async submitQualityAndCleanup(e,t=!1){let s=this.webRTC,n={end_reason:e};this.callStartedAt!=null&&(n.duration_seconds=Math.max(0,(Date.now()-this.callStartedAt)/1e3));try{if(s){let i=await s.getCallStats();Object.assign(n,i)}}catch{}if(this.submitCallQuality)try{await this.submitCallQuality(this.conversationId,n,e)}catch(i){this.logger.debug("submitCallQuality failed:",i)}s==null||s.cleanup(),this.webRTC=t?null:W.isAvailable()?new W({}):null,this.webRTCEventsSetup=!1,this.callStartedAt=null,this.callConnectedEmitted=!1}async getCallStats(){let e={};if(this.callStartedAt!=null&&(e.duration_seconds=Math.max(0,(Date.now()-this.callStartedAt)/1e3)),this.webRTC){let t=await this.webRTC.getCallStats();Object.assign(e,t)}return e}cleanup(){this.clearRingTimeout(),this.submitQualityAndCleanup("user_left",!0),this.webRTC=null,this.webRTCEventsSetup=!1,this.callRole=null,this.participants.clear(),this.removeAllListeners()}},rt=class extends k.default{constructor(e,t=!1){var n,i,a;super(),this._isJoined=!1;let s=typeof t=="boolean"?{debug:t}:t;this.id=e,this.messageManager=new et((n=s.debug)!=null?n:!1),this.typingManager=new tt((i=s.debug)!=null?i:!1),this.callManager=new it(e,{debug:(a=s.debug)!=null?a:!1,getIceServers:s.getIceServers}),this.callManager.on("call_invite",r=>this.emit("call_invite",r)),this.callManager.on("call_accept",r=>this.emit("call_accept",r)),this.callManager.on("call_join",r=>this.emit("call_join",r)),this.callManager.on("call_reject",r=>this.emit("call_reject",r)),this.callManager.on("call_hangup",r=>this.emit("call_hangup",r)),this.callManager.on("peer_left",r=>this.emit("peer_left",r)),this.callManager.on("peer_mute",r=>this.emit("peer_mute",r)),this.callManager.on("peer_unmute",r=>this.emit("peer_unmute",r)),this.callManager.on("peer_video_off",r=>this.emit("peer_video_off",r)),this.callManager.on("peer_video_on",r=>this.emit("peer_video_on",r)),this.callManager.on("local_mute_changed",r=>this.emit("local_mute_changed",r)),this.callManager.on("local_video_changed",r=>this.emit("local_video_changed",r)),this.callManager.on("screen_share_changed",r=>this.emit("screen_share_changed",r)),this.callManager.on("call_connected",r=>this.emit("call_connected",r)),this.callManager.on("call_error",r=>this.emit("call_error",r)),this.callManager.on("call_timeout",r=>this.emit("call_timeout",r)),this.messageManager.on("message",r=>{r.conversationId===this.id&&this.emit("message",r)}),this.messageManager.on("receipt",r=>{this.emit("receipt",r)}),this.typingManager.on("typing",r=>{r.conversationId===this.id&&this.emit("typing",{userId:r.userId,state:r.state})})}get isJoined(){return this._isJoined}setSendMessageHandler(e){this.sendMessageHandler=e}setSendTypingHandler(e){this.sendTypingHandler=e}setSendReadHandler(e){this.sendReadHandler=e}setSendCallHandler(e){this.callManager.setSendCallHandler(e)}setSubmitCallQuality(e){this.callManager.setSubmitCallQuality(e)}setFetchMessagesCallback(e){this.fetchMessagesCallback=e}async fetchMessages(e){var s;if(!this.fetchMessagesCallback)throw new Error("REST not configured. Set apiUrl in Vocantly config for fetchMessages() (token, API keys, or getRestHeaders).");let t=await this.fetchMessagesCallback({externalUserId:(s=e==null?void 0:e.externalUserId)!=null?s:"",cursor:e==null?void 0:e.cursor,limit:e==null?void 0:e.limit});return this.messageManager.trackMessageIdsFromHistory(this.id,t.messages.map(n=>n.id)),t}markJoined(){this._isJoined||(this._isJoined=!0,this.emit("joined",{conversation_id:this.id}))}markLeft(){this._isJoined&&(this._isJoined=!1,this.callManager.cleanup(),this.emit("left",{conversation_id:this.id}))}sendMessage(e,t="text",s){if(!this.sendMessageHandler){let a=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",a),a}if(!this._isJoined){let a=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",a),a}let n=ie();this.messageManager.createOptimisticMessage(this.id,n,e,t,s);let i={type:"message",conversation_id:this.id,payload:{message_id:n,content:e,message_type:t}};try{this.sendMessageHandler(i)}catch(a){let r=a instanceof Error?a:new Error("Failed to send message");throw this.emit("error",r),r}}handleIncomingMessage(e){this.messageManager.handleIncomingMessage(this.id,e)}handleReceipt(e,t,s){this.messageManager.handleReceipt(this.id,e,t,s)}handleTyping(e){e.state==="start"?this.typingManager.handleTypingStart(this.id,e.user_id):e.state==="stop"&&this.typingManager.handleTypingStop(this.id,e.user_id)}emitPresence(e){this.emit("presence",{userId:e.user_id,state:e.state})}clearMessages(){this.messageManager.clearConversation(this.id)}clearTyping(){this.typingManager.clearConversation(this.id)}startCall(e="video"){this.ensureJoinedForCall(),this.callManager.startCall(e)}acceptCall(){this.ensureJoinedForCall(),this.callManager.acceptCall()}joinCall(e){this.ensureJoinedForCall(),this.callManager.joinCall(e!=null?e:"video")}rejectCall(e){this.ensureJoinedForCall(),this.callManager.rejectCall(e)}endCall(e){this.ensureJoinedForCall(),this.callManager.endCall(e)}setMuted(e){this.ensureJoinedForCall(),this.callManager.setMuted(e)}setVideoEnabled(e){this.ensureJoinedForCall(),this.callManager.setVideoEnabled(e)}setCallOptions(e){this.callManager.setCallOptions(e)}listDevices(){return this.callManager.listDevices()}applyVideoConstraints(e){return this.callManager.applyVideoConstraints(e)}applyAudioConstraints(e){return this.callManager.applyAudioConstraints(e)}switchDevice(e,t){return this.callManager.switchDevice(e,t)}setLowBandwidth(e){return this.callManager.setLowBandwidth(e)}startScreenShare(){return this.ensureJoinedForCall(),this.callManager.startScreenShare()}stopScreenShare(){return this.callManager.stopScreenShare()}isScreenSharing(){return this.callManager.isScreenSharing()}getLocalStream(){return this.callManager.getLocalStream()}getRemoteStream(e){return this.callManager.getRemoteStream(e)}getAllRemoteStreams(){return this.callManager.getAllRemoteStreams()}getCallStats(){return this.callManager.getCallStats()}handleIncomingCallMessage(e,t){switch(e){case"call_invite":this.callManager.handleCallInvite(t||{});break;case"call_accept":this.callManager.handleCallAccept(t);break;case"call_join":this.callManager.handleCallJoin(t||{});break;case"call_reject":this.callManager.handleCallReject(t);break;case"call_hangup":this.callManager.handleCallHangup(t);break;case"call_participant_left":this.callManager.handleCallParticipantLeft(t||{});break;case"call_error":this.callManager.handleCallError(t||{});break;case"call_offer":this.callManager.handleCallOffer(t||{});break;case"call_answer":this.callManager.handleCallAnswer(t||{});break;case"call_ice_candidate":this.callManager.handleCallICECandidate(t||{});break;case"call_mute":this.callManager.handleCallMute(t||{});break;case"call_unmute":this.callManager.handleCallUnmute(t||{});break;case"video_off":this.callManager.handleVideoOff(t||{});break;case"video_on":this.callManager.handleVideoOn(t||{});break}}ensureJoinedForCall(){if(!this._isJoined)throw new Error("Conversation is not joined. Call join() first.")}startTyping(){if(!this.sendTypingHandler){let t=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",t),t}if(!this._isJoined){let t=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",t),t}let e={type:"typing_start",conversation_id:this.id};try{this.sendTypingHandler(e)}catch(t){let s=t instanceof Error?t:new Error("Failed to send typing_start");throw this.emit("error",s),s}}stopTyping(){if(!this.sendTypingHandler){let t=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",t),t}if(!this._isJoined){let t=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",t),t}let e={type:"typing_stop",conversation_id:this.id};try{this.sendTypingHandler(e)}catch(t){let s=t instanceof Error?t:new Error("Failed to send typing_stop");throw this.emit("error",s),s}}markRead(e){if(!this.sendReadHandler){let s=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",s),s}if(!this._isJoined){let s=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",s),s}let t={type:"read",conversation_id:this.id,payload:{message_id:e}};try{this.sendReadHandler(t)}catch(s){let n=s instanceof Error?s:new Error("Failed to send read receipt");throw this.emit("error",n),n}}},at=class extends k.default{constructor(e,t=!1,s,n,i){super(),this.conversations=new Map,this.restFetcher=null,this.appId=null,this.iceServersCache=null,this.sendMessageHandler=e,this.sendTypingHandler=n,this.sendCallHandler=i,this.supportMessageHandler=s,this.debug=t,this.logger=new M(t)}setRestFetcher(e){if(this.restFetcher=e,e)for(let t of this.conversations.values())t.setSubmitCallQuality((s,n,i)=>this.submitCallQualityToApi(s,n,i))}setAppId(e){this.appId=e&&e.trim()||null,this.iceServersCache=null}getIceServersFetcher(){if(!(!this.restFetcher||!this.appId))return async()=>{var r;if(this.iceServersCache)return this.iceServersCache;let t=`${this.restFetcher.apiUrl.replace(/\/$/,"")}/apps/${encodeURIComponent(this.appId)}/ice-servers`,s=await this.restFetcher.getHeaders(),n=await fetch(t,{method:"GET",headers:s});if(!n.ok)throw new Error(`ICE servers fetch failed: ${n.status}`);let i=await n.json();if(!i.success||!((r=i.data)!=null&&r.iceServers))throw new Error("Invalid ICE servers response");let a=i.data.iceServers;return this.iceServersCache=a,a}}setSupportMessageHandler(e){this.supportMessageHandler=e}setSendTypingHandler(e){this.sendTypingHandler=e;for(let t of this.conversations.values())t.setSendTypingHandler(e)}setSendCallHandler(e){this.sendCallHandler=e;for(let t of this.conversations.values())t.setSendCallHandler(e)}setSendReadHandler(e){this.sendReadHandler=e;for(let t of this.conversations.values())t.setSendReadHandler(e)}async list(e){var m,v,R;if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for list() and fetchMessages() (token, API keys, or getRestHeaders).");let t=this.restFetcher.apiUrl.replace(/\/$/,""),s=(m=e==null?void 0:e.externalUserId)==null?void 0:m.trim(),n=Math.min(Math.max(1,(v=e==null?void 0:e.limit)!=null?v:20),100),i=new URLSearchParams({limit:String(n)});s&&i.set("external_user_id",s),e!=null&&e.cursor&&i.set("cursor",e.cursor);let a=i.toString(),r=`${t}/sdk/conversations${a?`?${a}`:""}`,l=await this.restFetcher.getHeaders(),c=await fetch(r,{method:"GET",headers:l});if(!c.ok){let H=await c.text(),A={};try{A=JSON.parse(H)}catch{A={message:H}}throw new Error(A.message||`HTTP ${c.status}: ${H}`)}let h=await c.json();if(!h.success||!h.data)throw new Error(h.message||"Invalid response");let f=h.data,d=Array.isArray(f.conversations)?f.conversations:[],y=(R=f.pagination)!=null?R:{has_next_page:!1,next_cursor:null,limit:n};return{conversations:d,pagination:y}}async getMessages(e,t,s={}){var d;if(!(e!=null&&e.trim()))throw new Error("conversationId is required");if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for list() and fetchMessages().");let n=Math.min(Math.max(1,(d=s.limit)!=null?d:20),100),i=new URLSearchParams({limit:String(n)});t!=null&&t.trim()&&i.set("external_user_id",t.trim()),s.cursor&&i.set("cursor",s.cursor);let a=this.restFetcher.apiUrl.replace(/\/$/,""),r=i.toString(),l=`${a}/sdk/conversations/${encodeURIComponent(e.trim())}/messages${r?`?${r}`:""}`,c=await this.restFetcher.getHeaders(),h=await fetch(l,{method:"GET",headers:c});if(!h.ok){let y=await h.text(),m={};try{m=JSON.parse(y)}catch{m={message:y}}throw new Error(m.message||`HTTP ${h.status}: ${y}`)}let f=await h.json();if(!f.success||!f.data)throw new Error(f.message||"Invalid response");return{messages:f.data.messages||[],pagination:f.data.pagination||{has_next_page:!1,next_cursor:null,limit:n}}}async submitCallQualityToApi(e,t,s){if(!this.restFetcher)return;let i=`${this.restFetcher.apiUrl.replace(/\/$/,"")}/conversations/${encodeURIComponent(e)}/call-quality`,a=await this.restFetcher.getHeaders(),r={end_reason:s};typeof t.duration_seconds=="number"&&(r.duration_seconds=t.duration_seconds),typeof t.packets_lost_in=="number"&&(r.packets_lost_in=t.packets_lost_in),typeof t.packets_lost_out=="number"&&(r.packets_lost_out=t.packets_lost_out),typeof t.packets_received=="number"&&(r.packets_received=t.packets_received),typeof t.packets_sent=="number"&&(r.packets_sent=t.packets_sent),typeof t.bytes_received=="number"&&(r.bytes_received=t.bytes_received),typeof t.bytes_sent=="number"&&(r.bytes_sent=t.bytes_sent),typeof t.jitter_ms=="number"&&(r.jitter_ms=t.jitter_ms),typeof t.rtt_ms=="number"&&(r.rtt_ms=t.rtt_ms);let l=await fetch(i,{method:"POST",headers:{...a,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!l.ok){let c=await l.text();throw this.logger.debug("submitCallQuality failed:",l.status,c),new Error(`Call quality submit failed: ${l.status}`)}}get(e){if(!e||e.length===0)throw new Error("Conversation ID is required");let t=this.conversations.get(e);return t||(t=new rt(e,{debug:this.debug,getIceServers:this.getIceServersFetcher()}),this.sendMessageHandler&&t.setSendMessageHandler(this.sendMessageHandler),this.sendTypingHandler&&t.setSendTypingHandler(this.sendTypingHandler),this.sendCallHandler&&t.setSendCallHandler(this.sendCallHandler),this.sendReadHandler&&t.setSendReadHandler(this.sendReadHandler),this.conversations.set(e,t),this.logger.debug("Created conversation instance:",e)),this.restFetcher&&(t.setFetchMessagesCallback(s=>this.getMessages(e,s.externalUserId,{cursor:s.cursor,limit:s.limit})),t.setSubmitCallQuality((s,n,i)=>this.submitCallQualityToApi(s,n,i))),t}has(e){return this.conversations.has(e)}async join(e,t){if(!e||e.length===0)throw new Error("Conversation ID is required");this.get(e),await t(e),this.logger.debug("Join requested for conversation:",e)}async leave(e,t){if(!e||e.length===0)throw new Error("Conversation ID is required");let s=this.conversations.get(e);s?(s.clearTyping(),await t(e),s.markLeft(),this.logger.debug("Left conversation:",e)):(await t(e),this.logger.warn("Attempted to leave unknown conversation:",e))}routeIncomingMessage(e){var s;let t;switch(e.type){case"joined":t=((s=e.payload)==null?void 0:s.conversation_id)||e.conversation_id,t&&this.get(t).markJoined();break;case"message":if(t=e.conversation_id,t){let n=this.conversations.get(t);n?n.isJoined?n.handleIncomingMessage(e.payload):this.logger.warn("Received message for conversation that is not joined:",t):this.logger.warn("Received message for unknown conversation:",t)}break;case"typing":if(t=e.conversation_id,t){let n=this.conversations.get(t);n?n.isJoined?n.handleTyping(e.payload):this.logger.warn("Received typing indicator for conversation that is not joined:",t):this.logger.warn("Received typing indicator for unknown conversation:",t)}break;case"receipt":if(t=e.conversation_id,t){let n=this.conversations.get(t);if(n)if(n.isJoined){let i=e;n.handleReceipt(i.payload.message_id,i.payload.state,i.payload.user_id)}else this.logger.warn("Received receipt for conversation that is not joined:",t);else this.logger.warn("Received receipt for unknown conversation:",t)}break;case"support":break;case"presence":if(t=e.conversation_id,t){let n=this.conversations.get(t);n&&n.isJoined&&n.emitPresence(e.payload)}break;case"error":this.logger.error("Received error message:",e.error);break;case"call_invite":case"call_accept":case"call_join":case"call_reject":case"call_hangup":case"call_participant_left":case"call_error":case"call_offer":case"call_answer":case"call_ice_candidate":case"call_mute":case"call_unmute":case"video_off":case"video_on":if(t=e.conversation_id,t){let n=this.get(t),i="payload"in e?e.payload:void 0;n.handleIncomingCallMessage(e.type,i)}break}}routeSupportMessage(e){var n;if(!this.supportMessageHandler){this.logger.warn("Support message handler not set, ignoring support message");return}let t=e.conversation_id;if(!t){this.logger.warn("Support message missing conversation_id");return}let s=(n=e.payload)==null?void 0:n.event;if(!s){this.logger.warn("Support message missing event type");return}this.supportMessageHandler(t,s,e.payload)}remove(e){let t=this.conversations.get(e);t&&(t.markLeft(),t.clearMessages(),t.clearTyping(),this.conversations.delete(e),this.logger.debug("Removed conversation instance:",e))}getAllIds(){return Array.from(this.conversations.keys())}clear(){for(let e of this.conversations.values())e.markLeft(),e.clearMessages(),e.clearTyping();this.conversations.clear(),this.logger.debug("Cleared all conversations")}},ot=class extends k.default{constructor(e=!1){super(),this.presenceState=new Map,this.logger=new M(e)}handlePresence(e,t){if(!e){this.logger.warn("Invalid presence event: missing userId");return}if(t!=="online"&&t!=="offline"){this.logger.warn("Invalid presence state:",t);return}this.presenceState.get(e)!==t?(this.presenceState.set(e,t),this.emit("presence",{userId:e,state:t}),this.logger.debug("Presence changed:",e,t)):this.logger.debug("Duplicate presence event ignored:",e,t)}getPresence(e){return this.presenceState.get(e)}getPresences(e){let t=new Map;for(let s of e){let n=this.presenceState.get(s);n&&t.set(s,n)}return t}clearPresence(e){let t=this.presenceState.has(e);this.presenceState.delete(e),t&&this.logger.debug("Cleared presence for user:",e)}clear(){this.presenceState.clear(),this.logger.debug("Cleared all presence state")}},ct=class extends k.default{constructor(e,t,s,n=!1){super(),this.botActive=!1,this.status="queued",this.conversationId=e,this.conversation=t,this.role=s,this.logger=new M(n),this.conversation.on("message",i=>this.emit("message",i)),this.conversation.on("typing",i=>this.emit("typing",i)),this.conversation.on("presence",i=>this.emit("presence",i)),this.conversation.on("joined",i=>this.emit("joined",i)),this.conversation.on("left",i=>this.emit("left",i)),this.conversation.on("error",i=>this.emit("error",i))}getConversation(){return this.conversation}isCustomer(){return this.role==="customer"}isAgent(){return this.role==="agent"}getRole(){return this.role}isBotActive(){return this.botActive}getAssignedAgent(){return this.assignedAgentId}getStatus(){return this.status}handleAssigned(e){if(this.assignedAgentId===e)return;let t=this.assignedAgentId,s=t!==void 0;this.assignedAgentId=e,this.status==="queued"?this.status="assigned":this.status==="assigned"||this.status,s?(this.emit("assigned",{agentId:e,previousAgentId:t}),this.logger.debug("Support session reassigned:",this.conversationId,e,"from",t)):(this.emit("assigned",{agentId:e}),this.logger.debug("Support session assigned:",this.conversationId,e))}handleUnassigned(){if(this.assignedAgentId===void 0)return;let e=this.assignedAgentId;this.assignedAgentId=void 0,(this.status==="assigned"||this.status==="active")&&(this.status="queued"),this.emit("unassigned",{previousAgentId:e}),this.logger.debug("Support session unassigned:",this.conversationId)}handleBotTakeover(){this.botActive||(this.botActive=!0,this.emit("bot_takeover",{}),this.logger.debug("Bot took over support session:",this.conversationId))}handleBotReleased(){this.botActive&&(this.botActive=!1,this.emit("bot_released",{}),this.logger.debug("Bot released support session:",this.conversationId))}handleClosed(){if(this.status==="closed")return;let e=this.status;this.status="closed",this.emit("closed",{previousStatus:e}),this.logger.debug("Support session closed:",this.conversationId)}handleReopened(){this.status==="closed"&&(this.status=this.assignedAgentId?"assigned":"queued",this.emit("reopened",{}),this.logger.debug("Support session reopened:",this.conversationId))}sendMessage(e,t="text",s){this.conversation.sendMessage(e,t,s)}async join(){}async leave(){}},lt=class extends k.default{constructor(e,t=!1){super(),this.sessions=new Map,this.getConversationHandler=e,this.debug=t,this.logger=new M(t)}get(e,t="customer"){if(!e||e.length===0)throw new Error("Conversation ID is required");let s=this.sessions.get(e);if(!s){let n=this.getConversationHandler(e);s=new ct(e,n,t,this.debug),s.on("assigned",i=>this.emit("assigned",{conversationId:e,...i})),s.on("unassigned",i=>this.emit("unassigned",{conversationId:e,...i})),s.on("bot_takeover",()=>this.emit("bot_takeover",{conversationId:e})),s.on("bot_released",()=>this.emit("bot_released",{conversationId:e})),s.on("closed",i=>this.emit("closed",{conversationId:e,...i})),s.on("reopened",()=>this.emit("reopened",{conversationId:e})),this.sessions.set(e,s),this.logger.debug("Created support session:",e,t)}return s}has(e){return this.sessions.has(e)}getSession(e){return this.sessions.get(e)}handleSupportEvent(e,t,s){let n=this.sessions.get(e);if(!n){t==="assigned"&&(s!=null&&s.agent_id)?this.logger.debug("Support event for unknown session, will be handled when session is created:",e):this.logger.warn("Support event for unknown session:",e,t);return}switch(t){case"assigned":s!=null&&s.agent_id&&typeof s.agent_id=="string"&&n.handleAssigned(s.agent_id);break;case"unassigned":n.handleUnassigned();break;case"bot_takeover":n.handleBotTakeover();break;case"bot_released":n.handleBotReleased();break;case"closed":n.handleClosed();break;case"reopened":n.handleReopened();break}}remove(e){this.sessions.get(e)&&(this.sessions.delete(e),this.logger.debug("Removed support session:",e))}clear(){this.sessions.clear(),this.logger.debug("Cleared all support sessions")}},Re=class extends k.default{constructor(e){var a,r;super(),this.connectionState="disconnected",this.wsClient=null,this.config={autoReconnect:!0,maxReconnectAttempts:5,reconnectDelay:1e3,debug:!1,apiUrl:"",...e},this.tokenManager=new Xe,this.tokenManager.setToken(e.token),this.logger=new M(this.config.debug),this.wsClient=new Ye(this.config.wsUrl,{autoReconnect:this.config.autoReconnect,maxReconnectAttempts:this.config.maxReconnectAttempts,reconnectDelay:this.config.reconnectDelay,debug:this.config.debug}),this.supportManager=new lt(l=>this.conversationManager.get(l),this.config.debug),this.conversationManager=new at(l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)},this.config.debug,void 0,l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)},l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)}),this.conversationManager.setSendReadHandler(l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)}),this.presenceManager=new ot(this.config.debug),this.presenceManager.on("presence",l=>{this.emit("presence",l)});let t=(a=this.config.apiUrl)==null?void 0:a.trim(),s=(r=this.config.appId)==null?void 0:r.trim(),n=this.config.apiKey&&this.config.apiSecret,i=typeof this.config.getRestHeaders=="function";if(t){let l=async()=>{if(i&&this.config.getRestHeaders)return this.config.getRestHeaders();if(n)return{"X-Api-Key":this.config.apiKey,"X-Api-Secret":this.config.apiSecret,"Content-Type":"application/json"};let c=this.tokenManager.getToken();if(!c)throw new Error("Token required for list() and fetchMessages() when API keys are not set");return{Authorization:`Bearer ${c}`,"Content-Type":"application/json"}};this.conversationManager.setRestFetcher({apiUrl:t,getHeaders:l}),s&&this.conversationManager.setAppId(s)}this.setupWebSocketHandlers()}setupWebSocketHandlers(){this.wsClient&&(this.wsClient.on("connected",()=>{this.connectionState="connected",this.emit("connected"),this.emit("state",this.connectionState),this.logger.debug("SDK connected")}),this.wsClient.on("disconnected",e=>{this.connectionState="disconnected",this.emit("disconnected",e),this.emit("state",this.connectionState),this.logger.debug("SDK disconnected")}),this.wsClient.on("reconnecting",e=>{this.connectionState="reconnecting",this.emit("reconnecting",e),this.emit("state",this.connectionState),this.logger.debug("SDK reconnecting",e)}),this.wsClient.on("message",e=>{if(this.emit("message",e),this.conversationManager.routeIncomingMessage(e),e.type==="presence"){let t=e;t.payload&&this.presenceManager.handlePresence(t.payload.user_id,t.payload.state)}switch(e.type==="support"&&this.conversationManager.routeSupportMessage(e),e.type){case"joined":this.emit("joined",e);break;case"message":this.emit("message",e);break;case"typing":this.emit("typing",e);break;case"receipt":break;case"support":break;case"presence":break;case"error":this.emit("error",e);break}}),this.wsClient.on("error",e=>{this.connectionState="error";let t=e instanceof Error?e.message:"";(e instanceof Error?e.name:"")==="TokenExpiredError"||t&&t.includes("Token expired")?(this.logger.error("Token expired:",t),this.logger.warn("To reconnect, call sdk.updateToken(newToken) then sdk.connect()")):this.logger.error("SDK error:",e);let n=e instanceof Error?e:new Error("WebSocket connection error");this.emit("error",n),this.emit("state",this.connectionState)}))}getState(){return this.connectionState}getConfig(){return{...this.config}}async connect(){if(this.connectionState==="connected"){this.logger.warn("Already connected");return}if(this.connectionState==="connecting"){this.logger.warn("Connection already in progress");return}this.tokenManager.validateTokenPresence();let e=this.tokenManager.getToken();if(!e)throw new Error("Token is required for connection");this.connectionState="connecting",this.emit("state",this.connectionState),this.logger.debug("Connecting...");try{if(!this.wsClient)throw new Error("WebSocket client not initialized");await this.wsClient.connect(e)}catch(t){this.connectionState="error",this.emit("state",this.connectionState);let s=t instanceof Error?t:new Error("Connection failed");throw this.emit("error",s),s}}async disconnect(){this.connectionState!=="disconnected"&&(this.logger.debug("Disconnecting..."),this.conversationManager.clear(),this.presenceManager.clear(),this.supportManager.clear(),this.wsClient&&this.wsClient.disconnect(),this.connectionState="disconnected",this.emit("disconnected"),this.emit("state",this.connectionState))}isConnected(){var e;return this.connectionState==="connected"&&((e=this.wsClient)==null?void 0:e.isConnected())===!0}updateToken(e){if(!e||e.length===0)throw new Error("Token cannot be empty");this.tokenManager.setToken(e),this.logger.debug("Token updated"),this.isConnected()&&(this.logger.warn("Token updated while connected. Disconnecting to reconnect with new token..."),this.disconnect())}get conversations(){return{get:e=>this.conversationManager.get(e),has:e=>this.conversationManager.has(e),join:e=>this.conversationManager.join(e,t=>this.joinConversation(t)),leave:e=>this.conversationManager.leave(e,t=>this.leaveConversation(t)),list:e=>this.conversationManager.list(e)}}get support(){return{get:(e,t="customer")=>this.supportManager.get(e,t),has:e=>this.supportManager.has(e)}}async joinConversation(e){if(!this.isConnected())throw new Error("Must be connected before joining a conversation");if(!e||e.length===0)throw new Error("Conversation ID is required");if(!this.wsClient)throw new Error("WebSocket client not initialized");let t={type:"join",conversation_id:e};try{this.wsClient.send(t),this.logger.debug("Join conversation sent:",e)}catch(s){let n=s instanceof Error?s:new Error("Failed to join conversation");throw this.emit("error",n),n}}async leaveConversation(e){if(!this.isConnected())throw new Error("Must be connected before leaving a conversation");if(!e||e.length===0)throw new Error("Conversation ID is required");if(!this.wsClient)throw new Error("WebSocket client not initialized");let t={type:"leave",conversation_id:e};try{this.wsClient.send(t),this.logger.debug("Leave conversation sent:",e)}catch(s){let n=s instanceof Error?s:new Error("Failed to leave conversation");throw this.emit("error",n),n}}rotateToken(e){this.tokenManager.rotateToken(e),this.logger.debug("Token rotated"),this.isConnected()&&this.logger.warn("Token rotated while connected. Reconnect may be required.")}getTokenManager(){return this.tokenManager}};var ht={wsUrl:"",token:"",conversationId:"",publicKey:"",title:"Support",subtitle:"We typically reply in a few minutes",greeting:"Hi! \u{1F44B} How can we help you today?",placeholder:"Type your message\u2026",primaryColor:"#195EFF",position:"bottom-right",launcher:"\u{1F4AC}",autoOpen:!1,debug:!1,collectContact:!1,requireEmail:!1,contactPrompt:"Before we start \u2014 how can we reach you?",csat:!0,csatPrompt:"How would you rate the support you received?",csatThanks:"Thanks for the feedback!",calls:!1,callBundleUrl:"https://cdn.jsdelivr.net/npm/@vocantly/widget@0.1.10/dist/vocantly-widget-call.js"},T=null;function Ae(e){try{let t=JSON.parse(atob(e.split(".")[1]));return t.user_id||t.sub||null}catch{return null}}function E(e){return String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Oe(e){return E(e).replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>").replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g,"$1<em>$2</em>").replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\n/g,"<br>")}var N=null;function gt(){try{let e=window.AudioContext||window.webkitAudioContext;if(!e)return;N=N||new e,N.resume&&N.resume();let t=N,s=t.currentTime,n=t.createOscillator(),i=t.createGain();n.type="sine",n.frequency.setValueAtTime(880,s),n.frequency.exponentialRampToValueAtTime(1320,s+.08),i.gain.setValueAtTime(1e-4,s),i.gain.exponentialRampToValueAtTime(.16,s+.02),i.gain.exponentialRampToValueAtTime(1e-4,s+.34),n.connect(i).connect(t.destination),n.start(s),n.stop(s+.36)}catch{}}function ut(e,t){if(document.getElementById("caf-widget-styles"))return;let s=t==="bottom-left"?"left:20px":"right:20px",n=`
2
- /* Reset so the host page's button/box styles can't bleed into the widget (WordPress
3
- themes add padding + min-width, which was stretching the round buttons into ovals). */
1
+ var $e=Object.create;var ve=Object.defineProperty;var Ve=Object.getOwnPropertyDescriptor;var Pe=Object.getOwnPropertyNames;var Fe=Object.getPrototypeOf,We=Object.prototype.hasOwnProperty;var H=(e=>typeof require!="undefined"?require:typeof Proxy!="undefined"?new Proxy(e,{get:(t,s)=>(typeof require!="undefined"?require:t)[s]}):e)(function(e){if(typeof require!="undefined")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var ye=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(s){throw t=0,s}};var Ne=(e,t,s,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Pe(t))!We.call(e,n)&&n!==s&&ve(e,n,{get:()=>t[n],enumerable:!(i=Ve(t,n))||i.enumerable});return e};var xe=(e,t,s)=>(s=e!=null?$e(Fe(e)):{},Ne(t||!e||!e.__esModule?ve(s,"default",{value:e,enumerable:!0}):s,e));var Ce=ye((mt,te)=>{"use strict";var qe=Object.prototype.hasOwnProperty,S="~";function P(){}Object.create&&(P.prototype=Object.create(null),new P().__proto__||(S=!1));function ze(e,t,s){this.fn=e,this.context=t,this.once=s||!1}function Se(e,t,s,i,n){if(typeof s!="function")throw new TypeError("The listener must be a function");var a=new ze(s,i||e,n),r=S?S+t:t;return e._events[r]?e._events[r].fn?e._events[r]=[e._events[r],a]:e._events[r].push(a):(e._events[r]=a,e._eventsCount++),e}function K(e,t){--e._eventsCount===0?e._events=new P:delete e._events[t]}function x(){this._events=new P,this._eventsCount=0}x.prototype.eventNames=function(){var t=[],s,i;if(this._eventsCount===0)return t;for(i in s=this._events)qe.call(s,i)&&t.push(S?i.slice(1):i);return Object.getOwnPropertySymbols?t.concat(Object.getOwnPropertySymbols(s)):t};x.prototype.listeners=function(t){var s=S?S+t:t,i=this._events[s];if(!i)return[];if(i.fn)return[i.fn];for(var n=0,a=i.length,r=new Array(a);n<a;n++)r[n]=i[n].fn;return r};x.prototype.listenerCount=function(t){var s=S?S+t:t,i=this._events[s];return i?i.fn?1:i.length:0};x.prototype.emit=function(t,s,i,n,a,r){var l=S?S+t:t;if(!this._events[l])return!1;var c=this._events[l],h=arguments.length,u,d;if(c.fn){switch(c.once&&this.removeListener(t,c.fn,void 0,!0),h){case 1:return c.fn.call(c.context),!0;case 2:return c.fn.call(c.context,s),!0;case 3:return c.fn.call(c.context,s,i),!0;case 4:return c.fn.call(c.context,s,i,n),!0;case 5:return c.fn.call(c.context,s,i,n,a),!0;case 6:return c.fn.call(c.context,s,i,n,a,r),!0}for(d=1,u=new Array(h-1);d<h;d++)u[d-1]=arguments[d];c.fn.apply(c.context,u)}else{var y=c.length,m;for(d=0;d<y;d++)switch(c[d].once&&this.removeListener(t,c[d].fn,void 0,!0),h){case 1:c[d].fn.call(c[d].context);break;case 2:c[d].fn.call(c[d].context,s);break;case 3:c[d].fn.call(c[d].context,s,i);break;case 4:c[d].fn.call(c[d].context,s,i,n);break;default:if(!u)for(m=1,u=new Array(h-1);m<h;m++)u[m-1]=arguments[m];c[d].fn.apply(c[d].context,u)}}return!0};x.prototype.on=function(t,s,i){return Se(this,t,s,i,!1)};x.prototype.once=function(t,s,i){return Se(this,t,s,i,!0)};x.prototype.removeListener=function(t,s,i,n){var a=S?S+t:t;if(!this._events[a])return this;if(!s)return K(this,a),this;var r=this._events[a];if(r.fn)r.fn===s&&(!n||r.once)&&(!i||r.context===i)&&K(this,a);else{for(var l=0,c=[],h=r.length;l<h;l++)(r[l].fn!==s||n&&!r[l].once||i&&r[l].context!==i)&&c.push(r[l]);c.length?this._events[a]=c.length===1?c[0]:c:K(this,a)}return this};x.prototype.removeAllListeners=function(t){var s;return t?(s=S?S+t:t,this._events[s]&&K(this,s)):(this._events=new P,this._eventsCount=0),this};x.prototype.off=x.prototype.removeListener;x.prototype.addListener=x.prototype.on;x.prefixed=S;x.EventEmitter=x;typeof te!="undefined"&&(te.exports=x)});var Te=ye((Mt,_e)=>{"use strict";_e.exports=function(){throw new Error("ws does not work in the browser. Browser clients must use the native WebSocket object")}});var k=xe(Ce(),1);var v=[];for(Q=0;Q<256;++Q)v.push((Q+256).toString(16).slice(1));var Q;function ke(e,t=0){return(v[e[t+0]]+v[e[t+1]]+v[e[t+2]]+v[e[t+3]]+"-"+v[e[t+4]]+v[e[t+5]]+"-"+v[e[t+6]]+v[e[t+7]]+"-"+v[e[t+8]]+v[e[t+9]]+"-"+v[e[t+10]]+v[e[t+11]]+v[e[t+12]]+v[e[t+13]]+v[e[t+14]]+v[e[t+15]]).toLowerCase()}var Y,Je=new Uint8Array(16);function se(){if(!Y&&(Y=typeof crypto!="undefined"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!Y))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return Y(Je)}var Be=typeof crypto!="undefined"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),ie={randomUUID:Be};function Ke(e,t,s){if(ie.randomUUID&&!t&&!e)return ie.randomUUID();e=e||{};var i=e.random||(e.rng||se)();if(i[6]=i[6]&15|64,i[8]=i[8]&63|128,t){s=s||0;for(var n=0;n<16;++n)t[s+n]=i[n];return t}return ke(i)}var ne=Ke;var dt={};var Qe=(e=>typeof H!="undefined"?H:typeof Proxy!="undefined"?new Proxy(e,{get:(t,s)=>(typeof H!="undefined"?H:t)[s]}):e)(function(e){if(typeof H!="undefined")return H.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var M=class{constructor(e=!1){this.enabled=e}setEnabled(e){this.enabled=e}debug(...e){this.enabled&&console.debug("[Vocantly SDK]",...e)}info(...e){this.enabled&&console.info("[Vocantly SDK]",...e)}warn(...e){this.enabled&&console.warn("[Vocantly SDK]",...e)}error(...e){this.enabled&&console.error("[Vocantly SDK]",...e)}},re=typeof process!="undefined"&&process.versions!=null&&process.versions.node!=null,$=null,F=null;async function Ye(){return re?$||F||(F=(async()=>{try{try{return $=await Promise.resolve().then(()=>xe(Te(),1)),$}catch(e){if(typeof Qe!="undefined"){let{createRequire:t}=await import("module");return $=t(dt.url||__filename)("ws"),$}throw e}}catch{return null}finally{F=null}})(),F):null}var Ge=class extends k.default{constructor(e,t={}){var s,i,n;super(),this.ws=null,this.token=null,this.reconnectAttempts=0,this.reconnectTimer=null,this.isManuallyDisconnected=!1,this.isTokenExpired=!1,this.url=e,this.autoReconnect=(s=t.autoReconnect)!=null?s:!0,this.maxReconnectAttempts=(i=t.maxReconnectAttempts)!=null?i:5,this.logger=new M((n=t.debug)!=null?n:!1)}connect(e){if(!e||e.length===0){let t=new Error("Token is required for connection");return this.emit("error",t),Promise.reject(t)}return this.token=e,this.isManuallyDisconnected=!1,this.isTokenExpired=!1,this.attemptConnection()}async attemptConnection(){var s;let e=new URL(this.url),t=(s=this.token)!=null?s:"";if(re){let i=await Ye();if(this.logger.debug("ws module loaded:",!!i),i&&i.WebSocket)return this.logger.debug("Using ws package with Authorization header"),new Promise((n,a)=>{try{let r=new i.WebSocket(e.toString(),{headers:{Authorization:`Bearer ${t}`}});this.ws=r;let l=!1,c=h=>{if(l)return;l=!0;let u=String((h==null?void 0:h.message)||h||"").toLowerCase();if(u.includes("401")||u.includes("unauthorized")||h!=null&&h.code&&String(h.code).includes("401")){this.handleTokenExpiration();let d=new Error("Token expired or invalid. Please refresh your token and reconnect.");d.name="TokenExpiredError",this.emit("error",d),a(d);return}};typeof r.addEventListener=="function"?(r.addEventListener("error",c),r.addEventListener("open",()=>{r.removeEventListener("error",c)})):typeof r.once=="function"&&(r.once("error",c),r.once("open",()=>{})),this.setupEventHandlers(n,a)}catch(r){this.logger.error("Failed to create ws.WebSocket:",r),a(r)}});this.logger.warn("ws package not available, falling back to native WebSocket")}return this.logger.debug("Using native WebSocket with query parameter",{url:e.toString(),hasToken:!!t}),new Promise((i,n)=>{try{if(!t||t.length===0){let l=new Error("Token is required for WebSocket connection");this.logger.error(l.message),n(l);return}let a=new URL(e.toString());a.searchParams.set("token",t),this.ws=new globalThis.WebSocket(a.toString()),this.logger.debug("WebSocket instance created",{readyState:this.ws.readyState,url:this.ws.url,protocol:this.ws.protocol});let r=this.ws;this.setupEventHandlers(i,n),setTimeout(()=>{(r.readyState===WebSocket.CLOSED||r.readyState===WebSocket.CLOSING)&&this.logger.warn("WebSocket closed immediately after creation",{readyState:r.readyState,url:r.url})},100)}catch(a){this.logger.error("Failed to create native WebSocket:",a),n(a)}})}setupEventHandlers(e,t){this.ws&&(this.ws.onopen=()=>{this.reconnectAttempts=0,this.isTokenExpired=!1;let s=this.ws;if(!s){this.logger.error("WebSocket is null in onopen handler"),t(new Error("WebSocket instance lost"));return}if(this.logger.debug("WebSocket connected",{readyState:s.readyState,url:s.url,protocol:s.protocol,extensions:s.extensions}),s.readyState!==WebSocket.OPEN){this.logger.error("WebSocket readyState is not OPEN after onopen",{readyState:s.readyState}),t(new Error(`WebSocket not open: readyState=${s.readyState}`));return}this.emit("connected"),e()},this.ws.onmessage=s=>{try{let i=JSON.parse(s.data);this.logger.debug("Received message:",i),this.emit("message",i)}catch(i){let n=i instanceof Error?i:new Error("Failed to parse message");this.logger.error("Failed to parse message:",n),this.emit("error",n)}},this.ws.onerror=s=>{this.logger.error("WebSocket error:",s);let i;if(s instanceof Error?i=s:s&&s.message?i=s instanceof Error?s:new Error(String(s.message)):i=new Error("WebSocket connection error"),re&&s&&s.message){let n=String(s.message).toLowerCase();if(n.includes("401")||n.includes("unauthorized")){this.handleTokenExpiration();let a=new Error("Token expired or invalid. Please refresh your token and reconnect.");a.name="TokenExpiredError",this.emit("error",a),t(a);return}}this.emit("error",i)},this.ws.onclose=s=>{if(this.logger.debug("WebSocket closed:",{code:s.code,reason:s.reason,wasClean:s.wasClean}),s.code===1006&&this.logger.warn("Abnormal closure (1006) - connection closed without proper handshake. Possible causes: network issue, server closed connection, CORS issue, or protocol mismatch."),s.code===1008||s.code===1002||s.reason&&s.reason.toLowerCase().includes("unauthorized")||s.reason&&s.reason.toLowerCase().includes("401")){this.handleTokenExpiration();let n=new Error("Token expired or invalid. Please refresh your token and reconnect.");n.name="TokenExpiredError",this.emit("error",n),this.emit("disconnected",{code:s.code,reason:s.reason,tokenExpired:!0});return}this.emit("disconnected",{code:s.code,reason:s.reason}),!this.isManuallyDisconnected&&!this.isTokenExpired&&this.autoReconnect?this.reconnectAttempts<this.maxReconnectAttempts?this.scheduleReconnect():(this.logger.warn("Max reconnection attempts reached"),this.emit("error",new Error("Max reconnection attempts reached"))):this.isTokenExpired&&this.logger.warn("Not reconnecting: token is expired")})}handleTokenExpiration(){this.isTokenExpired=!0,this.autoReconnect=!1,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}disconnect(){this.isManuallyDisconnected=!0,this.autoReconnect=!1,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.close(),this.ws=null),this.logger.debug("WebSocket disconnected")}send(e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN){let t=new Error("WebSocket is not connected");throw this.logger.error(t.message),this.emit("error",t),t}try{let t=JSON.stringify(e);this.logger.debug("Sending message:",e),this.ws.send(t)}catch(t){let s=t instanceof Error?t:new Error("Failed to send message");throw this.logger.error("Send error:",s),this.emit("error",s),s}}isConnected(){return this.ws!==null&&this.ws.readyState===WebSocket.OPEN}getReconnectAttempts(){return this.reconnectAttempts}scheduleReconnect(){if(this.reconnectTimer||!this.token)return;this.reconnectAttempts++;let e=[1e3,2e3,5e3,1e4],t=e[Math.min(this.reconnectAttempts-1,e.length-1)];this.logger.debug(`Scheduling reconnect attempt ${this.reconnectAttempts} in ${t}ms`),this.emit("reconnecting",{attempt:this.reconnectAttempts,delay:t}),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.attemptConnection().catch(()=>{})},t)}},Xe=class{constructor(){this.token=null}setToken(e){if(!this.isValidFormat(e))throw new Error("Invalid token format");this.token=e}getToken(){return this.token}hasToken(){return this.token!==null&&this.token.length>0}clearToken(){this.token=null}rotateToken(e){if(!this.isValidFormat(e))throw new Error("Invalid token format");this.token=e}isValidFormat(e){return typeof e=="string"&&e.length>0}validateTokenPresence(){if(!this.hasToken())throw new Error("Token is required but not set")}},W=class{static createOptimistic(e,t,s,i="text",n){return{id:t,conversationId:e,content:s,messageType:i,optimistic:!0,receiptState:"sent",createdAt:new Date,metadata:n}}static fromServerPayload(e,t){var n,a;let s=(n=t.sender_id)!=null?n:t.senderId,i=(a=t.sender_type)!=null?a:t.senderType;return{id:t.message_id,conversationId:e,content:t.content,senderId:s,senderName:t.sender_name,senderType:i,messageType:t.message_type==="system"?"system":"text",optimistic:!1,receiptState:"sent",createdAt:t.sent_at?new Date(t.sent_at):new Date}}static confirmOptimistic(e,t){var n,a,r;let s=(n=t.sender_id)!=null?n:t.senderId,i=(a=t.sender_type)!=null?a:t.senderType;return{...e,optimistic:!1,senderId:s,senderName:t.sender_name,senderType:i,receiptState:(r=e.receiptState)!=null?r:"sent",createdAt:t.sent_at?new Date(t.sent_at):e.createdAt}}static updateReceiptState(e,t){return{...e,receiptState:t}}},Ze=class extends k.default{constructor(e=!1){super(),this.receiptState=new Map,this.logger=new M(e)}getReceiptState(e){return this.receiptState.get(e)}handleDelivered(e,t){var i;if(!e)return this.logger.warn("Invalid delivered receipt: missing messageId"),!1;let s=(i=this.receiptState.get(e))!=null?i:"sent";return this.compareStates(s,"delivered")<0?(this.receiptState.set(e,"delivered"),this.emit("receipt",{messageId:e,state:"delivered",userId:t}),this.logger.debug("Receipt delivered:",e),!0):(this.logger.debug("Delivered receipt ignored (already delivered or read):",e),!1)}handleRead(e,t){var i;if(!e)return this.logger.warn("Invalid read receipt: missing messageId"),!1;let s=(i=this.receiptState.get(e))!=null?i:"sent";return this.compareStates(s,"read")<0?(this.receiptState.set(e,"read"),this.emit("receipt",{messageId:e,state:"read",userId:t}),this.logger.debug("Receipt read:",e),!0):(this.logger.debug("Read receipt ignored (already read):",e),!1)}compareStates(e,t){let s=["sent","delivered","read"],i=s.indexOf(e),n=s.indexOf(t);return i===-1||n===-1?0:i-n}clearConversation(e,t){let s=0;for(let i of t)this.receiptState.has(i)&&(this.receiptState.delete(i),s++);s>0&&this.logger.debug("Cleared receipt state for conversation:",e,`(${s} messages)`)}clearMessage(e){this.receiptState.delete(e)&&this.logger.debug("Cleared receipt state for message:",e)}clear(){this.receiptState.clear(),this.logger.debug("Cleared all receipt state")}},et=class extends k.default{constructor(e=!1){super(),this.seenMessageIds=new Map,this.optimisticMessages=new Map,this.confirmedMessages=new Map,this.warnedMissingSenderId=new Set,this.logger=new M(e),this.receiptManager=new Ze(e),this.receiptManager.on("receipt",t=>{this.updateMessageReceiptState(t)})}createOptimisticMessage(e,t,s,i="text",n){let a=W.createOptimistic(e,t,s,i,n);return this.trackOptimisticMessage(e,t,a),this.trackMessageId(e,t),this.emit("message",a),this.logger.debug("Created optimistic message:",t),a}handleIncomingMessage(e,t){let s=t.message_id,i=this.getOptimisticMessage(e,s);if(i){let a=this.reconcileOptimisticMessage(i,t);return a.senderId==null&&!this.warnedMissingSenderId.has(e)&&(this.warnedMissingSenderId.add(e),console.warn('[Vocantly SDK] Message payload missing sender_id (and sender_type). Clients cannot show "You" vs other participant. The realtime backend MUST include sender_id and sender_type in every type:"message" payload. See docs/REALTIME_WEBSOCKET_CONTRACT.md.')),this.removeOptimisticMessage(e,s),this.trackMessageId(e,s),this.trackConfirmedMessage(e,s,a),a}if(this.hasSeenMessage(e,s))return this.logger.debug("Duplicate message ignored:",s),null;this.trackMessageId(e,s);let n=W.fromServerPayload(e,t);return n.senderId==null&&!this.warnedMissingSenderId.has(e)&&(this.warnedMissingSenderId.add(e),console.warn('[Vocantly SDK] Message payload missing sender_id (and sender_type). Clients cannot show "You" vs other participant. The realtime backend MUST include sender_id and sender_type in every type:"message" payload. See docs/REALTIME_WEBSOCKET_CONTRACT.md.')),this.trackConfirmedMessage(e,s,n),this.emit("message",n),this.logger.debug("Handled incoming message:",s),n}handleReceipt(e,t,s,i){s==="delivered"?this.receiptManager.handleDelivered(t,i):s==="read"&&this.receiptManager.handleRead(t,i)}updateMessageReceiptState(e){for(let[,t]of this.confirmedMessages.entries()){let s=t.get(e.messageId);if(s){let i=W.updateReceiptState(s,e.state);t.set(e.messageId,i),this.emit("message",i),this.logger.debug("Updated message receipt state:",e.messageId,e.state);return}}for(let[,t]of this.optimisticMessages.entries()){let s=t.get(e.messageId);if(s){let i=W.updateReceiptState(s,e.state);t.set(e.messageId,i),this.emit("message",i),this.logger.debug("Updated optimistic message receipt state:",e.messageId,e.state);return}}this.logger.warn("Receipt for unknown message:",e.messageId)}reconcileOptimisticMessage(e,t){let s=W.confirmOptimistic(e,t);return this.logger.debug("Reconciled optimistic message (no second emit):",e.id),s}hasSeenMessage(e,t){var i;let s=this.seenMessageIds.get(e);return(i=s==null?void 0:s.has(t))!=null?i:!1}trackMessageIdsFromHistory(e,t){for(let s of t)this.trackMessageId(e,s)}trackMessageId(e,t){let s=this.seenMessageIds.get(e);s||(s=new Set,this.seenMessageIds.set(e,s)),s.add(t)}trackOptimisticMessage(e,t,s){let i=this.optimisticMessages.get(e);i||(i=new Map,this.optimisticMessages.set(e,i)),i.set(t,s)}getOptimisticMessage(e,t){let s=this.optimisticMessages.get(e);return s==null?void 0:s.get(t)}removeOptimisticMessage(e,t){let s=this.optimisticMessages.get(e);s&&(s.delete(t),s.size===0&&this.optimisticMessages.delete(e))}trackConfirmedMessage(e,t,s){let i=this.confirmedMessages.get(e);i||(i=new Map,this.confirmedMessages.set(e,i)),i.set(t,s)}getMessageIds(e){let t=this.confirmedMessages.get(e),s=this.optimisticMessages.get(e),i=new Set;if(t)for(let n of t.keys())i.add(n);if(s)for(let n of s.keys())i.add(n);return Array.from(i)}clearConversation(e){this.seenMessageIds.delete(e),this.optimisticMessages.delete(e);let t=this.getMessageIds(e);this.receiptManager.clearConversation(e,t),this.confirmedMessages.delete(e),this.logger.debug("Cleared message tracking for conversation:",e)}clear(){this.seenMessageIds.clear(),this.optimisticMessages.clear(),this.confirmedMessages.clear(),this.receiptManager.clear(),this.logger.debug("Cleared all message tracking")}},tt=class extends k.default{constructor(e=!1){super(),this.typingTimers=new Map,this.activeTyping=new Map,this.TYPING_TIMEOUT_MS=5e3,this.logger=new M(e)}handleTypingStart(e,t){if(!e||!t){this.logger.warn("Invalid typing_start: missing conversationId or userId");return}let s=this.activeTyping.get(e);s||(s=new Set,this.activeTyping.set(e,s));let i=this.typingTimers.get(e);i||(i=new Map,this.typingTimers.set(e,i));let n=s.has(t),a=i.get(t);a&&clearTimeout(a),s.add(t);let r=setTimeout(()=>{this.handleTypingStop(e,t)},this.TYPING_TIMEOUT_MS);i.set(t,r),n?this.logger.debug("Typing timer restarted:",e,t):(this.emit("typing",{conversationId:e,userId:t,state:"start"}),this.logger.debug("Typing started:",e,t))}handleTypingStop(e,t){if(!e||!t){this.logger.warn("Invalid typing_stop: missing conversationId or userId");return}let s=this.activeTyping.get(e),i=this.typingTimers.get(e);if(!s||!s.has(t)){this.logger.debug("Typing stop ignored (user not typing):",e,t);return}let n=i==null?void 0:i.get(t);n&&(clearTimeout(n),i==null||i.delete(t)),s.delete(t),s.size===0&&this.activeTyping.delete(e),i&&i.size===0&&this.typingTimers.delete(e),this.emit("typing",{conversationId:e,userId:t,state:"stop"}),this.logger.debug("Typing stopped:",e,t)}clearConversation(e){let t=this.activeTyping.get(e),s=this.typingTimers.get(e);if(t){for(let i of t)this.emit("typing",{conversationId:e,userId:i,state:"stop"});this.activeTyping.delete(e)}if(s){for(let i of s.values())clearTimeout(i);this.typingTimers.delete(e)}this.logger.debug("Cleared typing state for conversation:",e)}clear(){for(let e of this.typingTimers.values())for(let t of e.values())clearTimeout(t);this.typingTimers.clear(),this.activeTyping.clear(),this.logger.debug("Cleared all typing state")}getTypingUsers(e){let t=this.activeTyping.get(e);return t?Array.from(t):[]}},st=[{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun1.l.google.com:19302"}],Ee,Me=typeof RTCPeerConnection!="undefined"&&typeof navigator!="undefined"&&typeof((Ee=navigator.mediaDevices)==null?void 0:Ee.getUserMedia)=="function",N=class extends k.default{constructor(e={}){var t;super(),this.peers=new Map,this.localStream=null,this.mediaOptions={},this.screenTrack=null,this.cameraTrack=null,this.options=e,this.iceServers=(t=e.iceServers)!=null?t:st,this.getIceServers=e.getIceServers,this.iceServersResolved=!e.getIceServers}static isAvailable(){return Me}static async enumerateDevices(){var e;if(!((e=navigator.mediaDevices)!=null&&e.enumerateDevices))return[];try{return await navigator.mediaDevices.enumerateDevices()}catch{return[]}}setMediaOptions(e){this.mediaOptions={...this.mediaOptions,...e}}async getMedia(e){if(this.localStream)return this.localStream;let t=this.mediaOptions,s={echoCancellation:!0,noiseSuppression:t.noiseSuppression!==!1};t.audioDeviceId&&(s.deviceId={exact:t.audioDeviceId});let i=!1;if(e==="video"){let a={};t.videoDeviceId&&(a.deviceId={exact:t.videoDeviceId}),t.lowBandwidth&&(a.width={ideal:640},a.height={ideal:360},a.frameRate={ideal:15,max:20}),i=Object.keys(a).length?a:!0}let n=await navigator.mediaDevices.getUserMedia({audio:s,video:i});return t.startMuted&&n.getAudioTracks().forEach(a=>a.enabled=!1),t.startVideoOff&&n.getVideoTracks().forEach(a=>a.enabled=!1),this.localStream=n,n}async createPeerConnection(e,t){if(!Me)return null;if(!this.iceServersResolved&&this.getIceServers)try{this.iceServers=await this.getIceServers(),this.iceServersResolved=!0}catch(n){this.options.debug&&console.warn("Failed to fetch ICE servers, using defaults:",n),this.iceServersResolved=!0}let s=new RTCPeerConnection({iceServers:this.iceServers}),i={pc:s};return this.peers.set(e,i),s.ontrack=n=>{n.streams[0]&&(i.remoteStream=n.streams[0],this.emit("remote_stream",{userId:e,stream:n.streams[0]}))},s.onicecandidate=n=>{n.candidate&&this.emit("ice_candidate",{userId:e,candidate:n.candidate.toJSON()})},s.oniceconnectionstatechange=()=>{if(s.iceConnectionState==="connected"||s.iceConnectionState==="completed")this.mediaOptions.lowBandwidth&&this.setVideoBitrate(35e4),this.emit("peer_connected",{userId:e});else if(s.iceConnectionState==="failed"){let n=this.peers.get(e);n&&!n.hasRestartedIce&&typeof s.restartIce=="function"?(n.hasRestartedIce=!0,s.restartIce()):this.emit("ice_failed",{userId:e})}},s}async createOffer(e,t){let s=await this.getMedia(t),i=await this.createPeerConnection(e,t);if(!i)return null;s.getTracks().forEach(a=>i.addTrack(a,s));let n=await i.createOffer();return await i.setLocalDescription(n),n}async handleOffer(e,t,s){let i=await this.getMedia(s),n=await this.createPeerConnection(e,s);if(!n)return null;i.getTracks().forEach(r=>n.addTrack(r,i)),await n.setRemoteDescription(new RTCSessionDescription(t));let a=await n.createAnswer();return await n.setLocalDescription(a),a}async handleAnswer(e,t){let s=this.peers.get(e);s&&await s.pc.setRemoteDescription(new RTCSessionDescription(t))}async addIceCandidate(e,t){let s=this.peers.get(e);if(s)try{await s.pc.addIceCandidate(new RTCIceCandidate(t))}catch(i){this.options.debug&&console.warn("addIceCandidate failed:",i)}}getLocalStream(){return this.localStream}getRemoteStream(e){var t;return(t=this.peers.get(e))==null?void 0:t.remoteStream}getAllRemoteStreams(){let e=new Map;for(let[t,s]of this.peers)s.remoteStream&&e.set(t,s.remoteStream);return e}async applyVideoConstraints(e){var s;let t=(s=this.localStream)==null?void 0:s.getVideoTracks()[0];if(t)try{await t.applyConstraints(e)}catch{}}async applyAudioConstraints(e){var s;let t=(s=this.localStream)==null?void 0:s.getAudioTracks()[0];if(t)try{await t.applyConstraints(e)}catch{}}async setVideoBitrate(e){for(let[,t]of this.peers){let s=t.pc.getSenders().find(n=>{var a;return((a=n.track)==null?void 0:a.kind)==="video"});if(!s)continue;let i=s.getParameters();(!i.encodings||i.encodings.length===0)&&(i.encodings=[{}]);for(let n of i.encodings)e==null?delete n.maxBitrate:n.maxBitrate=e;try{await s.setParameters(i)}catch{}}}async setLowBandwidth(e){this.mediaOptions.lowBandwidth=e,await this.applyVideoConstraints(e?{width:{max:640},height:{max:360},frameRate:{max:20}}:{width:{ideal:1280},height:{ideal:720},frameRate:{ideal:30}}),await this.setVideoBitrate(e?35e4:null)}async switchDevice(e,t){if(!this.localStream)return;let s=e==="video"?{video:{deviceId:{exact:t}}}:{audio:{deviceId:{exact:t}}},i=await navigator.mediaDevices.getUserMedia(s),n=e==="video"?i.getVideoTracks()[0]:i.getAudioTracks()[0];if(!n)return;let a=e==="video"?this.localStream.getVideoTracks()[0]:this.localStream.getAudioTracks()[0];a&&(n.enabled=a.enabled);for(let[,r]of this.peers){let l=r.pc.getSenders().find(c=>{var h;return((h=c.track)==null?void 0:h.kind)===e});l&&await l.replaceTrack(n)}a&&(this.localStream.removeTrack(a),a.stop()),this.localStream.addTrack(n)}isScreenSharing(){return!!this.screenTrack}async startScreenShare(){var n,a;if(this.screenTrack)return!0;let e=navigator.mediaDevices;if(!e.getDisplayMedia)return!1;let t=await e.getDisplayMedia({video:!0,audio:!1}),s=t.getVideoTracks()[0];if(!s)return!1;this.cameraTrack=(a=(n=this.localStream)==null?void 0:n.getVideoTracks()[0])!=null?a:null;let i=!1;for(let[,r]of this.peers){let l=r.pc.getSenders().find(c=>{var h;return((h=c.track)==null?void 0:h.kind)==="video"});l&&(await l.replaceTrack(s),i=!0)}return i?(this.screenTrack=s,s.onended=()=>{this.stopScreenShare()},this.emit("screen_share_changed",{active:!0,stream:t}),!0):(s.stop(),!1)}async stopScreenShare(){var e;if(this.screenTrack){for(let[,t]of this.peers){let s=t.pc.getSenders().find(i=>{var n;return((n=i.track)==null?void 0:n.kind)==="video"||i.track===this.screenTrack});s&&await s.replaceTrack((e=this.cameraTrack)!=null?e:null)}this.screenTrack.stop(),this.screenTrack=null,this.emit("screen_share_changed",{active:!1})}}closePeer(e){let t=this.peers.get(e);t&&t.pc.signalingState!=="closed"&&t.pc.close(),this.peers.delete(e)}cleanup(){for(let[e]of this.peers)this.closePeer(e);this.localStream&&(this.localStream.getTracks().forEach(e=>e.stop()),this.localStream=null),this.removeAllListeners()}async getCallStats(){let e={};for(let[,t]of this.peers)try{(await t.pc.getStats()).forEach(i=>{var a,r,l,c,h,u,d,y,m,b;let n=i;if(i.type==="inbound-rtp")e.packets_lost_in=((a=e.packets_lost_in)!=null?a:0)+((r=n.packetsLost)!=null?r:0),e.packets_received=((l=e.packets_received)!=null?l:0)+((c=n.packetsReceived)!=null?c:0),e.bytes_received=((h=e.bytes_received)!=null?h:0)+((u=n.bytesReceived)!=null?u:0),typeof n.jitter=="number"&&(e.jitter_ms=n.jitter*1e3);else if(i.type==="outbound-rtp")e.packets_sent=((d=e.packets_sent)!=null?d:0)+((y=n.packetsSent)!=null?y:0),e.bytes_sent=((m=e.bytes_sent)!=null?m:0)+((b=n.bytesSent)!=null?b:0);else if(i.type==="candidate-pair"&&i.state==="succeeded"){let R=i.currentRoundTripTime;typeof R=="number"&&(e.rtt_ms=R*1e3)}})}catch{}return e}setLocalAudioEnabled(e){this.localStream&&this.localStream.getAudioTracks().forEach(t=>t.enabled=e)}setLocalVideoEnabled(e){this.localStream&&this.localStream.getVideoTracks().forEach(t=>t.enabled=e)}},it=6e4,nt=class extends k.default{constructor(e,t={}){var s,i;super(),this.ringTimeoutId=null,this.webRTC=null,this.callRole=null,this.currentCallType="video",this.participants=new Set,this.webRTCEventsSetup=!1,this.callStartedAt=null,this.callConnectedEmitted=!1,this.conversationId=e,this.logger=new M((s=t.debug)!=null?s:!1),this.ringTimeoutMs=(i=t.ringTimeout)!=null?i:it,this.submitCallQuality=t.submitCallQuality,N.isAvailable()&&(this.webRTC=new N({debug:t.debug,getIceServers:t.getIceServers}))}emitCallConnected(e,t,s){this.callConnectedEmitted||(this.callConnectedEmitted=!0,this.callStartedAt==null&&(this.callStartedAt=Date.now()),this.emit("call_connected",{localStream:e,remoteStream:t,remoteUserId:s}))}setSendCallHandler(e){this.sendCallHandler=e}setCallOptions(e){var t;(t=this.webRTC)==null||t.setMediaOptions(e)}listDevices(){return N.enumerateDevices()}applyVideoConstraints(e){return this.webRTC?this.webRTC.applyVideoConstraints(e):Promise.resolve()}applyAudioConstraints(e){return this.webRTC?this.webRTC.applyAudioConstraints(e):Promise.resolve()}switchDevice(e,t){return this.webRTC?this.webRTC.switchDevice(e,t):Promise.resolve()}setLowBandwidth(e){return this.webRTC?this.webRTC.setLowBandwidth(e):Promise.resolve()}startScreenShare(){return this.webRTC?this.webRTC.startScreenShare():Promise.resolve(!1)}stopScreenShare(){return this.webRTC?this.webRTC.stopScreenShare():Promise.resolve()}isScreenSharing(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.isScreenSharing())!=null?t:!1}setSubmitCallQuality(e){this.submitCallQuality=e}send(e){if(!this.sendCallHandler){let t=new Error("Call not connected. Join conversation first.");throw this.emit("call_error",{reason:"websocket_lost",message:t.message}),t}this.sendCallHandler(e)}clearRingTimeout(){this.ringTimeoutId&&(clearTimeout(this.ringTimeoutId),this.ringTimeoutId=null)}startCall(e="video"){this.clearRingTimeout(),this.callConnectedEmitted=!1,this.callRole="caller",this.currentCallType=e;let t={type:"call_invite",conversation_id:this.conversationId,payload:{call_type:e}};this.send(t),this.logger.debug("call.invite sent",{conversation_id:this.conversationId,call_type:e}),this.ringTimeoutId=setTimeout(()=>{this.ringTimeoutId=null,this.logger.debug("call ring timeout - no answer"),this.endCall("no_answer"),this.emit("call_timeout",{reason:"no_answer"})},this.ringTimeoutMs)}acceptCall(){this.clearRingTimeout(),this.callConnectedEmitted=!1,this.callRole="callee";let e={type:"call_accept",conversation_id:this.conversationId};this.send(e),this.logger.debug("call.accept sent",{conversation_id:this.conversationId})}joinCall(e="video"){this.callRole="joiner",this.currentCallType=e;let t={type:"call_join",conversation_id:this.conversationId};this.send(t),this.logger.debug("call.join sent",{conversation_id:this.conversationId})}rejectCall(e){this.clearRingTimeout();let t={type:"call_reject",conversation_id:this.conversationId,payload:e?{reason:e}:void 0};this.send(t),this.logger.debug("call.reject sent",{conversation_id:this.conversationId,reason:e})}endCall(e){this.clearRingTimeout();let t={type:"call_hangup",conversation_id:this.conversationId,payload:e?{reason:e}:void 0};this.send(t),this.logger.debug("call.hangup sent",{conversation_id:this.conversationId,reason:e})}handleCallInvite(e){this.currentCallType=e.call_type||"video",this.emit("call_invite",{callType:this.currentCallType,callerId:e.caller_id,callerName:e.caller_name})}async handleCallAccept(e){this.clearRingTimeout();let t=e==null?void 0:e.sender_id;if(this.participants.add(t!=null?t:"unknown"),this.webRTC&&t&&this.callRole==="caller")try{let s=await this.webRTC.createOffer(t,this.currentCallType);s&&(this.sendOffer(t,s),this.setupWebRTCEvents())}catch(s){this.logger.debug("WebRTC createOffer failed:",s),this.emit("call_error",{reason:"permission_denied",message:String(s)})}this.emit("call_accept",{accepterId:t})}async handleCallJoin(e){let t=e.joiner_id;if(t&&this.participants.add(t),this.webRTC&&t&&(this.callRole==="caller"||this.callRole==="callee"))try{let s=await this.webRTC.createOffer(t,this.currentCallType);s&&(this.sendOffer(t,s),this.setupWebRTCEvents())}catch(s){this.logger.debug("WebRTC createOffer for joiner failed:",s)}this.emit("call_join",{joinerId:e.joiner_id,joinerName:e.joiner_name})}handleCallReject(e){this.clearRingTimeout(),this.emit("call_reject",{reason:e==null?void 0:e.reason})}async handleCallHangup(e){var s;this.clearRingTimeout();let t=(s=e==null?void 0:e.reason)!=null?s:"user_hangup";await this.submitQualityAndCleanup(t),this.callRole=null,this.participants.clear(),this.emit("call_hangup",{endedBy:e==null?void 0:e.ended_by,reason:t})}handleCallParticipantLeft(e){var s;let t=e.left_user_id;t&&(this.participants.delete(t),(s=this.webRTC)==null||s.closePeer(t)),this.emit("peer_left",{userId:t})}handleCallError(e){this.clearRingTimeout(),this.emit("call_error",{code:e.code,message:e.message,details:e.details})}async handleCallOffer(e){var i;let t=(i=e.from_user_id)!=null?i:e.sender_id,s=e.sdp;if(!(!t||!s)){if(this.participants.add(t),this.webRTC&&(this.callRole==="callee"||this.callRole==="joiner"))try{this.setupWebRTCEvents();let n=await this.webRTC.handleOffer(t,s,this.currentCallType);if(n){this.sendAnswer(t,n);let a=this.webRTC.getLocalStream(),r=this.webRTC.getRemoteStream(t);a&&r&&this.emitCallConnected(a,r,t)}}catch(n){this.logger.debug("WebRTC handleOffer failed:",n),this.emit("call_error",{reason:"permission_denied",message:String(n)})}this.emit("call_offer",{sdp:s,fromUserId:t})}}async handleCallAnswer(e){var i;let t=(i=e.from_user_id)!=null?i:e.sender_id,s=e.sdp;if(!(!t||!s)){if(this.webRTC){await this.webRTC.handleAnswer(t,s);let n=this.webRTC.getLocalStream(),a=this.webRTC.getRemoteStream(t);n&&a&&this.emitCallConnected(n,a,t)}this.emit("call_answer",{sdp:s,fromUserId:t})}}async handleCallICECandidate(e){var i;let t=(i=e.from_user_id)!=null?i:e.sender_id,s=e.candidate;!t||!s||(this.webRTC&&await this.webRTC.addIceCandidate(t,s),this.emit("call_ice_candidate",{candidate:s,fromUserId:t}))}handleCallMute(e){this.emit("peer_mute",{userId:e==null?void 0:e.user_id})}handleCallUnmute(e){this.emit("peer_unmute",{userId:e==null?void 0:e.user_id})}handleVideoOff(e){this.emit("peer_video_off",{userId:e==null?void 0:e.user_id})}handleVideoOn(e){this.emit("peer_video_on",{userId:e==null?void 0:e.user_id})}setMuted(e){var s;(s=this.webRTC)==null||s.setLocalAudioEnabled(!e);let t=e?{type:"call_mute",conversation_id:this.conversationId}:{type:"call_unmute",conversation_id:this.conversationId};this.send(t),this.emit("local_mute_changed",{muted:e})}setVideoEnabled(e){var s;(s=this.webRTC)==null||s.setLocalVideoEnabled(e);let t=e?{type:"video_on",conversation_id:this.conversationId}:{type:"video_off",conversation_id:this.conversationId};this.send(t),this.emit("local_video_changed",{enabled:e})}getLocalStream(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.getLocalStream())!=null?t:null}getRemoteStream(e){var t;return(t=this.webRTC)==null?void 0:t.getRemoteStream(e)}getAllRemoteStreams(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.getAllRemoteStreams())!=null?t:new Map}sendOffer(e,t){let s={sdp:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_offer",conversation_id:this.conversationId,payload:s})}sendAnswer(e,t){let s={sdp:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_answer",conversation_id:this.conversationId,payload:s})}setupWebRTCEvents(){!this.webRTC||this.webRTCEventsSetup||(this.webRTCEventsSetup=!0,this.webRTC.on("ice_candidate",({userId:e,candidate:t})=>{let s={candidate:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_ice_candidate",conversation_id:this.conversationId,payload:s})}),this.webRTC.on("remote_stream",({userId:e,stream:t})=>{var i;let s=(i=this.webRTC)==null?void 0:i.getLocalStream();s&&this.emitCallConnected(s,t,e)}),this.webRTC.on("ice_failed",async()=>{await this.submitQualityAndCleanup("ice_failed"),this.emit("call_error",{reason:"ice_failed",message:"ICE connection failed"})}),this.webRTC.on("screen_share_changed",e=>this.emit("screen_share_changed",e)))}async submitQualityAndCleanup(e,t=!1){let s=this.webRTC,i={end_reason:e};this.callStartedAt!=null&&(i.duration_seconds=Math.max(0,(Date.now()-this.callStartedAt)/1e3));try{if(s){let n=await s.getCallStats();Object.assign(i,n)}}catch{}if(this.submitCallQuality)try{await this.submitCallQuality(this.conversationId,i,e)}catch(n){this.logger.debug("submitCallQuality failed:",n)}s==null||s.cleanup(),this.webRTC=t?null:N.isAvailable()?new N({}):null,this.webRTCEventsSetup=!1,this.callStartedAt=null,this.callConnectedEmitted=!1}async getCallStats(){let e={};if(this.callStartedAt!=null&&(e.duration_seconds=Math.max(0,(Date.now()-this.callStartedAt)/1e3)),this.webRTC){let t=await this.webRTC.getCallStats();Object.assign(e,t)}return e}cleanup(){this.clearRingTimeout(),this.submitQualityAndCleanup("user_left",!0),this.webRTC=null,this.webRTCEventsSetup=!1,this.callRole=null,this.participants.clear(),this.removeAllListeners()}},rt=class extends k.default{constructor(e,t=!1){var i,n,a;super(),this._isJoined=!1;let s=typeof t=="boolean"?{debug:t}:t;this.id=e,this.messageManager=new et((i=s.debug)!=null?i:!1),this.typingManager=new tt((n=s.debug)!=null?n:!1),this.callManager=new nt(e,{debug:(a=s.debug)!=null?a:!1,getIceServers:s.getIceServers}),this.callManager.on("call_invite",r=>this.emit("call_invite",r)),this.callManager.on("call_accept",r=>this.emit("call_accept",r)),this.callManager.on("call_join",r=>this.emit("call_join",r)),this.callManager.on("call_reject",r=>this.emit("call_reject",r)),this.callManager.on("call_hangup",r=>this.emit("call_hangup",r)),this.callManager.on("peer_left",r=>this.emit("peer_left",r)),this.callManager.on("peer_mute",r=>this.emit("peer_mute",r)),this.callManager.on("peer_unmute",r=>this.emit("peer_unmute",r)),this.callManager.on("peer_video_off",r=>this.emit("peer_video_off",r)),this.callManager.on("peer_video_on",r=>this.emit("peer_video_on",r)),this.callManager.on("local_mute_changed",r=>this.emit("local_mute_changed",r)),this.callManager.on("local_video_changed",r=>this.emit("local_video_changed",r)),this.callManager.on("screen_share_changed",r=>this.emit("screen_share_changed",r)),this.callManager.on("call_connected",r=>this.emit("call_connected",r)),this.callManager.on("call_error",r=>this.emit("call_error",r)),this.callManager.on("call_timeout",r=>this.emit("call_timeout",r)),this.messageManager.on("message",r=>{r.conversationId===this.id&&this.emit("message",r)}),this.messageManager.on("receipt",r=>{this.emit("receipt",r)}),this.typingManager.on("typing",r=>{r.conversationId===this.id&&this.emit("typing",{userId:r.userId,state:r.state})})}get isJoined(){return this._isJoined}setSendMessageHandler(e){this.sendMessageHandler=e}setSendTypingHandler(e){this.sendTypingHandler=e}setSendReadHandler(e){this.sendReadHandler=e}setSendCallHandler(e){this.callManager.setSendCallHandler(e)}setSubmitCallQuality(e){this.callManager.setSubmitCallQuality(e)}setFetchMessagesCallback(e){this.fetchMessagesCallback=e}async fetchMessages(e){var s;if(!this.fetchMessagesCallback)throw new Error("REST not configured. Set apiUrl in Vocantly config for fetchMessages() (token, API keys, or getRestHeaders).");let t=await this.fetchMessagesCallback({externalUserId:(s=e==null?void 0:e.externalUserId)!=null?s:"",cursor:e==null?void 0:e.cursor,limit:e==null?void 0:e.limit});return this.messageManager.trackMessageIdsFromHistory(this.id,t.messages.map(i=>i.id)),t}markJoined(){this._isJoined||(this._isJoined=!0,this.emit("joined",{conversation_id:this.id}))}markLeft(){this._isJoined&&(this._isJoined=!1,this.callManager.cleanup(),this.emit("left",{conversation_id:this.id}))}sendMessage(e,t="text",s){if(!this.sendMessageHandler){let a=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",a),a}if(!this._isJoined){let a=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",a),a}let i=ne();this.messageManager.createOptimisticMessage(this.id,i,e,t,s);let n={type:"message",conversation_id:this.id,payload:{message_id:i,content:e,message_type:t}};try{this.sendMessageHandler(n)}catch(a){let r=a instanceof Error?a:new Error("Failed to send message");throw this.emit("error",r),r}}handleIncomingMessage(e){this.messageManager.handleIncomingMessage(this.id,e)}handleReceipt(e,t,s){this.messageManager.handleReceipt(this.id,e,t,s)}handleTyping(e){e.state==="start"?this.typingManager.handleTypingStart(this.id,e.user_id):e.state==="stop"&&this.typingManager.handleTypingStop(this.id,e.user_id)}emitPresence(e){this.emit("presence",{userId:e.user_id,state:e.state})}clearMessages(){this.messageManager.clearConversation(this.id)}clearTyping(){this.typingManager.clearConversation(this.id)}startCall(e="video"){this.ensureJoinedForCall(),this.callManager.startCall(e)}acceptCall(){this.ensureJoinedForCall(),this.callManager.acceptCall()}joinCall(e){this.ensureJoinedForCall(),this.callManager.joinCall(e!=null?e:"video")}rejectCall(e){this.ensureJoinedForCall(),this.callManager.rejectCall(e)}endCall(e){this.ensureJoinedForCall(),this.callManager.endCall(e)}setMuted(e){this.ensureJoinedForCall(),this.callManager.setMuted(e)}setVideoEnabled(e){this.ensureJoinedForCall(),this.callManager.setVideoEnabled(e)}setCallOptions(e){this.callManager.setCallOptions(e)}listDevices(){return this.callManager.listDevices()}applyVideoConstraints(e){return this.callManager.applyVideoConstraints(e)}applyAudioConstraints(e){return this.callManager.applyAudioConstraints(e)}switchDevice(e,t){return this.callManager.switchDevice(e,t)}setLowBandwidth(e){return this.callManager.setLowBandwidth(e)}startScreenShare(){return this.ensureJoinedForCall(),this.callManager.startScreenShare()}stopScreenShare(){return this.callManager.stopScreenShare()}isScreenSharing(){return this.callManager.isScreenSharing()}getLocalStream(){return this.callManager.getLocalStream()}getRemoteStream(e){return this.callManager.getRemoteStream(e)}getAllRemoteStreams(){return this.callManager.getAllRemoteStreams()}getCallStats(){return this.callManager.getCallStats()}handleIncomingCallMessage(e,t){switch(e){case"call_invite":this.callManager.handleCallInvite(t||{});break;case"call_accept":this.callManager.handleCallAccept(t);break;case"call_join":this.callManager.handleCallJoin(t||{});break;case"call_reject":this.callManager.handleCallReject(t);break;case"call_hangup":this.callManager.handleCallHangup(t);break;case"call_participant_left":this.callManager.handleCallParticipantLeft(t||{});break;case"call_error":this.callManager.handleCallError(t||{});break;case"call_offer":this.callManager.handleCallOffer(t||{});break;case"call_answer":this.callManager.handleCallAnswer(t||{});break;case"call_ice_candidate":this.callManager.handleCallICECandidate(t||{});break;case"call_mute":this.callManager.handleCallMute(t||{});break;case"call_unmute":this.callManager.handleCallUnmute(t||{});break;case"video_off":this.callManager.handleVideoOff(t||{});break;case"video_on":this.callManager.handleVideoOn(t||{});break}}ensureJoinedForCall(){if(!this._isJoined)throw new Error("Conversation is not joined. Call join() first.")}startTyping(){if(!this.sendTypingHandler){let t=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",t),t}if(!this._isJoined){let t=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",t),t}let e={type:"typing_start",conversation_id:this.id};try{this.sendTypingHandler(e)}catch(t){let s=t instanceof Error?t:new Error("Failed to send typing_start");throw this.emit("error",s),s}}stopTyping(){if(!this.sendTypingHandler){let t=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",t),t}if(!this._isJoined){let t=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",t),t}let e={type:"typing_stop",conversation_id:this.id};try{this.sendTypingHandler(e)}catch(t){let s=t instanceof Error?t:new Error("Failed to send typing_stop");throw this.emit("error",s),s}}markRead(e){if(!this.sendReadHandler){let s=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",s),s}if(!this._isJoined){let s=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",s),s}let t={type:"read",conversation_id:this.id,payload:{message_id:e}};try{this.sendReadHandler(t)}catch(s){let i=s instanceof Error?s:new Error("Failed to send read receipt");throw this.emit("error",i),i}}},at=class extends k.default{constructor(e,t=!1,s,i,n){super(),this.conversations=new Map,this.restFetcher=null,this.appId=null,this.iceServersCache=null,this.sendMessageHandler=e,this.sendTypingHandler=i,this.sendCallHandler=n,this.supportMessageHandler=s,this.debug=t,this.logger=new M(t)}setRestFetcher(e){if(this.restFetcher=e,e)for(let t of this.conversations.values())t.setSubmitCallQuality((s,i,n)=>this.submitCallQualityToApi(s,i,n))}setAppId(e){this.appId=e&&e.trim()||null,this.iceServersCache=null}getIceServersFetcher(){if(!(!this.restFetcher||!this.appId))return async()=>{var r;if(this.iceServersCache)return this.iceServersCache;let t=`${this.restFetcher.apiUrl.replace(/\/$/,"")}/apps/${encodeURIComponent(this.appId)}/ice-servers`,s=await this.restFetcher.getHeaders(),i=await fetch(t,{method:"GET",headers:s});if(!i.ok)throw new Error(`ICE servers fetch failed: ${i.status}`);let n=await i.json();if(!n.success||!((r=n.data)!=null&&r.iceServers))throw new Error("Invalid ICE servers response");let a=n.data.iceServers;return this.iceServersCache=a,a}}setSupportMessageHandler(e){this.supportMessageHandler=e}setSendTypingHandler(e){this.sendTypingHandler=e;for(let t of this.conversations.values())t.setSendTypingHandler(e)}setSendCallHandler(e){this.sendCallHandler=e;for(let t of this.conversations.values())t.setSendCallHandler(e)}setSendReadHandler(e){this.sendReadHandler=e;for(let t of this.conversations.values())t.setSendReadHandler(e)}async list(e){var m,b,R;if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for list() and fetchMessages() (token, API keys, or getRestHeaders).");let t=this.restFetcher.apiUrl.replace(/\/$/,""),s=(m=e==null?void 0:e.externalUserId)==null?void 0:m.trim(),i=Math.min(Math.max(1,(b=e==null?void 0:e.limit)!=null?b:20),100),n=new URLSearchParams({limit:String(i)});s&&n.set("external_user_id",s),e!=null&&e.cursor&&n.set("cursor",e.cursor);let a=n.toString(),r=`${t}/sdk/conversations${a?`?${a}`:""}`,l=await this.restFetcher.getHeaders(),c=await fetch(r,{method:"GET",headers:l});if(!c.ok){let D=await c.text(),A={};try{A=JSON.parse(D)}catch{A={message:D}}throw new Error(A.message||`HTTP ${c.status}: ${D}`)}let h=await c.json();if(!h.success||!h.data)throw new Error(h.message||"Invalid response");let u=h.data,d=Array.isArray(u.conversations)?u.conversations:[],y=(R=u.pagination)!=null?R:{has_next_page:!1,next_cursor:null,limit:i};return{conversations:d,pagination:y}}async getMessages(e,t,s={}){var d;if(!(e!=null&&e.trim()))throw new Error("conversationId is required");if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for list() and fetchMessages().");let i=Math.min(Math.max(1,(d=s.limit)!=null?d:20),100),n=new URLSearchParams({limit:String(i)});t!=null&&t.trim()&&n.set("external_user_id",t.trim()),s.cursor&&n.set("cursor",s.cursor);let a=this.restFetcher.apiUrl.replace(/\/$/,""),r=n.toString(),l=`${a}/sdk/conversations/${encodeURIComponent(e.trim())}/messages${r?`?${r}`:""}`,c=await this.restFetcher.getHeaders(),h=await fetch(l,{method:"GET",headers:c});if(!h.ok){let y=await h.text(),m={};try{m=JSON.parse(y)}catch{m={message:y}}throw new Error(m.message||`HTTP ${h.status}: ${y}`)}let u=await h.json();if(!u.success||!u.data)throw new Error(u.message||"Invalid response");return{messages:u.data.messages||[],pagination:u.data.pagination||{has_next_page:!1,next_cursor:null,limit:i}}}async submitCallQualityToApi(e,t,s){if(!this.restFetcher)return;let n=`${this.restFetcher.apiUrl.replace(/\/$/,"")}/conversations/${encodeURIComponent(e)}/call-quality`,a=await this.restFetcher.getHeaders(),r={end_reason:s};typeof t.duration_seconds=="number"&&(r.duration_seconds=t.duration_seconds),typeof t.packets_lost_in=="number"&&(r.packets_lost_in=t.packets_lost_in),typeof t.packets_lost_out=="number"&&(r.packets_lost_out=t.packets_lost_out),typeof t.packets_received=="number"&&(r.packets_received=t.packets_received),typeof t.packets_sent=="number"&&(r.packets_sent=t.packets_sent),typeof t.bytes_received=="number"&&(r.bytes_received=t.bytes_received),typeof t.bytes_sent=="number"&&(r.bytes_sent=t.bytes_sent),typeof t.jitter_ms=="number"&&(r.jitter_ms=t.jitter_ms),typeof t.rtt_ms=="number"&&(r.rtt_ms=t.rtt_ms);let l=await fetch(n,{method:"POST",headers:{...a,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!l.ok){let c=await l.text();throw this.logger.debug("submitCallQuality failed:",l.status,c),new Error(`Call quality submit failed: ${l.status}`)}}get(e){if(!e||e.length===0)throw new Error("Conversation ID is required");let t=this.conversations.get(e);return t||(t=new rt(e,{debug:this.debug,getIceServers:this.getIceServersFetcher()}),this.sendMessageHandler&&t.setSendMessageHandler(this.sendMessageHandler),this.sendTypingHandler&&t.setSendTypingHandler(this.sendTypingHandler),this.sendCallHandler&&t.setSendCallHandler(this.sendCallHandler),this.sendReadHandler&&t.setSendReadHandler(this.sendReadHandler),this.conversations.set(e,t),this.logger.debug("Created conversation instance:",e)),this.restFetcher&&(t.setFetchMessagesCallback(s=>this.getMessages(e,s.externalUserId,{cursor:s.cursor,limit:s.limit})),t.setSubmitCallQuality((s,i,n)=>this.submitCallQualityToApi(s,i,n))),t}has(e){return this.conversations.has(e)}async join(e,t){if(!e||e.length===0)throw new Error("Conversation ID is required");this.get(e),await t(e),this.logger.debug("Join requested for conversation:",e)}async leave(e,t){if(!e||e.length===0)throw new Error("Conversation ID is required");let s=this.conversations.get(e);s?(s.clearTyping(),await t(e),s.markLeft(),this.logger.debug("Left conversation:",e)):(await t(e),this.logger.warn("Attempted to leave unknown conversation:",e))}routeIncomingMessage(e){var s;let t;switch(e.type){case"joined":t=((s=e.payload)==null?void 0:s.conversation_id)||e.conversation_id,t&&this.get(t).markJoined();break;case"message":if(t=e.conversation_id,t){let i=this.conversations.get(t);i?i.isJoined?i.handleIncomingMessage(e.payload):this.logger.warn("Received message for conversation that is not joined:",t):this.logger.warn("Received message for unknown conversation:",t)}break;case"typing":if(t=e.conversation_id,t){let i=this.conversations.get(t);i?i.isJoined?i.handleTyping(e.payload):this.logger.warn("Received typing indicator for conversation that is not joined:",t):this.logger.warn("Received typing indicator for unknown conversation:",t)}break;case"receipt":if(t=e.conversation_id,t){let i=this.conversations.get(t);if(i)if(i.isJoined){let n=e;i.handleReceipt(n.payload.message_id,n.payload.state,n.payload.user_id)}else this.logger.warn("Received receipt for conversation that is not joined:",t);else this.logger.warn("Received receipt for unknown conversation:",t)}break;case"support":break;case"presence":if(t=e.conversation_id,t){let i=this.conversations.get(t);i&&i.isJoined&&i.emitPresence(e.payload)}break;case"error":this.logger.error("Received error message:",e.error);break;case"call_invite":case"call_accept":case"call_join":case"call_reject":case"call_hangup":case"call_participant_left":case"call_error":case"call_offer":case"call_answer":case"call_ice_candidate":case"call_mute":case"call_unmute":case"video_off":case"video_on":if(t=e.conversation_id,t){let i=this.get(t),n="payload"in e?e.payload:void 0;i.handleIncomingCallMessage(e.type,n)}break}}routeSupportMessage(e){var i;if(!this.supportMessageHandler){this.logger.warn("Support message handler not set, ignoring support message");return}let t=e.conversation_id;if(!t){this.logger.warn("Support message missing conversation_id");return}let s=(i=e.payload)==null?void 0:i.event;if(!s){this.logger.warn("Support message missing event type");return}this.supportMessageHandler(t,s,e.payload)}remove(e){let t=this.conversations.get(e);t&&(t.markLeft(),t.clearMessages(),t.clearTyping(),this.conversations.delete(e),this.logger.debug("Removed conversation instance:",e))}getAllIds(){return Array.from(this.conversations.keys())}clear(){for(let e of this.conversations.values())e.markLeft(),e.clearMessages(),e.clearTyping();this.conversations.clear(),this.logger.debug("Cleared all conversations")}},ot=class extends k.default{constructor(e=!1){super(),this.presenceState=new Map,this.logger=new M(e)}handlePresence(e,t){if(!e){this.logger.warn("Invalid presence event: missing userId");return}if(t!=="online"&&t!=="offline"){this.logger.warn("Invalid presence state:",t);return}this.presenceState.get(e)!==t?(this.presenceState.set(e,t),this.emit("presence",{userId:e,state:t}),this.logger.debug("Presence changed:",e,t)):this.logger.debug("Duplicate presence event ignored:",e,t)}getPresence(e){return this.presenceState.get(e)}getPresences(e){let t=new Map;for(let s of e){let i=this.presenceState.get(s);i&&t.set(s,i)}return t}clearPresence(e){let t=this.presenceState.has(e);this.presenceState.delete(e),t&&this.logger.debug("Cleared presence for user:",e)}clear(){this.presenceState.clear(),this.logger.debug("Cleared all presence state")}},ct=class extends k.default{constructor(e,t,s,i=!1){super(),this.botActive=!1,this.status="queued",this.conversationId=e,this.conversation=t,this.role=s,this.logger=new M(i),this.conversation.on("message",n=>this.emit("message",n)),this.conversation.on("typing",n=>this.emit("typing",n)),this.conversation.on("presence",n=>this.emit("presence",n)),this.conversation.on("joined",n=>this.emit("joined",n)),this.conversation.on("left",n=>this.emit("left",n)),this.conversation.on("error",n=>this.emit("error",n))}getConversation(){return this.conversation}isCustomer(){return this.role==="customer"}isAgent(){return this.role==="agent"}getRole(){return this.role}isBotActive(){return this.botActive}getAssignedAgent(){return this.assignedAgentId}getStatus(){return this.status}handleAssigned(e){if(this.assignedAgentId===e)return;let t=this.assignedAgentId,s=t!==void 0;this.assignedAgentId=e,this.status==="queued"?this.status="assigned":this.status==="assigned"||this.status,s?(this.emit("assigned",{agentId:e,previousAgentId:t}),this.logger.debug("Support session reassigned:",this.conversationId,e,"from",t)):(this.emit("assigned",{agentId:e}),this.logger.debug("Support session assigned:",this.conversationId,e))}handleUnassigned(){if(this.assignedAgentId===void 0)return;let e=this.assignedAgentId;this.assignedAgentId=void 0,(this.status==="assigned"||this.status==="active")&&(this.status="queued"),this.emit("unassigned",{previousAgentId:e}),this.logger.debug("Support session unassigned:",this.conversationId)}handleBotTakeover(){this.botActive||(this.botActive=!0,this.emit("bot_takeover",{}),this.logger.debug("Bot took over support session:",this.conversationId))}handleBotReleased(){this.botActive&&(this.botActive=!1,this.emit("bot_released",{}),this.logger.debug("Bot released support session:",this.conversationId))}handleClosed(){if(this.status==="closed")return;let e=this.status;this.status="closed",this.emit("closed",{previousStatus:e}),this.logger.debug("Support session closed:",this.conversationId)}handleReopened(){this.status==="closed"&&(this.status=this.assignedAgentId?"assigned":"queued",this.emit("reopened",{}),this.logger.debug("Support session reopened:",this.conversationId))}sendMessage(e,t="text",s){this.conversation.sendMessage(e,t,s)}async join(){}async leave(){}},lt=class extends k.default{constructor(e,t=!1){super(),this.sessions=new Map,this.getConversationHandler=e,this.debug=t,this.logger=new M(t)}get(e,t="customer"){if(!e||e.length===0)throw new Error("Conversation ID is required");let s=this.sessions.get(e);if(!s){let i=this.getConversationHandler(e);s=new ct(e,i,t,this.debug),s.on("assigned",n=>this.emit("assigned",{conversationId:e,...n})),s.on("unassigned",n=>this.emit("unassigned",{conversationId:e,...n})),s.on("bot_takeover",()=>this.emit("bot_takeover",{conversationId:e})),s.on("bot_released",()=>this.emit("bot_released",{conversationId:e})),s.on("closed",n=>this.emit("closed",{conversationId:e,...n})),s.on("reopened",()=>this.emit("reopened",{conversationId:e})),this.sessions.set(e,s),this.logger.debug("Created support session:",e,t)}return s}has(e){return this.sessions.has(e)}getSession(e){return this.sessions.get(e)}handleSupportEvent(e,t,s){let i=this.sessions.get(e);if(!i){t==="assigned"&&(s!=null&&s.agent_id)?this.logger.debug("Support event for unknown session, will be handled when session is created:",e):this.logger.warn("Support event for unknown session:",e,t);return}switch(t){case"assigned":s!=null&&s.agent_id&&typeof s.agent_id=="string"&&i.handleAssigned(s.agent_id);break;case"unassigned":i.handleUnassigned();break;case"bot_takeover":i.handleBotTakeover();break;case"bot_released":i.handleBotReleased();break;case"closed":i.handleClosed();break;case"reopened":i.handleReopened();break}}remove(e){this.sessions.get(e)&&(this.sessions.delete(e),this.logger.debug("Removed support session:",e))}clear(){this.sessions.clear(),this.logger.debug("Cleared all support sessions")}},Re=class extends k.default{constructor(e){var a,r;super(),this.connectionState="disconnected",this.wsClient=null,this.config={autoReconnect:!0,maxReconnectAttempts:5,reconnectDelay:1e3,debug:!1,apiUrl:"",...e},this.tokenManager=new Xe,this.tokenManager.setToken(e.token),this.logger=new M(this.config.debug),this.wsClient=new Ge(this.config.wsUrl,{autoReconnect:this.config.autoReconnect,maxReconnectAttempts:this.config.maxReconnectAttempts,reconnectDelay:this.config.reconnectDelay,debug:this.config.debug}),this.supportManager=new lt(l=>this.conversationManager.get(l),this.config.debug),this.conversationManager=new at(l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)},this.config.debug,void 0,l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)},l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)}),this.conversationManager.setSendReadHandler(l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)}),this.presenceManager=new ot(this.config.debug),this.presenceManager.on("presence",l=>{this.emit("presence",l)});let t=(a=this.config.apiUrl)==null?void 0:a.trim(),s=(r=this.config.appId)==null?void 0:r.trim(),i=this.config.apiKey&&this.config.apiSecret,n=typeof this.config.getRestHeaders=="function";if(t){let l=async()=>{if(n&&this.config.getRestHeaders)return this.config.getRestHeaders();if(i)return{"X-Api-Key":this.config.apiKey,"X-Api-Secret":this.config.apiSecret,"Content-Type":"application/json"};let c=this.tokenManager.getToken();if(!c)throw new Error("Token required for list() and fetchMessages() when API keys are not set");return{Authorization:`Bearer ${c}`,"Content-Type":"application/json"}};this.conversationManager.setRestFetcher({apiUrl:t,getHeaders:l}),s&&this.conversationManager.setAppId(s)}this.setupWebSocketHandlers()}setupWebSocketHandlers(){this.wsClient&&(this.wsClient.on("connected",()=>{this.connectionState="connected",this.emit("connected"),this.emit("state",this.connectionState),this.logger.debug("SDK connected")}),this.wsClient.on("disconnected",e=>{this.connectionState="disconnected",this.emit("disconnected",e),this.emit("state",this.connectionState),this.logger.debug("SDK disconnected")}),this.wsClient.on("reconnecting",e=>{this.connectionState="reconnecting",this.emit("reconnecting",e),this.emit("state",this.connectionState),this.logger.debug("SDK reconnecting",e)}),this.wsClient.on("message",e=>{if(this.emit("message",e),this.conversationManager.routeIncomingMessage(e),e.type==="presence"){let t=e;t.payload&&this.presenceManager.handlePresence(t.payload.user_id,t.payload.state)}switch(e.type==="support"&&this.conversationManager.routeSupportMessage(e),e.type){case"joined":this.emit("joined",e);break;case"message":this.emit("message",e);break;case"typing":this.emit("typing",e);break;case"receipt":break;case"support":break;case"presence":break;case"error":this.emit("error",e);break}}),this.wsClient.on("error",e=>{this.connectionState="error";let t=e instanceof Error?e.message:"";(e instanceof Error?e.name:"")==="TokenExpiredError"||t&&t.includes("Token expired")?(this.logger.error("Token expired:",t),this.logger.warn("To reconnect, call sdk.updateToken(newToken) then sdk.connect()")):this.logger.error("SDK error:",e);let i=e instanceof Error?e:new Error("WebSocket connection error");this.emit("error",i),this.emit("state",this.connectionState)}))}getState(){return this.connectionState}getConfig(){return{...this.config}}async connect(){if(this.connectionState==="connected"){this.logger.warn("Already connected");return}if(this.connectionState==="connecting"){this.logger.warn("Connection already in progress");return}this.tokenManager.validateTokenPresence();let e=this.tokenManager.getToken();if(!e)throw new Error("Token is required for connection");this.connectionState="connecting",this.emit("state",this.connectionState),this.logger.debug("Connecting...");try{if(!this.wsClient)throw new Error("WebSocket client not initialized");await this.wsClient.connect(e)}catch(t){this.connectionState="error",this.emit("state",this.connectionState);let s=t instanceof Error?t:new Error("Connection failed");throw this.emit("error",s),s}}async disconnect(){this.connectionState!=="disconnected"&&(this.logger.debug("Disconnecting..."),this.conversationManager.clear(),this.presenceManager.clear(),this.supportManager.clear(),this.wsClient&&this.wsClient.disconnect(),this.connectionState="disconnected",this.emit("disconnected"),this.emit("state",this.connectionState))}isConnected(){var e;return this.connectionState==="connected"&&((e=this.wsClient)==null?void 0:e.isConnected())===!0}updateToken(e){if(!e||e.length===0)throw new Error("Token cannot be empty");this.tokenManager.setToken(e),this.logger.debug("Token updated"),this.isConnected()&&(this.logger.warn("Token updated while connected. Disconnecting to reconnect with new token..."),this.disconnect())}get conversations(){return{get:e=>this.conversationManager.get(e),has:e=>this.conversationManager.has(e),join:e=>this.conversationManager.join(e,t=>this.joinConversation(t)),leave:e=>this.conversationManager.leave(e,t=>this.leaveConversation(t)),list:e=>this.conversationManager.list(e)}}get support(){return{get:(e,t="customer")=>this.supportManager.get(e,t),has:e=>this.supportManager.has(e)}}async joinConversation(e){if(!this.isConnected())throw new Error("Must be connected before joining a conversation");if(!e||e.length===0)throw new Error("Conversation ID is required");if(!this.wsClient)throw new Error("WebSocket client not initialized");let t={type:"join",conversation_id:e};try{this.wsClient.send(t),this.logger.debug("Join conversation sent:",e)}catch(s){let i=s instanceof Error?s:new Error("Failed to join conversation");throw this.emit("error",i),i}}async leaveConversation(e){if(!this.isConnected())throw new Error("Must be connected before leaving a conversation");if(!e||e.length===0)throw new Error("Conversation ID is required");if(!this.wsClient)throw new Error("WebSocket client not initialized");let t={type:"leave",conversation_id:e};try{this.wsClient.send(t),this.logger.debug("Leave conversation sent:",e)}catch(s){let i=s instanceof Error?s:new Error("Failed to leave conversation");throw this.emit("error",i),i}}rotateToken(e){this.tokenManager.rotateToken(e),this.logger.debug("Token rotated"),this.isConnected()&&this.logger.warn("Token rotated while connected. Reconnect may be required.")}getTokenManager(){return this.tokenManager}};var ht={wsUrl:"",token:"",conversationId:"",publicKey:"",title:"Support",subtitle:"We typically reply in a few minutes",greeting:"Hi! \u{1F44B} How can we help you today?",placeholder:"Type your message\u2026",primaryColor:"#195EFF",position:"bottom-right",launcher:"\u{1F4AC}",autoOpen:!1,debug:!1,collectContact:!1,requireEmail:!1,contactPrompt:"Let\u2019s get you connected",csat:!0,csatPrompt:"How would you rate the support you received?",csatThanks:"Thanks for the feedback!",calls:!1,callBundleUrl:"https://cdn.jsdelivr.net/npm/@vocantly/widget@0.1.11/dist/vocantly-widget-call.js"},_=null;function Ae(e){try{let t=JSON.parse(atob(e.split(".")[1]));return t.user_id||t.sub||null}catch{return null}}function E(e){return String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Oe(e){return E(e).replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>").replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g,"$1<em>$2</em>").replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\n/g,"<br>")}var q=null;function gt(){try{let e=window.AudioContext||window.webkitAudioContext;if(!e)return;q=q||new e,q.resume&&q.resume();let t=q,s=t.currentTime,i=t.createOscillator(),n=t.createGain();i.type="sine",i.frequency.setValueAtTime(880,s),i.frequency.exponentialRampToValueAtTime(1320,s+.08),n.gain.setValueAtTime(1e-4,s),n.gain.exponentialRampToValueAtTime(.16,s+.02),n.gain.exponentialRampToValueAtTime(1e-4,s+.34),i.connect(n).connect(t.destination),i.start(s),i.stop(s+.36)}catch{}}function pt(e,t){if(document.getElementById("caf-widget-styles"))return;let a=`
2
+ /* Deliberately scoped, high-specificity reset. The widget is injected into arbitrary
3
+ sites, so host button/input rules must never change its geometry. */
4
4
  .caf-root,.caf-root *{box-sizing:border-box;}
5
- .caf-root button{margin:0;padding:0;min-width:0;min-height:0;line-height:normal;text-transform:none;letter-spacing:normal;appearance:none;-webkit-appearance:none;}
6
- .caf-root{position:fixed;bottom:20px;${s};z-index:2147483000;font:14px/1.5 -apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;}
7
- .caf-launcher{width:56px !important;height:56px !important;padding:0 !important;border-radius:50% !important;background:${e};color:#fff;border:none;cursor:pointer;
8
- box-shadow:0 6px 20px rgba(0,0,0,.18);display:flex;align-items:center;justify-content:center;transition:transform .15s;flex:none;}
9
- .caf-launcher svg{width:26px;height:26px;}
10
- .caf-launcher:hover{transform:scale(1.06);}
11
- .caf-callbtn{width:34px !important;height:34px !important;padding:0 !important;border-radius:50% !important;background:rgba(255,255,255,.18);border:0;color:#fff;cursor:pointer;
12
- display:none;align-items:center;justify-content:center;flex:none;}
13
- .caf-callbtn:hover{background:rgba(255,255,255,.3);}
14
- .caf-callbtn svg{width:17px;height:17px;}
15
- .caf-badge{position:absolute;top:-2px;right:-2px;min-width:18px;height:18px;padding:0 5px;border-radius:9px;background:#EF4444;color:#fff;
16
- font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;box-shadow:0 0 0 2px #fff;}
17
- .caf-panel{position:absolute;bottom:72px;${s};width:370px;max-width:calc(100vw - 40px);height:560px;max-height:calc(100vh - 120px);
18
- background:#fff;border-radius:16px;box-shadow:0 12px 40px rgba(0,0,0,.22);display:none;flex-direction:column;overflow:hidden;}
19
- .caf-panel.open{display:flex;}
20
- .caf-header{background:${e};color:#fff;padding:16px 18px;display:flex;align-items:center;gap:10px;}
21
- .caf-header .caf-t{font-weight:700;font-size:15px;}
22
- .caf-header .caf-s{font-size:12px;opacity:.85;}
23
- .caf-dot{width:8px;height:8px;border-radius:50%;background:#9CA3AF;flex:none;}
24
- .caf-dot.on{background:#34D399;} .caf-dot.err{background:#F87171;}
25
- .caf-x{margin-left:auto;background:transparent;border:none;color:#fff;font-size:18px;cursor:pointer;opacity:.85;}
26
- .caf-x:hover{opacity:1;}
27
- .caf-msgs{flex:1;overflow-y:auto;padding:16px;background:#F7F8FA;display:flex;flex-direction:column;gap:8px;}
28
- .caf-m{max-width:82%;padding:9px 12px;border-radius:14px;white-space:normal;word-wrap:break-word;}
29
- .caf-m code{background:rgba(0,0,0,.06);padding:1px 4px;border-radius:4px;font-size:12px;}
30
- .caf-self{align-self:flex-end;background:${e};color:#fff;border-bottom-right-radius:4px;}
31
- .caf-other{align-self:flex-start;background:#fff;color:#111827;border:1px solid #E5E7EB;border-bottom-left-radius:4px;}
32
- .caf-sys{align-self:center;background:#FEF3C7;color:#92400E;font-size:12px;text-align:center;max-width:92%;}
33
- .caf-note{align-self:center;background:transparent;color:#6B7280;font-size:12px;line-height:1.45;text-align:center;max-width:92%;padding:2px 8px;}
34
- .caf-csat{align-self:center;text-align:center;color:#6B7280;font-size:12px;padding:2px 8px 6px;}
35
- .caf-csat-row{display:flex;gap:4px;justify-content:center;margin-top:6px;}
36
- .caf-csat-b{border:1px solid #E5E7EB;background:#fff;border-radius:8px;padding:3px 7px;font-size:16px;line-height:1.2;cursor:pointer;}
37
- .caf-csat-b:hover{border-color:${e};background:#F9FAFB;}
38
- .caf-composer{border-top:1px solid #E5E7EB;padding:10px;display:flex;gap:8px;align-items:flex-end;background:#fff;}
39
- .caf-input{flex:1;resize:none;max-height:96px;border:1px solid #E5E7EB;border-radius:10px;padding:9px 11px;font:inherit;outline:none;}
40
- .caf-input:focus{border-color:${e};}
41
- .caf-send{width:40px !important;height:40px !important;padding:0 !important;flex:none;background:${e};color:#fff;border:none;border-radius:50% !important;
42
- display:flex;align-items:center;justify-content:center;cursor:pointer;transition:transform .12s ease,opacity .12s ease;box-shadow:0 2px 6px rgba(0,0,0,.12);}
43
- .caf-send svg{width:18px;height:18px;transform:translateX(1px);}
44
- .caf-send:hover{transform:scale(1.06);}
45
- .caf-send:active{transform:scale(.96);}
46
- .caf-send:disabled{opacity:.4;cursor:not-allowed;transform:none;box-shadow:none;}
47
- .caf-foot{text-align:center;font-size:10px;color:#9CA3AF;padding:6px;background:#fff;}
48
- .caf-form{background:#fff;border:1px solid #E5E7EB;border-radius:12px;padding:14px;margin:2px 0;display:flex;flex-direction:column;gap:8px;}
49
- .caf-form-t{font-weight:600;font-size:13px;color:#111827;}
50
- .caf-fi{border:1px solid #E5E7EB;border-radius:10px;padding:10px 12px;font:inherit;font-size:13px;outline:none;transition:border-color .12s ease;}
51
- .caf-fi:focus{border-color:${e};}
52
- .caf-ferr{color:#DC2626;font-size:12px;min-height:0;}
53
- .caf-fbtn{width:100%;background:${e};color:#fff;border:none;border-radius:10px;padding:12px 14px;font-size:14px;font-weight:600;cursor:pointer;
54
- margin-top:2px;transition:opacity .12s ease,transform .12s ease;box-shadow:0 2px 8px ${e}33;}
55
- .caf-fbtn:hover{opacity:.94;}
56
- .caf-fbtn:active{transform:scale(.99);}
57
- .caf-fskip{align-self:center;background:transparent;border:none;color:#9CA3AF;font-size:12.5px;cursor:pointer;padding:4px 8px;margin-top:-2px;}
58
- .caf-fskip:hover{color:#4B5563;text-decoration:underline;text-underline-offset:2px;}
59
- `,i=document.createElement("style");i.id="caf-widget-styles",i.textContent=n,document.head.appendChild(i)}function ft(e){let t=document.createElement("div");return t.className="caf-root",t.innerHTML=`
60
- <div class="caf-panel" role="dialog" aria-label="${E(e.title)}">
5
+ .caf-root{--caf-primary:${e};position:fixed;bottom:20px;${t==="bottom-left"?"left:20px":"right:20px"};z-index:2147483000;color:#172033;
6
+ font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;}
7
+ .caf-root button,.caf-root input,.caf-root textarea{font:inherit!important;text-transform:none!important;letter-spacing:normal!important;}
8
+ .caf-root button{margin:0!important;min-width:0;min-height:0;appearance:none;-webkit-appearance:none;}
9
+ .caf-root button:focus-visible{outline:3px solid rgba(25,94,255,.22);outline-offset:2px;}
10
+
11
+ .caf-root .caf-launcher{position:relative;width:60px!important;height:60px!important;padding:0!important;border:0!important;border-radius:20px!important;
12
+ background:radial-gradient(circle at 74% 18%,rgba(255,255,255,.25),transparent 34%),${e}!important;color:#fff!important;cursor:pointer;
13
+ box-shadow:0 16px 34px rgba(20,38,85,.24),0 4px 12px rgba(20,38,85,.14);display:flex!important;align-items:center;justify-content:center;
14
+ transition:transform .18s ease,box-shadow .18s ease;flex:none;}
15
+ .caf-root .caf-launcher svg{width:27px;height:27px;}
16
+ .caf-root .caf-launcher:hover{transform:translateY(-2px);box-shadow:0 20px 40px rgba(20,38,85,.28),0 6px 14px rgba(20,38,85,.16);}
17
+ .caf-root .caf-launcher:active{transform:translateY(0) scale(.97);}
18
+ .caf-badge{position:absolute;top:-5px;right:-5px;min-width:20px;height:20px;padding:0 6px;border-radius:10px;background:#ef4444;color:#fff;
19
+ font-size:11px;font-weight:800;display:flex;align-items:center;justify-content:center;box-shadow:0 0 0 3px #fff;}
20
+
21
+ .caf-panel{position:absolute;bottom:76px;${t==="bottom-left"?"left:0":"right:0"};width:392px;max-width:calc(100vw - 40px);height:620px;max-height:calc(100vh - 112px);
22
+ background:#fff;border:1px solid rgba(24,34,55,.08);border-radius:24px;box-shadow:0 28px 80px rgba(18,29,54,.22),0 8px 24px rgba(18,29,54,.12);
23
+ display:flex;flex-direction:column;overflow:hidden;opacity:0;visibility:hidden;pointer-events:none;transform:translateY(16px) scale(.975);
24
+ transform-origin:${t==="bottom-left"?"bottom left":"bottom right"};transition:opacity .18s ease,transform .22s cubic-bezier(.2,.8,.2,1),visibility .18s;}
25
+ .caf-panel.open{opacity:1;visibility:visible;pointer-events:auto;transform:translateY(0) scale(1);}
26
+
27
+ .caf-header{position:relative;min-height:92px;background:radial-gradient(circle at 92% -30%,rgba(255,255,255,.3),transparent 48%),${e};
28
+ color:#fff;padding:18px;display:flex;align-items:center;gap:12px;overflow:hidden;}
29
+ .caf-headmark{position:relative;width:46px;height:46px;border:1px solid rgba(255,255,255,.24);border-radius:15px;background:rgba(255,255,255,.16);
30
+ display:flex;align-items:center;justify-content:center;flex:none;box-shadow:inset 0 1px 0 rgba(255,255,255,.18);}
31
+ .caf-headmark svg{width:23px;height:23px;}
32
+ .caf-headcopy{flex:1;min-width:0;}
33
+ .caf-header .caf-t{font-weight:750;font-size:16px;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
34
+ .caf-header .caf-s{margin-top:2px;font-size:12.5px;line-height:1.35;color:rgba(255,255,255,.82);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
35
+ .caf-dot{position:absolute;right:-2px;bottom:-2px;width:12px;height:12px;border:2px solid ${e};border-radius:50%;background:#a7b0c0;}
36
+ .caf-dot.on{background:#34d399;} .caf-dot.err{background:#fb7185;}
37
+ .caf-root .caf-callbtn,.caf-root .caf-x{width:36px!important;height:36px!important;padding:0!important;border:1px solid rgba(255,255,255,.16)!important;
38
+ border-radius:12px!important;background:rgba(255,255,255,.12)!important;color:#fff!important;cursor:pointer;align-items:center;justify-content:center;flex:none;
39
+ transition:background .15s ease,transform .15s ease;}
40
+ .caf-root .caf-callbtn{display:none;}
41
+ .caf-root .caf-x{display:flex!important;}
42
+ .caf-root .caf-callbtn:hover,.caf-root .caf-x:hover{background:rgba(255,255,255,.22)!important;transform:translateY(-1px);}
43
+ .caf-callbtn svg{width:17px;height:17px;}.caf-x svg{width:18px;height:18px;}
44
+
45
+ .caf-msgs{flex:1;min-height:0;overflow-y:auto;padding:18px;background:linear-gradient(180deg,#f7f9fc 0%,#f4f6fa 100%);
46
+ display:flex;flex-direction:column;gap:9px;scrollbar-width:thin;scrollbar-color:#cbd3df transparent;}
47
+ .caf-msgs::-webkit-scrollbar{width:6px}.caf-msgs::-webkit-scrollbar-thumb{background:#cbd3df;border-radius:999px}.caf-msgs::-webkit-scrollbar-track{background:transparent}
48
+ .caf-m{max-width:84%;padding:10px 13px;border-radius:16px;white-space:normal;overflow-wrap:anywhere;font-size:13.5px;line-height:1.55;}
49
+ .caf-m code{background:rgba(0,0,0,.06);padding:2px 5px;border-radius:5px;font-size:12px;}
50
+ .caf-self{align-self:flex-end;background:${e};color:#fff;border-bottom-right-radius:5px;box-shadow:0 5px 14px rgba(20,38,85,.12);}
51
+ .caf-other{align-self:flex-start;background:#fff;color:#172033;border:1px solid #e5e9f0;border-bottom-left-radius:5px;box-shadow:0 4px 14px rgba(20,38,85,.05);}
52
+ .caf-sys{align-self:center;max-width:94%;background:#eaf0ff;color:#33446b;border:1px solid #dce6ff;font-size:12.5px;text-align:center;border-radius:12px;}
53
+ .caf-note{align-self:center;background:transparent;color:#718096;font-size:12px;line-height:1.45;text-align:center;max-width:92%;padding:4px 8px;}
54
+ .caf-csat{align-self:center;text-align:center;color:#667085;font-size:12px;padding:8px 10px;}
55
+ .caf-csat-row{display:flex;gap:5px;justify-content:center;margin-top:8px;}
56
+ .caf-root .caf-csat-b{border:1px solid #e1e6ee!important;background:#fff!important;border-radius:10px!important;padding:5px 8px!important;font-size:17px!important;line-height:1.2!important;cursor:pointer;}
57
+ .caf-root .caf-csat-b:hover{border-color:${e}!important;background:#f8faff!important;transform:translateY(-1px);}
58
+
59
+ .caf-composer{border-top:1px solid #e8ecf2;padding:12px 14px;display:flex;gap:9px;align-items:flex-end;background:#fff;}
60
+ .caf-input{flex:1;resize:none;min-height:44px;max-height:100px;border:1px solid transparent!important;border-radius:14px!important;background:#f5f7fa!important;
61
+ color:#172033!important;padding:11px 13px!important;font-size:13.5px!important;line-height:20px!important;outline:none;box-shadow:inset 0 0 0 1px #e7ebf1;transition:background .15s,border-color .15s,box-shadow .15s;}
62
+ .caf-input::placeholder{color:#98a2b3;opacity:1;}
63
+ .caf-input:focus{background:#fff!important;border-color:${e}!important;box-shadow:0 0 0 3px rgba(25,94,255,.1);}
64
+ .caf-root .caf-send{width:44px!important;height:44px!important;padding:0!important;flex:none;background:${e}!important;color:#fff!important;border:0!important;border-radius:14px!important;
65
+ display:flex!important;align-items:center;justify-content:center;cursor:pointer;transition:transform .14s ease,opacity .14s ease,box-shadow .14s ease;box-shadow:0 7px 16px rgba(20,38,85,.18);}
66
+ .caf-send svg{width:19px;height:19px;transform:translateX(1px);}
67
+ .caf-root .caf-send:hover{transform:translateY(-1px);box-shadow:0 9px 20px rgba(20,38,85,.22);}
68
+ .caf-root .caf-send:active{transform:scale(.96);}
69
+ .caf-root .caf-send:disabled{opacity:.38;cursor:not-allowed;transform:none;box-shadow:none;}
70
+ .caf-foot{text-align:center;font-size:10.5px;line-height:1.3;color:#98a2b3;padding:7px 10px 9px;background:#fff;letter-spacing:.01em;}
71
+
72
+ .caf-panel.caf-prechat .caf-msgs{justify-content:center;padding:22px;background:linear-gradient(160deg,#f7f9ff 0%,#f5f7fb 58%,#fff 100%);}
73
+ .caf-panel.caf-prechat .caf-composer{display:none;}
74
+ .caf-form{width:100%;background:#fff;border:1px solid #e1e6ee;border-radius:20px;padding:22px;margin:0;display:flex;flex-direction:column;
75
+ box-shadow:0 14px 36px rgba(24,34,55,.08);}
76
+ .caf-form-icon{width:46px;height:46px;border-radius:14px;background:#eaf0ff;color:${e};display:flex;align-items:center;justify-content:center;margin-bottom:16px;}
77
+ .caf-form-icon svg{width:23px;height:23px;}
78
+ .caf-form-t{font-weight:750;font-size:18px;line-height:1.32;color:#172033;}
79
+ .caf-form-d{margin:7px 0 18px;color:#667085;font-size:12.5px;line-height:1.5;}
80
+ .caf-fl{display:block;margin:0 0 7px;color:#344054;font-size:12px;font-weight:700;}
81
+ .caf-fl+.caf-fl{margin-top:14px;}
82
+ .caf-fi{display:block;width:100%;height:48px;border:1px solid #dce1e9!important;border-radius:13px!important;background:#fff!important;color:#172033!important;
83
+ padding:0 13px!important;font-size:13.5px!important;line-height:48px!important;outline:none;box-shadow:0 1px 2px rgba(16,24,40,.03);transition:border-color .15s,box-shadow .15s;}
84
+ .caf-fi::placeholder{color:#98a2b3;opacity:1;}
85
+ .caf-fi:focus{border-color:${e}!important;box-shadow:0 0 0 3px rgba(25,94,255,.1);}
86
+ .caf-ferr{color:#d92d20;font-size:12px;line-height:1.4;min-height:17px;margin-top:6px;}
87
+ .caf-root .caf-fbtn{width:100%!important;height:48px!important;background:${e}!important;color:#fff!important;border:0!important;border-radius:13px!important;
88
+ padding:0 16px!important;font-size:14px!important;font-weight:750!important;cursor:pointer;display:flex!important;align-items:center;justify-content:center;gap:8px;
89
+ margin-top:6px!important;transition:transform .14s ease,box-shadow .14s ease,opacity .14s ease;box-shadow:0 9px 20px rgba(20,38,85,.18);}
90
+ .caf-fbtn svg{width:17px;height:17px;transition:transform .14s ease;}
91
+ .caf-root .caf-fbtn:hover{transform:translateY(-1px);box-shadow:0 12px 24px rgba(20,38,85,.22);}
92
+ .caf-root .caf-fbtn:hover svg{transform:translateX(2px);}
93
+ .caf-root .caf-fbtn:active{transform:scale(.99);}
94
+ .caf-root .caf-fbtn:disabled{opacity:.62;cursor:wait;transform:none;}
95
+ .caf-root .caf-fskip{align-self:center;background:transparent!important;border:0!important;color:#667085!important;font-size:12.5px!important;font-weight:650!important;
96
+ cursor:pointer;padding:8px 10px!important;margin:4px 0 0!important;}
97
+ .caf-root .caf-fskip:hover{color:#172033!important;text-decoration:underline;text-underline-offset:3px;}
98
+ .caf-form-privacy{margin:2px 0 0;text-align:center;color:#98a2b3;font-size:10.5px;line-height:1.4;}
99
+
100
+ @media (max-width:480px){
101
+ .caf-root{bottom:12px;${t==="bottom-left"?"left:12px":"right:12px"};}
102
+ .caf-panel{position:fixed;left:12px!important;right:12px!important;bottom:84px;width:auto;max-width:none;height:min(650px,calc(100dvh - 100px));max-height:none;border-radius:22px;}
103
+ .caf-root .caf-launcher{width:56px!important;height:56px!important;border-radius:18px!important;}
104
+ .caf-header{min-height:86px;padding:16px;}.caf-headmark{width:42px;height:42px;border-radius:13px;}
105
+ .caf-panel.caf-prechat .caf-msgs{padding:16px}.caf-form{padding:20px;border-radius:18px;}
106
+ }
107
+ @media (max-height:620px) and (min-width:481px){.caf-panel{height:calc(100vh - 96px);bottom:70px}.caf-header{min-height:82px}.caf-form{padding:18px}.caf-form-d{margin-bottom:13px}}
108
+ @media (prefers-reduced-motion:reduce){.caf-panel,.caf-launcher,.caf-send,.caf-fbtn{transition:none!important;}}
109
+ `,r=document.createElement("style");r.id="caf-widget-styles",r.textContent=a,document.head.appendChild(r)}function ut(e){let t=document.createElement("div");return t.className="caf-root",t.innerHTML=`
110
+ <div class="caf-panel" id="caf-support-panel" role="dialog" aria-labelledby="caf-support-title" aria-hidden="true">
61
111
  <div class="caf-header">
62
- <span class="caf-dot" data-caf="dot"></span>
63
- <div style="flex:1;min-width:0"><div class="caf-t">${E(e.title)}</div><div class="caf-s">${E(e.subtitle)}</div></div>
112
+ <div class="caf-headmark" aria-hidden="true">
113
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.4 8.4 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.4 8.4 0 0 1-3.8-.9L3 21l1.9-5.7a8.4 8.4 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.4 8.4 0 0 1 3.8-.9h.5a8.5 8.5 0 0 1 8 8v.5Z"/></svg>
114
+ <span class="caf-dot" data-caf="dot"></span>
115
+ </div>
116
+ <div class="caf-headcopy"><div class="caf-t" id="caf-support-title">${E(e.title)}</div><div class="caf-s">${E(e.subtitle)}</div></div>
64
117
  <button class="caf-callbtn" data-caf="call" title="Start a voice call" aria-label="Start a voice call">
65
118
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.9.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
66
119
  </button>
67
- <button class="caf-x" data-caf="close" aria-label="Close">\u2715</button>
120
+ <button class="caf-x" data-caf="close" aria-label="Close support chat" title="Close">
121
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="m7 7 10 10M17 7 7 17"/></svg>
122
+ </button>
68
123
  </div>
69
124
  <div class="caf-msgs" data-caf="msgs"></div>
70
125
  <div class="caf-composer">
@@ -73,13 +128,22 @@ var Fe=Object.create;var be=Object.defineProperty;var Pe=Object.getOwnPropertyDe
73
128
  </div>
74
129
  <div class="caf-foot">powered by Vocantly</div>
75
130
  </div>
76
- <button class="caf-launcher" data-caf="launcher" aria-label="Open chat">
131
+ <button class="caf-launcher" data-caf="launcher" aria-label="Open support chat" aria-controls="caf-support-panel" aria-expanded="false">
77
132
  <span data-caf="launcher-icon">${e.launcher==="\u{1F4AC}"?'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>':E(e.launcher)}</span>
78
133
  <span class="caf-badge" data-caf="badge" style="display:none">0</span>
79
- </button>`,document.body.appendChild(t),{root:t,$:n=>t.querySelector(`[data-caf="${n}"]`)}}function Kt(e){if(T)return T.open(),T.api;let t={...ht,...e||{}};if(!t.wsUrl)throw new Error("VocantlyWidget.init requires wsUrl");if(!t.publicKey&&!(t.token&&t.conversationId))throw new Error("VocantlyWidget.init requires either { publicKey } (anonymous) or { token, conversationId }");if(t.publicKey&&!t.apiUrl)throw new Error("VocantlyWidget.init with publicKey also requires apiUrl");ut(t.primaryColor,t.position);let{root:s,$:n}=ft(t),i={panel:s.querySelector(".caf-panel"),msgs:n("msgs"),input:n("input"),send:n("send"),dot:n("dot"),badge:n("badge"),launcher:n("launcher"),call:n("call")},a=null,r=!1,l=()=>t.calls===!0||r;function c(){a&&D&&i.call&&(i.call.style.display="flex")}function h(){return new Promise((o,u)=>{if(window.VocantlyWidgetCall)return o();let g=document.createElement("script");g.src=t.callBundleUrl,g.async=!0,g.onload=()=>o(),g.onerror=()=>u(new Error("call bundle load failed")),document.head.appendChild(g)})}async function f(){if(!(a||!O))try{if(await h(),!window.VocantlyWidgetCall)return;let o=document.createElement("div");document.body.appendChild(o),a=window.VocantlyWidgetCall.mount(o,O,{logoText:"CA",sessionTitle:t.title||"Call",sessionSubtitle:t.subtitle||"Secured via Vocantly",primaryColor:t.primaryColor,self:{name:"You",role:"Visitor"},peer:{name:t.title||"Support",role:"Agent"},voiceOnly:!0,simple:!0}),i.call&&(i.call.onclick=()=>a.startCall("voice")),c()}catch(o){t.debug&&console.error("[caf] call setup failed",o)}}let d=t.token||"",y=t.conversationId||"",m=d?Ae(d):null,v=null,R=new Set,H=!1,A=!1,ae=0,oe=!1,D=!t.collectContact,ce=!1,U=o=>{i.dot.className="caf-dot"+(o?" "+o:"")},Y=()=>{i.msgs.scrollTop=i.msgs.scrollHeight},le=o=>{ae=o,i.badge.textContent=String(o),i.badge.style.display=o>0?"flex":"none"};function X(o,u){let g=document.createElement("div");g.className="caf-m "+o,g.innerHTML=u,i.msgs.appendChild(g),Y()}function de(o){var I,_,z,me,we,ve;if(o.optimistic)return;let u=(I=o.id)!=null?I:o.message_id,g=(z=(_=o.senderId)!=null?_:o.sender_id)!=null?z:"",p=(we=(me=o.content)!=null?me:o.text)!=null?we:"",w=u||`${g}:${p}`;if(R.has(w))return;if(R.add(w),((ve=o.messageType)!=null?ve:o.message_type)==="system"){X("caf-note",Oe(p)),t.csat&&Ie();return}let x=m&&g===m;X(x?"caf-self":"caf-other",Oe(p)),!x&&H&&gt(),!x&&!A&&le(ae+1)}let he=!1;function Ie(){if(he||!t.apiUrl||!y)return;he=!0;let o=document.createElement("div");o.className="caf-csat",o.innerHTML=`<div>${E(t.csatPrompt)}</div><div class="caf-csat-row"></div>`;let u=o.querySelector(".caf-csat-row");[["1","\u{1F61E}"],["2","\u{1F641}"],["3","\u{1F610}"],["4","\u{1F642}"],["5","\u{1F600}"]].forEach(([g,p])=>{let w=document.createElement("button");w.className="caf-csat-b",w.type="button",w.textContent=p,w.setAttribute("aria-label",g+" out of 5"),w.onclick=()=>je(Number(g),o),u.appendChild(w)}),i.msgs.appendChild(o),Y()}async function je(o,u){u.innerHTML=`<div>${E(t.csatThanks)}</div>`,Y();try{await fetch(t.apiUrl.replace(/\/$/,"")+"/sdk/conversations/"+y+"/rating",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer "+d},body:JSON.stringify({rating:o})})}catch(g){t.debug&&console.error("[caf] csat failed",g)}}function ge(o){i.input.disabled=!o,i.send.disabled=!o,i.input.placeholder=o?t.placeholder:"\u2026"}function ue(){!oe&&t.greeting&&(X("caf-sys",E(t.greeting)),oe=!0)}async function He(o,u){if(t.apiUrl)try{if(await J(),!d)return;let g=await fetch(t.apiUrl.replace(/\/$/,"")+"/sdk/customers/me",{method:"PATCH",headers:{"Content-Type":"application/json",Authorization:"Bearer "+d},body:JSON.stringify({name:o||void 0,email:u||void 0})});!g.ok&&t.debug&&console.error("[caf] saveContact HTTP "+g.status)}catch(g){t.debug&&console.error("[caf] saveContact failed",g)}}function De(){ce=!0;let o=document.createElement("div");o.className="caf-form",o.innerHTML=`
134
+ </button>`,document.body.appendChild(t),{root:t,$:i=>t.querySelector(`[data-caf="${i}"]`)}}function Kt(e){if(_)return _.open(),_.api;let t={...ht,...e||{}};if(!t.wsUrl)throw new Error("VocantlyWidget.init requires wsUrl");if(!t.publicKey&&!(t.token&&t.conversationId))throw new Error("VocantlyWidget.init requires either { publicKey } (anonymous) or { token, conversationId }");if(t.publicKey&&!t.apiUrl)throw new Error("VocantlyWidget.init with publicKey also requires apiUrl");pt(t.primaryColor,t.position);let{root:s,$:i}=ut(t),n={panel:s.querySelector(".caf-panel"),msgs:i("msgs"),input:i("input"),send:i("send"),dot:i("dot"),badge:i("badge"),launcher:i("launcher"),call:i("call")},a=null,r=!1,l=()=>t.calls===!0||r;function c(){a&&L&&n.call&&(n.call.style.display="flex")}function h(){return new Promise((o,p)=>{if(window.VocantlyWidgetCall)return o();let g=document.createElement("script");g.src=t.callBundleUrl,g.async=!0,g.onload=()=>o(),g.onerror=()=>p(new Error("call bundle load failed")),document.head.appendChild(g)})}async function u(){if(!(a||!O))try{if(await h(),!window.VocantlyWidgetCall)return;let o=document.createElement("div");document.body.appendChild(o),a=window.VocantlyWidgetCall.mount(o,O,{logoText:"CA",sessionTitle:t.title||"Call",sessionSubtitle:t.subtitle||"Secured via Vocantly",primaryColor:t.primaryColor,self:{name:"You",role:"Visitor"},peer:{name:t.title||"Support",role:"Agent"},voiceOnly:!0,simple:!0}),n.call&&(n.call.onclick=()=>a.startCall("voice")),c()}catch(o){t.debug&&console.error("[caf] call setup failed",o)}}let d=t.token||"",y=t.conversationId||"",m=d?Ae(d):null,b=null,R=new Set,D=!1,A=!1,ae=0,oe=!1,L=!t.collectContact,ce=!1,U=o=>{n.dot.className="caf-dot"+(o?" "+o:"")},G=()=>{n.msgs.scrollTop=n.msgs.scrollHeight},le=o=>{ae=o,n.badge.textContent=String(o),n.badge.style.display=o>0?"flex":"none"};function X(o,p){let g=document.createElement("div");g.className="caf-m "+o,g.innerHTML=p,n.msgs.appendChild(g),G()}function de(o){var j,C,I,me,we,be;if(o.optimistic)return;let p=(j=o.id)!=null?j:o.message_id,g=(I=(C=o.senderId)!=null?C:o.sender_id)!=null?I:"",f=(we=(me=o.content)!=null?me:o.text)!=null?we:"",w=p||`${g}:${f}`;if(R.has(w))return;if(R.add(w),((be=o.messageType)!=null?be:o.message_type)==="system"){X("caf-note",Oe(f)),t.csat&&je();return}let T=m&&g===m;X(T?"caf-self":"caf-other",Oe(f)),!T&&D&&gt(),!T&&!A&&le(ae+1)}let he=!1;function je(){if(he||!t.apiUrl||!y)return;he=!0;let o=document.createElement("div");o.className="caf-csat",o.innerHTML=`<div>${E(t.csatPrompt)}</div><div class="caf-csat-row"></div>`;let p=o.querySelector(".caf-csat-row");[["1","\u{1F61E}"],["2","\u{1F641}"],["3","\u{1F610}"],["4","\u{1F642}"],["5","\u{1F600}"]].forEach(([g,f])=>{let w=document.createElement("button");w.className="caf-csat-b",w.type="button",w.textContent=f,w.setAttribute("aria-label",g+" out of 5"),w.onclick=()=>Ie(Number(g),o),p.appendChild(w)}),n.msgs.appendChild(o),G()}async function Ie(o,p){p.innerHTML=`<div>${E(t.csatThanks)}</div>`,G();try{await fetch(t.apiUrl.replace(/\/$/,"")+"/sdk/conversations/"+y+"/rating",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer "+d},body:JSON.stringify({rating:o})})}catch(g){t.debug&&console.error("[caf] csat failed",g)}}function ge(o){n.input.disabled=!o,n.send.disabled=!o,n.input.placeholder=o?t.placeholder:"\u2026"}function pe(){!oe&&t.greeting&&(X("caf-sys",E(t.greeting)),oe=!0)}async function He(o,p){if(t.apiUrl)try{if(await B(),!d)return;let g=await fetch(t.apiUrl.replace(/\/$/,"")+"/sdk/customers/me",{method:"PATCH",headers:{"Content-Type":"application/json",Authorization:"Bearer "+d},body:JSON.stringify({name:o||void 0,email:p||void 0})});!g.ok&&t.debug&&console.error("[caf] saveContact HTTP "+g.status)}catch(g){t.debug&&console.error("[caf] saveContact failed",g)}}function De(){ce=!0,n.panel.classList.add("caf-prechat");let o=document.createElement("form");o.className="caf-form",o.noValidate=!0,o.innerHTML=`
135
+ <div class="caf-form-icon" aria-hidden="true">
136
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M20 15a4 4 0 0 1-4 4H8l-5 3v-7a4 4 0 0 1-1-2.65V7a4 4 0 0 1 4-4h10a4 4 0 0 1 4 4v8Z"/><path d="M7 9h10M7 13h6"/></svg>
137
+ </div>
80
138
  <div class="caf-form-t">${E(t.contactPrompt)}</div>
81
- <input class="caf-fi" data-caf="f-name" placeholder="Your name" />
82
- <input class="caf-fi" data-caf="f-email" type="email" placeholder="Email${t.requireEmail?"":" (optional)"}" />
83
- <div class="caf-ferr" data-caf="f-err"></div>
84
- <button class="caf-fbtn" data-caf="f-go">Start chat</button>
85
- ${t.requireEmail?"":'<button class="caf-fskip" data-caf="f-skip">Skip</button>'}`,i.msgs.appendChild(o);let u=x=>o.querySelector(`[data-caf="${x}"]`),g=/^[^\s@]+@[^\s@]+\.[^\s@]+$/,p=async x=>{let I=u("f-name").value.trim(),_=u("f-email").value.trim();if(x){if(t.requireEmail&&!_){u("f-err").textContent="Email is required.";return}if(_&&!g.test(_)){u("f-err").textContent="Enter a valid email.";return}await He(I,_)}o.remove(),D=!0,c(),ge(!0),ue(),setTimeout(()=>i.input.focus(),30)};u("f-go").onclick=()=>p(!0);let w=u("f-skip");w&&(w.onclick=()=>p(!1)),setTimeout(()=>u("f-name").focus(),40)}function q(){if(A=!0,i.panel.classList.add("open"),le(0),J(),!D&&!ce){De();return}D&&(ue(),setTimeout(()=>i.input.focus(),50))}function B(){A=!1,i.panel.classList.remove("open")}i.launcher.onclick=()=>A?B():q(),n("close").onclick=B;let O=null;function Z(){if(!D)return;let o=i.input.value.trim();if(!(!o||!O))try{O.sendMessage(o),i.input.value="",i.input.style.height="auto"}catch(u){t.debug&&console.error("[caf] send failed",u)}}i.send.onclick=Z,ge(D),i.input.addEventListener("input",()=>{i.input.style.height="auto",i.input.style.height=Math.min(i.input.scrollHeight,96)+"px"}),i.input.addEventListener("keydown",o=>{o.key==="Enter"&&!o.shiftKey&&(o.preventDefault(),Z())});let ee=t.publicKey?"caf_session_"+t.publicKey.slice(-10):"",Ue=t.publicKey?"caf_vid_"+t.publicKey.slice(-10):"";function fe(){if(!ee)return null;try{let o=JSON.parse(localStorage.getItem(ee)||"null");if(o&&o.token&&o.conversationId&&o.exp>Date.now())return o}catch{}return null}async function Le(){let o=t.apiUrl.replace(/\/$/,""),u=ee,g=Ue,p=fe();if(p)return r=!!p.calls_enabled,{token:p.token,conversationId:p.conversationId};let w=null;try{w=localStorage.getItem(g)}catch{}if(!w){w="v_"+Math.random().toString(36).slice(2)+Date.now().toString(36);try{localStorage.setItem(g,w)}catch{}}let x=await fetch(o+"/sdk/anonymous-session",{method:"POST",headers:{"Content-Type":"application/json","X-Api-Key":t.publicKey},body:JSON.stringify({visitor_id:w})});if(!x.ok)throw new Error("anonymous-session HTTP "+x.status);let I=await x.json(),_=I.data||I;r=!!_.calls_enabled;let z=Date.now()+Math.max(0,(_.expires_in||3600)*1e3-6e4);try{localStorage.setItem(u,JSON.stringify({token:_.token,conversationId:_.conversation_id,exp:z,calls_enabled:r}))}catch{}return{token:_.token,conversationId:_.conversation_id}}let F=null;function J(){return F||(F=(async()=>{var o,u,g;try{if(U(""),t.publicKey){let p=await Le();d=p.token,y=p.conversationId,m=Ae(d)}if(v=new Re({wsUrl:t.wsUrl,token:d,apiUrl:t.apiUrl,autoReconnect:!0,debug:t.debug}),v.on("connected",()=>U("on")),v.on("disconnected",()=>U("")),v.on("reconnecting",()=>U("")),v.on("error",()=>U("err")),await v.connect(),await v.conversations.join(y),O=v.conversations.get(y),O.on("message",de),t.apiUrl&&typeof O.fetchMessages=="function")try{let p=await O.fetchMessages({limit:30});((g=(u=p==null?void 0:p.messages)!=null?u:(o=p==null?void 0:p.data)==null?void 0:o.messages)!=null?g:[]).forEach(de)}catch{}H=!0,l()&&f()}catch(p){U("err"),F=null,t.debug&&console.error("[caf] connect/provision failed",p)}})(),F)}(!t.publicKey||fe())&&J(),t.autoOpen&&q();let pe={open:q,close:B,send:async o=>{await J(),i.input.value=o,Z()},destroy:()=>{try{v==null||v.disconnect()}catch{}s.remove(),T=null}};return T={open:q,close:B,api:pe},pe}function Qt(){T==null||T.open()}function Gt(){T==null||T.close()}function Yt(){T==null||T.api.destroy()}export{Gt as close,Yt as destroy,Kt as init,Qt as open};
139
+ <p class="caf-form-d">Share a few details so our team can give you more personal support.</p>
140
+ <label class="caf-fl" for="caf-contact-name">Name
141
+ <input class="caf-fi" id="caf-contact-name" data-caf="f-name" name="name" autocomplete="name" placeholder="How should we address you?" />
142
+ </label>
143
+ <label class="caf-fl" for="caf-contact-email">Email${t.requireEmail?"":" (optional)"}
144
+ <input class="caf-fi" id="caf-contact-email" data-caf="f-email" name="email" autocomplete="email" inputmode="email" type="email" placeholder="you@company.com" />
145
+ </label>
146
+ <div class="caf-ferr" data-caf="f-err" role="alert" aria-live="polite"></div>
147
+ <button class="caf-fbtn" data-caf="f-go" type="submit"><span>Start conversation</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button>
148
+ ${t.requireEmail?"":'<button class="caf-fskip" data-caf="f-skip" type="button">Continue without details</button>'}
149
+ <p class="caf-form-privacy">Your details are only used to support this conversation.</p>`,n.msgs.appendChild(o);let p=T=>o.querySelector(`[data-caf="${T}"]`),g=/^[^\s@]+@[^\s@]+\.[^\s@]+$/,f=async T=>{let j=p("f-name").value.trim(),C=p("f-email").value.trim();if(p("f-err").textContent="",T){if(t.requireEmail&&!C){p("f-err").textContent="Email is required.";return}if(C&&!g.test(C)){p("f-err").textContent="Enter a valid email.";return}let I=p("f-go");I.disabled=!0,I.querySelector("span").textContent="Starting\u2026",await He(j,C)}o.remove(),n.panel.classList.remove("caf-prechat"),L=!0,c(),ge(!0),pe(),setTimeout(()=>n.input.focus(),30)};o.onsubmit=T=>{T.preventDefault(),f(!0)};let w=p("f-skip");w&&(w.onclick=()=>f(!1)),setTimeout(()=>p("f-name").focus(),40)}function z(){if(A=!0,n.panel.classList.add("open"),n.panel.setAttribute("aria-hidden","false"),n.launcher.setAttribute("aria-expanded","true"),n.launcher.setAttribute("aria-label","Close support chat"),le(0),B(),!L&&!ce){De();return}L&&(pe(),setTimeout(()=>n.input.focus(),50))}function J(){A=!1,n.panel.classList.remove("open"),n.panel.setAttribute("aria-hidden","true"),n.launcher.setAttribute("aria-expanded","false"),n.launcher.setAttribute("aria-label","Open support chat"),n.launcher.focus()}n.launcher.onclick=()=>A?J():z(),i("close").onclick=J;let O=null;function Z(){if(!L)return;let o=n.input.value.trim();if(!(!o||!O))try{O.sendMessage(o),n.input.value="",n.input.style.height="auto"}catch(p){t.debug&&console.error("[caf] send failed",p)}}n.send.onclick=Z,ge(L),n.input.addEventListener("input",()=>{n.input.style.height="auto",n.input.style.height=Math.min(n.input.scrollHeight,96)+"px"}),n.input.addEventListener("keydown",o=>{o.key==="Enter"&&!o.shiftKey&&(o.preventDefault(),Z())});let ee=t.publicKey?"caf_session_"+t.publicKey.slice(-10):"",Le=t.publicKey?"caf_vid_"+t.publicKey.slice(-10):"";function ue(){if(!ee)return null;try{let o=JSON.parse(localStorage.getItem(ee)||"null");if(o&&o.token&&o.conversationId&&o.exp>Date.now())return o}catch{}return null}async function Ue(){let o=t.apiUrl.replace(/\/$/,""),p=ee,g=Le,f=ue();if(f)return r=!!f.calls_enabled,{token:f.token,conversationId:f.conversationId};let w=null;try{w=localStorage.getItem(g)}catch{}if(!w){w="v_"+Math.random().toString(36).slice(2)+Date.now().toString(36);try{localStorage.setItem(g,w)}catch{}}let T=await fetch(o+"/sdk/anonymous-session",{method:"POST",headers:{"Content-Type":"application/json","X-Api-Key":t.publicKey},body:JSON.stringify({visitor_id:w})});if(!T.ok)throw new Error("anonymous-session HTTP "+T.status);let j=await T.json(),C=j.data||j;r=!!C.calls_enabled;let I=Date.now()+Math.max(0,(C.expires_in||3600)*1e3-6e4);try{localStorage.setItem(p,JSON.stringify({token:C.token,conversationId:C.conversation_id,exp:I,calls_enabled:r}))}catch{}return{token:C.token,conversationId:C.conversation_id}}let V=null;function B(){return V||(V=(async()=>{var o,p,g;try{if(U(""),t.publicKey){let f=await Ue();d=f.token,y=f.conversationId,m=Ae(d)}if(b=new Re({wsUrl:t.wsUrl,token:d,apiUrl:t.apiUrl,autoReconnect:!0,debug:t.debug}),b.on("connected",()=>U("on")),b.on("disconnected",()=>U("")),b.on("reconnecting",()=>U("")),b.on("error",()=>U("err")),await b.connect(),await b.conversations.join(y),O=b.conversations.get(y),O.on("message",de),t.apiUrl&&typeof O.fetchMessages=="function")try{let f=await O.fetchMessages({limit:30});((g=(p=f==null?void 0:f.messages)!=null?p:(o=f==null?void 0:f.data)==null?void 0:o.messages)!=null?g:[]).forEach(de)}catch{}D=!0,l()&&u()}catch(f){U("err"),V=null,t.debug&&console.error("[caf] connect/provision failed",f)}})(),V)}(!t.publicKey||ue())&&B(),t.autoOpen&&z();let fe={open:z,close:J,send:async o=>{await B(),n.input.value=o,Z()},destroy:()=>{try{b==null||b.disconnect()}catch{}s.remove(),_=null}};return _={open:z,close:J,api:fe},fe}function Qt(){_==null||_.open()}function Yt(){_==null||_.close()}function Gt(){_==null||_.api.destroy()}export{Yt as close,Gt as destroy,Kt as init,Qt as open};