UNPKG

dash-renderer

Version:

render dash components in react

1 lines 8.36 kB
(()=>{"use strict";const e={maxRetries:10,initialRetryDelay:1e3,maxRetryDelay:3e4,heartbeatInterval:3e4,heartbeatTimeout:1e4,inactivityTimeout:3e5};var t;!function(e){e.CONNECT="connect",e.DISCONNECT="disconnect",e.CALLBACK_REQUEST="callback_request",e.GET_PROPS_RESPONSE="get_props_response",e.TAB_VISIBLE="tab_visible",e.CONNECTED="connected",e.DISCONNECTED="disconnected",e.CALLBACK_RESPONSE="callback_response",e.SET_PROPS="set_props",e.SET_PROPS_BATCH="set_props_batch",e.GET_PROPS_REQUEST="get_props_request",e.ERROR="error",e.HEARTBEAT_ACK="heartbeat_ack"}(t||(t={}));const r=new class{constructor(t={}){this.ws=null,this.serverUrl=null,this.retryCount=0,this.retryTimeout=null,this.heartbeatInterval=null,this.heartbeatTimeout=null,this.lastActivityTime=Date.now(),this.messageQueue=[],this.isConnecting=!1,this.onOpen=null,this.onClose=null,this.onMessage=null,this.onError=null,this.config={...e,...t}}setConfig(e){this.config={...this.config,...e}}connect(e){this.ws&&this.ws.readyState===WebSocket.OPEN||this.isConnecting||(this.serverUrl=e,this.isConnecting=!0,this.retryCount=0,this.createConnection())}disconnect(){this.cleanup(),this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(1e3,"Client disconnect"),this.ws=null,this.serverUrl=null,this.retryCount=0}send(e){const t=JSON.stringify(e);"heartbeat"!==e.type&&(this.lastActivityTime=Date.now()),this.ws&&this.ws.readyState===WebSocket.OPEN?this.ws.send(t):(this.messageQueue.push(t),this.serverUrl&&!this.isConnecting&&(this.isConnecting=!0,this.retryCount=0,this.createConnection()))}get isConnected(){return null!==this.ws&&this.ws.readyState===WebSocket.OPEN}resetActivity(){this.lastActivityTime=Date.now()}createConnection(){if(this.serverUrl)try{this.ws=new WebSocket(this.serverUrl),this.ws.onopen=this.handleOpen.bind(this),this.ws.onclose=this.handleClose.bind(this),this.ws.onmessage=this.handleMessage.bind(this),this.ws.onerror=this.handleError.bind(this)}catch(e){this.isConnecting=!1,this.scheduleReconnect()}}handleOpen(){for(this.isConnecting=!1,this.retryCount=0,this.lastActivityTime=Date.now();this.messageQueue.length>0;){const e=this.messageQueue.shift();e&&this.ws&&this.ws.send(e)}this.startHeartbeat(),this.onOpen&&this.onOpen()}handleClose(e){this.isConnecting=!1,this.cleanup();const t=e.reason||"Connection closed";this.onClose&&this.onClose(t),this.serverUrl&&1e3!==e.code&&4001!==e.code&&this.scheduleReconnect()}handleMessage(e){try{const t=JSON.parse(e.data);if(Array.isArray(t)){let e=!1,r=!1;for(const s of t)"heartbeat_ack"===s.type?e=!0:r=!0;return e&&this.clearHeartbeatTimeout(),void(r&&(this.lastActivityTime=Date.now(),this.onMessage&&this.onMessage(t)))}if("heartbeat_ack"===t.type)return void this.clearHeartbeatTimeout();this.lastActivityTime=Date.now(),this.onMessage&&this.onMessage(t)}catch(e){this.onError&&this.onError(new Error("Failed to parse message"))}}handleError(){this.isConnecting=!1}scheduleReconnect(){if(this.retryTimeout&&clearTimeout(this.retryTimeout),this.retryCount>=this.config.maxRetries)return void(this.onError&&this.onError(new Error("Max reconnection attempts reached")));const e=Math.min(this.config.initialRetryDelay*Math.pow(2,this.retryCount)+1e3*Math.random(),this.config.maxRetryDelay);this.retryCount++,this.retryTimeout=setTimeout(()=>{this.createConnection()},e)}startHeartbeat(){this.stopHeartbeat(),this.heartbeatInterval=setInterval(()=>{if(this.ws&&this.ws.readyState===WebSocket.OPEN){if(this.config.inactivityTimeout>0&&Date.now()-this.lastActivityTime>=this.config.inactivityTimeout)return void this.ws.close(4001,"Inactivity timeout");this.ws.send(JSON.stringify({type:"heartbeat"})),this.setHeartbeatTimeout()}},this.config.heartbeatInterval)}stopHeartbeat(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null),this.clearHeartbeatTimeout()}setHeartbeatTimeout(){this.clearHeartbeatTimeout(),this.heartbeatTimeout=setTimeout(()=>{this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(4e3,"Heartbeat timeout")},this.config.heartbeatTimeout)}clearHeartbeatTimeout(){this.heartbeatTimeout&&(clearTimeout(this.heartbeatTimeout),this.heartbeatTimeout=null)}cleanup(){this.stopHeartbeat(),this.retryTimeout&&(clearTimeout(this.retryTimeout),this.retryTimeout=null)}},s=new class{constructor(){this.renderers=new Map,this.sendToServer=null}registerRenderer(e,t){this.renderers.set(e,t)}unregisterRenderer(e){this.renderers.delete(e)}get rendererCount(){return this.renderers.size}handleRendererMessage(e,r){switch(r.type){case t.CALLBACK_REQUEST:this.forwardCallbackRequest(e,r);break;case t.GET_PROPS_RESPONSE:this.forwardGetPropsResponse(e,r);break;default:console.warn(`Unknown message type from renderer: ${r.type}`)}}handleServerMessage(e){if(Array.isArray(e))return void this.handleBatchedMessages(e);const r=e,s=r.rendererId;switch(r.type){case t.CALLBACK_RESPONSE:this.forwardToRenderer(s,r);break;case t.SET_PROPS:this.forwardSetProps(s,r);break;case t.GET_PROPS_REQUEST:this.forwardGetPropsRequest(s,r);break;case t.ERROR:this.forwardToRenderer(s,r);break;default:console.warn(`Unknown message type from server: ${r.type}`)}}handleBatchedMessages(e){const r=new Map,s=[];for(const n of e)if(n.type!==t.HEARTBEAT_ACK)if(n.type===t.SET_PROPS){const e=n,t=e.rendererId,s=r.get(t);s?s.push(e.payload):r.set(t,[e.payload])}else s.push(n);for(const[e,s]of r){const r=this.renderers.get(e);if(r)try{r.postMessage({type:t.SET_PROPS_BATCH,rendererId:e,payload:s})}catch(t){console.warn(`Failed to forward batch to renderer ${e}, removing`),this.renderers.delete(e)}else console.warn(`Renderer ${e} not found for batch, skipping`)}for(const e of s)this.handleServerMessage(e)}broadcastToRenderers(e){for(const[t,r]of this.renderers)try{r.postMessage(e)}catch(e){console.warn(`Failed to send to renderer ${t}, removing`),this.renderers.delete(t)}}notifyConnected(e){const r=this.renderers.get(e);if(r)try{r.postMessage({type:t.CONNECTED,rendererId:e})}catch(t){console.warn(`Failed to notify renderer ${e}, removing`),this.renderers.delete(e)}}notifyDisconnected(e){this.broadcastToRenderers({type:t.DISCONNECTED,rendererId:"",payload:{reason:e}})}notifyError(e,r,s){const n=this.renderers.get(e);if(n)try{n.postMessage({type:t.ERROR,rendererId:e,payload:{message:r,code:s}})}catch(t){console.warn(`Failed to send error to renderer ${e}, removing`),this.renderers.delete(e)}}forwardCallbackRequest(e,r){this.sendToServer&&this.sendToServer({type:t.CALLBACK_REQUEST,rendererId:e,requestId:r.requestId,payload:r.payload})}forwardGetPropsResponse(e,r){this.sendToServer&&this.sendToServer({type:t.GET_PROPS_RESPONSE,rendererId:e,requestId:r.requestId,payload:r.payload})}forwardToRenderer(e,t){const r=this.renderers.get(e);if(r)try{r.postMessage(t)}catch(t){console.warn(`Failed to forward to renderer ${e}, removing`),this.renderers.delete(e)}else console.warn(`Renderer ${e} not found for message`)}forwardSetProps(e,t){this.forwardToRenderer(e,t)}forwardGetPropsRequest(e,t){this.forwardToRenderer(e,t)}};let n=null;r.onOpen=()=>{console.log("[DashWSWorker] WebSocket connected");for(const e of Array.from(o))s.notifyConnected(e)},r.onClose=e=>{console.log(`[DashWSWorker] WebSocket closed: ${e}`),s.notifyDisconnected(e)},r.onMessage=e=>{s.handleServerMessage(e)},r.onError=e=>{console.error("[DashWSWorker] WebSocket error:",e.message)},s.sendToServer=e=>{r.send(e)};const o=new Set;self.onconnect=e=>{const i=e.ports[0];i.onmessage=e=>{const a=e.data;switch(a.type){case t.CONNECT:{const e=a,t=e.rendererId,h=e.payload.serverUrl,c=e.payload.inactivityTimeout,d=e.payload.heartbeatInterval;s.registerRenderer(t,i),o.add(t),console.log(`[DashWSWorker] Renderer ${t} connected, inactivityTimeout: ${c}, heartbeatInterval: ${d}`);const l={};"number"==typeof c&&(l.inactivityTimeout=c),"number"==typeof d&&(l.heartbeatInterval=d),Object.keys(l).length>0&&r.setConfig(l),r.isConnected?s.notifyConnected(t):(n!==h&&(n=h),r.connect(n));break}case t.DISCONNECT:{const e=a.rendererId;s.unregisterRenderer(e),o.delete(e),console.log(`[DashWSWorker] Renderer ${e} disconnected`),0===s.rendererCount&&(r.disconnect(),n=null,console.log("[DashWSWorker] All renderers disconnected, closing WebSocket"));break}case t.TAB_VISIBLE:r.resetActivity();break;default:s.handleRendererMessage(a.rendererId,a)}},i.start()},console.log("[DashWSWorker] SharedWorker initialized")})();