@klever-one/web-sdk
Version:
Web SDK for integrating real-time room management and streaming functionality into web applications
1 lines • 95.2 kB
JavaScript
"use client";"use strict";var Fe=Object.defineProperty;var Ee=r=>{throw TypeError(r)};var He=(r,e,t)=>e in r?Fe(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var s=(r,e,t)=>He(r,typeof e!="symbol"?e+"":e,t),ye=(r,e,t)=>e.has(r)||Ee("Cannot "+t);var p=(r,e,t)=>(ye(r,e,"read from private field"),t?t.call(r):e.get(r)),ce=(r,e,t)=>e.has(r)?Ee("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(r):e.set(r,t),z=(r,e,t,i)=>(ye(r,e,"write to private field"),i?i.call(r,t):e.set(r,t),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Ce="initialData",ve="azureSttOpenai",j="openai",ze="occupyingResource",Ve="roomSessionTimeout",Ie="stt",Te="openai",xe="timeout",Ge="X-Nlp-Api-Key",$e="Pending",je="Failed",Ke=500,Je={1:"av_2",2:"av_5",3:"av_3",4:"av_4",5:"av_1"},Qe={"en-US":"Introduce yourself in exactly 2–3 sentences. Do not exceed 3 sentences. Be concise and clear about your main role and how you can help.","ko-KR":"자신을 반드시 2~3문장으로만 간단히 소개해주세요. 3문장을 넘기지 말고, 주요 역할과 어떻게 도울 수 있는지만 간결하게 설명해주세요."},qe=new Proxy(Qe,{get(r,e){return typeof e=="string"&&e in r?r[e]:r["ko-KR"]}}),Xe=60*1e3;var c=(r=>(r.IDLE="IDLE",r.DISCONNECTED="DISCONNECTED",r.CONNECTING="CONNECTING",r.CONNECTED="CONNECTED",r.RECONNECTING="RECONNECTING",r.FAILED="FAILED",r.ALLOCATING="ALLOCATING",r))(c||{}),w=(r=>(r.SUCCESS="SUCCESS",r.ERROR="ERROR",r.IN_PROGRESS="IN_PROGRESS",r))(w||{}),N=(r=>(r.SYSTEM="system",r.USER="user",r.ASSISTANT="assistant",r.DATA="data",r))(N||{}),h=(r=>(r.ROOM_CONNECTED="ROOM_CONNECTED",r.ROOM_DISCONNECTED="ROOM_DISCONNECTED",r.ROOM_CONNECTING="ROOM_CONNECTING",r.ROOM_RECONNECTING="ROOM_RECONNECTING",r.ROOM_CONNECTION_FAILED="ROOM_CONNECTION_FAILED",r.ROOM_INFO_UPDATED="ROOM_INFO_UPDATED",r.ROOM_SESSION_TIMEOUT="ROOM_SESSION_TIMEOUT",r.STREAMING_CONNECTED="STREAMING_CONNECTED",r.STREAMING_DISCONNECTED="STREAMING_DISCONNECTED",r.STREAMING_CONNECTING="STREAMING_CONNECTING",r.STREAMING_ALLOCATING="STREAMING_ALLOCATING",r.STREAMING_RECONNECTING="STREAMING_RECONNECTING",r.STREAMING_CONNECTION_FAILED="STREAMING_CONNECTION_FAILED",r.IDLE_TIMEOUT_EXPIRED="IDLE_TIMEOUT_EXPIRED",r.PAGE_UNLOAD="PAGE_UNLOAD",r.LIP_MOTION_START="LIP_MOTION_START",r.END_CONVERSATION="END_CONVERSATION",r.STREAMER_LIST_UPDATED="STREAMER_LIST_UPDATED",r.UNREAL_CONNECTED="UNREAL_CONNECTED",r.RECORDER_ERROR="RECORDER_ERROR",r))(h||{});const D=class D{constructor(){s(this,"listeners",new Map);Object.values(h).forEach(e=>{this.listeners.set(e,new Set)})}static getInstance(){return D.instance||(D.instance=new D),D.instance}emit(e){const t=this.listeners.get(e.type);t&&t.forEach(i=>{try{i(e)}catch{}})}subscribe(e,t){const i=this.listeners.get(e);return i?(i.add(t),()=>i.delete(t)):()=>{}}subscribeToMultiple(e,t){const i=e.map(n=>this.subscribe(n,t));return()=>i.forEach(n=>n())}clear(){this.listeners.forEach(e=>e.clear())}};s(D,"instance",null);let b=D;class Ye{constructor(e){s(this,"isDataChannelOpen",!1);s(this,"isVideoInitialized",!1);s(this,"isVideoStable",!1);s(this,"videoFrameCallbackHandle",null);s(this,"state","Initializing");s(this,"metrics",{networkQuality:-1,bitrate:-1,resolution:null});s(this,"connectionTimeout",null);s(this,"callbacks");s(this,"onDataChannelOpen",()=>{this.isDataChannelOpen=!0,this.checkReadyState()});s(this,"onVideoInitialized",()=>{this.isVideoInitialized=!0,this.verifyVideoStreamPlayback()});s(this,"onVideoStalled",()=>{this.state==="Ready"&&(this.state="Stalled",this.callbacks.onStalled())});s(this,"onVideoResumed",()=>{this.state==="Stalled"&&(this.state="Ready",this.callbacks.onResumed())});s(this,"onNetworkQualityChanged",e=>{this.metrics.networkQuality=e,this.callbacks.onMetricsUpdate(this.metrics)});s(this,"onBitrateChanged",e=>{this.metrics.bitrate=e,this.callbacks.onMetricsUpdate(this.metrics)});s(this,"onResolutionChanged",(e,t)=>{this.metrics.resolution={width:e,height:t},this.callbacks.onMetricsUpdate(this.metrics)});s(this,"onStreamerListMessage",e=>{const t=e.data?.messageStreamerList?.ids||[];this.callbacks.onStreamerListUpdate(t)});s(this,"onWebRtcFailed",e=>{this.callbacks.onWebRtcFailed(e)});s(this,"startConnectionTimer",(e=Xe)=>{typeof window>"u"||(this.clearConnectionTimer(),this.connectionTimeout=window.setTimeout(()=>{this.state!=="Ready"&&this.callbacks.onWebRtcFailed(`스트리밍 연결에 실패했습니다. 타임아웃: ${e}ms.`)},e))});s(this,"clearConnectionTimer",()=>{typeof window>"u"||this.connectionTimeout&&(window.clearTimeout(this.connectionTimeout),this.connectionTimeout=null)});s(this,"reset",()=>{this.isDataChannelOpen=!1,this.isVideoInitialized=!1,this.isVideoStable=!1,this.state="Initializing",this.metrics={networkQuality:-1,bitrate:-1,resolution:null},this.cancelVideoStreamVerification(),this.clearConnectionTimer()});this.callbacks=e}checkReadyState(){this.state==="Initializing"&&this.isDataChannelOpen&&this.isVideoInitialized&&this.isVideoStable&&(this.state="Ready",this.clearConnectionTimer(),this.callbacks.onReady())}cancelVideoStreamVerification(){if(!(typeof document>"u")&&this.videoFrameCallbackHandle){const e=document.getElementById("streamingVideo");if(e)try{e.cancelVideoFrameCallback(this.videoFrameCallbackHandle)}catch{}this.videoFrameCallbackHandle=null}}verifyVideoStreamPlayback(){if(typeof document>"u")return;const e=document.getElementById("streamingVideo");if(!e||typeof e.requestVideoFrameCallback!="function"){this.isVideoStable=!0,this.checkReadyState();return}let t=0;const i=()=>{t++,t>=5?(this.isVideoStable=!0,this.checkReadyState()):this.videoFrameCallbackHandle=e.requestVideoFrameCallback(i)};this.videoFrameCallbackHandle=e.requestVideoFrameCallback(i)}}var Ze=typeof global=="object"&&global&&global.Object===Object&&global,et=typeof self=="object"&&self&&self.Object===Object&&self,Le=Ze||et||Function("return this")(),F=Le.Symbol,Ue=Object.prototype,tt=Ue.hasOwnProperty,it=Ue.toString,V=F?F.toStringTag:void 0;function nt(r){var e=tt.call(r,V),t=r[V];try{r[V]=void 0;var i=!0}catch{}var n=it.call(r);return i&&(e?r[V]=t:delete r[V]),n}var st=Object.prototype,rt=st.toString;function at(r){return rt.call(r)}var ot="[object Null]",ct="[object Undefined]",be=F?F.toStringTag:void 0;function lt(r){return r==null?r===void 0?ct:ot:be&&be in Object(r)?nt(r):at(r)}function dt(r){return r!=null&&typeof r=="object"}var ht="[object Symbol]";function _e(r){return typeof r=="symbol"||dt(r)&<(r)==ht}function ut(r,e){for(var t=-1,i=r==null?0:r.length,n=Array(i);++t<i;)n[t]=e(r[t],t,r);return n}var mt=Array.isArray,Re=F?F.prototype:void 0,Ae=Re?Re.toString:void 0;function Pe(r){if(typeof r=="string")return r;if(mt(r))return ut(r,Pe)+"";if(_e(r))return Ae?Ae.call(r):"";var e=r+"";return e=="0"&&1/r==-1/0?"-0":e}var gt=/\s/;function ft(r){for(var e=r.length;e--&>.test(r.charAt(e)););return e}var St=/^\s+/;function pt(r){return r&&r.slice(0,ft(r)+1).replace(St,"")}function Y(r){var e=typeof r;return r!=null&&(e=="object"||e=="function")}var we=NaN,Et=/^[-+]0x[0-9a-f]+$/i,yt=/^0b[01]+$/i,Ct=/^0o[0-7]+$/i,vt=parseInt;function Ne(r){if(typeof r=="number")return r;if(_e(r))return we;if(Y(r)){var e=typeof r.valueOf=="function"?r.valueOf():r;r=Y(e)?e+"":e}if(typeof r!="string")return r===0?r:+r;r=pt(r);var t=yt.test(r);return t||Ct.test(r)?vt(r.slice(2),t?2:8):Et.test(r)?we:+r}function It(r){return r==null?"":Pe(r)}var le=function(){return Le.Date.now()},Tt="Expected a function",bt=Math.max,Rt=Math.min;function We(r,e,t){var i,n,a,o,l,d,m=0,u=!1,g=!1,y=!0;if(typeof r!="function")throw new TypeError(Tt);e=Ne(e)||0,Y(t)&&(u=!!t.leading,g="maxWait"in t,a=g?bt(Ne(t.maxWait)||0,e):a,y="trailing"in t?!!t.trailing:y);function v(S){var R=i,H=n;return i=n=void 0,m=S,o=r.apply(H,R),o}function se(S){return m=S,l=setTimeout(B,e),u?v(S):o}function re(S){var R=S-d,H=S-m,pe=e-R;return g?Rt(pe,a-H):pe}function G(S){var R=S-d,H=S-m;return d===void 0||R>=e||R<0||g&&H>=a}function B(){var S=le();if(G(S))return $(S);l=setTimeout(B,re(S))}function $(S){return l=void 0,y&&i?v(S):(i=n=void 0,o)}function ae(){l!==void 0&&clearTimeout(l),m=0,i=d=n=l=void 0}function Be(){return l===void 0?o:$(le())}function oe(){var S=le(),R=G(S);if(i=arguments,n=this,d=S,R){if(l===void 0)return se(d);if(g)return clearTimeout(l),l=setTimeout(B,e),v(d)}return l===void 0&&(l=setTimeout(B,e)),o}return oe.cancel=ae,oe.flush=Be,oe}var At="Expected a function";function wt(r,e,t){var i=!0,n=!0;if(typeof r!="function")throw new TypeError(At);return Y(t)&&(i="leading"in t?!1:i,n="trailing"in t?!0:n),We(r,e,{leading:i,maxWait:e,trailing:n})}var Nt=0;function W(r){var e=++Nt;return It(r)+e}const O=class O{constructor(){s(this,"stream",null);s(this,"mediaState",{isStreamReady:!1,isReadyToSend:!1,keepAliveInterval:null});s(this,"isReadyToSendListeners",[]);s(this,"hasSentResetMessage",!1);s(this,"eventBus");this.setupBeforeUnloadHandler(),this.eventBus=b.getInstance()}static getInstance(){return O.instance||(O.instance=new O),O.instance}setStream(e){this.stream=e,e?(this.setupStreamMediaHandlers(),this.startKeepAlive()):this.stopKeepAlive()}setupStreamMediaHandlers(){this.stream&&this.stream.addResponseEventListener("handleResponseFunction",e=>{this.handleResponseFunction(e)})}setupBeforeUnloadHandler(){typeof window>"u"||window.addEventListener("beforeunload",()=>{if(this.stream&&!this.hasSentResetMessage)try{this.emitToDataChannel({messageType:"reset",timestamp:Date.now(),sessionId:W("session_"),payload:{}}),this.hasSentResetMessage=!0}catch{}})}startKeepAlive(){this.mediaState.keepAliveInterval&&clearInterval(this.mediaState.keepAliveInterval),this.mediaState.keepAliveInterval=setInterval(()=>{this.sendKeepAlive()},3e4)}stopKeepAlive(){this.mediaState.keepAliveInterval&&(clearInterval(this.mediaState.keepAliveInterval),this.mediaState.keepAliveInterval=null)}emitToDataChannel(e){if(this.stream)try{this.stream.emitUIInteraction(e)}catch{}}sendKeepAlive(){if(this.stream)try{const e={messageType:"keepAlive",timestamp:Date.now(),sessionId:W("session_"),payload:{}};this.emitToDataChannel(e)}catch{}}async handleResponseFunction(e){try{if(e==="UnrealConnected"){this.mediaState.isStreamReady&&(this.eventBus.emit({type:h.UNREAL_CONNECTED,timestamp:Date.now()}),this.sendWebConnectedInfo(),this.sendAccountType(),this.setIsReadyToSend(!0));return}else if(e==="LipmotionStart"){this.handleLipmotionStart();return}const t=JSON.parse(e);t.messageType==="updateMessage"&&this.handleUpdateMessage(t)}catch{e.includes("UnrealConnected")&&this.setIsReadyToSend(!0)}}sendWebConnectedInfo(){this.stream&&this.emitToDataChannel({Category:"SystemSetting",Type:"WebConnected",Width:typeof window<"u"?window.innerWidth:0})}handleLipmotionStart(){this.eventBus.emit({type:h.LIP_MOTION_START,timestamp:Date.now()})}handleUpdateMessage(e){if(typeof window>"u"||typeof CustomEvent>"u")return;const{text:t,index:i,isAllCompleted:n}=e.payload,a=new CustomEvent("updateMessage",{detail:{source:"streaming",text:t,index:i,isAllCompleted:n}});window.dispatchEvent(a)}ttsInteractions(e,t,i,n){!this.stream||!e||(this.emitToDataChannel({Category:"VoiceSetting",Type:"TTS",Value:n}),this.emitToDataChannel({Category:"VoiceSetting",Type:"Voice",Value:t}),this.emitToDataChannel({Category:"VoiceSetting",Type:"Language",Value:i}),this.emitToDataChannel({Category:"VoiceSetting",Type:"Script",Value:encodeURIComponent(e)}))}sendStartConversation(){this.stream&&this.emitToDataChannel({Category:"ConversationSetting",Type:"StartConversation"})}endConversation(){if(this.stream)try{this.emitToDataChannel({Category:"ConversationSetting",Type:"EndConversation"}),this.eventBus.emit({type:h.END_CONVERSATION,timestamp:Date.now()})}catch{}}autoPlayVideo(){if(this.stream)try{this.stream.play()}catch{}}play(){if(this.stream)try{this.stream.play()}catch{}}isReadyToSendMessages(){return this.mediaState.isReadyToSend}isStreamReady(){return this.mediaState.isStreamReady}resetIsReadyToSend(){this.setIsReadyToSend(!1)}resetIsStreamReady(){this.setIsStreamReady(!1)}setIsReadyToSend(e){this.mediaState.isReadyToSend!==e&&(this.mediaState.isReadyToSend=e,this.isReadyToSendListeners.forEach(t=>{try{t(e)}catch{}}))}setIsStreamReady(e){this.mediaState.isStreamReady!==e&&(this.mediaState.isStreamReady=e)}sendAccountType(){this.stream&&this.emitToDataChannel({Category:"SystemSetting",Type:"RoleId",Value:"ROLE_3"})}addIsReadyToSendListener(e){this.removeIsReadyToSendListener(e),this.isReadyToSendListeners.push(e),e(this.mediaState.isReadyToSend)}removeIsReadyToSendListener(e){this.isReadyToSendListeners=this.isReadyToSendListeners.filter(t=>t!==e)}cleanup(){this.stopKeepAlive(),this.setIsReadyToSend(!1),this.isReadyToSendListeners.length=0,this.hasSentResetMessage=!1,this.stream=null}};s(O,"instance",null);let Z=O;const M=class M{constructor(){s(this,"stream",null);s(this,"resizeHandler",null);s(this,"currentCamera","cam1");s(this,"containerElement",null);s(this,"handleMouseDown",e=>{if(typeof document>"u"||e.button!==0)return;const t=document.getElementById("streamingVideo");t&&(t.style.cursor="default",this.emitUnrealCoordinates(e,"MouseDown"))});s(this,"handleMouseUp",e=>{if(typeof document>"u"||e.button!==0)return;const t=document.getElementById("streamingVideo");t&&(t.style.cursor="default",this.emitUnrealCoordinates(e,"MouseUp"))});s(this,"emitUnrealCoordinates",(e,t)=>{if(typeof document>"u")return;const i=document.getElementById("streamingVideo");if(!i)return;const n=i.videoWidth,a=i.videoHeight,o=i.getBoundingClientRect(),l=o.width,d=o.height,m=Math.max(l/n,d/a),u=n*m,g=a*m,y=(u-l)/2,v=(g-d)/2,se=e.clientX-o.left+y,re=e.clientY-o.top+v,G=se/u,B=re/g,$=G*n,ae=B*a;this.emitToDataChannel({Category:"PageSetting",type:t,x:$,y:ae})})}static getInstance(){return M.instance||(M.instance=new M),M.instance}setupResizeHandler(){typeof window>"u"||!this.containerElement||!this.stream||(this.resizeHandler=()=>{setTimeout(()=>{const e=this.containerElement?.offsetWidth,t=this.containerElement?.offsetHeight;this.stream&&e&&t&&this.resize(e,t)},100)},window.addEventListener("resize",this.resizeHandler),this.resizeHandler())}setStream(e){this.stream=e}setContainer(e){this.containerElement=e}getContainer(){return this.containerElement}getResizeHandler(){return this.resizeHandler}resetResizeHandler(){this.resizeHandler=null}resize(e,t,i){if(!this.stream)return;const n=e/3*2,a=t/3*2;try{const o=`r.setRes ${n}x${a}`;this.emitToDataChannel({Category:"PageSetting",Type:"WindowSize",Value:o,Width:e,Height:t,CamNum:this.currentCamera})}catch{}}emitToDataChannel(e){if(this.stream)try{this.stream.emitUIInteraction(e)}catch{}}enteredFullpage(){if(typeof window>"u"||typeof document>"u"||!this.stream)return;const e=window.innerWidth,t=window.innerHeight;this.emitToDataChannel({Category:"PageSetting",Type:"EnteredFullPage"}),this.setCameraConfig("cam2");const i=document.getElementById("streamingVideo");i?.addEventListener("mousedown",this.handleMouseDown),i?.addEventListener("mouseup",this.handleMouseUp),this.resize(e,t)}exitedFullpage(e){if(this.stream)try{if(this.emitToDataChannel({Category:"UI",Type:"ExitedFullpage"}),e.current){const t=e.current.getBoundingClientRect();this.resize(t.width,t.height,"exitFullpage")}}catch{}}setCameraConfig(e){this.currentCamera=e}sendAvatarNum(e){if(this.stream)try{this.emitToDataChannel({Category:"AvatarSetting",Type:"AvatarNum",Value:e})}catch{}}sendAvatarAppearanceChange(e){if(this.stream)try{if(e.avatarType.includes("Custom")&&!["1","2","3","4","5"].includes(e.avatarAppearanceRefVal1??"")){const i={TaskID:e.avatarAppearanceRefVal1??"",Type:"Custom",Gender:e.gender??"M",selectedAppearance:e.outfit};this.sendAvatarAppearanceChangeInternal({payload:i,isCustom:!0})}else{const i={PresetID:Je[e.avatarAppearanceRefVal1??""]??"av_3",Type:"Preset",selectedAppearance:e.outfit};this.sendAvatarAppearanceChangeInternal({payload:i,isCustom:!1})}}catch{}}sendAvatarAppearanceChangeInternal({payload:e}){if(this.stream)try{const t={sessionId:W(`#sess_${Date.now()}_`),messageType:"avatar.change",timestamp:Date.now(),payload:e};this.emitToDataChannel({Category:"AvatarSetting",Type:"AvatarAppearance",Value:t})}catch{}}generateDigitalHuman(e,t){if(this.stream)try{this.emitToDataChannel({Category:"CustomSetting",Type:"GenerateAvatarHead",TaskId:e,Gender:t})}catch{}}sendFailedGenerateAvatarHead(){if(this.stream)try{this.emitToDataChannel({Category:"CustomSetting",Type:"FailedGenerateAvatarHead"})}catch{}}sendPlaceBackgroundChange(e){if(this.stream)try{this.emitToDataChannel({Category:"BackgoundSetting",Type:"BackgroundImage",Value:e})}catch{}}sendPageSetting(e,t){if(this.stream)try{this.emitToDataChannel({Category:"PageSetting",Type:e,Value:t})}catch{}}sendVoiceSetting(e){if(this.stream)try{this.emitToDataChannel({Category:"VoiceSetting",Type:"Voice",Value:e})}catch{}}emitUIInteraction(e){if(this.stream)try{this.emitToDataChannel(e)}catch{}}reset(){if(this.stream)try{this.emitToDataChannel({Category:"SystemSetting",Type:"Reset"})}catch{}}cleanup(){typeof window>"u"||(this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null),this.stream=null)}};s(M,"instance",null);let ee=M;const k=class k{constructor(){s(this,"stream",null);s(this,"application",null);s(this,"currentStatus",c.IDLE);s(this,"hasSentResetMessage",!1);s(this,"onWebRtcConnectedHandler",null);s(this,"onWebRtcDisconnectedHandler",null);s(this,"onWebRtcPlayStreamErrorHandler",null);s(this,"onWebRtcFailedEventHandler",null);s(this,"beforeUnloadHandler",null);s(this,"reconnectTimeout",null);s(this,"hasFailedConnection",!1);s(this,"reconnectAttempts",0);s(this,"maxReconnectAttempts",3);s(this,"isDisconnecting",!1);s(this,"signalingServerUrl",null);s(this,"config",{});s(this,"eventBus");s(this,"statusListeners",[]);s(this,"disconnectListeners",[]);s(this,"eventUnsubscribeFunctions",[]);s(this,"keepAliveInterval",null);s(this,"uiService");s(this,"mediaService");s(this,"readyStateManager");s(this,"initialAudioTrack",null);s(this,"lockedAudioElement",null);s(this,"onStreamReady",()=>{const e=this.uiService.getContainer(),t=(typeof document<"u"?document.getElementById("streaming-container"):null)||e;if(t){const{offsetWidth:i,offsetHeight:n}=t;this.uiService.resize(i,n)}this.mediaService.setIsStreamReady(!0)});s(this,"onStreamStalled",()=>{});s(this,"onStreamResumed",()=>{});s(this,"onStreamMetricsUpdate",e=>{});s(this,"onStreamerListUpdate",e=>{this.eventBus.emit({type:h.STREAMER_LIST_UPDATED,timestamp:Date.now(),data:{streamerList:e}})});s(this,"onWebRtcFailed",e=>{const t=e||"연결에 실패했습니다. 잠시 후 다시 시도해 주세요.";this.eventBus.emit({type:h.STREAMING_CONNECTION_FAILED,timestamp:Date.now(),data:{message:t}})});this.eventBus=b.getInstance(),this.uiService=ee.getInstance(),this.mediaService=Z.getInstance();const e={onReady:this.onStreamReady,onStalled:this.onStreamStalled,onResumed:this.onStreamResumed,onMetricsUpdate:this.onStreamMetricsUpdate,onStreamerListUpdate:this.onStreamerListUpdate,onWebRtcFailed:this.onWebRtcFailed};this.readyStateManager=new Ye(e),this.setupEventListeners()}static getInstance(){return k.instance||(k.instance=new k),k.instance}async loadEpicGamesLibraries(){if(typeof window>"u")throw new Error("Epic Games libraries can only be loaded in browser environment");const[e,t]=await Promise.all([Promise.resolve().then(()=>require("../lib-pixelstreamingfrontend.esm-BJoFoV0d.cjs")),Promise.resolve().then(()=>require("../lib-pixelstreamingfrontend-ui.esm-CwJ1KGGf.cjs"))]);return{Config:e.Config,PixelStreaming:e.PixelStreaming,NumericParameters:e.NumericParameters,Application:t.Application,PixelStreamingApplicationStyle:t.PixelStreamingApplicationStyle,UIElementCreationMode:t.UIElementCreationMode}}cleanupEventListeners(){this.eventUnsubscribeFunctions.forEach(e=>e()),this.eventUnsubscribeFunctions.length=0,this.beforeUnloadHandler&&typeof window<"u"&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null)}setupEventListeners(){this.cleanupEventListeners();const e=this.eventBus.subscribe(h.ROOM_INFO_UPDATED,n=>{const a=n.data?.roomInfo;a?.resourceInfo?.url&&this.connect(a.resourceInfo.url).catch(o=>{this.setStatus(c.FAILED)})}),t=this.eventBus.subscribe(h.ROOM_DISCONNECTED,()=>{this.disconnect()}),i=this.eventBus.subscribe(h.UNREAL_CONNECTED,()=>{this.stream&&this.setupLockedAudio(this.stream)});this.eventUnsubscribeFunctions.push(e,t,i),typeof window<"u"&&(this.beforeUnloadHandler=()=>{if(this.stream&&!this.hasSentResetMessage)try{this.stream.webSocketController?.webSocket?.readyState===WebSocket.OPEN&&(this.reset(),this.hasSentResetMessage=!0)}catch{}},window.addEventListener("beforeunload",this.beforeUnloadHandler))}reset(){this.stream&&this.emitToDataChannel({Category:"SystemSetting",Type:"Reset"})}emitToDataChannel(e){if(this.stream)try{this.stream.emitUIInteraction(e)}catch{}}setContainer(e){this.uiService.setContainer(e),this.application?.rootElement&&e&&(e.appendChild(this.application.rootElement),this.uiService.setupResizeHandler())}forceResetContainer(e){const t=this.uiService.getContainer();if(this.application?.rootElement)try{t&&t.contains(this.application.rootElement)&&t.removeChild(this.application.rootElement),this.application.rootElement.parentElement&&this.application.rootElement.parentElement.removeChild(this.application.rootElement)}catch{}this.uiService.setContainer(e)}async connect(e,t={}){if(!(this.currentStatus===c.CONNECTING||this.currentStatus===c.CONNECTED)){this.signalingServerUrl=e,this.config=t,this.setStatus(c.CONNECTING);try{await this.createStreamingConnection()}catch(i){throw this.setStatus(c.FAILED),i}}}async createStreamingConnection(){if(!this.signalingServerUrl)throw new Error("Signaling server URL not provided");const e=await this.loadEpicGamesLibraries(),t=new e.Config({useUrlParams:!0});t.setTextSetting("ss",this.signalingServerUrl),t.setNumericSetting(e.NumericParameters.MaxReconnectAttempts,0),t.setFlagEnabled("KeyboardInput",this.config.setKeyboardEnabled??!1),t.setFlagEnabled("HoveringMouse",!1),t.setFlagEnabled("MouseInput",!1);const i=new e.PixelStreamingApplicationStyle;i.applyStyleSheet(),this.stream=new e.PixelStreaming(t),this.uiService.setStream(this.stream),this.application=new e.Application({stream:this.stream,onColorModeChanged:a=>i.setColorMode(a),fullScreenControlsConfig:{creationMode:e.UIElementCreationMode.Disable},settingsPanelConfig:{isEnabled:!1,visibilityButtonConfig:{creationMode:e.UIElementCreationMode.Disable}},statsPanelConfig:{isEnabled:!1,visibilityButtonConfig:{creationMode:e.UIElementCreationMode.Disable}},videoQpIndicatorConfig:{disableIndicator:!0}});const n=this.uiService.getContainer();if(n&&this.application.rootElement&&(n.innerHTML="",n.appendChild(this.application.rootElement),typeof document<"u")){const a=document.getElementById("streamingVideo");a&&(a.style.objectFit="cover",document.addEventListener("pointerlockchange",()=>{document.exitPointerLock()}))}this.stream._onDisconnect=()=>{this.setStatus(c.DISCONNECTED),this.notifyDisconnectListeners(),!this.isDisconnecting&&this.hasFailedConnection&&this.scheduleReconnect()},this.registerEventHandlers();try{this.readyStateManager.startConnectionTimer(),this.stream.connect(),this.keepAliveInterval&&clearInterval(this.keepAliveInterval),this.keepAliveInterval=setInterval(()=>{try{this.stream?.webSocketController?.webSocket?.readyState===WebSocket.OPEN&&this.stream.webSocketController.webSocket.send(JSON.stringify({type:"ping"}))}catch(a){}},3e4)}catch(a){throw this.hasFailedConnection=!0,this.setStatus(c.FAILED),a}}registerEventHandlers(){this.stream&&(this.onWebRtcConnectedHandler=()=>{this.hasFailedConnection=!1,this.reconnectAttempts=0,this.setStatus(c.CONNECTED)},this.stream.addEventListener("webRtcConnected",this.onWebRtcConnectedHandler),this.onWebRtcDisconnectedHandler=()=>{this.isDisconnecting||this.setStatus(c.DISCONNECTED)},this.stream.addEventListener("webRtcDisconnected",this.onWebRtcDisconnectedHandler),this.onWebRtcPlayStreamErrorHandler=()=>{},this.stream.addEventListener("playStreamError",this.onWebRtcPlayStreamErrorHandler),this.onWebRtcFailedEventHandler=()=>{this.readyStateManager.onWebRtcFailed("Failure reported by Streaming library.")},this.stream.addEventListener("webRtcFailed",this.onWebRtcFailedEventHandler),this.stream.addEventListener("videoInitialized",this.readyStateManager.onVideoInitialized),this.stream.addEventListener("dataChannelOpen",this.readyStateManager.onDataChannelOpen),this.stream.addEventListener("streamerListMessage",this.readyStateManager.onStreamerListMessage))}unregisterEventHandlers(){this.stream&&(this.onWebRtcConnectedHandler&&(this.stream.removeEventListener("webRtcConnected",this.onWebRtcConnectedHandler),this.onWebRtcConnectedHandler=null),this.onWebRtcDisconnectedHandler&&(this.stream.removeEventListener("webRtcDisconnected",this.onWebRtcDisconnectedHandler),this.onWebRtcDisconnectedHandler=null),this.onWebRtcPlayStreamErrorHandler&&(this.stream.removeEventListener("playStreamError",this.onWebRtcPlayStreamErrorHandler),this.onWebRtcPlayStreamErrorHandler=null),this.onWebRtcFailedEventHandler&&(this.stream.removeEventListener("webRtcFailed",this.onWebRtcFailedEventHandler),this.onWebRtcFailedEventHandler=null),this.stream.removeEventListener("videoInitialized",this.readyStateManager.onVideoInitialized),this.stream.removeEventListener("dataChannelOpen",this.readyStateManager.onDataChannelOpen),this.stream.removeEventListener("streamerListMessage",this.readyStateManager.onStreamerListMessage))}setupLockedAudio(e){const i=e._webRtcController?.streamController?.audioElement;if(!i)return;const n=i.srcObject;if(!n)return;const a=n.getAudioTracks();a.length>0&&!this.initialAudioTrack&&(this.initialAudioTrack=a[0],this.createLockedAudio(this.initialAudioTrack));const o=l=>{l.track.kind==="audio"&&!this.initialAudioTrack&&(this.initialAudioTrack=l.track,this.createLockedAudio(this.initialAudioTrack))};n.addEventListener("addtrack",o)}createLockedAudio(e){typeof document>"u"||(this.lockedAudioElement=document.createElement("audio"),this.lockedAudioElement.autoplay=!0,this.lockedAudioElement.controls=!1,this.lockedAudioElement.muted=!1,this.lockedAudioElement.srcObject=new MediaStream([e]),document.body.appendChild(this.lockedAudioElement))}disconnect(){if(!this.isDisconnecting)if(this.isDisconnecting=!0,this.stream)try{this.reset(),setTimeout(()=>{this.performDisconnect()},100)}catch{this.performDisconnect()}else this.performDisconnect()}performDisconnect(){if(this.readyStateManager.reset(),this.reconnectTimeout&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null),this.initialAudioTrack){try{this.initialAudioTrack.stop()}catch{}this.initialAudioTrack=null}if(this.lockedAudioElement&&(this.lockedAudioElement.srcObject=null,this.lockedAudioElement.remove(),this.lockedAudioElement=null),this.keepAliveInterval&&(clearInterval(this.keepAliveInterval),this.keepAliveInterval=null),this.uiService.getResizeHandler()&&typeof window<"u"&&(window.removeEventListener("resize",this.uiService.getResizeHandler()),this.uiService.resetResizeHandler()),this.stream&&(this.unregisterEventHandlers(),this.stream.disconnect(),this.stream.webSocketController?.webSocket&&this.stream.webSocketController.webSocket.close(),this.stream=null),this.application)try{const e=this.uiService.getContainer();e&&this.application.rootElement&&e.removeChild(this.application.rootElement),this.application=null}catch{}this.uiService.setContainer(null),this.setStatus(c.DISCONNECTED),this.notifyDisconnectListeners(),this.cleanupEventListeners(),this.isDisconnecting=!1,this.reconnectAttempts=0,this.hasFailedConnection=!1,this.hasSentResetMessage=!1}async reconnect(){this.eventBus.emit({type:h.STREAMING_RECONNECTING,timestamp:Date.now()})}scheduleReconnect(){if(this.reconnectAttempts>=this.maxReconnectAttempts){this.setStatus(c.FAILED);return}const e=Math.pow(2,this.reconnectAttempts)*1e3;this.reconnectTimeout=setTimeout(async()=>{this.reconnectAttempts++,this.setStatus(c.CONNECTING);try{this.signalingServerUrl&&await this.connect(this.signalingServerUrl,this.config)}catch{this.scheduleReconnect()}},e)}setStatus(e){if(this.currentStatus===e)return;this.currentStatus=e;const t=this.getConnectionEventType(e);this.eventBus.emit({type:t,timestamp:Date.now(),data:{status:e}}),this.statusListeners.forEach((i,n)=>{try{`${n}`,i(e)}catch{}})}getConnectionEventType(e){switch(e){case c.CONNECTED:return h.STREAMING_CONNECTED;case c.DISCONNECTED:return h.STREAMING_DISCONNECTED;case c.CONNECTING:return h.STREAMING_CONNECTING;case c.ALLOCATING:return h.STREAMING_ALLOCATING;case c.FAILED:return h.STREAMING_CONNECTION_FAILED;default:return h.STREAMING_DISCONNECTED}}notifyDisconnectListeners(){this.disconnectListeners.forEach(e=>{try{e()}catch{}})}updateInputOptions(e,t){this.stream&&(this.stream.config.setFlagEnabled("MouseInput",e),this.stream.config.setFlagEnabled("KeyboardInput",t)),this.config.setMouseEnabled=e,this.config.setKeyboardEnabled=t}getStatus(){return this.currentStatus}getStream(){return this.stream}getUIService(){return this.uiService}addStatusListener(e){this.statusListeners.push(e),e(this.currentStatus)}removeStatusListener(e){this.statusListeners=this.statusListeners.filter(t=>t!==e)}addDisconnectListener(e){this.disconnectListeners.push(e)}removeDisconnectListener(e){this.disconnectListeners=this.disconnectListeners.filter(t=>t!==e)}addResponseListener(e){this.stream&&this.stream.addResponseEventListener("handle_responses",e)}removeResponseListener(e){this.stream&&this.stream.removeResponseEventListener("handle_responses")}};s(k,"instance",null);let de=k;const I=class I{constructor(){s(this,"connectionService");s(this,"mediaService");s(this,"uiService");this.connectionService=de.getInstance(),this.mediaService=Z.getInstance(),this.uiService=ee.getInstance(),this.setupServiceCoordination()}static getInstance(){return I.instance||(I.instance=new I),I.instance}setupServiceCoordination(){this.connectionService.addStatusListener(e=>{const t=this.connectionService.getStream();e===c.CONNECTED&&t?(this.mediaService.setStream(t),this.uiService.setStream(t)):e===c.DISCONNECTED&&(this.mediaService.setStream(null),this.uiService.setStream(null))}),this.connectionService.addDisconnectListener(()=>{this.mediaService.cleanup()})}setContainer(e){this.connectionService.setContainer(e)}forceResetContainer(e){this.connectionService.forceResetContainer(e)}async connect(e,t={}){const i={setKeyboardEnabled:t.setKeyboardEnabled,setMouseEnabled:t.setMouseEnabled,initStreamingConfig:t.initStreamingConfig};return this.connectionService.connect(e,i)}disconnect(){this.connectionService.disconnect(),this.mediaService.cleanup(),this.uiService.cleanup()}async reconnect(){return this.connectionService.reconnect()}updateInputOptions(e,t){this.connectionService.updateInputOptions(e,t)}getStatus(){return this.connectionService.getStatus()}getStream(){return this.connectionService.getStream()}getApplication(){return this.connectionService.application}getConnectionService(){return this.connectionService}getContainer(){return this.uiService.getContainer()}addStatusListener(e){this.connectionService.addStatusListener(e)}removeStatusListener(e){this.connectionService.removeStatusListener(e)}addDisconnectListener(e){this.connectionService.addDisconnectListener(e)}removeDisconnectListener(e){this.connectionService.removeDisconnectListener(e)}ttsInteractions(e,t,i,n){this.mediaService.ttsInteractions(e,t,i,n)}sendStartConversation(){this.mediaService.sendStartConversation()}endConversation(){this.mediaService.endConversation()}autoPlayVideo(){this.mediaService.autoPlayVideo()}play(){this.mediaService.play()}isReadyToSendMessages(){return this.mediaService.isReadyToSendMessages()}resetIsReadyToSend(){this.mediaService.resetIsReadyToSend()}resetIsStreamReady(){this.mediaService.resetIsStreamReady()}addIsReadyToSendListener(e){this.mediaService.addIsReadyToSendListener(e)}removeIsReadyToSendListener(e){this.mediaService.removeIsReadyToSendListener(e)}resize(e,t,i){this.uiService.resize(e,t,i)}enteredFullpage(){this.uiService.enteredFullpage()}exitedFullpage(e){this.uiService.exitedFullpage(e)}sendAvatarNum(e){this.uiService.sendAvatarNum(e)}sendAvatarAppearanceChange(e){this.uiService.sendAvatarAppearanceChange(e)}generateDigitalHuman(e,t){this.uiService.generateDigitalHuman(e,t)}sendFailedGenerateAvatarHead(){this.uiService.sendFailedGenerateAvatarHead()}sendPlaceBackgroundChange(e){this.uiService.sendPlaceBackgroundChange(e)}sendPageSetting(e,t){this.uiService.sendPageSetting(e,t)}sendVoiceSetting(e){this.uiService.sendVoiceSetting(e)}reset(){this.uiService.reset()}async handleResponseFunction(e){return this.mediaService.handleResponseFunction(e)}emitUIInteraction(e){this.uiService.emitUIInteraction(e)}cleanup(){this.disconnect()}static destroyInstance(){I.instance&&(I.instance.cleanup(),I.instance=null)}};s(I,"instance",null);let x=I;const L=class L{constructor(){s(this,"autoDisconnectTimer",null);s(this,"idleTimer",null);s(this,"IDLE_TIMEOUT",300*1e3);s(this,"AUTO_DISCONNECT_INTERVAL",5*1e3);s(this,"userActivityEventsRegistered",!1);s(this,"eventBus");s(this,"userEvents",["mousemove","keydown","click","scroll","wheel","contextmenu","focus","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointercancel","dragstart","dragend","drop","input","change","submit","paste","copy","cut","select","selectstart","selectionchange","gesturestart","gesturechange","gestureend","orientationchange"]);s(this,"userEventHandler");s(this,"boundHandleVisibilityChange");s(this,"boundHandleBeforeUnload");this.eventBus=b.getInstance(),this.userEventHandler=We(()=>this.onUserActivity(),200),this.boundHandleVisibilityChange=this.handleVisibilityChange.bind(this),this.boundHandleBeforeUnload=this.handleBeforeUnload.bind(this)}static getInstance(){return L.instance||(L.instance=new L),L.instance}startIdleTimer(){this.clearIdleTimer();const e=Date.now();this.idleTimer=setInterval(()=>{Date.now()-e>=this.IDLE_TIMEOUT&&(this.clearIdleTimer(),this.startAutoDisconnectTimer())},1e3)}resetIdleTimer(){this.clearIdleTimer(),this.startIdleTimer()}clearIdleTimer(){this.idleTimer&&(clearInterval(this.idleTimer),this.idleTimer=null)}startAutoDisconnectTimer(){this.stopAutoDisconnectTimer();const e=Date.now();this.autoDisconnectTimer=setInterval(()=>{Date.now()-e>=this.AUTO_DISCONNECT_INTERVAL&&(this.autoDisconnectTimer&&(clearInterval(this.autoDisconnectTimer),this.autoDisconnectTimer=null),this.eventBus.emit({type:h.IDLE_TIMEOUT_EXPIRED,timestamp:Date.now()}))},1e3)}stopAutoDisconnectTimer(){this.autoDisconnectTimer&&(clearInterval(this.autoDisconnectTimer),this.autoDisconnectTimer=null)}registerUserActivityEvents(){typeof window>"u"||typeof document>"u"||typeof navigator>"u"||this.userActivityEventsRegistered||(this.userActivityEventsRegistered=!0,this.userEvents.forEach(e=>window.addEventListener(e,this.userEventHandler)),document.addEventListener("visibilitychange",this.boundHandleVisibilityChange),window.addEventListener("online",this.userEventHandler),window.addEventListener("offline",this.userEventHandler),"connection"in navigator&&navigator.connection?.addEventListener("change",this.userEventHandler),window.addEventListener("beforeunload",this.boundHandleBeforeUnload),window.addEventListener("pagehide",this.boundHandleBeforeUnload))}handleVisibilityChange(){typeof document>"u"||document.hidden||this.onUserActivity()}handleBeforeUnload(){this.eventBus.emit({type:h.PAGE_UNLOAD,timestamp:Date.now()})}removeUserActivityEvents(){typeof window>"u"||typeof document>"u"||this.userActivityEventsRegistered&&(this.userEvents.forEach(e=>window.removeEventListener(e,this.userEventHandler)),document.removeEventListener("visibilitychange",this.boundHandleVisibilityChange),window.removeEventListener("online",this.userEventHandler),window.removeEventListener("offline",this.userEventHandler),typeof navigator<"u"&&"connection"in navigator&&navigator.connection?.removeEventListener("change",this.userEventHandler),window.removeEventListener("beforeunload",this.boundHandleBeforeUnload),window.removeEventListener("pagehide",this.boundHandleBeforeUnload),this.userActivityEventsRegistered=!1)}onUserActivity(){this.userActivityEventsRegistered&&(this.stopAutoDisconnectTimer(),this.resetIdleTimer())}clearAllTimers(){this.clearIdleTimer(),this.stopAutoDisconnectTimer(),this.removeUserActivityEvents(),this.userEventHandler.cancel()}getTimerStatus(){return{idleTimerActive:this.idleTimer!==null,autoDisconnectTimerActive:this.autoDisconnectTimer!==null,eventsRegistered:this.userActivityEventsRegistered,idleTimeout:this.IDLE_TIMEOUT,autoDisconnectInterval:this.AUTO_DISCONNECT_INTERVAL}}};s(L,"instance",null);let he=L;const fe="klever_location_info",Dt=3600*1e3,De=300*1e3,Ot=30*1e3,J=()=>{if(typeof sessionStorage>"u")return null;try{const r=sessionStorage.getItem(fe);if(r){const e=JSON.parse(r),t=Date.now();if(e.timestamp&&t-e.timestamp<Dt)return e}}catch{}return null},Oe=r=>{if(!(typeof sessionStorage>"u"))try{r.timestamp=Date.now(),sessionStorage.setItem(fe,JSON.stringify(r))}catch{}},Mt=async()=>{const r=J();if(r)return r;const e={},t=[{url:"https://free.freeipapi.com/api/json",parser:i=>({country:i.countryCode,timezone:i.timeZones?.[0],ip:i.ipAddress})},{url:"https://api.ipify.org?format=json",parser:i=>({ip:i.ip})}];for(const i of t)try{const n=await fetch(i.url,{signal:AbortSignal.timeout(5e3)});if(n.ok){const a=await n.json(),o=i.parser(a);return Object.assign(e,o),Oe(e),e}else if(n.status===429){`${i.url}`;continue}}catch{continue}return Oe(e),e};let Q=null,q=null,X=null,Me=typeof navigator<"u"?navigator.onLine:!0;const ke=async()=>{try{const r=await fetch("https://api.ipify.org?format=json",{signal:AbortSignal.timeout(3e3)});if(r.ok)return(await r.json()).ip}catch{}return null},K=async()=>{if(typeof sessionStorage<"u")try{sessionStorage.removeItem(fe)}catch{}return Q=null,Se()},kt=()=>{if(typeof window>"u"||typeof document>"u"||typeof navigator>"u")return;const r=()=>{const t=navigator.onLine;t&&!Me&&K(),Me=t};window.addEventListener("online",r),window.addEventListener("offline",r);const e=async()=>{if(!document.hidden){const t=J();if(t&&t.ip){const i=await ke();i&&i!==t.ip&&K()}}};document.addEventListener("visibilitychange",e),X=setInterval(async()=>{if(document.hidden||!navigator.onLine)return;const t=J();if(t&&t.ip){const i=await ke();i&&i!==t.ip&&K()}},Ot),q=setInterval(()=>{const t=J();t&&t.timestamp&&Date.now()-t.timestamp>De&&K()},De)},Se=()=>(Q||(Q=Mt().then(r=>r).catch(r=>({}))),Q),Lt=()=>{q&&(clearInterval(q),q=null),X&&(clearInterval(X),X=null)};typeof window<"u"&&(Se(),kt(),window.addEventListener("beforeunload",Lt));var E,T;const C=class C{constructor(e,t,i,n,a){ce(this,E,c.IDLE);ce(this,T,null);s(this,"apiKey");s(this,"templatePersonaId");s(this,"personaId");s(this,"userId");s(this,"onMessageReceived");s(this,"roomId",null);s(this,"token",null);s(this,"checkRoom",null);s(this,"webSocket",null);s(this,"reconnectAttempts",0);s(this,"maxReconnectAttempts",3);s(this,"reconnectTimeout",null);s(this,"STUDIO_API_PREFIX");s(this,"STUDIO_ROOM_PREFIX");s(this,"API_KEY_HEADER",Ge);s(this,"BASE_URL","https://kleverone-apigw.klever-one.com/kleverone-studio");s(this,"heartbeatInterval",null);s(this,"HEARTBEAT_INTERVAL",3e4);s(this,"HEARTBEAT_MESSAGE","ping");s(this,"connectionManager");s(this,"eventBus");s(this,"statusListeners",[]);s(this,"roomInfoListeners",[]);s(this,"disconnectListeners",[]);s(this,"errorHandlers",[]);s(this,"idleTimeoutHandler");s(this,"pageUnloadHandler");s(this,"unsubscribeIdleTimeout");s(this,"unsubscribePageUnload");this.apiKey=e,this.templatePersonaId=t,this.personaId=i,this.userId=n,this.onMessageReceived=a,this.STUDIO_API_PREFIX=`${this.BASE_URL}/api`,this.STUDIO_ROOM_PREFIX=`${this.BASE_URL}/ws`,this.eventBus=b.getInstance(),this.connectionManager=he.getInstance(),this.idleTimeoutHandler=()=>{this.disconnect()},this.pageUnloadHandler=()=>{this.disconnect()},this.unsubscribeIdleTimeout=this.eventBus.subscribe(h.IDLE_TIMEOUT_EXPIRED,this.idleTimeoutHandler),this.unsubscribePageUnload=this.eventBus.subscribe(h.PAGE_UNLOAD,this.pageUnloadHandler)}static hasInstance(){return C.instance!==null}static getInstance(e,t,i,n,a){if(C.instance)e&&C.instance.apiKey!==e&&(C.instance.apiKey=e),t!==void 0&&(C.instance.templatePersonaId=t),i!==void 0&&(C.instance.personaId=i),n!==void 0&&(C.instance.userId=n);else{if(!e)throw new Error("RoomManager가 초기화되지 않았습니다. API 키가 필요합니다.");C.instance=new C(e,t??null,i??null,n??null,a)}return a&&(C.instance.onMessageReceived=a),C.instance}get connectionStatus(){return p(this,E)}get roomInfo(){return p(this,T)}addStatusListener(e){this.statusListeners.push(e),e(p(this,E))}removeStatusListener(e){this.statusListeners=this.statusListeners.filter(t=>t!==e)}addRoomInfoListener(e){this.roomInfoListeners.push(e),e(p(this,T))}removeRoomInfoListener(e){this.roomInfoListeners=this.roomInfoListeners.filter(t=>t!==e)}addDisconnectListener(e){this.disconnectListeners.push(e)}removeDisconnectListener(e){this.disconnectListeners=this.disconnectListeners.filter(t=>t!==e)}addErrorHandler(e){return this.errorHandlers.push(e),()=>{const t=this.errorHandlers.indexOf(e);t>-1&&this.errorHandlers.splice(t,1)}}setConnectionStatus(e){z(this,E,e),this.statusListeners.forEach(i=>i(e));let t;switch(e){case c.CONNECTED:t=h.ROOM_CONNECTED;break;case c.DISCONNECTED:t=h.ROOM_DISCONNECTED;break;case c.CONNECTING:t=h.ROOM_CONNECTING;break;case c.RECONNECTING:t=h.ROOM_RECONNECTING;break;default:return}this.eventBus.emit({type:t,timestamp:Date.now(),data:{status:e}})}setRoomInfo(e){z(this,T,e),this.roomInfoListeners.forEach(t=>t(e)),this.eventBus.emit({type:h.ROOM_INFO_UPDATED,timestamp:Date.now(),data:{roomInfo:e}}),e&&this.connectionManager.startIdleTimer()}notifyStreamingManagerDisconnect(){}handleStreamingDisconnect(){}async createRoom({isReconnect:e}){if(!(p(this,E)===c.IDLE||p(this,E)===c.DISCONNECTED))try{this.setConnectionStatus(e?c.RECONNECTING:c.CONNECTING);const t=await Se(),i=await fetch(`${this.STUDIO_API_PREFIX}/no-auth/room/create-room`,{method:"POST",headers:{"Content-Type":"application/json","X-Client-IP":t?.ip?t?.ip:"",[this.API_KEY_HEADER]:this.apiKey},body:JSON.stringify({})});if(!i.ok){if(i.status===401){const a=await i.json();this.handleError(a.message)}else this.handleError("방 생성에 실패했습니다.");`${i.status}`,this.setConnectionStatus(c.DISCONNECTED);return}const n=await i.json();if(!n.success||!n.data){this.handleError("서버 응답이 올바르지 않습니다."),this.setConnectionStatus(c.DISCONNECTED);return}this.roomId=n.data.roomId,this.token=n.data.token,this.roomId}catch{this.handleError("방 생성 중 오류가 발생했습니다."),this.setConnectionStatus(c.DISCONNECTED)}}async checkRoomStatus(){if(this.roomId)try{const e=await fetch(`${this.STUDIO_API_PREFIX}/no-auth/room/check-room/${this.roomId}`,{method:"GET",headers:{[this.API_KEY_HEADER]:this.apiKey}});if(!e.ok){this.handleError("방 상태 확인에 실패했습니다."),`${e.status}`;return}const t=await e.json();if(!t.success||!t.data){this.handleError("서버 응답이 올바르지 않습니다.");return}this.checkRoom=t.data.exists,this.checkRoom}catch{this.handleError("방 상태 확인 중 오류가 발생했습니다.")}}async connectRoom(){if(!(!this.roomId||!this.token||p(this,E)===c.CONNECTED)){this.setConnectionStatus(c.CONNECTING);try{const e=await fetch(`${this.STUDIO_API_PREFIX}/no-auth/room/connect`,{method:"POST",headers:{"Content-Type":"application/json",[this.API_KEY_HEADER]:this.apiKey},body:JSON.stringify({roomId:this.roomId,token:this.token})});if(!e.ok){this.handleError("방 연결에 실패했습니다."),`${e.status}`,this.setConnectionStatus(c.DISCONNECTED);return}const t=await e.json();if(!t.success||!t.data){const i=t.message||"서버 응답이 올바르지 않습니다.";this.handleError(i),this.setConnectionStatus(c.DISCONNECTED);return}this.roomId,await this.waitForWebSocketConnection(t.data.address)}catch{this.handleError("방 연결 중 오류가 발생했습니다."),this.setConnectionStatus(c.DISCONNECTED)}}}requestInitialData(){this.sendMessage({actionName:Ce,payload:{id:this.templatePersonaId??this.personaId??"-1",userId:this.userId??null,type:this.templatePersonaId?"template":"persona"}}),this.eventBus.emit({type:h.STREAMING_CONNECTING,timestamp:Date.now()})}handleWebSocketOpen(e){this.setConnectionStatus(c.CONNECTED),this.requestInitialData(),this.startHeartbeat(),this.connectionManager.registerUserActivityEvents(),this.connectionManager.startIdleTimer()}handleWebSocketMessage(e){try{const t=JSON.parse(e.data);if(t.action===Ce){if(!this.handleInitialDataMessage(t))return}else if(t.kind===xe&&t.action===Ve){typeof t.body=="string"&&this.eventBus.emit({type:h.ROOM_SESSION_TIMEOUT,timestamp:Date.now(),data:{message:t.body}});return}}catch{}this.onMessageReceived?.(e)}handleInitialDataMessage(e){return typeof e.body=="string"?(e.body.startsWith($e)?this.eventBus.emit({type:h.STREAMING_ALLOCATING,timestamp:Date.now()}):e.body.startsWith(je)&&this.eventBus.emit({type:h.STREAMING_CONNECTION_FAILED,timestamp:Date.now()}),!1):(this.setRoomInfo(e.body),!0)}handleError(e){const t=new Error(e||"Room error");this.errorHandlers.forEach(i=>{try{i(t)}catch{}})}handleWebSocketError(e){this.setConnectionStatus(c.DISCONNECTED),p(this,E)!==c.DISCONNECTED&&this.attemptReconnect()}handleWebSocketClose(e){this.stopHeartbeat(),this.setConnectionStatus(c.DISCONNECTED),this.disconnectListeners.forEach(t=>t()),e.code!==1e3&&this.notifyStreamingManagerDisconnect()}attemptReconnect(){if(p(this,E)===c.RECONNECTING||this.reconnectAttempts>=this.maxReconnectAttempts||p(this,E)===c.DISCONNECTED)return;this.reconnectAttempts++;const e=Math.pow(2,this.reconnectAttempts-1)*1e3;this.reconnectTimeout&&clearTimeout(this.reconnectTimeout),this.reconnectTimeout=setTimeout(()=>{this.setConnectionStatus(c.RECONNECTING),this.initializeRoom({isReconnect:!0}).catch(t=>{this.setConnectionStatus(c.DISCONNECTED),this.reconnectAttempts>=this.maxReconnectAttempts&&(this.setConnectionStatus(c.DISCONNECTED),this.notifyStreamingManagerDisconnect())})},e)}initiateWebSocketConnection(e){this.webSocket&&this.webSocket.close(),this.webSocket=new WebSocket(`${this.STUDIO_ROOM_PREFIX}${e}?roomId=${this.roomId}&token=${this.token}`),this.webSocket.onopen=t=>this.handleWebSocketOpen(t),this.webSocket.onmessage=t=>this.handleWebSocketMessage(t),this.webSocket.onerror=t=>this.handleWebSocketError(t),this.webSocket.onclose=t=>this.handleWebSocketClose(t)}async waitForWebSocketConnection(e){return new Promise((t,i)=>{this.initiateWebSocketConnection(e);let n=0;const a=10,o=setInterval(()=>{p(this,E)===c.CONNECTED&&(clearInterval(o),t()),n++,n>=a&&(clearInterval(o),this.setConnectionStatus(c.DISCONNECTED),this.attemptReconnect(),i(new Error("WebSocket connection timeout")))},500)})}async reconnect(){if(!(p(this,E)===c.RECONNECTING||!this.apiKey)){this.roomId=null,this.token=null,z(this,T,null);try{await this.initializeRoom({isReconnect:!0}),this.setConnectionStatus(c.CONNECTED),this.reconnectAttempts=0;const e=p(this,T);e?.resourceInfo?.url&&this.setRoomInfo(e)}catch{if(this.setConnectionStatus(c.DISCONNECTED),this.reconnectAttempts++,this.reconnectAttempts<this.maxReconnectAttempts){const t=Math.pow(2,this.reconnectAttempts)*1e3;this.reconnectTimeout&&clearTimeout(this.reconnectTimeout),this.reconnectTimeout=setTimeout(()=>{this.setConnectionStatus(c.RECONNECTING),this.initializeRoom({isReconnect:!0}).catch(i=>{this.setConnectionStatus(c.DISCONNECTED),this.reconnectAttempts++,this.reconnectAttempts>=this.maxReconnectAttempts&&this.setConnectionStatus(c.DISCONNECTED)})},t)}else this.reconnectAttempts=0,this.setConnectionStatus(c.DISCONNECTED)}}}async _initializeRoom({isReconnect:e=!1}){if(!(p(this,E)===c.CONNECTING||p(this,E)===c.RECONNECTING))try{this.setConnectionStatus(e?c.RECONNECTING:c.CONNECTING),await this.createRoom({isReconnect:e}),await this.checkRoomStatus(),await this.connectRoom(),p(this,E),c.CONNECTED}catch(t){throw t}}async initializeRoom({isReconnect:e=!1}){return C.throttledInitializeRoom(this,{isReconnect:e})}async sendMessage(e){if(!this.webSocket||this.webSocket.readyState!==WebSocket.OPEN){try{if(await this.reconnect(),p(this,E)===c.CONNECTED)this.sendMessageInternal(e);else throw new Error("Failed to reconnect WebSocket")}catch(t){throw t}return}this.sendMessageInternal(e)}sendMessageInternal(e){try{if(e instanceof Blob||e instanceof ArrayBuffer||ArrayBuffer.isView(e))this.webSocket.send(e);else if(typeof e=="object"){const t=this.generateEventId(),i={...e,eventId:t},n=JSON.stringify(i);this.webSocket.send(n)}else typeof e=="string"&&this.webSocket.send(e)}catch(t){throw t}}createMessageBuffer({action:e,payload:t,header:i}){const n=new TextEncoder().encode(e),a=new Uint8Array(4);new DataView(a.buffer).setUint32(0,n.length,!0);const o=i||new Uint8Array(0),l=new Uint8Array(4);new DataView(l.buffer).setUint32(0,o.length,!0);const d=a.length+n.length+l.length+o.length+t.length,m=new Uint8Array(d);let u=0;return m.set(a,u),u+=a.length,m.set(n,u),u+=n.length,m.set(l,u),u+=l.length,m.set(o,u),u+=o.length,m.set(t,u),m}synchronizedSendMessage(e){try{if(!this.webSocket)return;if(this.webSocket.readyState!==WebSocket.OPEN){this.webSocket.readyState;return}e()}catch{}}async sendDataInChunks({payload:e,actionName:t,chunkSize:i=16e3}){try{const n=this.generateEventId(),a=Math.ceil(e.length/i);e.length;for(let o=0;o<a;o++){const l=o*i,d=Math.min(l+i,e.length),m=e.slice(l,d);let u;if(o===0){const y=JSON.stringify({eventId:n,totalChunks:a,chunkIndex:o}),v=new TextEncoder().encode(y);u=this.createMessageBuffer({action:t,header:v,payload:m})}else{const y=JSON.stringify({eventId:n,chunkIndex:o}),v=new TextEncoder().encode(y);u=this.createMessageBuffer({action:"",header:v,payload:m})}this.synchronizedSendMessage(()=>this.sendMessage(u))}}catch{}}disconnect(){try{this.unsubscribeIdleTimeout&&this.unsubscribeIdleTimeout(),this.unsubscribePageUnload&&this.unsubscribePageUnload()}catch{}if(this.stopHeartbeat(),this.connectionManager.clearAllTimers(),this.reconnectTimeout&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null),this.webSocket)try{(this.webSocket.readyState===WebSocket.OPEN||this.webSocket.readyState===WebSocket.CONNECTING)&&this.