fleeta-api-lib
Version:
A comprehensive library for fleet management applications - API, Auth, Device management
1 lines • 160 kB
JavaScript
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("zustand"),require("crypto-js")):"function"==typeof define&&define.amd?define(["exports","zustand","crypto-js"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).FleetAComponents={},e.zustand,e.CryptoJS)}(this,(function(e,t,n){"use strict";var i=Object.defineProperty,r=(e,t,n)=>((e,t,n)=>t in e?i(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n)(e,"symbol"!=typeof t?t+"":t,n);const s=t.create((e=>({config:null,isInitialized:!1,setConfig:t=>{e({config:t,isInitialized:!0})},clearConfig:()=>{e({config:null,isInitialized:!1})}})));function o(){const e=s.getState();return e.isInitialized&&e.config?e.config:null}function a(e){const t=o();return t?t[e]:null}function c(){const e=a("apiGateway");if(!e)throw new Error("API Gateway not configured. Please set VITE_API_GATEWAY in .env file.");return e}function l(){const e=a("iotApiGateway");if(!e)throw new Error("IoT API Gateway not configured. Please set VITE_IOT_API_GATEWAY in .env file.");return e}function h(){const e=a("eventWebSocketUrl");if(!e)throw new Error("Event WebSocket URL not configured. Please set VITE_EVENT_WEBSOCKET_URL in .env file.");return e}function d(e,t){let n;try{n=e()}catch(i){return}return{getItem:e=>{var t;const i=e=>null===e?null:JSON.parse(e,void 0),r=null!=(t=n.getItem(e))?t:null;return r instanceof Promise?r.then(i):i(r)},setItem:(e,t)=>n.setItem(e,JSON.stringify(t,void 0)),removeItem:e=>n.removeItem(e)}}const u=e=>t=>{try{const n=e(t);return n instanceof Promise?n:{then:e=>u(e)(n),catch(e){return this}}}catch(n){return{then(e){return this},catch:e=>u(e)(n)}}},g=(e,t)=>"getStorage"in t||"serialize"in t||"deserialize"in t?((e,t)=>(n,i,r)=>{let s={getStorage:()=>localStorage,serialize:JSON.stringify,deserialize:JSON.parse,partialize:e=>e,version:0,merge:(e,t)=>({...t,...e}),...t},o=!1;const a=new Set,c=new Set;let l;try{l=s.getStorage()}catch(E){}if(!l)return e(((...e)=>{n(...e)}),i,r);const h=u(s.serialize),d=()=>{const e=s.partialize({...i()});let t;const n=h({state:e,version:s.version}).then((e=>l.setItem(s.name,e))).catch((e=>{t=e}));if(t)throw t;return n},g=r.setState;r.setState=(e,t)=>{g(e,t),d()};const p=e(((...e)=>{n(...e),d()}),i,r);let m;const f=()=>{var e;if(!l)return;o=!1,a.forEach((e=>e(i())));const t=(null==(e=s.onRehydrateStorage)?void 0:e.call(s,i()))||void 0;return u(l.getItem.bind(l))(s.name).then((e=>{if(e)return s.deserialize(e)})).then((e=>{if(e){if("number"!=typeof e.version||e.version===s.version)return e.state;if(s.migrate)return s.migrate(e.state,e.version)}})).then((e=>{var t;return m=s.merge(e,null!=(t=i())?t:p),n(m,!0),d()})).then((()=>{null==t||t(m,void 0),o=!0,c.forEach((e=>e(m)))})).catch((e=>{null==t||t(void 0,e)}))};return r.persist={setOptions:e=>{s={...s,...e},e.getStorage&&(l=e.getStorage())},clearStorage:()=>{null==l||l.removeItem(s.name)},getOptions:()=>s,rehydrate:()=>f(),hasHydrated:()=>o,onHydrate:e=>(a.add(e),()=>{a.delete(e)}),onFinishHydration:e=>(c.add(e),()=>{c.delete(e)})},f(),m||p})(e,t):((e,t)=>(n,i,r)=>{let s={storage:d((()=>localStorage)),partialize:e=>e,version:0,merge:(e,t)=>({...t,...e}),...t},o=!1;const a=new Set,c=new Set;let l=s.storage;if(!l)return e(((...e)=>{n(...e)}),i,r);const h=()=>{const e=s.partialize({...i()});return l.setItem(s.name,{state:e,version:s.version})},g=r.setState;r.setState=(e,t)=>{g(e,t),h()};const p=e(((...e)=>{n(...e),h()}),i,r);let m;r.getInitialState=()=>p;const f=()=>{var e,t;if(!l)return;o=!1,a.forEach((e=>{var t;return e(null!=(t=i())?t:p)}));const r=(null==(t=s.onRehydrateStorage)?void 0:t.call(s,null!=(e=i())?e:p))||void 0;return u(l.getItem.bind(l))(s.name).then((e=>{if(e){if("number"!=typeof e.version||e.version===s.version)return[!1,e.state];if(s.migrate)return[!0,s.migrate(e.state,e.version)]}return[!1,void 0]})).then((e=>{var t;const[r,o]=e;if(m=s.merge(o,null!=(t=i())?t:p),n(m,!0),r)return h()})).then((()=>{null==r||r(m,void 0),m=i(),o=!0,c.forEach((e=>e(m)))})).catch((e=>{null==r||r(void 0,e)}))};return r.persist={setOptions:e=>{s={...s,...e},e.storage&&(l=e.storage)},clearStorage:()=>{null==l||l.removeItem(s.name)},getOptions:()=>s,rehydrate:()=>f(),hasHydrated:()=>o,onHydrate:e=>(a.add(e),()=>{a.delete(e)}),onFinishHydration:e=>(c.add(e),()=>{c.delete(e)})},s.skipHydration||f(),m||p})(e,t),p=t.create()(g((e=>({isAuthenticated:!1,userToken:null,jwtToken:null,serverConfig:null,userType:null,subscribeInfo:null,latestLoginInfo:null,error:null,setAuth:t=>{var n;if("BC_ERR_OK"===t.resultcode){const i=t.response||{},r=null==(n=i["logged in list"])?void 0:n.ids;e({isAuthenticated:!0,userToken:i.user_token??t.user_token??null,jwtToken:i.jsonwebtoken??t.jsonwebtoken??t.jwt_token??null,serverConfig:{was_server:i.was_server??"",was_port:i.was_port??"",gps_server:i.gps_server??"",gps_port:i.gps_port??"",gps_tracking_server:i.gps_tracking_server??"",gps_tracking_port:i.gps_tracking_port??"",geo_fence_server:i.geo_fence_server??"",geo_fence_port:i.geo_fence_port??""},userType:i.userType??null,subscribeInfo:i.subscribeinfo??null,latestLoginInfo:{timestamp:i.latestlogindt??"",mobileOS:i.lastMobileOS??"",appVersion:i.lastAppVer??"",webVersion:i.lastWebVer??"",duplicateLogins:r},error:null})}else e({error:t.message??"Login failed",isAuthenticated:!1})},clearAuth:()=>{e({isAuthenticated:!1,userToken:null,jwtToken:null,serverConfig:null,userType:null,subscribeInfo:null,latestLoginInfo:null,error:null})},setError:t=>e({error:t})})),{name:"auth-storage",skipHydration:!1}));let m=!1,f=!1;const E={BC_ERR_AUTHENTICATION:"Authentication failed. Please check your credentials.",BC_ERR_SIGNATURE:"Invalid security signature. Please try again.",BC_ERR_INVALID_DATA:"Something went wrong. Please try again.",BC_ERR_INVALID_PARAMETER:"Invalid login parameters. Please check your input.",BC_ERR_NEED_TO_CONFIRM:"Please verify your email address before logging in.",BC_ERR_BLACK_LIST:"Your account has been blocked for security reasons. Please contact support.",BC_ERR_SERVER:"Server error. Please try again later.",BC_ERR_DUPLICATED:"Account is already logged in on another device.",BC_ERR_GOOGLEAPPLE_ACCOUNT:"This account uses Google or Apple sign-in. Please continue with one of those."},v={BC_ERR_AUTHENTICATION:"Authentication failed. Please try again.",BC_ERR_SIGNATURE:"Security verification failed. Please try again.",BC_ERR_INVALID_DATA:"Invalid data received. Please try again.",BC_ERR_ALREADY_REQUESTED:"Password reset email has already been sent. Please check your inbox.",BC_ERR_INVALID_PARAMETER:"Invalid request parameters. Please try again.",BC_ERR_SERVER:"Internal server error. Please try again later.",BC_ERR_NOT_FOUND:"Email address not found. Please check your email and try again."},w={BC_ERR_AUTHENTICATION:"BCS Token not exists",BC_ERR_INVALID_PARAMETER:"Invalid parameter",BC_ERR_SERVER:"Internal server error"};function C(){const e=navigator.platform.toLowerCase();return e.includes("win")?"win32":e.includes("mac")?"macos":e.includes("linux")?"linux":"web"}function S(){var e;const t=p.getState();return t.isAuthenticated&&t.userToken&&t.jwtToken&&(null==(e=t.subscribeInfo)?void 0:e.userEmail)?{userToken:t.userToken,userEmail:t.subscribeInfo.userEmail,jwtToken:t.jwtToken}:null}function A(){return null!==S()}function y(){const e=S();return!!e&&(e.userToken.length>0&&e.jwtToken.length>0&&e.userEmail.includes("@"))}function R(){const e=S();if(!e)throw new Error("Authentication required. Please login first.");return{Authorization:`Bearer ${e.jwtToken}`,"Content-Type":"application/json"}}function T(){return"Authentication required. Please login first."}const b={BC_ERR_AUTHENTICATION:"Unauthorized - Authentication failed",BC_ERR_INVALID_PARAMETER:"Invalid parameter provided",BC_ERR_SERVER:"Internal server error occurred"},_={BC_ERR_AUTHENTICATION:"Unauthorized - Authentication failed",BC_ERR_INVALID_PARAMETER:"Invalid PSN parameter provided",BC_ERR_SERVER:"Internal server error occurred"};function I(e){const t=function(e){const t=e.length,n=e.filter((e=>"on"===e.device.active)).length,i=t-n,r=e.filter((e=>!!e.groupManagementID)).length,s=t-r,o=e.filter((e=>"on"===e.device.share_video)).length,a=e.filter((e=>"0"===e.device.mode)).length,c=e.filter((e=>"1"===e.device.mode)).length,l=e.filter((e=>"wifi"===e.device.reg_category)).length,h=e.filter((e=>"cloud"===e.device.reg_category)).length,d=e.filter((e=>!!e.device.battery)).length;return{total:t,online:n,offline:i,grouped:r,ungrouped:s,videoSharing:o,normalMode:a,parkingMode:c,wifiOnly:l,cloudEnabled:h,withBattery:d}}(e),n=function(e){const t=new Map;e.forEach((e=>{const n=e.groupManagementID||"ungrouped";t.has(n)||t.set(n,[]),t.get(n).push(e)}));const n=[];return t.forEach(((e,t)=>{const i=e[0];n.push({groupId:t,groupName:i.groupName||"Ungrouped",groupRegDate:i.groupRegDT,devices:e,isExpanded:"ungrouped"===t})})),n.sort(((e,t)=>"ungrouped"===e.groupId?-1:"ungrouped"===t.groupId?1:e.groupName.localeCompare(t.groupName))),n}(e);return{groups:n,statistics:t}}async function M(e){const t=S();if(!t)throw new Error("Authentication required. Please login first.");try{const n=c(),i={email:t.userEmail,user_token:t.userToken,tokenType:"web",...e},r=new URLSearchParams;r.append("email",i.email),r.append("user_token",i.user_token),r.append("tokenType",i.tokenType||"web"),i.keyword&&r.append("keyword",i.keyword),i.startIndex&&i.startIndex>=1&&r.append("startIndex",i.startIndex.toString()),i.endIndex&&i.endIndex>=1&&r.append("endIndex",i.endIndex.toString());const s=await fetch(`${n}/DashCam/DeviceList?${r.toString()}`,{method:"GET",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.jwtToken}`}});if(!s.ok)throw new Error(`HTTP error! status: ${s.status}`);const o=await s.json();if("BC_ERR_OK"!==o.resultcode){throw new Error(b[o.resultcode]||o.message||"Unknown error occurred")}const a=o.response||{deviceCount:o.deviceCount||0,deviceLimitCount:o.deviceLimitCount||0,sharedEventMap:o.sharedEventMap||"off",deviceListInfo:o.deviceListInfo||[]};if(!a)return{resultcode:o.resultcode,message:o.message,deviceCount:0,deviceLimitCount:0,sharedEventMap:"off",deviceListInfo:[],groupedData:{groups:[],statistics:{total:0,online:0,offline:0,grouped:0,ungrouped:0,videoSharing:0,normalMode:0,parkingMode:0,wifiOnly:0,cloudEnabled:0,withBattery:0}}};a.deviceListInfo||(a.deviceListInfo=[]);const{groups:l,statistics:h}=I(a.deviceListInfo);return{resultcode:o.resultcode,message:o.message,deviceCount:a.deviceCount||0,deviceLimitCount:a.deviceLimitCount||0,sharedEventMap:a.sharedEventMap||"off",deviceListInfo:a.deviceListInfo||[],groupedData:{groups:l,statistics:h}}}catch(n){throw n}}async function k(e){const t=S();if(!t)throw new Error("Authentication required. Please login first.");try{const n=c(),i=new URLSearchParams;i.append("psn",e.psn);const r=await fetch(`${n}/DashCam/DeviceList/status?${i.toString()}`,{method:"GET",headers:{Authorization:`Bearer ${t.jwtToken}`}});if(!r.ok)throw new Error(`HTTP error! status: ${r.status}`);const s=await r.json();if("BC_ERR_OK"!==s.resultcode){throw new Error(_[s.resultcode]||s.message||"Unknown error occurred")}return s}catch(n){throw n}}const D=5,P=20,N=["Seoul Fleet Alpha","Busan Transport Beta","Daegu Logistics Gamma","Incheon Delivery Delta","Gwangju Express Epsilon"],U=["DR970X-2CH","DR970X-1CH","DR900X-2CH","DR900X-1CH","DR750X-2CH"],L=["Wifi","Cat4","Cat-M1"];function O(){const e=Math.floor(100*Math.random()),t=Math.floor(60*Math.random())+10;return{ssid:`MOCK_BATTERY_${Math.random().toString(36).substr(2,9)}`,celsius:t,battery_percent:e,createdAt:new Date(Date.now()-6048e5*Math.random()).toISOString(),updatedAt:(new Date).toISOString(),state:e>70?"latest":e>30?"lateup1":"lateup2"}}function G(e,t,n){const i=function(e,t){return`MOCK${(e+1).toString().padStart(2,"0")}${(t+1).toString().padStart(3,"0")}`}(e,t),r=Math.random()>.3,s=Math.random()>.4,o=Math.random()>.5,a=Math.random()>.8,c=new Date,l=new Date(c.getTime()-31536e6*Math.random()),h=r?new Date(c.getTime()-864e5*Math.random()):new Date(c.getTime()-2592e6*Math.random());return{valid:"valid",psn:i,model:U[Math.floor(Math.random()*U.length)],active:r?"on":"off",dev_name:`${n.split(" ")[0]}-${(t+1).toString().padStart(3,"0")}`,battery:Math.random()>.2?O():void 0,lb_server_name:`mock-lb-${Math.floor(10*Math.random())}`,lb_http_port:80,lb_rtmp_port:1935,share_video:s?"on":"off",share_audio:s&&Math.random()>.5?"on":"off",share_dev_name:"on",share_gps:r?"on":"off",agree_gps:"on",fw_ver:`${Math.floor(3*Math.random())+1}.${Math.floor(10*Math.random())}.${Math.floor(100*Math.random())}`,upload_server:o?`cloud-upload-${Math.floor(5*Math.random())}.fleeta.io`:void 0,upload_port:o?443:void 0,lang:"eng",mode:a?"1":"0",communication_identifier:L[Math.floor(Math.random()*L.length)],temperature_level:Math.floor(5*Math.random())+1,sensitivity_level:Math.floor(5*Math.random())+1,dms_type:"standard",dms_version:"1.0.0",reg_date:l.toISOString(),login_date:h.toISOString(),iot_conn:r?"on":"off",reg_category:o?"cloud":"wifi",continentCode:"AS",countryCode:"KR",ip:`192.168.${Math.floor(255*Math.random())}.${Math.floor(255*Math.random())}`,liveview_rating:Math.floor(5*Math.random())+1,regInfoUpdateDT:(new Date).toISOString(),countryCodeUpdateDT:(new Date).toISOString(),profile_img:Math.random()>.7?`https://api.dicebear.com/7.x/identicon/svg?seed=${i}`:void 0,profile_img_v2:Math.random()>.7?`https://api.dicebear.com/7.x/avataaars/svg?seed=${i}`:void 0,cldn:`cldn_${i.toLowerCase()}`,ap_ssid:o?void 0:`MockWiFi_${t}`,ap_pw:o?void 0:`mock_pass_${t}`,require_time_sync:Math.random()>.5?"on":"off"}}const B={BC_ERR_AUTHENTICATION:"Authentication failed. Please check your credentials.",BC_ERR_INVALID_PARAMETER:"Invalid parameters provided.",BC_ERR_SERVER:"Internal server error occurred.",BC_ERR_UNAVAILABLE:"Device is not available for live streaming.",BC_ERR_BUSY:"Device is currently busy with another live stream.",BC_ERR_UNPLUGED:"Device is unplugged and cannot stream video."};let V=new Set;async function F(e){const t=S();if(!t)throw new Error("Authentication required. Please login first.");const n=`${e.psn}-${e.param1||"1"}`;if(V.has(n))throw new Error("PlayLiveView request already in progress for this device");try{V.add(n);const i=l(),r={command:"PlayLiveView",email:t.userEmail,user_token:t.userToken,tokenType:e.tokenType||"web",psn:e.psn,param1:e.param1||"1"},s=await fetch(`${i}/IoT/devicecommand`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.jwtToken}`},body:JSON.stringify(r)});if(!s.ok)throw new Error(`HTTP error! status: ${s.status}`);const o=await s.json();if("BC_ERR_AUTHENTICATION"===o.resultcode)throw new Error("Authentication failed");if("BC_ERR_OK"!==o.resultcode){throw new Error(B[o.resultcode]||o.message||"Unknown error occurred")}return o}catch(i){throw i}finally{V.delete(n)}}async function $(e){const t=S();if(!t)throw new Error("Authentication required. Please login first.");try{const n=l(),i=await fetch(`${n}/IoT/devicecommand`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.jwtToken}`},body:JSON.stringify({command:"StopLiveView",email:t.userEmail,user_token:t.userToken,tokenType:e.tokenType||"web",psn:e.psn})});if(!i.ok)throw new Error(`HTTP error! status: ${i.status}`);const r=await i.json();if("BC_ERR_AUTHENTICATION"===r.resultcode)throw new Error("Authentication failed");if("BC_ERR_OK"!==r.resultcode){throw new Error(B[r.resultcode]||r.message||"Unknown error occurred")}return r}catch(n){throw n}}async function H(e){const t=S();if(!t)throw new Error("Authentication required. Please login first.");try{const n=l(),i={command:"LiveDirection",email:t.userEmail,user_token:t.userToken,tokenType:"web",psn:e.psn,param1:e.param1},r=await fetch(`${n}/IoT/devicecommand`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.jwtToken}`},body:JSON.stringify(i)});if(!r.ok)throw new Error(`HTTP error! status: ${r.status}`);const s=await r.json();if("BC_ERR_AUTHENTICATION"===s.resultcode)throw new Error("Authentication failed");if("BC_ERR_OK"!==s.resultcode){throw new Error(B[s.resultcode]||s.message||"Unknown error occurred")}return s}catch(n){throw n}}async function q(e,t=1,n=3e3){let i=0;for(;t>i;)try{const r=await H(e);if("busy"===r.status&&t-1>i){await new Promise((e=>setTimeout(e,n))),i++;continue}return r}catch(r){if(i>=t-1)throw r;await new Promise((e=>setTimeout(e,n))),i++}throw new Error(`Failed to change live direction after ${t} attempts`)}async function x(e){const t=S();if(!t)throw new Error("Authentication required. Please login first.");try{const n=c(),i=await fetch(`${n}/CloudService/LivePresignURL`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.jwtToken}`},body:JSON.stringify(e)});if(!i.ok)throw new Error(`HTTP error! status: ${i.status}`);const r=await i.json();if("BC_ERR_OK"!==r.resultcode){throw new Error("BC_ERR_TIMEOUT"===r.resultcode?"Request timeout - please try again":"BC_ERR_AUTHENTICATION"===r.resultcode?"Authentication failed":"BC_ERR_SERVER"===r.resultcode?"Internal server error":r.message||"Unknown error occurred")}return r}catch(n){throw n}}async function z(e,t="1",n,i="VIEWER"){try{if("onLiveStreaming"===(await F({psn:e,param1:t})).status)throw new Error("Device is currently streaming to another client. Please try again later.");const r=`${e}_live`,s=await x({streamName:r,role:i,psn:e});if(!(s.region&&s.channelARN&&s.url&&s.iceServers&&s.endpointsByProtocol))throw new Error("Incomplete WebRTC configuration received");return{region:s.region,channelARN:s.channelARN,url:s.url,iceServers:s.iceServers,endpointsByProtocol:s.endpointsByProtocol,role:i,clientId:n||e}}catch(r){throw r}}async function W(e){const t=S();if(!t)throw new Error("Authentication required. Please login first.");try{const n=l(),i={command:"TwoWayStart",email:t.userEmail,user_token:t.userToken,tokenType:e.tokenType||"web",psn:e.psn},r=await fetch(`${n}/IoT/devicecommand`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.jwtToken}`},body:JSON.stringify(i)});if(!r.ok)throw new Error(`HTTP error! status: ${r.status}`);const s=await r.json();if("BC_ERR_AUTHENTICATION"===s.resultcode)throw new Error("Authentication failed");if("BC_ERR_OK"!==s.resultcode){throw new Error(B[s.resultcode]||s.message||"Unknown error occurred")}return s}catch(n){throw n}}async function K(e){const t=S();if(!t)throw new Error("Authentication required. Please login first.");try{const n=l(),i={command:"TwoWayStop",email:t.userEmail,user_token:t.userToken,tokenType:e.tokenType||"web",psn:e.psn},r=await fetch(`${n}/IoT/devicecommand`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.jwtToken}`},body:JSON.stringify(i)});if(!r.ok)throw new Error(`HTTP error! status: ${r.status}`);const s=await r.json();if("BC_ERR_AUTHENTICATION"===s.resultcode)throw new Error("Authentication failed");if("BC_ERR_OK"!==s.resultcode){throw new Error(B[s.resultcode]||s.message||"Unknown error occurred")}return s}catch(n){throw n}}class j{constructor(e,t){r(this,"config"),r(this,"webrtcConfig",null),r(this,"provider"),r(this,"connectionId"),r(this,"audioManager"),r(this,"stateMachine"),r(this,"dependencies"),r(this,"error",null),r(this,"peerConnection",null),r(this,"signalingClient",null),r(this,"stream",null),r(this,"receivedStreams",new Set),r(this,"connectionEstablished",!1),r(this,"audioRenegotiationInProgress",!1),r(this,"lastOfferCreationTime",0),r(this,"stats",null),r(this,"statsInterval",null),r(this,"eventHandlers",{}),r(this,"rtpMonitor",null),r(this,"lastAudioBytesSent",0),r(this,"lastAudioBytesReceived",0),this.config=e,this.provider=e.provider,this.connectionId=`${e.deviceId}_${e.channel}_${Date.now()}`,this.audioManager=t.audioManager,this.stateMachine=t.stateMachine,this.dependencies=t.dependencies,this.stateMachine.setCallbacks({onStateChange:(e,t,n)=>{var i,r;null==(r=(i=this.eventHandlers).onStateChange)||r.call(i,e)},onInvalidTransition:(e,t)=>{}}),e.onStateChange&&(this.eventHandlers.onStateChange=e.onStateChange),e.onError&&(this.eventHandlers.onError=e.onError),e.onStreamReceived&&(this.eventHandlers.onStreamReceived=e.onStreamReceived),e.onStatsUpdate&&(this.eventHandlers.onStatsUpdate=e.onStatsUpdate)}async connect(){if("connecting"!==this.stateMachine.getState()&&"connected"!==this.stateMachine.getState())try{if(this.stateMachine.transition("CONNECT"),this.setError(null),!this.dependencies.webrtcSDK){const e="undefined"!=typeof window?window.KVSWebRTC:null;if(!e)throw new Error("WebRTC SDK not available. Please ensure SDK is loaded before creating connection.");this.dependencies.webrtcSDK=e}if(this.config.webrtcConfig)this.webrtcConfig=this.config.webrtcConfig;else{const e={deviceId:this.config.deviceId,channel:this.config.channel,clientId:this.config.clientId||this.config.deviceId,role:this.config.role||"VIEWER"};this.webrtcConfig=await this.provider.getWebRTCConfig(e)}await this.initializeWebRTCConnection(this.webrtcConfig),this.config.enableStats&&this.startStatsMonitoring()}catch(e){const t=e instanceof Error?e:new Error("Connection failed");throw this.setError(t),this.stateMachine.transition("ERROR"),t}}async disconnect(){try{if(this.stopStatsMonitoring(),this.receivedStreams.clear(),this.connectionEstablished=!1,this.stream=null,this.peerConnection&&(this.peerConnection.close(),this.peerConnection=null),this.signalingClient){try{this.signalingClient.close()}catch(e){}this.signalingClient=null}try{await this.provider.stopStreaming(this.config.deviceId)}catch(e){}this.webrtcConfig=null,this.stats=null,this.stateMachine.transition("DISCONNECT")}catch(e){this.stateMachine.transition("DISCONNECT")}}async changeChannel(e){const t=this.stateMachine.getState();if(e!==this.config.channel&&"connecting"!==t&&"idle"!==t)try{this.stateMachine.transition("CHANNEL_CHANGE_START"),this.receivedStreams.clear(),this.stream=null,await this.provider.changeChannel(this.config.deviceId,e),this.config.channel=e,this.stateMachine.transition("CHANNEL_CHANGE_COMPLETE")}catch(n){const t=new Error(`Channel ${e} is not available`);throw this.setError(t),"connected"===this.stateMachine.getState()&&this.stateMachine.transition("CHANNEL_CHANGE_COMPLETE"),t}}async startTwoWayAudio(){if(!this.peerConnection||"connected"!==this.stateMachine.getState())throw new Error("WebRTC connection not established");if(await this.audioManager.startTwoWayAudio(this.peerConnection),!this.peerConnection.localDescription||!this.signalingClient)throw new Error("Cannot send audio offer - signaling not ready");try{this.signalingClient.sendSdpOffer(this.peerConnection.localDescription)}catch(e){throw new Error(`Failed to send audio offer: ${e instanceof Error?e.message:"Unknown error"}`)}}async stopTwoWayAudio(){await this.audioManager.stopTwoWayAudio(this.peerConnection||void 0)}setLocalAudioEnabled(e){this.audioManager.setLocalAudioEnabled(e)}getLocalAudioStream(){return this.audioManager.getLocalAudioStream()}getRemoteAudioStream(){return this.stream}getState(){return this.stateMachine.getState()}getConfig(){return this.webrtcConfig}getStats(){return this.stats}getError(){return this.error}on(e,t){this.eventHandlers[e]=t}off(e,t){this.eventHandlers[e]===t&&delete this.eventHandlers[e]}destroy(){if(this.audioManager.cleanup(),this.stopRTPMonitoring(),this.stopStatsMonitoring(),this.peerConnection&&(this.peerConnection.close(),this.peerConnection=null),this.signalingClient){try{this.signalingClient.close()}catch(e){}this.signalingClient=null}this.receivedStreams.clear(),this.connectionEstablished=!1,this.eventHandlers={},this.webrtcConfig=null,this.stats=null,this.stateMachine.reset()}async initializeWebRTCConnection(e){try{const t={getSignedURL:()=>e.url},n=new RTCPeerConnection({iceServers:e.iceServers.map((e=>({urls:e.urls,username:e.username,credential:e.credential})))});this.peerConnection=n;const i=new this.dependencies.webrtcSDK.SignalingClient({requestSigner:t,region:e.region,role:this.dependencies.webrtcSDK.Role.VIEWER,clientId:e.clientId,channelARN:e.channelARN,channelEndpoint:e.endpointsByProtocol.WSS});this.signalingClient=i,n.ontrack=this._handleTrackEvent.bind(this),n.oniceconnectionstatechange=()=>{const e=this.stateMachine.getState();switch(n.iceConnectionState){case"connected":case"completed":this.connectionEstablished||"connected"===e||(this.connectionEstablished=!0,this.stateMachine.transition("CONNECTION_ESTABLISHED"),this.startRTPMonitoring(n));break;case"disconnected":case"closed":"connected"===e&&this.stateMachine.canTransition("CHANNEL_CHANGE_START")||(this.connectionEstablished=!1,this.stateMachine.transition("DISCONNECT"));break;case"failed":if("connected"!==e||!this.stateMachine.canTransition("CHANNEL_CHANGE_START")){this.connectionEstablished=!1;const e=new Error("ICE connection failed");this.setError(e),this.stateMachine.transition("ERROR")}}},i.on("open",(async()=>{try{const e=await n.createOffer({offerToReceiveAudio:!0,offerToReceiveVideo:!0});await n.setLocalDescription(e),i.sendSdpOffer(n.localDescription)}catch(e){const t=e instanceof Error?e:new Error("Failed to create SDP offer");this.setError(t),this.stateMachine.transition("ERROR")}})),i.on("sdpAnswer",(async e=>{var t,i,r,s;const o=Date.now()-this.lastOfferCreationTime;try{const a=this.audioRenegotiationInProgress&&1e4>o&&"offer"===(null==(t=n.localDescription)?void 0:t.type)&&(null==(r=null==(i=n.localDescription)?void 0:i.sdp)?void 0:r.includes("m=audio"))&&(null==(s=e.sdp)?void 0:s.includes("m=audio"));await n.setRemoteDescription(e),a&&(this.audioRenegotiationInProgress=!1,n.__audioRenegotiationInProgress=!1)}catch(a){const e=a instanceof Error?a:new Error("Failed to set remote description");this.setError(e),this.stateMachine.transition("ERROR")}})),i.on("iceCandidate",(async e=>{try{await n.addIceCandidate(e)}catch(t){}})),n.onicecandidate=({candidate:e})=>{e&&i.sendIceCandidate(e)},i.on("close",(()=>{this.stateMachine.canTransition("CHANNEL_CHANGE_START")||this.stateMachine.transition("DISCONNECT")})),i.on("error",(e=>{if(!this.stateMachine.canTransition("CHANNEL_CHANGE_START")){const t=new Error(`Signaling error: ${e.message}`);this.setError(t),this.stateMachine.transition("ERROR")}})),i.open()}catch(t){throw t}}startStatsMonitoring(){this.statsInterval&&clearInterval(this.statsInterval);this.statsInterval=setInterval((async()=>{var e,t;if(this.peerConnection&&"connected"===this.stateMachine.getState())try{const n=await this.peerConnection.getStats();let i=0,r=0,s=0,o=0,a=0;n.forEach((e=>{"inbound-rtp"===e.type&&"video"===e.mediaType&&(i+=e.bytesReceived||0,r+=e.packetsReceived||0,s+=e.packetsLost||0,o=e.jitter||0),"candidate-pair"===e.type&&"succeeded"===e.state&&(a=e.currentRoundTripTime||0)})),this.stats={bytesReceived:i,packetsReceived:r,packetsLost:s,jitter:o,roundTripTime:a},null==(t=(e=this.eventHandlers).onStatsUpdate)||t.call(e,this.stats)}catch(n){}}),this.config.statsInterval||2e3)}stopStatsMonitoring(){this.statsInterval&&(clearInterval(this.statsInterval),this.statsInterval=null)}setError(e){var t,n;this.error=e,e&&(null==(n=(t=this.eventHandlers).onError)||n.call(t,e))}_handleTrackEvent(e){var t,n;if(e.streams[0]){const i=e.streams[0].id,r=e.streams[0];!this.receivedStreams.has(i)?(this.receivedStreams.add(i),this.stream=r,r.getAudioTracks().forEach(((e,t)=>{})),null==(n=(t=this.eventHandlers).onStreamReceived)||n.call(t,r),this.connectionEstablished||"connected"===this.stateMachine.getState()||(this.connectionEstablished=!0,this.stateMachine.transition("CONNECTION_ESTABLISHED"))):this.stream=r}}startRTPMonitoring(e){this.rtpMonitor&&clearInterval(this.rtpMonitor),this.rtpMonitor=setInterval((async()=>{try{const t=await e.getStats();let n=!1,i=!1;t.forEach((e=>{if("outbound-rtp"===e.type&&"audio"===e.mediaType){const t=e.bytesSent||0;t>this.lastAudioBytesSent&&(n=!0),this.lastAudioBytesSent=t}if("inbound-rtp"===e.type&&"audio"===e.mediaType){const t=e.bytesReceived||0;t>this.lastAudioBytesReceived&&(i=!0),this.lastAudioBytesReceived=t}}))}catch(t){}}),2e3)}stopRTPMonitoring(){this.rtpMonitor&&(clearInterval(this.rtpMonitor),this.rtpMonitor=null)}}class Q{constructor(e={}){r(this,"localAudioStream",null),r(this,"isLocalAudioEnabled",!0),r(this,"isTwoWayActive",!1),r(this,"audioConfig"),r(this,"audioTrackMonitor",null),this.audioConfig={echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,...e}}async startTwoWayAudio(e){if(!this.isTwoWayActive){if(!e)throw new Error("Peer connection is required for two-way audio");try{this.localAudioStream=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:this.audioConfig.echoCancellation,noiseSuppression:this.audioConfig.noiseSuppression,autoGainControl:this.audioConfig.autoGainControl,sampleRate:this.audioConfig.sampleRate,channelCount:this.audioConfig.channelCount},video:!1}),this.localAudioStream.getAudioTracks().forEach((t=>{e.addTrack(t,this.localAudioStream),t.onended=()=>{},t.onmute=()=>{},t.onunmute=()=>{}})),await this.renegotiateForAudio(e),this.isTwoWayActive=!0,this.startAudioTransmissionMonitoring(e)}catch(t){throw this.localAudioStream&&(this.localAudioStream.getTracks().forEach((e=>e.stop())),this.localAudioStream=null),new Error(`Failed to start two-way audio: ${t instanceof Error?t.message:"Unknown error"}`)}}}async stopTwoWayAudio(e){if(this.isTwoWayActive)try{if(this.localAudioStream&&(this.localAudioStream.getTracks().forEach((e=>{e.stop()})),this.localAudioStream=null),this.stopAudioTransmissionMonitoring(),e){const t=e.getSenders().filter((e=>e.track&&"audio"===e.track.kind));for(const n of t)await e.removeTrack(n)}this.isTwoWayActive=!1}catch(t){}finally{this.isTwoWayActive=!1}}setLocalAudioEnabled(e){this.localAudioStream&&(this.localAudioStream.getAudioTracks().forEach((t=>{t.enabled=e})),this.isLocalAudioEnabled=e)}getLocalAudioStream(){return this.localAudioStream}isTwoWayAudioActive(){return this.isTwoWayActive}isLocalAudioMuted(){return!this.isLocalAudioEnabled}updateConfig(e){this.audioConfig={...this.audioConfig,...e}}cleanup(){this.stopAudioTransmissionMonitoring(),this.localAudioStream&&(this.localAudioStream.getTracks().forEach((e=>e.stop())),this.localAudioStream=null),this.isTwoWayActive=!1,this.isLocalAudioEnabled=!0}async renegotiateForAudio(e){if(!e)throw new Error("Peer connection not available for renegotiation");try{const t=await e.createOffer({offerToReceiveAudio:!0,offerToReceiveVideo:!0});await e.setLocalDescription(t),this.markAudioRenegotiationInProgress(e)}catch(t){throw t}}startAudioTransmissionMonitoring(e){this.audioTrackMonitor&&clearInterval(this.audioTrackMonitor),this.audioTrackMonitor=setInterval((async()=>{if(e&&"connected"===e.connectionState)try{(await e.getStats()).forEach((e=>{}))}catch(t){}}),2e3)}stopAudioTransmissionMonitoring(){this.audioTrackMonitor&&(clearInterval(this.audioTrackMonitor),this.audioTrackMonitor=null)}markAudioRenegotiationInProgress(e){e.__audioRenegotiationInProgress=!0,e.__lastOfferCreationTime=Date.now()}}class Z{constructor(e="idle"){r(this,"currentState","idle"),r(this,"transitions",[{from:"idle",event:"CONNECT",to:"connecting"},{from:"connecting",event:"CONNECTION_ESTABLISHED",to:"connected"},{from:"connecting",event:"CONNECTION_FAILED",to:"failed"},{from:"connecting",event:"ERROR",to:"failed"},{from:"connected",event:"CONNECTION_ESTABLISHED",to:"connected"},{from:"connected",event:"DISCONNECT",to:"disconnected"},{from:"failed",event:"DISCONNECT",to:"disconnected"},{from:"disconnected",event:"RECONNECT",to:"connecting"},{from:"failed",event:"RECONNECT",to:"connecting"},{from:"connected",event:"CHANNEL_CHANGE_START",to:"connected"},{from:"connected",event:"CHANNEL_CHANGE_COMPLETE",to:"connected"},{from:"connecting",event:"ERROR",to:"failed"},{from:"connected",event:"ERROR",to:"failed"},{from:"disconnected",event:"ERROR",to:"failed"}]),r(this,"callbacks",{}),this.currentState=e}getState(){return this.currentState}canTransition(e){return this.transitions.some((t=>t.from===this.currentState&&t.event===e&&(!t.guard||t.guard())))}transition(e){var t,n,i,r;const s=this.transitions.find((t=>t.from===this.currentState&&t.event===e&&(!t.guard||t.guard())));if(!s)return null==(n=(t=this.callbacks).onInvalidTransition)||n.call(t,this.currentState,e),!1;const o=this.currentState;return this.currentState=s.to,null==(r=(i=this.callbacks).onStateChange)||r.call(i,this.currentState,o,e),!0}setCallbacks(e){this.callbacks=e}reset(){var e,t;const n=this.currentState;this.currentState="idle",null==(t=(e=this.callbacks).onStateChange)||t.call(e,"idle",n,"DISCONNECT")}}const J={mediaDevices:navigator.mediaDevices,RTCPeerConnection:RTCPeerConnection,webrtcSDK:null,console:console};class Y{constructor(e={}){r(this,"dependencies"),this.dependencies={...J,...e}}createConnection(e){const t=this.createAudioManager(),n=this.createStateMachine();return new j(e,{audioManager:t,stateMachine:n,dependencies:this.dependencies})}createAudioManager(){return new Q({echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0})}createStateMachine(){return new Z("idle")}createPeerConnection(e){if(!this.dependencies.RTCPeerConnection)throw new Error("RTCPeerConnection not available");return new this.dependencies.RTCPeerConnection({iceServers:e.map((e=>({urls:e.urls,username:e.username,credential:e.credential})))})}createSignalingClient(e){if(!this.dependencies.webrtcSDK)throw new Error("WebRTC SDK not available");return new this.dependencies.webrtcSDK.SignalingClient({requestSigner:{getSignedURL:()=>e.url},region:e.region,role:this.dependencies.webrtcSDK.Role.VIEWER,clientId:e.clientId,channelARN:e.channelARN,channelEndpoint:e.channelEndpoint})}validateDependencies(){const e=["mediaDevices","RTCPeerConnection"];for(const t of e)if(!this.dependencies[t])throw new Error(`Required dependency missing: ${t}`)}updateDependencies(e){this.dependencies={...this.dependencies,...e}}}const X=class e{constructor(e){r(this,"config"),r(this,"state",{loaded:!1,loading:!1,error:null}),r(this,"loadPromise",null),this.config={...e,loadTimeout:e.loadTimeout||3e4}}static getInstance(t){const n=`${t.sdkUrl}-${t.globalObjectName}`;return e.instances.has(n)||e.instances.set(n,new e(t)),e.instances.get(n)}static async preloadGlobalSDK(t){if(e.globalLoadPromise)return e.globalLoadPromise;const n=e.getInstance(t);e.globalLoadPromise=n.loadSDK();try{await e.globalLoadPromise}catch(i){throw e.globalLoadPromise=null,i}return e.globalLoadPromise}static isGloballyLoaded(e){return!!window[e]}isLoaded(){const t=e.isGloballyLoaded(this.config.globalObjectName);return t&&!this.state.loaded&&(this.state.loaded=!0),t}isLoading(){return this.state.loading}getError(){return this.state.error}async loadSDK(){if(!this.isLoaded())return this.loadPromise||(this.loadPromise=this.performSDKLoad()),this.loadPromise}async performSDKLoad(){if(!this.state.loading){this.state.loading=!0,this.state.error=null;try{const e=document.createElement("script");e.src=this.config.sdkUrl,e.async=!0;const t=new Promise(((t,n)=>{const i=setTimeout((()=>{r(),n(new Error(`SDK loading timeout after ${this.config.loadTimeout}ms`))}),this.config.loadTimeout),r=()=>{clearTimeout(i),e.removeEventListener("load",s),e.removeEventListener("error",o)},s=()=>{if(r(),window[this.config.globalObjectName])this.state.loaded=!0,t();else{const e=`SDK loaded but ${this.config.globalObjectName} not available on window object`;this.state.error=e,n(new Error(e))}},o=()=>{r();const e=`Failed to load SDK from ${this.config.sdkUrl}`;this.state.error=e,n(new Error(e))};e.addEventListener("load",s),e.addEventListener("error",o)}));document.head.appendChild(e),await t}catch(e){const t=e instanceof Error?e.message:"Unknown error loading SDK";throw this.state.error=t,e}finally{this.state.loading=!1,this.loadPromise=null}}}reset(){this.state={loaded:!1,loading:!1,error:null},this.loadPromise=null}};r(X,"instances",new Map),r(X,"globalLoadPromise",null);let ee=X;function te(){return ee.getInstance({sdkUrl:"https://unpkg.com/amazon-kinesis-video-streams-webrtc@2.4.1/dist/kvs-webrtc.min.js",globalObjectName:"KVSWebRTC"})}class ne{constructor(e){r(this,"config"),r(this,"connection",null),r(this,"sdkLoader"),r(this,"factory"),r(this,"currentState","idle"),r(this,"currentError",null),r(this,"currentStats",null),r(this,"callbacks",{}),this.config=e,this.sdkLoader=te(),this.factory=new Y}setCallbacks(e){this.callbacks=e}async connect(){if("connecting"!==this.currentState&&"connected"!==this.currentState)try{this.setState("connecting"),this.setError(null),await this.ensureSDKLoaded();const e=await this.getWebRTCConfigFromAPI();this.connection=this.factory.createConnection({deviceId:this.config.deviceId,channel:this.config.channel,clientId:this.config.clientId||this.config.deviceId,role:this.config.role||"VIEWER",enableStats:this.config.enableStats??!0,statsInterval:this.config.statsInterval||2e3,provider:this,webrtcConfig:e,onStateChange:e=>this.setState(e),onError:e=>this.setError(e),onStreamReceived:e=>this.handleStreamReceived(e),onStatsUpdate:e=>this.handleStatsUpdate(e)}),await this.connection.connect()}catch(e){const t=e instanceof Error?e:new Error("Connection failed");throw this.setError(t),this.setState("failed"),t}}async disconnect(){try{this.connection&&(await this.connection.disconnect(),this.connection=null);try{await $({psn:this.config.deviceId})}catch(e){}this.currentStats=null,this.setState("disconnected")}catch(e){this.setState("disconnected")}}async changeChannel(e){if(e!==this.config.channel&&"connected"===this.currentState)try{const t={front:"1",rear:"2",interior:"3",exterior:"4"}[e]||"1";await q({psn:this.config.deviceId,param1:t}),this.config.channel=e}catch(t){throw new Error(`Failed to change channel: ${t instanceof Error?t.message:"Unknown error"}`)}}getState(){return this.currentState}getStats(){return this.currentStats}getError(){return this.currentError}getLocalAudioStream(){return this.connection?this.connection.getLocalAudioStream():null}updateConfig(e){this.config={...this.config,...e}}destroy(){this.connection&&(this.connection.destroy(),this.connection=null),this.callbacks={},this.currentStats=null,this.setState("idle")}setFactoryDependencies(e){this.factory.updateDependencies(e)}async startTwoWayAudio(e){try{const i=await W({psn:e,tokenType:"web"});if("busy"===i.status)throw new Error("Two-way audio is busy. Another user is currently using audio communication.");if(!this.connection)throw new Error("No WebRTC connection available");try{await this.connection.startTwoWayAudio()}catch(t){try{await K({psn:e,tokenType:"web"})}catch(n){}throw t}return i.status||"free"}catch(t){const e=`Failed to start two-way audio: ${t instanceof Error?t.message:"Unknown error"}`;throw new Error(e)}}async stopTwoWayAudio(e){try{if(this.connection)try{await this.connection.stopTwoWayAudio()}catch(t){}return(await K({psn:e,tokenType:"web"})).status||"free"}catch(t){const e=`Failed to stop two-way audio: ${t instanceof Error?t.message:"Unknown error"}`;throw new Error(e)}}setLocalAudioEnabled(e){this.connection&&this.connection.setLocalAudioEnabled(e)}async ensureSDKLoaded(){if(this.sdkLoader.isLoaded())this.updateFactoryWithLoadedSDK();else{if(this.sdkLoader.isLoading())return await this.sdkLoader.loadSDK(),void this.updateFactoryWithLoadedSDK();if(await this.sdkLoader.loadSDK(),this.sdkLoader.getError())throw new Error(`Failed to load WebRTC SDK: ${this.sdkLoader.getError()}`);this.updateFactoryWithLoadedSDK()}}updateFactoryWithLoadedSDK(){const e="undefined"!=typeof window?window.KVSWebRTC:null;if(!e)throw new Error("WebRTC SDK not available on window object after loading");this.factory.updateDependencies({webrtcSDK:e})}async getWebRTCConfigFromAPI(){try{const e={front:"1",rear:"2",interior:"3",exterior:"4"}[this.config.channel]||"1",t=await z(this.config.deviceId,e,this.config.clientId||this.config.deviceId,this.config.role||"VIEWER");return{region:t.region,channelARN:t.channelARN,url:t.url,iceServers:t.iceServers.map((e=>({urls:e.urls,username:e.username,credential:e.credential}))),endpointsByProtocol:t.endpointsByProtocol,role:t.role,clientId:t.clientId}}catch(e){throw new Error(`Failed to get WebRTC config: ${e instanceof Error?e.message:"Unknown error"}`)}}setState(e){var t,n;this.currentState!==e&&(this.currentState=e,null==(n=(t=this.callbacks).onStateChange)||n.call(t,e))}setError(e){var t,n;this.currentError=e,e&&(null==(n=(t=this.callbacks).onError)||n.call(t,e))}handleStreamReceived(e){var t,n;null==(n=(t=this.callbacks).onStreamReceived)||n.call(t,e)}handleStatsUpdate(e){var t,n;this.currentStats=e,null==(n=(t=this.callbacks).onStatsUpdate)||n.call(t,e)}async getWebRTCConfig(e){throw new Error("getWebRTCConfig() should not be called when pre-obtained config is available")}async stopStreaming(e){try{await $({psn:e})}catch(t){throw new Error(`Failed to stop streaming: ${t instanceof Error?t.message:"Unknown error"}`)}}}class ie{constructor(e,t=WebSocket,n={}){r(this,"config"),r(this,"callbacks",{}),r(this,"WebSocketClass"),r(this,"ws",null),r(this,"currentState","idle"),r(this,"reconnectTimer",null),r(this,"reconnectAttempts",0),r(this,"isConnecting",!1),this.config=e,this.WebSocketClass=t,this.callbacks=n}connect(){var e,t;if(!this.isConnecting&&"connected"!==this.currentState)try{this.isConnecting=!0,this.setState("connecting"),this.ws=new this.WebSocketClass(this.config.url),this.setupWebSocketHandlers()}catch(n){this.isConnecting=!1,this.setState("failed"),null==(t=(e=this.callbacks).onError)||t.call(e,n)}}disconnect(){this.clearReconnectTimer(),this.reconnectAttempts=this.config.maxReconnectAttempts,this.ws&&(this.ws.close(),this.ws=null),this.setState("disconnected"),this.isConnecting=!1}send(e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("WebSocket not connected");this.ws.send(e)}getState(){return this.currentState}isConnected(){var e;return"connected"===this.currentState&&(null==(e=this.ws)?void 0:e.readyState)===WebSocket.OPEN}updateConfig(e){this.config={...this.config,...e}}setCallbacks(e){this.callbacks={...this.callbacks,...e}}destroy(){this.clearReconnectTimer(),this.disconnect(),this.callbacks={}}setupWebSocketHandlers(){this.ws&&(this.ws.onopen=()=>{var e,t;this.isConnecting=!1,this.reconnectAttempts=0,this.setState("connected"),null==(t=(e=this.callbacks).onOpen)||t.call(e)},this.ws.onclose=e=>{var t,n;this.isConnecting=!1,this.setState("disconnected"),null==(n=(t=this.callbacks).onClose)||n.call(t,e),this.scheduleReconnect()},this.ws.onerror=e=>{var t,n;this.isConnecting=!1,this.setState("failed"),null==(n=(t=this.callbacks).onError)||n.call(t,e)},this.ws.onmessage=e=>{var t,n;null==(n=(t=this.callbacks).onMessage)||n.call(t,e)})}scheduleReconnect(){if(this.reconnectAttempts>=this.config.maxReconnectAttempts)return void this.setState("failed");this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.setState("reconnecting");const e=Math.min(this.config.reconnectDelay*Math.pow(2,this.reconnectAttempts),3e4);this.reconnectTimer=setTimeout((()=>{this.reconnectAttempts++,this.connect()}),e)}clearReconnectTimer(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}setState(e){var t,n;this.currentState!==e&&(this.currentState=e,null==(n=(t=this.callbacks).onStateChange)||n.call(t,e))}}class re{constructor(e){r(this,"config"),r(this,"currentToken",null),this.config=e}setToken(e){this.currentToken=e}getToken(){return this.currentToken}hasValidToken(){return!(!this.currentToken||0>=this.currentToken.length)}generateConnectionUrl(){if(!this.hasValidToken())return this.config.baseUrl;switch(this.config.authMethod){case"query":default:return`${this.config.baseUrl}?token=${encodeURIComponent(this.currentToken)}`;case"header":case"message":return this.config.baseUrl}}createAuthMessage(){return this.hasValidToken()&&"message"===this.config.authMethod?{type:"auth",token:this.currentToken,timestamp:Date.now()}:null}updateConfig(e){this.config={...this.config,...e}}clear(){this.currentToken=null}}class se{constructor(){r(this,"eventHandlers",{})}on(e,t){this.eventHandlers[e]||(this.eventHandlers[e]=[]),this.eventHandlers[e].push(t)}off(e,t){const n=this.eventHandlers[e];if(n){const e=n.indexOf(t);-1!==e&&n.splice(e,1)}}removeAllListeners(e){e?this.eventHandlers[e]=[]:this.eventHandlers={}}emit(e,t){(this.eventHandlers[e]||[]).forEach((e=>{try{e(t)}catch(n){}}))}getHandlerCount(e){var t;return(null==(t=this.eventHandlers[e])?void 0:t.length)||0}getRegisteredEvents(){return Object.keys(this.eventHandlers)}hasHandlers(e){return this.getHandlerCount(e)>0}destroy(){this.eventHandlers={}}}class oe{constructor(e=7e3){r(this,"pendingRequests",new Map),r(this,"defaultTimeout"),r(this,"sendFunction",null),this.defaultTimeout=e}setSendFunction(e){this.sendFunction=e}send(e,t,n){if(!this.sendFunction)return Promise.reject(new Error("Send function not configured"));const i=t.sequenceId??Date.now(),r={...t,action:e,sequenceId:i},s=n||this.defaultTimeout;return new Promise(((e,t)=>{const n=setTimeout((()=>{this.pendingRequests.delete(i),t(new Error(`Request timeout after ${s}ms`))}),s);this.pendingRequests.set(i,{resolve:e,reject:t,timer:n,timestamp:Date.now()});try{this.sendFunction(JSON.stringify(r))}catch(o){this.pendingRequests.delete(i),clearTimeout(n),t(o)}}))}handleMessage(e){var t;const n=(null==(t=e.response)?void 0:t.sequenceId)??e.sequenceId;if(!n||!this.pendingRequests.has(n))return!1;const i=this.pendingRequests.get(n);return clearTimeout(i.timer),this.pendingRequests.delete(n),"BC_ERR_OK"===e.resultcode?i.resolve(e):i.reject(new Error(e.message||"Request failed")),!0}cancelRequest(e){const t=this.pendingRequests.get(e);return!!t&&(clearTimeout(t.timer),this.pendingRequests.delete(e),t.reject(new Error("Request cancelled")),!0)}cancelAllRequests(){this.pendingRequests.forEach(((e,t)=>{clearTimeout(e.timer),e.reject(new Error("All requests cancelled"))})),this.pendingRequests.clear()}getPendingCount(){return this.pendingRequests.size}getPendingRequestIds(){return Array.from(this.pendingRequests.keys())}cleanupExpiredRequests(e=6e4){const t=Date.now();let n=0;return this.pendingRequests.forEach(((i,r)=>{t-i.timestamp>e&&(clearTimeout(i.timer),this.pendingRequests.delete(r),i.reject(new Error("Request expired")),n++)})),n}destroy(){this.cancelAllRequests(),this.sendFunction=null}}class ae{constructor(){r(this,"currentState","idle"),r(this,"transitions",[{from:"idle",event:"CONNECT",to:"connecting"},{from:"connecting",event:"CONNECTION_ESTABLISHED",to:"connected"},{from:"connecting",event:"FAILURE",to:"failed"},{from:"connected",event:"CONNECTION_LOST",to:"reconnecting"},{from:"reconnecting",event:"CONNECTION_ESTABLISHED",to:"connected"},{from:"reconnecting",event:"FAILURE",to:"failed"},{from:"reconnecting",event:"RECONNECT_ATTEMPT",to:"connecting"},{from:"connected",event:"DISCONNECT",to:"disconnected"},{from:"connecting",event:"DISCONNECT",to:"disconnected"},{from:"reconnecting",event:"DISCONNECT",to:"disconnected"},{from:"failed",event:"DISCONNECT",to:"disconnected"},{from:"disconnected",event:"CONNECT",to:"connecting"},{from:"failed",event:"CONNECT",to:"connecting"},{from:"disconnected",event:"RESET",to:"idle"},{from:"failed",event:"RESET",to:"idle"}]),r(this,"callbacks",{})}getState(){return this.currentState}canTransition(e){return this.transitions.some((t=>t.from===this.currentState&&t.event===e&&(!t.guard||t.guard())))}transition(e){var t,n,i,r;const s=this.transitions.find((t=>t.from===this.currentState&&t.event===e&&(!t.guard||t.guard())));if(!s)return null==(n=(t=this.callbacks).onInvalidTransition)||n.call(t,this.currentState,e),!1;const o=this.currentState;return this.currentState=s.to,null==(r=(i=this.callbacks).onStateChange)||r.call(i,this.currentState,o,e),!0}setCallbacks(e){this.callbacks=e}reset(){var e,t;const n=this.currentState;this.currentState="idle",null==(t=(e=this.callbacks).onStateChange)||t.call(e,"idle",n,"RESET")}isConnected(){return"connected"===this.currentState}isConnecting(){return"connecting"===this.currentState||"reconnecting"===this.currentState}hasFailed(){return"failed"===this.currentState}getStateDescription(){return{idle:"Not connected",connecting:"Establishing connection",connected:"Connected and ready",reconnecting:"Attempting to reconnect",disconnected:"Disconnected",failed:"Connection failed"}[this.currentState]}}const ce={WebSocket:WebSocket,console:console,setTimeout:setTimeout,clearTimeout:clearTimeout};class le{constructor(e={}){r(this,"dependencies"),this.dependencies={...ce,...e}}createConnectionManager(e,t=5,n=1e3){return new ie({url:e,maxReconnectAttempts:t,reconnectDelay:n},this.dependencies.WebSocket)}createAuthManager(e,t="query"){return new re({baseUrl:e,authMethod:t})}createEventManager(){return new se}createRequestManager(e=7e3){return new oe(e)}createStateMachine(){return new ae}createComponents(e){const t=this.createAuthManager(e.url,e.authMethod);return{connectionManager:this.createConnectionManager(e.url,e.maxReconnectAttempts,e.reconnectDelay),authManager:t,eventManager:this.createEventManager(),requestManager:this.createRequestManager(),stateMachine:this.createStateMachine()}}updateDependencies(e){this.dependencies={...this.dependencies,...e}}}class he{constructor(e={}){r(this,"connectionManager"),r(this,"authManager"),r(this,"eventManager"),r(this,"requestManager"),r(this,"stateMachine");const t=S(),n={url:function(){const e=a("gpsWebSocketUrl");if(!e)throw new Error("GPS WebSocket URL not configured. Please set VITE_GPS_WEBSOCKET_URL in .env file.");return e}(),token:(null==t?void 0:t.jwtToken)||"",email:(null==t?void 0:t.userEmail)||"",authMethod:"query",maxReconnectAttempts:5,reconnectDelay:1e3,requestTimeout:7e3,...e},i=(new le).createComponents(n);this.connectionManager=i.connectionManager,this.authManager=i.authManager,this.eventManager=i.eventManager,this.requestManager=i.requestManager,this.stateMachine=i.stateMachine,this.setupManagerIntegration()}setToken(e){this.authManager.setToken(e),this.updateConnectionUrl()}updateAuthFromS