verani 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/dist/client.cjs +1 -1
- package/dist/client.d.cts +10 -54
- package/dist/client.d.mts +10 -54
- package/dist/client.mjs +1 -1
- package/dist/verani.cjs +1 -1
- package/dist/verani.d.cts +111 -2
- package/dist/verani.d.mts +111 -2
- package/dist/verani.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Verani
|
|
2
2
|
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
[](https://github.com/v0id-user)
|
|
6
|
+
|
|
7
|
+
</div>
|
|
8
|
+
|
|
3
9
|
> A simple, focused realtime SDK for Cloudflare Actors with Socket.io-like semantics
|
|
4
10
|
|
|
5
11
|
Verani brings the familiar developer experience of Socket.io to Cloudflare's Durable Objects (Actors), with proper hibernation support and minimal overhead.
|
|
@@ -173,6 +179,7 @@ Verani handles Cloudflare's hibernation automatically:
|
|
|
173
179
|
- Event-based API (on/off/once/emit)
|
|
174
180
|
- Promise-based connection waiting
|
|
175
181
|
- Lifecycle callbacks
|
|
182
|
+
- **Ping/pong keepalive** with automatic Page Visibility API resync
|
|
176
183
|
|
|
177
184
|
### RPC Support
|
|
178
185
|
|
|
@@ -187,7 +194,7 @@ Try out Verani with working examples:
|
|
|
187
194
|
|
|
188
195
|
```bash
|
|
189
196
|
# Clone and run
|
|
190
|
-
git clone https://github.com/
|
|
197
|
+
git clone https://github.com/v0id-user/verani
|
|
191
198
|
cd verani
|
|
192
199
|
bun install # or npm install
|
|
193
200
|
bun run dev # or npm run dev
|
package/dist/client.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const require_types=require(`./types-083oWz55.cjs`);function encodeClientMessage$1(t){return require_types.n(t)}function decodeServerMessage$1(t){return require_types.s(t)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,t){this.config=e,this.onStateChange=t,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}isValidStateTransition(e,t){return{disconnected:[`connecting`,`reconnecting`],connecting:[`connected`,`disconnected`,`error`,`reconnecting`],connected:[`disconnected`,`reconnecting`],reconnecting:[`connecting`,`disconnected`,`error`],error:[`reconnecting`,`disconnected`,`connecting`]}[e]?.includes(t)??!1}setState(e){this.state!==e&&(this.isValidStateTransition(this.state,e),this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}},VeraniClient=class{constructor(e,t={}){this.url=e,this.listeners=new Map,this.messageQueue=[],this.isConnecting=!1,this.connectionId=0,this.lastPongReceived=0,this.options={reconnection:{enabled:t.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:t.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:t.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:t.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:t.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:t.maxQueueSize??100,connectionTimeout:t.connectionTimeout??1e4,pingInterval:t.pingInterval??5e3,pongTimeout:t.pongTimeout??5e3},this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.connect()}startPingInterval(){this.options.pingInterval===0||this.pingInterval!==void 0||(this.lastPongReceived=Date.now(),this.pingInterval=setInterval(()=>{if(!this.ws||this.ws.readyState!==WebSocket.OPEN){this.stopPingInterval();return}if(Date.now()-this.lastPongReceived>this.options.pongTimeout+this.options.pingInterval){this.stopPingInterval(),this.ws.close(1006,`Pong timeout`);return}try{this.ws.send(encodeClientMessage$1({type:`ping`}))}catch{}},this.options.pingInterval))}stopPingInterval(){this.pingInterval!==void 0&&(clearInterval(this.pingInterval),this.pingInterval=void 0),this.pongTimeout!==void 0&&(clearTimeout(this.pongTimeout),this.pongTimeout=void 0)}cleanupWebSocket(){if(this.stopPingInterval(),this.connectionTimeout!==void 0&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=void 0),this.ws){let e=this.ws;if(this.ws=void 0,e.readyState===WebSocket.OPEN||e.readyState===WebSocket.CONNECTING)try{e.close(1e3,`Cleanup`)}catch{}}}connect(){if(!this.isConnecting&&!this.isConnected()){this.cleanupWebSocket();try{this.isConnecting=!0,this.connectionId++;let e=this.connectionId;this.connectionManager.setState(`connecting`),this.emitLifecycleEvent(`connecting`),this.ws=new WebSocket(this.url),this.connectionTimeout=setTimeout(()=>{this.isConnecting&&this.connectionId===e&&(this.ws?.close(),this.handleConnectionError(Error(`Connection timeout`)))},this.options.connectionTimeout),this.ws.addEventListener(`open`,()=>{this.connectionId===e&&this.handleOpen()}),this.ws.addEventListener(`message`,t=>{this.connectionId===e&&this.handleMessage(t)}),this.ws.addEventListener(`close`,t=>{this.connectionId===e&&this.handleClose(t)}),this.ws.addEventListener(`error`,t=>{this.connectionId===e&&this.handleError(t)})}catch(e){this.isConnecting=!1,this.handleConnectionError(e)}}}handleOpen(){this.isConnecting=!1,this.connectionTimeout!==void 0&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=void 0),this.connectionManager.setState(`connected`),this.connectionManager.resetReconnection(),this.startPingInterval(),this.flushMessageQueue(),this.connectionResolve&&(this.connectionResolve(),this.connectionPromise=void 0,this.connectionResolve=void 0,this.connectionReject=void 0),this.emitLifecycleEvent(`open`),this.emitLifecycleEvent(`connected`),this.onOpenCallback?.()}handleMessage(e){let t=decodeServerMessage$1(e.data);if(!t)return;if(t.type===`pong`){this.lastPongReceived=Date.now();return}let r=t.type,i=t.data;t.type===`event`&&t.data&&typeof t.data==`object`&&`type`in t.data&&(r=t.data.type,i=t.data);let a=this.listeners.get(r);if(a)for(let e of a)try{e(i)}catch{}}handleClose(e){this.isConnecting=!1,this.connectionTimeout!==void 0&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=void 0),this.connectionManager.setState(`disconnected`),this.connectionReject&&=(this.connectionReject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`close`,e),this.emitLifecycleEvent(`disconnected`,e),this.onCloseCallback?.(e),e.code!==1e3&&e.code!==1001&&this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}handleError(e){this.isConnecting=!1,this.connectionTimeout!==void 0&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=void 0),this.emitLifecycleEvent(`error`,e),this.onErrorCallback?.(e),this.handleConnectionError(Error(`WebSocket error`))}handleConnectionError(e){this.isConnecting=!1,this.connectionTimeout!==void 0&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=void 0),this.connectionReject&&=(this.connectionReject(e),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`error`,e),this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}emitLifecycleEvent(e,t){let n=this.listeners.get(e);if(n)for(let e of n)try{e(t)}catch{}}flushMessageQueue(){if(!(!this.ws||this.ws.readyState!==WebSocket.OPEN))for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();try{this.ws.send(encodeClientMessage$1(e))}catch{}}}getState(){return this.connectionManager.getState()}isConnected(){return this.ws?.readyState===WebSocket.OPEN&&this.connectionManager.getState()===`connected`}getConnectionState(){return{state:this.connectionManager.getState(),isConnected:this.isConnected(),isConnecting:this.isConnecting,reconnectAttempts:this.connectionManager.getReconnectAttempts(),connectionId:this.connectionId}}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromise||=new Promise((e,t)=>{this.connectionResolve=e,this.connectionReject=t;let n=setTimeout(()=>{this.connectionReject&&=(this.connectionReject(Error(`Connection wait timeout`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0)},this.options.connectionTimeout*2);this.connectionPromise&&this.connectionPromise.finally(()=>{clearTimeout(n)})}),this.connectionPromise)}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t)}off(e,t){let n=this.listeners.get(e);n&&(n.delete(t),n.size===0&&this.listeners.delete(e))}once(e,t){let n=r=>{this.off(e,n),t(r)};this.on(e,n)}emit(e,n){let r={type:e,data:n};if(this.isConnected())try{this.ws.send(encodeClientMessage$1(r))}catch{this.queueMessage(r)}else this.queueMessage(r)}queueMessage(e){this.messageQueue.length>=this.options.maxQueueSize&&this.messageQueue.shift(),this.messageQueue.push(e)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.connectionManager.resetReconnection(),this.connectionManager.cancelReconnect(),this.cleanupWebSocket(),this.isConnecting=!1,this.connectionManager.setState(`disconnected`),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.isConnecting=!1,this.connectionReject&&=(this.connectionReject(Error(`Connection disconnected`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.cleanupWebSocket(),this.connectionManager.setState(`disconnected`)}close(){this.connectionReject&&=(this.connectionReject(Error(`Client closed`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.disconnect(),this.listeners.clear(),this.messageQueue=[],this.connectionManager.destroy()}};exports.ConnectionManager=ConnectionManager,exports.DEFAULT_RECONNECTION_CONFIG=DEFAULT_RECONNECTION_CONFIG,exports.PROTOCOL_VERSION=require_types.t,exports.VeraniClient=VeraniClient,exports.decodeClientMessage=require_types.a,exports.decodeFrame=require_types.o,exports.decodeServerMessage=require_types.s,exports.encodeClientMessage=require_types.n,exports.encodeFrame=require_types.r,exports.encodeServerMessage=require_types.i;
|
|
1
|
+
const require_types=require(`./types-083oWz55.cjs`);function encodeClientMessage$1(l){return require_types.n(l)}function decodeServerMessage$1(l){return require_types.s(l)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,l){this.config=e,this.onStateChange=l,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}isValidStateTransition(e,l){return{disconnected:[`connecting`,`reconnecting`],connecting:[`connected`,`disconnected`,`error`,`reconnecting`],connected:[`disconnected`,`reconnecting`],reconnecting:[`connecting`,`disconnected`,`error`],error:[`reconnecting`,`disconnected`,`connecting`]}[e]?.includes(l)??!1}setState(e){this.state!==e&&(this.isValidStateTransition(this.state,e),this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}};function resolveClientOptions(e){return{reconnection:{enabled:e.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:e.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:e.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:e.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:e.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:e.maxQueueSize??100,connectionTimeout:e.connectionTimeout??1e4,pingInterval:e.pingInterval??5e3,pongTimeout:e.pongTimeout??5e3}}var MessageQueue=class{constructor(e){this.maxQueueSize=e,this.queue=[]}queueMessage(e){this.queue.length>=this.maxQueueSize&&this.queue.shift(),this.queue.push(e)}flushMessageQueue(e){if(!(!e||e.readyState!==WebSocket.OPEN))for(;this.queue.length>0;){let u=this.queue.shift();try{e.send(encodeClientMessage$1(u))}catch{}}}clear(){this.queue=[]}getLength(){return this.queue.length}};function isBrowserEnvironment(){return typeof globalThis<`u`&&`document`in globalThis&&globalThis.document!==void 0&&globalThis.document.hidden!==void 0&&typeof globalThis.document.addEventListener==`function`}function getDocument(){return isBrowserEnvironment()&&`document`in globalThis?globalThis.document:null}function isPageVisible(){let e=getDocument();return e?!e.hidden:!0}function onVisibilityChange(e){let l=getDocument();if(!l)return null;let u=()=>{e(isPageVisible())};return l.addEventListener(`visibilitychange`,u),()=>{l.removeEventListener(`visibilitychange`,u)}}var KeepaliveManager=class{constructor(e,l,u){this.options=e,this.getWebSocket=l,this.onTimeout=u,this.lastPongReceived=0}startPingInterval(){this.options.pingInterval===0||this.pingInterval!==void 0||(this.lastPongReceived=Date.now(),this.visibilityCleanup=onVisibilityChange(e=>{e&&this.resyncPingInterval()}),this.pingInterval=setInterval(()=>{let e=this.getWebSocket();if(!e||e.readyState!==WebSocket.OPEN){this.stopPingInterval();return}if(Date.now()-this.lastPongReceived>this.options.pongTimeout+this.options.pingInterval){this.stopPingInterval(),e.close(1006,`Pong timeout`);return}try{e.send(encodeClientMessage$1({type:`ping`}))}catch{}},this.options.pingInterval))}resyncPingInterval(){let e=this.getWebSocket();if(!(!e||e.readyState!==WebSocket.OPEN)){this.pingInterval!==void 0&&(clearInterval(this.pingInterval),this.pingInterval=void 0);try{e.send(encodeClientMessage$1({type:`ping`}))}catch{}this.lastPongReceived=Date.now(),this.pingInterval=setInterval(()=>{let e=this.getWebSocket();if(!e||e.readyState!==WebSocket.OPEN){this.stopPingInterval();return}if(Date.now()-this.lastPongReceived>this.options.pongTimeout+this.options.pingInterval){this.stopPingInterval(),e.close(1006,`Pong timeout`);return}try{e.send(encodeClientMessage$1({type:`ping`}))}catch{}},this.options.pingInterval)}}stopPingInterval(){this.pingInterval!==void 0&&(clearInterval(this.pingInterval),this.pingInterval=void 0),this.pongTimeout!==void 0&&(clearTimeout(this.pongTimeout),this.pongTimeout=void 0),this.visibilityCleanup&&=(this.visibilityCleanup(),void 0)}recordPong(){this.lastPongReceived=Date.now()}},EventEmitter=class{constructor(){this.listeners=new Map}on(e,l){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(l)}off(e,l){let u=this.listeners.get(e);u&&(u.delete(l),u.size===0&&this.listeners.delete(e))}once(e,l){let u=d=>{this.off(e,u),l(d)};this.on(e,u)}emitLifecycleEvent(e,l){let u=this.listeners.get(e);if(u)for(let e of u)try{e(l)}catch{}}dispatch(e,l){let u=this.listeners.get(e);if(u)for(let e of u)try{e(l)}catch{}}clear(){this.listeners.clear()}};function handleWebSocketOpen(e,l,u,d,f,p,m,h){e.clear(),l.setState(`connected`),l.resetReconnection(),u.startPingInterval(),d.flushMessageQueue(f),p.resolve&&(p.resolve(),p.clear()),m.emitLifecycleEvent(`open`),m.emitLifecycleEvent(`connected`),h?.()}function handleWebSocketMessage(e,l,d){let f=decodeServerMessage$1(e.data);if(!f)return;if(f.type===`pong`){l.recordPong();return}let p=f.type,m=f.data;f.type===`event`&&f.data&&typeof f.data==`object`&&`type`in f.data&&(p=f.data.type,m=f.data),d.dispatch(p,m)}function handleWebSocketClose(e,l,u,d,f,p,m,h){m&&(m.value=!1),l.clear(),u.setState(`disconnected`),d.reject&&(d.reject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),d.clear()),f.emitLifecycleEvent(`close`,e),f.emitLifecycleEvent(`disconnected`,e),h?.(e),e.code!==1e3&&e.code!==1001&&u.scheduleReconnect(p)&&f.emitLifecycleEvent(`reconnecting`)}function handleWebSocketError(e,l,u,d,f,p){f&&(f.value=!1),l.clear(),u.emitLifecycleEvent(`error`,e),p?.(e),d(Error(`WebSocket error`))}function handleConnectionError(e,l,u,d,f,p,m){m&&(m.value=!1),l.clear(),u.reject&&(u.reject(e),u.clear()),f.emitLifecycleEvent(`error`,e),d.scheduleReconnect(p)&&f.emitLifecycleEvent(`reconnecting`)}var ConnectionHandler=class{constructor(e,l,u,d,f,p,m,h,g,_,v,y){this.url=e,this.options=l,this.connectionManager=u,this.keepalive=d,this.eventEmitter=f,this.messageQueue=p,this.connectionPromise=m,this.isConnectingRef=h,this.isConnectedFn=g,this.onOpenCallback=_,this.onCloseCallback=v,this.onErrorCallback=y,this.connectionId=0,this.connectionTimeoutState={value:void 0,clear:()=>{this.connectionTimeoutState.value!==void 0&&(clearTimeout(this.connectionTimeoutState.value),this.connectionTimeoutState.value=void 0)}}}connect(){if(!this.isConnectingRef.value&&!this.isConnectedFn()){this.cleanupWebSocket();try{this.isConnectingRef.value=!0,this.connectionId++;let e=this.connectionId;this.connectionManager.setState(`connecting`),this.eventEmitter.emitLifecycleEvent(`connecting`),this.ws=new WebSocket(this.url),this.connectionTimeoutState.value=setTimeout(()=>{this.isConnectingRef.value&&this.connectionId===e&&(this.ws?.close(),this.handleConnectionErrorInternal(Error(`Connection timeout`)))},this.options.connectionTimeout),this.ws.addEventListener(`open`,()=>{this.connectionId===e&&this.handleOpenInternal()}),this.ws.addEventListener(`message`,l=>{this.connectionId===e&&handleWebSocketMessage(l,this.keepalive,this.eventEmitter)}),this.ws.addEventListener(`close`,l=>{this.connectionId===e&&handleWebSocketClose(l,this.connectionTimeoutState,this.connectionManager,this.connectionPromise,this.eventEmitter,()=>this.connect(),this.isConnectingRef,this.onCloseCallback)}),this.ws.addEventListener(`error`,l=>{this.connectionId===e&&handleWebSocketError(l,this.connectionTimeoutState,this.eventEmitter,e=>this.handleConnectionErrorInternal(e),this.isConnectingRef,this.onErrorCallback)})}catch(e){this.isConnectingRef.value=!1,this.handleConnectionErrorInternal(e)}}}cleanupWebSocket(){if(this.keepalive.stopPingInterval(),this.connectionTimeoutState.clear(),this.ws){let e=this.ws;if(this.ws=void 0,e.readyState===WebSocket.OPEN||e.readyState===WebSocket.CONNECTING)try{e.close(1e3,`Cleanup`)}catch{}}}getWebSocket(){return this.ws}getConnectionId(){return this.connectionId}handleOpenInternal(){handleWebSocketOpen(this.connectionTimeoutState,this.connectionManager,this.keepalive,this.messageQueue,this.ws,this.connectionPromise,this.eventEmitter,this.onOpenCallback),this.isConnectingRef.value=!1}handleConnectionErrorInternal(e){handleConnectionError(e,this.connectionTimeoutState,this.connectionPromise,this.connectionManager,this.eventEmitter,()=>this.connect(),this.isConnectingRef)}},VeraniClient=class{constructor(e,l={}){this.url=e,this.connectionPromiseState={promise:void 0,resolve:void 0,reject:void 0,clear:()=>{this.connectionPromiseState.promise=void 0,this.connectionPromiseState.resolve=void 0,this.connectionPromiseState.reject=void 0}},this.options=resolveClientOptions(l),this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.messageQueue=new MessageQueue(this.options.maxQueueSize),this.eventEmitter=new EventEmitter,this.isConnectingRef={value:!1},this.keepalive=new KeepaliveManager(this.options,()=>this.connectionHandler.getWebSocket(),()=>{let e=this.connectionHandler.getWebSocket();e&&e.close(1006,`Pong timeout`)}),this.connectionHandler=new ConnectionHandler(this.url,this.options,this.connectionManager,this.keepalive,this.eventEmitter,this.messageQueue,this.connectionPromiseState,this.isConnectingRef,()=>this.isConnected(),this.onOpenCallback,this.onCloseCallback,this.onErrorCallback),Object.defineProperty(this,`isConnecting`,{get:()=>this.isConnectingRef.value,enumerable:!0,configurable:!0}),this.connect()}connect(){this.connectionHandler.connect()}cleanupWebSocket(){this.connectionHandler.cleanupWebSocket()}getState(){return this.connectionManager.getState()}isConnected(){return this.connectionHandler.getWebSocket()?.readyState===WebSocket.OPEN&&this.connectionManager.getState()===`connected`}getConnectionState(){return{state:this.connectionManager.getState(),isConnected:this.isConnected(),isConnecting:this.isConnectingRef.value,reconnectAttempts:this.connectionManager.getReconnectAttempts(),connectionId:this.connectionHandler.getConnectionId()}}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromiseState.promise||(this.connectionPromiseState.promise=new Promise((e,l)=>{this.connectionPromiseState.resolve=e,this.connectionPromiseState.reject=l;let u=setTimeout(()=>{this.connectionPromiseState.reject&&(this.connectionPromiseState.reject(Error(`Connection wait timeout`)),this.connectionPromiseState.clear())},this.options.connectionTimeout*2);this.connectionPromiseState.promise&&this.connectionPromiseState.promise.finally(()=>{clearTimeout(u)})})),this.connectionPromiseState.promise)}on(e,l){this.eventEmitter.on(e,l)}off(e,l){this.eventEmitter.off(e,l)}once(e,l){this.eventEmitter.once(e,l)}emit(e,u){let d={type:e,data:u};if(this.isConnected()){let e=this.connectionHandler.getWebSocket();if(e)try{e.send(encodeClientMessage$1(d))}catch{this.messageQueue.queueMessage(d)}}else this.messageQueue.queueMessage(d)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.connectionManager.resetReconnection(),this.connectionManager.cancelReconnect(),this.cleanupWebSocket(),this.isConnectingRef.value=!1,this.connectionManager.setState(`disconnected`),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.isConnectingRef.value=!1,this.connectionPromiseState.reject&&(this.connectionPromiseState.reject(Error(`Connection disconnected`)),this.connectionPromiseState.clear()),this.cleanupWebSocket(),this.connectionManager.setState(`disconnected`)}close(){this.connectionPromiseState.reject&&(this.connectionPromiseState.reject(Error(`Client closed`)),this.connectionPromiseState.clear()),this.disconnect(),this.eventEmitter.clear(),this.messageQueue.clear(),this.connectionManager.destroy()}};exports.ConnectionManager=ConnectionManager,exports.DEFAULT_RECONNECTION_CONFIG=DEFAULT_RECONNECTION_CONFIG,exports.PROTOCOL_VERSION=require_types.t,exports.VeraniClient=VeraniClient,exports.decodeClientMessage=require_types.a,exports.decodeFrame=require_types.o,exports.decodeServerMessage=require_types.s,exports.encodeClientMessage=require_types.n,exports.encodeFrame=require_types.r,exports.encodeServerMessage=require_types.i;
|
package/dist/client.d.cts
CHANGED
|
@@ -71,7 +71,7 @@ declare class ConnectionManager {
|
|
|
71
71
|
destroy(): void;
|
|
72
72
|
}
|
|
73
73
|
//#endregion
|
|
74
|
-
//#region src/client/
|
|
74
|
+
//#region src/client/runtime/configuration.d.ts
|
|
75
75
|
/**
|
|
76
76
|
* Client options for configuring the Verani client
|
|
77
77
|
*/
|
|
@@ -87,79 +87,39 @@ interface VeraniClientOptions {
|
|
|
87
87
|
/** Pong timeout in milliseconds (default: 5000) */
|
|
88
88
|
pongTimeout?: number;
|
|
89
89
|
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/client/client.d.ts
|
|
90
92
|
/**
|
|
91
93
|
* Verani WebSocket client with automatic reconnection and lifecycle management
|
|
92
94
|
*/
|
|
93
95
|
declare class VeraniClient {
|
|
94
96
|
private url;
|
|
95
|
-
private
|
|
96
|
-
private listeners;
|
|
97
|
+
private connectionHandler;
|
|
97
98
|
private connectionManager;
|
|
98
99
|
private messageQueue;
|
|
100
|
+
private keepalive;
|
|
101
|
+
private eventEmitter;
|
|
99
102
|
private options;
|
|
100
103
|
private onOpenCallback?;
|
|
101
104
|
private onCloseCallback?;
|
|
102
105
|
private onErrorCallback?;
|
|
103
106
|
private onStateChangeCallback?;
|
|
104
|
-
private
|
|
105
|
-
private
|
|
106
|
-
private connectionReject?;
|
|
107
|
-
private connectionTimeout?;
|
|
108
|
-
private isConnecting;
|
|
109
|
-
private connectionId;
|
|
110
|
-
private pingInterval?;
|
|
111
|
-
private pongTimeout?;
|
|
112
|
-
private lastPongReceived;
|
|
107
|
+
private connectionPromiseState;
|
|
108
|
+
private isConnectingRef;
|
|
113
109
|
/**
|
|
114
110
|
* Creates a new Verani client
|
|
115
111
|
* @param url - WebSocket URL to connect to
|
|
116
112
|
* @param options - Client configuration options
|
|
117
113
|
*/
|
|
118
114
|
constructor(url: string, options?: VeraniClientOptions);
|
|
119
|
-
/**
|
|
120
|
-
* Starts the ping interval to keep the connection alive
|
|
121
|
-
*/
|
|
122
|
-
private startPingInterval;
|
|
123
|
-
/**
|
|
124
|
-
* Stops the ping interval
|
|
125
|
-
*/
|
|
126
|
-
private stopPingInterval;
|
|
127
|
-
/**
|
|
128
|
-
* Cleans up existing WebSocket connection and resources
|
|
129
|
-
*/
|
|
130
|
-
private cleanupWebSocket;
|
|
131
115
|
/**
|
|
132
116
|
* Establishes WebSocket connection
|
|
133
117
|
*/
|
|
134
118
|
private connect;
|
|
135
119
|
/**
|
|
136
|
-
*
|
|
137
|
-
*/
|
|
138
|
-
private handleOpen;
|
|
139
|
-
/**
|
|
140
|
-
* Handles incoming WebSocket messages
|
|
141
|
-
*/
|
|
142
|
-
private handleMessage;
|
|
143
|
-
/**
|
|
144
|
-
* Handles WebSocket closure
|
|
145
|
-
*/
|
|
146
|
-
private handleClose;
|
|
147
|
-
/**
|
|
148
|
-
* Handles WebSocket errors
|
|
149
|
-
*/
|
|
150
|
-
private handleError;
|
|
151
|
-
/**
|
|
152
|
-
* Handles connection errors
|
|
153
|
-
*/
|
|
154
|
-
private handleConnectionError;
|
|
155
|
-
/**
|
|
156
|
-
* Emits a lifecycle event to registered listeners
|
|
157
|
-
*/
|
|
158
|
-
private emitLifecycleEvent;
|
|
159
|
-
/**
|
|
160
|
-
* Flushes queued messages when connection is established
|
|
120
|
+
* Cleans up existing WebSocket connection and resources
|
|
161
121
|
*/
|
|
162
|
-
private
|
|
122
|
+
private cleanupWebSocket;
|
|
163
123
|
/**
|
|
164
124
|
* Gets the current connection state
|
|
165
125
|
*/
|
|
@@ -207,10 +167,6 @@ declare class VeraniClient {
|
|
|
207
167
|
* @param data - Optional message data
|
|
208
168
|
*/
|
|
209
169
|
emit(type: string, data?: any): void;
|
|
210
|
-
/**
|
|
211
|
-
* Queues a message for sending when connected
|
|
212
|
-
*/
|
|
213
|
-
private queueMessage;
|
|
214
170
|
/**
|
|
215
171
|
* Registers lifecycle callback for connection open
|
|
216
172
|
*/
|
package/dist/client.d.mts
CHANGED
|
@@ -71,7 +71,7 @@ declare class ConnectionManager {
|
|
|
71
71
|
destroy(): void;
|
|
72
72
|
}
|
|
73
73
|
//#endregion
|
|
74
|
-
//#region src/client/
|
|
74
|
+
//#region src/client/runtime/configuration.d.ts
|
|
75
75
|
/**
|
|
76
76
|
* Client options for configuring the Verani client
|
|
77
77
|
*/
|
|
@@ -87,79 +87,39 @@ interface VeraniClientOptions {
|
|
|
87
87
|
/** Pong timeout in milliseconds (default: 5000) */
|
|
88
88
|
pongTimeout?: number;
|
|
89
89
|
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/client/client.d.ts
|
|
90
92
|
/**
|
|
91
93
|
* Verani WebSocket client with automatic reconnection and lifecycle management
|
|
92
94
|
*/
|
|
93
95
|
declare class VeraniClient {
|
|
94
96
|
private url;
|
|
95
|
-
private
|
|
96
|
-
private listeners;
|
|
97
|
+
private connectionHandler;
|
|
97
98
|
private connectionManager;
|
|
98
99
|
private messageQueue;
|
|
100
|
+
private keepalive;
|
|
101
|
+
private eventEmitter;
|
|
99
102
|
private options;
|
|
100
103
|
private onOpenCallback?;
|
|
101
104
|
private onCloseCallback?;
|
|
102
105
|
private onErrorCallback?;
|
|
103
106
|
private onStateChangeCallback?;
|
|
104
|
-
private
|
|
105
|
-
private
|
|
106
|
-
private connectionReject?;
|
|
107
|
-
private connectionTimeout?;
|
|
108
|
-
private isConnecting;
|
|
109
|
-
private connectionId;
|
|
110
|
-
private pingInterval?;
|
|
111
|
-
private pongTimeout?;
|
|
112
|
-
private lastPongReceived;
|
|
107
|
+
private connectionPromiseState;
|
|
108
|
+
private isConnectingRef;
|
|
113
109
|
/**
|
|
114
110
|
* Creates a new Verani client
|
|
115
111
|
* @param url - WebSocket URL to connect to
|
|
116
112
|
* @param options - Client configuration options
|
|
117
113
|
*/
|
|
118
114
|
constructor(url: string, options?: VeraniClientOptions);
|
|
119
|
-
/**
|
|
120
|
-
* Starts the ping interval to keep the connection alive
|
|
121
|
-
*/
|
|
122
|
-
private startPingInterval;
|
|
123
|
-
/**
|
|
124
|
-
* Stops the ping interval
|
|
125
|
-
*/
|
|
126
|
-
private stopPingInterval;
|
|
127
|
-
/**
|
|
128
|
-
* Cleans up existing WebSocket connection and resources
|
|
129
|
-
*/
|
|
130
|
-
private cleanupWebSocket;
|
|
131
115
|
/**
|
|
132
116
|
* Establishes WebSocket connection
|
|
133
117
|
*/
|
|
134
118
|
private connect;
|
|
135
119
|
/**
|
|
136
|
-
*
|
|
137
|
-
*/
|
|
138
|
-
private handleOpen;
|
|
139
|
-
/**
|
|
140
|
-
* Handles incoming WebSocket messages
|
|
141
|
-
*/
|
|
142
|
-
private handleMessage;
|
|
143
|
-
/**
|
|
144
|
-
* Handles WebSocket closure
|
|
145
|
-
*/
|
|
146
|
-
private handleClose;
|
|
147
|
-
/**
|
|
148
|
-
* Handles WebSocket errors
|
|
149
|
-
*/
|
|
150
|
-
private handleError;
|
|
151
|
-
/**
|
|
152
|
-
* Handles connection errors
|
|
153
|
-
*/
|
|
154
|
-
private handleConnectionError;
|
|
155
|
-
/**
|
|
156
|
-
* Emits a lifecycle event to registered listeners
|
|
157
|
-
*/
|
|
158
|
-
private emitLifecycleEvent;
|
|
159
|
-
/**
|
|
160
|
-
* Flushes queued messages when connection is established
|
|
120
|
+
* Cleans up existing WebSocket connection and resources
|
|
161
121
|
*/
|
|
162
|
-
private
|
|
122
|
+
private cleanupWebSocket;
|
|
163
123
|
/**
|
|
164
124
|
* Gets the current connection state
|
|
165
125
|
*/
|
|
@@ -207,10 +167,6 @@ declare class VeraniClient {
|
|
|
207
167
|
* @param data - Optional message data
|
|
208
168
|
*/
|
|
209
169
|
emit(type: string, data?: any): void;
|
|
210
|
-
/**
|
|
211
|
-
* Queues a message for sending when connected
|
|
212
|
-
*/
|
|
213
|
-
private queueMessage;
|
|
214
170
|
/**
|
|
215
171
|
* Registers lifecycle callback for connection open
|
|
216
172
|
*/
|
package/dist/client.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as decodeClientMessage,i as encodeServerMessage,n as encodeClientMessage,o as decodeFrame,r as encodeFrame,s as decodeServerMessage,t as PROTOCOL_VERSION}from"./types-CJLnZrA8.mjs";function encodeClientMessage$1(e){return encodeClientMessage(e)}function decodeServerMessage$1(e){return decodeServerMessage(e)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,c){this.config=e,this.onStateChange=c,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}isValidStateTransition(e,c){return{disconnected:[`connecting`,`reconnecting`],connecting:[`connected`,`disconnected`,`error`,`reconnecting`],connected:[`disconnected`,`reconnecting`],reconnecting:[`connecting`,`disconnected`,`error`],error:[`reconnecting`,`disconnected`,`connecting`]}[e]?.includes(c)??!1}setState(e){this.state!==e&&(this.isValidStateTransition(this.state,e),this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}},VeraniClient=class{constructor(e,c={}){this.url=e,this.listeners=new Map,this.messageQueue=[],this.isConnecting=!1,this.connectionId=0,this.lastPongReceived=0,this.options={reconnection:{enabled:c.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:c.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:c.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:c.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:c.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:c.maxQueueSize??100,connectionTimeout:c.connectionTimeout??1e4,pingInterval:c.pingInterval??5e3,pongTimeout:c.pongTimeout??5e3},this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.connect()}startPingInterval(){this.options.pingInterval===0||this.pingInterval!==void 0||(this.lastPongReceived=Date.now(),this.pingInterval=setInterval(()=>{if(!this.ws||this.ws.readyState!==WebSocket.OPEN){this.stopPingInterval();return}if(Date.now()-this.lastPongReceived>this.options.pongTimeout+this.options.pingInterval){this.stopPingInterval(),this.ws.close(1006,`Pong timeout`);return}try{this.ws.send(encodeClientMessage$1({type:`ping`}))}catch{}},this.options.pingInterval))}stopPingInterval(){this.pingInterval!==void 0&&(clearInterval(this.pingInterval),this.pingInterval=void 0),this.pongTimeout!==void 0&&(clearTimeout(this.pongTimeout),this.pongTimeout=void 0)}cleanupWebSocket(){if(this.stopPingInterval(),this.connectionTimeout!==void 0&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=void 0),this.ws){let e=this.ws;if(this.ws=void 0,e.readyState===WebSocket.OPEN||e.readyState===WebSocket.CONNECTING)try{e.close(1e3,`Cleanup`)}catch{}}}connect(){if(!this.isConnecting&&!this.isConnected()){this.cleanupWebSocket();try{this.isConnecting=!0,this.connectionId++;let e=this.connectionId;this.connectionManager.setState(`connecting`),this.emitLifecycleEvent(`connecting`),this.ws=new WebSocket(this.url),this.connectionTimeout=setTimeout(()=>{this.isConnecting&&this.connectionId===e&&(this.ws?.close(),this.handleConnectionError(Error(`Connection timeout`)))},this.options.connectionTimeout),this.ws.addEventListener(`open`,()=>{this.connectionId===e&&this.handleOpen()}),this.ws.addEventListener(`message`,c=>{this.connectionId===e&&this.handleMessage(c)}),this.ws.addEventListener(`close`,c=>{this.connectionId===e&&this.handleClose(c)}),this.ws.addEventListener(`error`,c=>{this.connectionId===e&&this.handleError(c)})}catch(e){this.isConnecting=!1,this.handleConnectionError(e)}}}handleOpen(){this.isConnecting=!1,this.connectionTimeout!==void 0&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=void 0),this.connectionManager.setState(`connected`),this.connectionManager.resetReconnection(),this.startPingInterval(),this.flushMessageQueue(),this.connectionResolve&&(this.connectionResolve(),this.connectionPromise=void 0,this.connectionResolve=void 0,this.connectionReject=void 0),this.emitLifecycleEvent(`open`),this.emitLifecycleEvent(`connected`),this.onOpenCallback?.()}handleMessage(e){let c=decodeServerMessage$1(e.data);if(!c)return;if(c.type===`pong`){this.lastPongReceived=Date.now();return}let l=c.type,u=c.data;c.type===`event`&&c.data&&typeof c.data==`object`&&`type`in c.data&&(l=c.data.type,u=c.data);let d=this.listeners.get(l);if(d)for(let e of d)try{e(u)}catch{}}handleClose(e){this.isConnecting=!1,this.connectionTimeout!==void 0&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=void 0),this.connectionManager.setState(`disconnected`),this.connectionReject&&=(this.connectionReject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`close`,e),this.emitLifecycleEvent(`disconnected`,e),this.onCloseCallback?.(e),e.code!==1e3&&e.code!==1001&&this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}handleError(e){this.isConnecting=!1,this.connectionTimeout!==void 0&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=void 0),this.emitLifecycleEvent(`error`,e),this.onErrorCallback?.(e),this.handleConnectionError(Error(`WebSocket error`))}handleConnectionError(e){this.isConnecting=!1,this.connectionTimeout!==void 0&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=void 0),this.connectionReject&&=(this.connectionReject(e),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`error`,e),this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}emitLifecycleEvent(e,c){let l=this.listeners.get(e);if(l)for(let e of l)try{e(c)}catch{}}flushMessageQueue(){if(!(!this.ws||this.ws.readyState!==WebSocket.OPEN))for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();try{this.ws.send(encodeClientMessage$1(e))}catch{}}}getState(){return this.connectionManager.getState()}isConnected(){return this.ws?.readyState===WebSocket.OPEN&&this.connectionManager.getState()===`connected`}getConnectionState(){return{state:this.connectionManager.getState(),isConnected:this.isConnected(),isConnecting:this.isConnecting,reconnectAttempts:this.connectionManager.getReconnectAttempts(),connectionId:this.connectionId}}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromise||=new Promise((e,c)=>{this.connectionResolve=e,this.connectionReject=c;let l=setTimeout(()=>{this.connectionReject&&=(this.connectionReject(Error(`Connection wait timeout`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0)},this.options.connectionTimeout*2);this.connectionPromise&&this.connectionPromise.finally(()=>{clearTimeout(l)})}),this.connectionPromise)}on(e,c){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(c)}off(e,c){let l=this.listeners.get(e);l&&(l.delete(c),l.size===0&&this.listeners.delete(e))}once(e,c){let l=u=>{this.off(e,l),c(u)};this.on(e,l)}emit(e,c){let l={type:e,data:c};if(this.isConnected())try{this.ws.send(encodeClientMessage$1(l))}catch{this.queueMessage(l)}else this.queueMessage(l)}queueMessage(e){this.messageQueue.length>=this.options.maxQueueSize&&this.messageQueue.shift(),this.messageQueue.push(e)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.connectionManager.resetReconnection(),this.connectionManager.cancelReconnect(),this.cleanupWebSocket(),this.isConnecting=!1,this.connectionManager.setState(`disconnected`),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.isConnecting=!1,this.connectionReject&&=(this.connectionReject(Error(`Connection disconnected`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.cleanupWebSocket(),this.connectionManager.setState(`disconnected`)}close(){this.connectionReject&&=(this.connectionReject(Error(`Client closed`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.disconnect(),this.listeners.clear(),this.messageQueue=[],this.connectionManager.destroy()}};export{ConnectionManager,DEFAULT_RECONNECTION_CONFIG,PROTOCOL_VERSION,VeraniClient,decodeClientMessage,decodeFrame,decodeServerMessage,encodeClientMessage,encodeFrame,encodeServerMessage};
|
|
1
|
+
import{a as decodeClientMessage,i as encodeServerMessage,n as encodeClientMessage,o as decodeFrame,r as encodeFrame,s as decodeServerMessage,t as PROTOCOL_VERSION}from"./types-CJLnZrA8.mjs";function encodeClientMessage$1(e){return encodeClientMessage(e)}function decodeServerMessage$1(e){return decodeServerMessage(e)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,h){this.config=e,this.onStateChange=h,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}isValidStateTransition(e,h){return{disconnected:[`connecting`,`reconnecting`],connecting:[`connected`,`disconnected`,`error`,`reconnecting`],connected:[`disconnected`,`reconnecting`],reconnecting:[`connecting`,`disconnected`,`error`],error:[`reconnecting`,`disconnected`,`connecting`]}[e]?.includes(h)??!1}setState(e){this.state!==e&&(this.isValidStateTransition(this.state,e),this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}};function resolveClientOptions(e){return{reconnection:{enabled:e.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:e.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:e.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:e.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:e.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:e.maxQueueSize??100,connectionTimeout:e.connectionTimeout??1e4,pingInterval:e.pingInterval??5e3,pongTimeout:e.pongTimeout??5e3}}var MessageQueue=class{constructor(e){this.maxQueueSize=e,this.queue=[]}queueMessage(e){this.queue.length>=this.maxQueueSize&&this.queue.shift(),this.queue.push(e)}flushMessageQueue(e){if(!(!e||e.readyState!==WebSocket.OPEN))for(;this.queue.length>0;){let h=this.queue.shift();try{e.send(encodeClientMessage$1(h))}catch{}}}clear(){this.queue=[]}getLength(){return this.queue.length}};function isBrowserEnvironment(){return typeof globalThis<`u`&&`document`in globalThis&&globalThis.document!==void 0&&globalThis.document.hidden!==void 0&&typeof globalThis.document.addEventListener==`function`}function getDocument(){return isBrowserEnvironment()&&`document`in globalThis?globalThis.document:null}function isPageVisible(){let e=getDocument();return e?!e.hidden:!0}function onVisibilityChange(e){let h=getDocument();if(!h)return null;let g=()=>{e(isPageVisible())};return h.addEventListener(`visibilitychange`,g),()=>{h.removeEventListener(`visibilitychange`,g)}}var KeepaliveManager=class{constructor(e,h,g){this.options=e,this.getWebSocket=h,this.onTimeout=g,this.lastPongReceived=0}startPingInterval(){this.options.pingInterval===0||this.pingInterval!==void 0||(this.lastPongReceived=Date.now(),this.visibilityCleanup=onVisibilityChange(e=>{e&&this.resyncPingInterval()}),this.pingInterval=setInterval(()=>{let e=this.getWebSocket();if(!e||e.readyState!==WebSocket.OPEN){this.stopPingInterval();return}if(Date.now()-this.lastPongReceived>this.options.pongTimeout+this.options.pingInterval){this.stopPingInterval(),e.close(1006,`Pong timeout`);return}try{e.send(encodeClientMessage$1({type:`ping`}))}catch{}},this.options.pingInterval))}resyncPingInterval(){let e=this.getWebSocket();if(!(!e||e.readyState!==WebSocket.OPEN)){this.pingInterval!==void 0&&(clearInterval(this.pingInterval),this.pingInterval=void 0);try{e.send(encodeClientMessage$1({type:`ping`}))}catch{}this.lastPongReceived=Date.now(),this.pingInterval=setInterval(()=>{let e=this.getWebSocket();if(!e||e.readyState!==WebSocket.OPEN){this.stopPingInterval();return}if(Date.now()-this.lastPongReceived>this.options.pongTimeout+this.options.pingInterval){this.stopPingInterval(),e.close(1006,`Pong timeout`);return}try{e.send(encodeClientMessage$1({type:`ping`}))}catch{}},this.options.pingInterval)}}stopPingInterval(){this.pingInterval!==void 0&&(clearInterval(this.pingInterval),this.pingInterval=void 0),this.pongTimeout!==void 0&&(clearTimeout(this.pongTimeout),this.pongTimeout=void 0),this.visibilityCleanup&&=(this.visibilityCleanup(),void 0)}recordPong(){this.lastPongReceived=Date.now()}},EventEmitter=class{constructor(){this.listeners=new Map}on(e,h){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(h)}off(e,h){let g=this.listeners.get(e);g&&(g.delete(h),g.size===0&&this.listeners.delete(e))}once(e,h){let g=_=>{this.off(e,g),h(_)};this.on(e,g)}emitLifecycleEvent(e,h){let g=this.listeners.get(e);if(g)for(let e of g)try{e(h)}catch{}}dispatch(e,h){let g=this.listeners.get(e);if(g)for(let e of g)try{e(h)}catch{}}clear(){this.listeners.clear()}};function handleWebSocketOpen(e,h,g,_,v,y,b,x){e.clear(),h.setState(`connected`),h.resetReconnection(),g.startPingInterval(),_.flushMessageQueue(v),y.resolve&&(y.resolve(),y.clear()),b.emitLifecycleEvent(`open`),b.emitLifecycleEvent(`connected`),x?.()}function handleWebSocketMessage(e,h,g){let _=decodeServerMessage$1(e.data);if(!_)return;if(_.type===`pong`){h.recordPong();return}let v=_.type,y=_.data;_.type===`event`&&_.data&&typeof _.data==`object`&&`type`in _.data&&(v=_.data.type,y=_.data),g.dispatch(v,y)}function handleWebSocketClose(e,h,g,_,v,y,b,x){b&&(b.value=!1),h.clear(),g.setState(`disconnected`),_.reject&&(_.reject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),_.clear()),v.emitLifecycleEvent(`close`,e),v.emitLifecycleEvent(`disconnected`,e),x?.(e),e.code!==1e3&&e.code!==1001&&g.scheduleReconnect(y)&&v.emitLifecycleEvent(`reconnecting`)}function handleWebSocketError(e,h,g,_,v,y){v&&(v.value=!1),h.clear(),g.emitLifecycleEvent(`error`,e),y?.(e),_(Error(`WebSocket error`))}function handleConnectionError(e,h,g,_,v,y,b){b&&(b.value=!1),h.clear(),g.reject&&(g.reject(e),g.clear()),v.emitLifecycleEvent(`error`,e),_.scheduleReconnect(y)&&v.emitLifecycleEvent(`reconnecting`)}var ConnectionHandler=class{constructor(e,h,g,_,v,y,b,x,S,C,w,T){this.url=e,this.options=h,this.connectionManager=g,this.keepalive=_,this.eventEmitter=v,this.messageQueue=y,this.connectionPromise=b,this.isConnectingRef=x,this.isConnectedFn=S,this.onOpenCallback=C,this.onCloseCallback=w,this.onErrorCallback=T,this.connectionId=0,this.connectionTimeoutState={value:void 0,clear:()=>{this.connectionTimeoutState.value!==void 0&&(clearTimeout(this.connectionTimeoutState.value),this.connectionTimeoutState.value=void 0)}}}connect(){if(!this.isConnectingRef.value&&!this.isConnectedFn()){this.cleanupWebSocket();try{this.isConnectingRef.value=!0,this.connectionId++;let e=this.connectionId;this.connectionManager.setState(`connecting`),this.eventEmitter.emitLifecycleEvent(`connecting`),this.ws=new WebSocket(this.url),this.connectionTimeoutState.value=setTimeout(()=>{this.isConnectingRef.value&&this.connectionId===e&&(this.ws?.close(),this.handleConnectionErrorInternal(Error(`Connection timeout`)))},this.options.connectionTimeout),this.ws.addEventListener(`open`,()=>{this.connectionId===e&&this.handleOpenInternal()}),this.ws.addEventListener(`message`,h=>{this.connectionId===e&&handleWebSocketMessage(h,this.keepalive,this.eventEmitter)}),this.ws.addEventListener(`close`,h=>{this.connectionId===e&&handleWebSocketClose(h,this.connectionTimeoutState,this.connectionManager,this.connectionPromise,this.eventEmitter,()=>this.connect(),this.isConnectingRef,this.onCloseCallback)}),this.ws.addEventListener(`error`,h=>{this.connectionId===e&&handleWebSocketError(h,this.connectionTimeoutState,this.eventEmitter,e=>this.handleConnectionErrorInternal(e),this.isConnectingRef,this.onErrorCallback)})}catch(e){this.isConnectingRef.value=!1,this.handleConnectionErrorInternal(e)}}}cleanupWebSocket(){if(this.keepalive.stopPingInterval(),this.connectionTimeoutState.clear(),this.ws){let e=this.ws;if(this.ws=void 0,e.readyState===WebSocket.OPEN||e.readyState===WebSocket.CONNECTING)try{e.close(1e3,`Cleanup`)}catch{}}}getWebSocket(){return this.ws}getConnectionId(){return this.connectionId}handleOpenInternal(){handleWebSocketOpen(this.connectionTimeoutState,this.connectionManager,this.keepalive,this.messageQueue,this.ws,this.connectionPromise,this.eventEmitter,this.onOpenCallback),this.isConnectingRef.value=!1}handleConnectionErrorInternal(e){handleConnectionError(e,this.connectionTimeoutState,this.connectionPromise,this.connectionManager,this.eventEmitter,()=>this.connect(),this.isConnectingRef)}},VeraniClient=class{constructor(e,h={}){this.url=e,this.connectionPromiseState={promise:void 0,resolve:void 0,reject:void 0,clear:()=>{this.connectionPromiseState.promise=void 0,this.connectionPromiseState.resolve=void 0,this.connectionPromiseState.reject=void 0}},this.options=resolveClientOptions(h),this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.messageQueue=new MessageQueue(this.options.maxQueueSize),this.eventEmitter=new EventEmitter,this.isConnectingRef={value:!1},this.keepalive=new KeepaliveManager(this.options,()=>this.connectionHandler.getWebSocket(),()=>{let e=this.connectionHandler.getWebSocket();e&&e.close(1006,`Pong timeout`)}),this.connectionHandler=new ConnectionHandler(this.url,this.options,this.connectionManager,this.keepalive,this.eventEmitter,this.messageQueue,this.connectionPromiseState,this.isConnectingRef,()=>this.isConnected(),this.onOpenCallback,this.onCloseCallback,this.onErrorCallback),Object.defineProperty(this,`isConnecting`,{get:()=>this.isConnectingRef.value,enumerable:!0,configurable:!0}),this.connect()}connect(){this.connectionHandler.connect()}cleanupWebSocket(){this.connectionHandler.cleanupWebSocket()}getState(){return this.connectionManager.getState()}isConnected(){return this.connectionHandler.getWebSocket()?.readyState===WebSocket.OPEN&&this.connectionManager.getState()===`connected`}getConnectionState(){return{state:this.connectionManager.getState(),isConnected:this.isConnected(),isConnecting:this.isConnectingRef.value,reconnectAttempts:this.connectionManager.getReconnectAttempts(),connectionId:this.connectionHandler.getConnectionId()}}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromiseState.promise||(this.connectionPromiseState.promise=new Promise((e,h)=>{this.connectionPromiseState.resolve=e,this.connectionPromiseState.reject=h;let g=setTimeout(()=>{this.connectionPromiseState.reject&&(this.connectionPromiseState.reject(Error(`Connection wait timeout`)),this.connectionPromiseState.clear())},this.options.connectionTimeout*2);this.connectionPromiseState.promise&&this.connectionPromiseState.promise.finally(()=>{clearTimeout(g)})})),this.connectionPromiseState.promise)}on(e,h){this.eventEmitter.on(e,h)}off(e,h){this.eventEmitter.off(e,h)}once(e,h){this.eventEmitter.once(e,h)}emit(e,h){let g={type:e,data:h};if(this.isConnected()){let e=this.connectionHandler.getWebSocket();if(e)try{e.send(encodeClientMessage$1(g))}catch{this.messageQueue.queueMessage(g)}}else this.messageQueue.queueMessage(g)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.connectionManager.resetReconnection(),this.connectionManager.cancelReconnect(),this.cleanupWebSocket(),this.isConnectingRef.value=!1,this.connectionManager.setState(`disconnected`),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.isConnectingRef.value=!1,this.connectionPromiseState.reject&&(this.connectionPromiseState.reject(Error(`Connection disconnected`)),this.connectionPromiseState.clear()),this.cleanupWebSocket(),this.connectionManager.setState(`disconnected`)}close(){this.connectionPromiseState.reject&&(this.connectionPromiseState.reject(Error(`Client closed`)),this.connectionPromiseState.clear()),this.disconnect(),this.eventEmitter.clear(),this.messageQueue.clear(),this.connectionManager.destroy()}};export{ConnectionManager,DEFAULT_RECONNECTION_CONFIG,PROTOCOL_VERSION,VeraniClient,decodeClientMessage,decodeFrame,decodeServerMessage,encodeClientMessage,encodeFrame,encodeServerMessage};
|
package/dist/verani.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const require_types=require(`./types-083oWz55.cjs`);let __cloudflare_actors=require(`@cloudflare/actors`);function defaultExtractMeta(e){let
|
|
1
|
+
const require_types=require(`./types-083oWz55.cjs`);let __cloudflare_actors=require(`@cloudflare/actors`);var RoomEventEmitterImpl=class{constructor(){this.handlers=new Map}on(e,x){this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(x)}off(e,x){let S=this.handlers.get(e);S&&(x?(S.delete(x),S.size===0&&this.handlers.delete(e)):this.handlers.delete(e))}async emit(e,x,S){let C=this.handlers.get(e);if(C&&C.size>0){let e=[];for(let w of C)try{let C=w(x,S);C instanceof Promise&&e.push(C)}catch{}await Promise.all(e)}let w=this.handlers.get(`*`);if(w&&w.size>0){let e=[];for(let C of w)try{let w=C(x,S);w instanceof Promise&&e.push(w)}catch{}await Promise.all(e)}}hasHandlers(e){return this.handlers.has(e)&&this.handlers.get(e).size>0||this.handlers.has(`*`)&&this.handlers.get(`*`).size>0}getEventNames(){return Array.from(this.handlers.keys())}};function createRoomEventEmitter(){return new RoomEventEmitterImpl}function defaultExtractMeta(e){let x=crypto.randomUUID(),S=crypto.randomUUID(),C=new URL(e.url).searchParams.get(`channels`);return{userId:x,clientId:S,channels:C?C.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){let x=e.eventEmitter||createRoomEventEmitter();return{name:e.name,websocketPath:e.websocketPath,extractMeta:e.extractMeta||defaultExtractMeta,onConnect:e.onConnect,onDisconnect:e.onDisconnect,onMessage:e.onMessage,onError:e.onError,onHibernationRestore:e.onHibernationRestore,eventEmitter:x,on(e,S){x.on(e,S)},off(e,S){x.off(e,S)}}}function cleanupStaleSessions(e){let x=0,S=[];for(let[x,C]of e.entries())x.readyState!==WebSocket.OPEN&&S.push(x);for(let C of S)e.delete(C),x++;return x}function decodeFrame$1(x){return require_types.o(x)??{type:`invalid`}}function encodeFrame$1(x){return require_types.r(x)}function broadcast(e,x,S,C){let w=0,T=encodeFrame$1({type:`event`,channel:x,data:S}),E=[];for(let{ws:S,meta:D}of e.values())if(D.channels.includes(x)&&!(C?.except&&S===C.except)&&!(C?.userIds&&!C.userIds.includes(D.userId))&&!(C?.clientIds&&!C.clientIds.includes(D.clientId))){if(S.readyState!==WebSocket.OPEN){E.push(S);continue}try{S.send(T),w++}catch{E.push(S)}}for(let x of E)e.delete(x);return E.length,w}function sendToUser(e,x,S,C){let w=0,T=encodeFrame$1({type:`event`,channel:S,data:C}),E=[];for(let{ws:C,meta:D}of e.values())if(D.userId===x&&D.channels.includes(S)){if(C.readyState!==WebSocket.OPEN){E.push(C);continue}try{C.send(T),w++}catch{E.push(C)}}for(let x of E)e.delete(x);return E.length,w}function getSessionCount(e){return e.size}function getConnectedUserIds(e){let x=new Set;for(let{meta:S}of e.values())x.add(S.userId);return Array.from(x)}function getUserSessions(e,x){let S=[];for(let{ws:C,meta:w}of e.values())w.userId===x&&S.push(C);return S}function getStorage(e){return e.storage}function sanitizeToClassName(e){return e.replace(/^\/+/,``).split(/[-_\/\s]+/).map(e=>e.replace(/[^a-zA-Z0-9]/g,``)).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createConfiguration(e){return function(x){return{locationHint:`me`,sockets:{upgradePath:e.websocketPath}}}}function isValidConnectionMeta(e){return!(!e||typeof e!=`object`||typeof e.userId!=`string`||!e.userId||typeof e.clientId!=`string`||!e.clientId||!Array.isArray(e.channels)||!e.channels.every(e=>typeof e==`string`))}function storeAttachment(e,x){e.serializeAttachment(x)}function restoreSessions(e){let x=0,S=0;for(let C of e.ctx.getWebSockets()){if(C.readyState!==WebSocket.OPEN){S++;continue}let w=C.deserializeAttachment();if(!w){S++;continue}if(!isValidConnectionMeta(w)){S++;continue}e.sessions.set(C,{ws:C,meta:w}),x++}}async function onInit(e,x){try{restoreSessions(e)}catch{}if(x.onHibernationRestore&&e.sessions.size>0)try{await x.onHibernationRestore(e)}catch{}else x.onHibernationRestore&&e.sessions.size}function createUserEmitBuilder(e,x,S){return{emit(C,w){return sendToUser(x,e,S,{type:C,...w})}}}function createChannelEmitBuilder(e,x,S){return{emit(C,w){return broadcast(x,e,{type:C,...w},S)}}}function createSocketEmit(e){let x=e.meta.channels[0]||`default`;return{emit(S,C){if(e.ws.readyState===WebSocket.OPEN)try{let w={type:`event`,channel:x,data:{type:S,...C}};e.ws.send(encodeFrame$1(w))}catch{}},to(S){return e.meta.channels.includes(S)?createChannelEmitBuilder(S,e.actor.sessions,{except:e.ws}):createUserEmitBuilder(S,e.actor.sessions,x)}}}function createActorEmit(e){return{emit(x,S){let C={type:x,...S};return broadcast(e.sessions,`default`,C)},to(x){return createChannelEmitBuilder(x,e.sessions)}}}async function onWebSocketConnect(e,x,S,C){let w;try{w=x.extractMeta?await x.extractMeta(C):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(S,w);let T={actor:e,ws:S,meta:w,frame:{type:`connect`}};if(x.onConnect){let C={actor:e,ws:S,meta:w,emit:createSocketEmit(T)};await x.onConnect(C)}e.sessions.set(S,{ws:S,meta:w})}catch(C){if(x.onError&&w)try{let T={actor:e,ws:S,meta:w,frame:{type:`error`}};await x.onError(C,{actor:e,ws:S,meta:w,emit:createSocketEmit(T)})}catch{}S.close(1011,`Internal server error`)}}async function onWebSocketMessage(e,x,S,C){let w;try{let T=decodeFrame$1(C);if(T&&T.type===`ping`){if(S.readyState===WebSocket.OPEN)try{S.send(encodeFrame$1({type:`pong`}))}catch{}return}if(!T||T.type===`invalid`||(w=e.sessions.get(S),!w))return;let E={actor:e,ws:S,meta:w.meta,frame:T,emit:createSocketEmit({actor:e,ws:S,meta:w.meta,frame:T})},O=x.eventEmitter;O&&O.hasHandlers&&O.hasHandlers(T.type)?await O.emit(T.type,E,T.data||{}):x.onMessage&&await x.onMessage(E,T)}catch(C){if(x.onError&&w)try{await x.onError(C,{actor:e,ws:S,meta:w.meta,emit:createSocketEmit({actor:e,ws:S,meta:w.meta,frame:{type:`error`}})})}catch{}}}async function onWebSocketDisconnect(e,x,S){try{let C=e.sessions.get(S);if(e.sessions.delete(S),C&&x.onDisconnect){let w={actor:e,ws:S,meta:C.meta,frame:{type:`disconnect`}},T={actor:e,ws:S,meta:C.meta,emit:createSocketEmit(w)};await x.onDisconnect(T)}}catch{}}function createActorHandler(e){let S=sanitizeToClassName(e.name||e.websocketPath||`VeraniActor`);class C extends __cloudflare_actors.Actor{constructor(...e){super(...e),this.sessions=new Map,this.emit=createActorEmit(this)}static{this.configuration=createConfiguration(e)}async shouldUpgradeWebSocket(e){return!0}async fetch(x){let S=new URL(x.url),C=x.headers.get(`Upgrade`);return S.pathname===e.websocketPath&&C===`websocket`&&await this.shouldUpgradeWebSocket(x)?this.onWebSocketUpgrade(x):this.onRequest(x)}async onInit(){await onInit(this,e)}async onWebSocketConnect(x,S){await onWebSocketConnect(this,e,x,S)}async onWebSocketMessage(x,S){await onWebSocketMessage(this,e,x,S)}async onWebSocketDisconnect(x){await onWebSocketDisconnect(this,e,x)}cleanupStaleSessions(){return cleanupStaleSessions(this.sessions)}broadcast(e,x,S){return broadcast(this.sessions,e,x,S)}getSessionCount(){return getSessionCount(this.sessions)}getConnectedUserIds(){return getConnectedUserIds(this.sessions)}getUserSessions(e){return getUserSessions(this.sessions,e)}sendToUser(e,x,S){return sendToUser(this.sessions,e,x,S)}getStorage(){return getStorage(this.ctx)}}return Object.defineProperty(C,`name`,{value:S,writable:!1,configurable:!0}),C}exports.PROTOCOL_VERSION=require_types.t,exports.createActorHandler=createActorHandler,exports.decodeClientMessage=require_types.a,exports.decodeFrame=require_types.o,exports.decodeServerMessage=require_types.s,exports.defineRoom=defineRoom,exports.encodeClientMessage=require_types.n,exports.encodeFrame=require_types.r,exports.encodeServerMessage=require_types.i,exports.restoreSessions=restoreSessions,exports.storeAttachment=storeAttachment;
|
package/dist/verani.d.cts
CHANGED
|
@@ -79,6 +79,87 @@ interface VeraniActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown
|
|
|
79
79
|
* @see @src/actor/actor-runtime.ts getStorage()
|
|
80
80
|
*/
|
|
81
81
|
getStorage(): DurableObjectStorage;
|
|
82
|
+
/**
|
|
83
|
+
* Socket.io-like emit API for actor-level broadcasting
|
|
84
|
+
*/
|
|
85
|
+
emit: ActorEmit<TMeta, E>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Event handler function type for socket.io-like event handling
|
|
89
|
+
*/
|
|
90
|
+
type EventHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> = (ctx: MessageContext<TMeta, E>, data: any) => void | Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Event emitter interface for room-level event handling
|
|
93
|
+
*/
|
|
94
|
+
interface RoomEventEmitter<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
95
|
+
/**
|
|
96
|
+
* Register an event handler
|
|
97
|
+
* @param event - Event name (supports wildcard "*")
|
|
98
|
+
* @param handler - Handler function
|
|
99
|
+
*/
|
|
100
|
+
on(event: string, handler: EventHandler<TMeta, E>): void;
|
|
101
|
+
/**
|
|
102
|
+
* Remove an event handler
|
|
103
|
+
* @param event - Event name
|
|
104
|
+
* @param handler - Optional specific handler to remove, or remove all handlers for event
|
|
105
|
+
*/
|
|
106
|
+
off(event: string, handler?: EventHandler<TMeta, E>): void;
|
|
107
|
+
/**
|
|
108
|
+
* Emit an event to registered handlers
|
|
109
|
+
* @param event - Event name
|
|
110
|
+
* @param ctx - Message context
|
|
111
|
+
* @param data - Event data
|
|
112
|
+
*/
|
|
113
|
+
emit(event: string, ctx: MessageContext<TMeta, E>, data: any): Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Builder interface for targeting specific scopes when emitting
|
|
117
|
+
*/
|
|
118
|
+
interface EmitBuilder<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
119
|
+
/**
|
|
120
|
+
* Emit to the targeted scope
|
|
121
|
+
* @param event - Event name
|
|
122
|
+
* @param data - Event data
|
|
123
|
+
* @returns Number of connections that received the message
|
|
124
|
+
*/
|
|
125
|
+
emit(event: string, data?: any): number;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Socket-level emit API (available on context)
|
|
129
|
+
* Allows emitting to current socket, user, or channel
|
|
130
|
+
*/
|
|
131
|
+
interface SocketEmit<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
132
|
+
/**
|
|
133
|
+
* Emit to the current socket
|
|
134
|
+
* @param event - Event name
|
|
135
|
+
* @param data - Event data
|
|
136
|
+
*/
|
|
137
|
+
emit(event: string, data?: any): void;
|
|
138
|
+
/**
|
|
139
|
+
* Target a specific user or channel for emitting
|
|
140
|
+
* @param target - User ID or channel name
|
|
141
|
+
* @returns Builder for emitting to the target
|
|
142
|
+
*/
|
|
143
|
+
to(target: string): EmitBuilder<TMeta, E>;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Actor-level emit API (available on actor)
|
|
147
|
+
* Allows broadcasting to channels
|
|
148
|
+
*/
|
|
149
|
+
interface ActorEmit<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
150
|
+
/**
|
|
151
|
+
* Broadcast to default channel
|
|
152
|
+
* @param event - Event name
|
|
153
|
+
* @param data - Event data
|
|
154
|
+
* @returns Number of connections that received the message
|
|
155
|
+
*/
|
|
156
|
+
emit(event: string, data?: any): number;
|
|
157
|
+
/**
|
|
158
|
+
* Target a specific channel for broadcasting
|
|
159
|
+
* @param channel - Channel name
|
|
160
|
+
* @returns Builder for emitting to the channel
|
|
161
|
+
*/
|
|
162
|
+
to(channel: string): EmitBuilder<TMeta, E>;
|
|
82
163
|
}
|
|
83
164
|
/**
|
|
84
165
|
* Context provided to room lifecycle hooks
|
|
@@ -90,6 +171,8 @@ interface RoomContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown
|
|
|
90
171
|
ws: WebSocket;
|
|
91
172
|
/** Connection metadata */
|
|
92
173
|
meta: TMeta;
|
|
174
|
+
/** Socket.io-like emit API for this connection */
|
|
175
|
+
emit: SocketEmit<TMeta, E>;
|
|
93
176
|
}
|
|
94
177
|
/**
|
|
95
178
|
* Context for onMessage hook with frame included
|
|
@@ -132,6 +215,9 @@ interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
132
215
|
* Called when a message is received from a connection.
|
|
133
216
|
* This hook is awaited if it returns a Promise. The actor will not process
|
|
134
217
|
* other messages from this connection until this hook completes.
|
|
218
|
+
*
|
|
219
|
+
* **Note:** If event handlers are registered via `eventEmitter`, they take priority.
|
|
220
|
+
* This hook is used as a fallback when no matching event handler is found.
|
|
135
221
|
*/
|
|
136
222
|
onMessage?(ctx: MessageContext<TMeta, E>, frame: MessageFrame): void | Promise<void>;
|
|
137
223
|
/**
|
|
@@ -145,15 +231,38 @@ interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
145
231
|
* sessions failed to restore, allowing you to handle partial restoration scenarios.
|
|
146
232
|
*/
|
|
147
233
|
onHibernationRestore?(actor: VeraniActor<TMeta, E>): void | Promise<void>;
|
|
234
|
+
/**
|
|
235
|
+
* Event emitter for socket.io-like event handling.
|
|
236
|
+
* If provided, event handlers registered here will be called for matching message types.
|
|
237
|
+
* If not provided, a default event emitter will be created.
|
|
238
|
+
*/
|
|
239
|
+
eventEmitter?: RoomEventEmitter<TMeta, E>;
|
|
148
240
|
}
|
|
149
241
|
//#endregion
|
|
150
242
|
//#region src/actor/router.d.ts
|
|
243
|
+
/**
|
|
244
|
+
* Extended room definition with socket.io-like convenience methods
|
|
245
|
+
*/
|
|
246
|
+
interface RoomDefinitionWithHandlers<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> extends RoomDefinition<TMeta, E> {
|
|
247
|
+
/**
|
|
248
|
+
* Register an event handler (socket.io-like API)
|
|
249
|
+
* @param event - Event name
|
|
250
|
+
* @param handler - Handler function
|
|
251
|
+
*/
|
|
252
|
+
on(event: string, handler: (ctx: any, data: any) => void | Promise<void>): void;
|
|
253
|
+
/**
|
|
254
|
+
* Remove an event handler (socket.io-like API)
|
|
255
|
+
* @param event - Event name
|
|
256
|
+
* @param handler - Optional specific handler to remove
|
|
257
|
+
*/
|
|
258
|
+
off(event: string, handler?: (ctx: any, data: any) => void | Promise<void>): void;
|
|
259
|
+
}
|
|
151
260
|
/**
|
|
152
261
|
* Defines a room with lifecycle hooks and metadata extraction
|
|
153
262
|
* @param def - Room definition with optional hooks
|
|
154
|
-
* @returns Normalized room definition with defaults
|
|
263
|
+
* @returns Normalized room definition with defaults and socket.io-like event handler methods
|
|
155
264
|
*/
|
|
156
|
-
declare function defineRoom<TMeta extends ConnectionMeta = ConnectionMeta>(def: RoomDefinition<TMeta>):
|
|
265
|
+
declare function defineRoom<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown>(def: RoomDefinition<TMeta, E>): RoomDefinitionWithHandlers<TMeta, E>;
|
|
157
266
|
//#endregion
|
|
158
267
|
//#region src/actor/actor-runtime.d.ts
|
|
159
268
|
/**
|
package/dist/verani.d.mts
CHANGED
|
@@ -79,6 +79,87 @@ interface VeraniActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown
|
|
|
79
79
|
* @see @src/actor/actor-runtime.ts getStorage()
|
|
80
80
|
*/
|
|
81
81
|
getStorage(): DurableObjectStorage;
|
|
82
|
+
/**
|
|
83
|
+
* Socket.io-like emit API for actor-level broadcasting
|
|
84
|
+
*/
|
|
85
|
+
emit: ActorEmit<TMeta, E>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Event handler function type for socket.io-like event handling
|
|
89
|
+
*/
|
|
90
|
+
type EventHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> = (ctx: MessageContext<TMeta, E>, data: any) => void | Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Event emitter interface for room-level event handling
|
|
93
|
+
*/
|
|
94
|
+
interface RoomEventEmitter<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
95
|
+
/**
|
|
96
|
+
* Register an event handler
|
|
97
|
+
* @param event - Event name (supports wildcard "*")
|
|
98
|
+
* @param handler - Handler function
|
|
99
|
+
*/
|
|
100
|
+
on(event: string, handler: EventHandler<TMeta, E>): void;
|
|
101
|
+
/**
|
|
102
|
+
* Remove an event handler
|
|
103
|
+
* @param event - Event name
|
|
104
|
+
* @param handler - Optional specific handler to remove, or remove all handlers for event
|
|
105
|
+
*/
|
|
106
|
+
off(event: string, handler?: EventHandler<TMeta, E>): void;
|
|
107
|
+
/**
|
|
108
|
+
* Emit an event to registered handlers
|
|
109
|
+
* @param event - Event name
|
|
110
|
+
* @param ctx - Message context
|
|
111
|
+
* @param data - Event data
|
|
112
|
+
*/
|
|
113
|
+
emit(event: string, ctx: MessageContext<TMeta, E>, data: any): Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Builder interface for targeting specific scopes when emitting
|
|
117
|
+
*/
|
|
118
|
+
interface EmitBuilder<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
119
|
+
/**
|
|
120
|
+
* Emit to the targeted scope
|
|
121
|
+
* @param event - Event name
|
|
122
|
+
* @param data - Event data
|
|
123
|
+
* @returns Number of connections that received the message
|
|
124
|
+
*/
|
|
125
|
+
emit(event: string, data?: any): number;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Socket-level emit API (available on context)
|
|
129
|
+
* Allows emitting to current socket, user, or channel
|
|
130
|
+
*/
|
|
131
|
+
interface SocketEmit<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
132
|
+
/**
|
|
133
|
+
* Emit to the current socket
|
|
134
|
+
* @param event - Event name
|
|
135
|
+
* @param data - Event data
|
|
136
|
+
*/
|
|
137
|
+
emit(event: string, data?: any): void;
|
|
138
|
+
/**
|
|
139
|
+
* Target a specific user or channel for emitting
|
|
140
|
+
* @param target - User ID or channel name
|
|
141
|
+
* @returns Builder for emitting to the target
|
|
142
|
+
*/
|
|
143
|
+
to(target: string): EmitBuilder<TMeta, E>;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Actor-level emit API (available on actor)
|
|
147
|
+
* Allows broadcasting to channels
|
|
148
|
+
*/
|
|
149
|
+
interface ActorEmit<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
150
|
+
/**
|
|
151
|
+
* Broadcast to default channel
|
|
152
|
+
* @param event - Event name
|
|
153
|
+
* @param data - Event data
|
|
154
|
+
* @returns Number of connections that received the message
|
|
155
|
+
*/
|
|
156
|
+
emit(event: string, data?: any): number;
|
|
157
|
+
/**
|
|
158
|
+
* Target a specific channel for broadcasting
|
|
159
|
+
* @param channel - Channel name
|
|
160
|
+
* @returns Builder for emitting to the channel
|
|
161
|
+
*/
|
|
162
|
+
to(channel: string): EmitBuilder<TMeta, E>;
|
|
82
163
|
}
|
|
83
164
|
/**
|
|
84
165
|
* Context provided to room lifecycle hooks
|
|
@@ -90,6 +171,8 @@ interface RoomContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown
|
|
|
90
171
|
ws: WebSocket;
|
|
91
172
|
/** Connection metadata */
|
|
92
173
|
meta: TMeta;
|
|
174
|
+
/** Socket.io-like emit API for this connection */
|
|
175
|
+
emit: SocketEmit<TMeta, E>;
|
|
93
176
|
}
|
|
94
177
|
/**
|
|
95
178
|
* Context for onMessage hook with frame included
|
|
@@ -132,6 +215,9 @@ interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
132
215
|
* Called when a message is received from a connection.
|
|
133
216
|
* This hook is awaited if it returns a Promise. The actor will not process
|
|
134
217
|
* other messages from this connection until this hook completes.
|
|
218
|
+
*
|
|
219
|
+
* **Note:** If event handlers are registered via `eventEmitter`, they take priority.
|
|
220
|
+
* This hook is used as a fallback when no matching event handler is found.
|
|
135
221
|
*/
|
|
136
222
|
onMessage?(ctx: MessageContext<TMeta, E>, frame: MessageFrame): void | Promise<void>;
|
|
137
223
|
/**
|
|
@@ -145,15 +231,38 @@ interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
145
231
|
* sessions failed to restore, allowing you to handle partial restoration scenarios.
|
|
146
232
|
*/
|
|
147
233
|
onHibernationRestore?(actor: VeraniActor<TMeta, E>): void | Promise<void>;
|
|
234
|
+
/**
|
|
235
|
+
* Event emitter for socket.io-like event handling.
|
|
236
|
+
* If provided, event handlers registered here will be called for matching message types.
|
|
237
|
+
* If not provided, a default event emitter will be created.
|
|
238
|
+
*/
|
|
239
|
+
eventEmitter?: RoomEventEmitter<TMeta, E>;
|
|
148
240
|
}
|
|
149
241
|
//#endregion
|
|
150
242
|
//#region src/actor/router.d.ts
|
|
243
|
+
/**
|
|
244
|
+
* Extended room definition with socket.io-like convenience methods
|
|
245
|
+
*/
|
|
246
|
+
interface RoomDefinitionWithHandlers<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> extends RoomDefinition<TMeta, E> {
|
|
247
|
+
/**
|
|
248
|
+
* Register an event handler (socket.io-like API)
|
|
249
|
+
* @param event - Event name
|
|
250
|
+
* @param handler - Handler function
|
|
251
|
+
*/
|
|
252
|
+
on(event: string, handler: (ctx: any, data: any) => void | Promise<void>): void;
|
|
253
|
+
/**
|
|
254
|
+
* Remove an event handler (socket.io-like API)
|
|
255
|
+
* @param event - Event name
|
|
256
|
+
* @param handler - Optional specific handler to remove
|
|
257
|
+
*/
|
|
258
|
+
off(event: string, handler?: (ctx: any, data: any) => void | Promise<void>): void;
|
|
259
|
+
}
|
|
151
260
|
/**
|
|
152
261
|
* Defines a room with lifecycle hooks and metadata extraction
|
|
153
262
|
* @param def - Room definition with optional hooks
|
|
154
|
-
* @returns Normalized room definition with defaults
|
|
263
|
+
* @returns Normalized room definition with defaults and socket.io-like event handler methods
|
|
155
264
|
*/
|
|
156
|
-
declare function defineRoom<TMeta extends ConnectionMeta = ConnectionMeta>(def: RoomDefinition<TMeta>):
|
|
265
|
+
declare function defineRoom<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown>(def: RoomDefinition<TMeta, E>): RoomDefinitionWithHandlers<TMeta, E>;
|
|
157
266
|
//#endregion
|
|
158
267
|
//#region src/actor/actor-runtime.d.ts
|
|
159
268
|
/**
|
package/dist/verani.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as decodeClientMessage,i as encodeServerMessage,n as encodeClientMessage,o as decodeFrame,r as encodeFrame,s as decodeServerMessage,t as PROTOCOL_VERSION}from"./types-CJLnZrA8.mjs";import{Actor}from"@cloudflare/actors";function defaultExtractMeta(
|
|
1
|
+
import{a as decodeClientMessage,i as encodeServerMessage,n as encodeClientMessage,o as decodeFrame,r as encodeFrame,s as decodeServerMessage,t as PROTOCOL_VERSION}from"./types-CJLnZrA8.mjs";import{Actor}from"@cloudflare/actors";var RoomEventEmitterImpl=class{constructor(){this.handlers=new Map}on(u,O){this.handlers.has(u)||this.handlers.set(u,new Set),this.handlers.get(u).add(O)}off(u,O){let k=this.handlers.get(u);k&&(O?(k.delete(O),k.size===0&&this.handlers.delete(u)):this.handlers.delete(u))}async emit(u,O,k){let A=this.handlers.get(u);if(A&&A.size>0){let u=[];for(let j of A)try{let A=j(O,k);A instanceof Promise&&u.push(A)}catch{}await Promise.all(u)}let j=this.handlers.get(`*`);if(j&&j.size>0){let u=[];for(let A of j)try{let j=A(O,k);j instanceof Promise&&u.push(j)}catch{}await Promise.all(u)}}hasHandlers(u){return this.handlers.has(u)&&this.handlers.get(u).size>0||this.handlers.has(`*`)&&this.handlers.get(`*`).size>0}getEventNames(){return Array.from(this.handlers.keys())}};function createRoomEventEmitter(){return new RoomEventEmitterImpl}function defaultExtractMeta(u){let O=crypto.randomUUID(),k=crypto.randomUUID(),A=new URL(u.url).searchParams.get(`channels`);return{userId:O,clientId:k,channels:A?A.split(`,`).map(u=>u.trim()).filter(Boolean):[`default`]}}function defineRoom(u){let O=u.eventEmitter||createRoomEventEmitter();return{name:u.name,websocketPath:u.websocketPath,extractMeta:u.extractMeta||defaultExtractMeta,onConnect:u.onConnect,onDisconnect:u.onDisconnect,onMessage:u.onMessage,onError:u.onError,onHibernationRestore:u.onHibernationRestore,eventEmitter:O,on(u,k){O.on(u,k)},off(u,k){O.off(u,k)}}}function cleanupStaleSessions(u){let O=0,k=[];for(let[O,A]of u.entries())O.readyState!==WebSocket.OPEN&&k.push(O);for(let A of k)u.delete(A),O++;return O}function decodeFrame$1(u){return decodeFrame(u)??{type:`invalid`}}function encodeFrame$1(u){return encodeFrame(u)}function broadcast(u,O,k,A){let j=0,M=encodeFrame$1({type:`event`,channel:O,data:k}),N=[];for(let{ws:k,meta:P}of u.values())if(P.channels.includes(O)&&!(A?.except&&k===A.except)&&!(A?.userIds&&!A.userIds.includes(P.userId))&&!(A?.clientIds&&!A.clientIds.includes(P.clientId))){if(k.readyState!==WebSocket.OPEN){N.push(k);continue}try{k.send(M),j++}catch{N.push(k)}}for(let O of N)u.delete(O);return N.length,j}function sendToUser(u,O,k,A){let j=0,M=encodeFrame$1({type:`event`,channel:k,data:A}),N=[];for(let{ws:A,meta:P}of u.values())if(P.userId===O&&P.channels.includes(k)){if(A.readyState!==WebSocket.OPEN){N.push(A);continue}try{A.send(M),j++}catch{N.push(A)}}for(let O of N)u.delete(O);return N.length,j}function getSessionCount(u){return u.size}function getConnectedUserIds(u){let O=new Set;for(let{meta:k}of u.values())O.add(k.userId);return Array.from(O)}function getUserSessions(u,O){let k=[];for(let{ws:A,meta:j}of u.values())j.userId===O&&k.push(A);return k}function getStorage(u){return u.storage}function sanitizeToClassName(u){return u.replace(/^\/+/,``).split(/[-_\/\s]+/).map(u=>u.replace(/[^a-zA-Z0-9]/g,``)).filter(u=>u.length>0).map(u=>u.charAt(0).toUpperCase()+u.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createConfiguration(u){return function(O){return{locationHint:`me`,sockets:{upgradePath:u.websocketPath}}}}function isValidConnectionMeta(u){return!(!u||typeof u!=`object`||typeof u.userId!=`string`||!u.userId||typeof u.clientId!=`string`||!u.clientId||!Array.isArray(u.channels)||!u.channels.every(u=>typeof u==`string`))}function storeAttachment(u,O){u.serializeAttachment(O)}function restoreSessions(u){let O=0,k=0;for(let A of u.ctx.getWebSockets()){if(A.readyState!==WebSocket.OPEN){k++;continue}let j=A.deserializeAttachment();if(!j){k++;continue}if(!isValidConnectionMeta(j)){k++;continue}u.sessions.set(A,{ws:A,meta:j}),O++}}async function onInit(u,O){try{restoreSessions(u)}catch{}if(O.onHibernationRestore&&u.sessions.size>0)try{await O.onHibernationRestore(u)}catch{}else O.onHibernationRestore&&u.sessions.size}function createUserEmitBuilder(u,O,k){return{emit(A,j){return sendToUser(O,u,k,{type:A,...j})}}}function createChannelEmitBuilder(u,O,k){return{emit(A,j){return broadcast(O,u,{type:A,...j},k)}}}function createSocketEmit(u){let O=u.meta.channels[0]||`default`;return{emit(k,A){if(u.ws.readyState===WebSocket.OPEN)try{let j={type:`event`,channel:O,data:{type:k,...A}};u.ws.send(encodeFrame$1(j))}catch{}},to(k){return u.meta.channels.includes(k)?createChannelEmitBuilder(k,u.actor.sessions,{except:u.ws}):createUserEmitBuilder(k,u.actor.sessions,O)}}}function createActorEmit(u){return{emit(O,k){let A={type:O,...k};return broadcast(u.sessions,`default`,A)},to(O){return createChannelEmitBuilder(O,u.sessions)}}}async function onWebSocketConnect(u,O,k,A){let j;try{j=O.extractMeta?await O.extractMeta(A):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(k,j);let M={actor:u,ws:k,meta:j,frame:{type:`connect`}};if(O.onConnect){let A={actor:u,ws:k,meta:j,emit:createSocketEmit(M)};await O.onConnect(A)}u.sessions.set(k,{ws:k,meta:j})}catch(A){if(O.onError&&j)try{let M={actor:u,ws:k,meta:j,frame:{type:`error`}};await O.onError(A,{actor:u,ws:k,meta:j,emit:createSocketEmit(M)})}catch{}k.close(1011,`Internal server error`)}}async function onWebSocketMessage(u,O,k,A){let j;try{let M=decodeFrame$1(A);if(M&&M.type===`ping`){if(k.readyState===WebSocket.OPEN)try{k.send(encodeFrame$1({type:`pong`}))}catch{}return}if(!M||M.type===`invalid`||(j=u.sessions.get(k),!j))return;let N={actor:u,ws:k,meta:j.meta,frame:M,emit:createSocketEmit({actor:u,ws:k,meta:j.meta,frame:M})},P=O.eventEmitter;P&&P.hasHandlers&&P.hasHandlers(M.type)?await P.emit(M.type,N,M.data||{}):O.onMessage&&await O.onMessage(N,M)}catch(A){if(O.onError&&j)try{await O.onError(A,{actor:u,ws:k,meta:j.meta,emit:createSocketEmit({actor:u,ws:k,meta:j.meta,frame:{type:`error`}})})}catch{}}}async function onWebSocketDisconnect(u,O,k){try{let A=u.sessions.get(k);if(u.sessions.delete(k),A&&O.onDisconnect){let j={actor:u,ws:k,meta:A.meta,frame:{type:`disconnect`}},M={actor:u,ws:k,meta:A.meta,emit:createSocketEmit(j)};await O.onDisconnect(M)}}catch{}}function createActorHandler(u){let O=sanitizeToClassName(u.name||u.websocketPath||`VeraniActor`);class k extends Actor{constructor(...u){super(...u),this.sessions=new Map,this.emit=createActorEmit(this)}static{this.configuration=createConfiguration(u)}async shouldUpgradeWebSocket(u){return!0}async fetch(O){let k=new URL(O.url),A=O.headers.get(`Upgrade`);return k.pathname===u.websocketPath&&A===`websocket`&&await this.shouldUpgradeWebSocket(O)?this.onWebSocketUpgrade(O):this.onRequest(O)}async onInit(){await onInit(this,u)}async onWebSocketConnect(O,k){await onWebSocketConnect(this,u,O,k)}async onWebSocketMessage(O,k){await onWebSocketMessage(this,u,O,k)}async onWebSocketDisconnect(O){await onWebSocketDisconnect(this,u,O)}cleanupStaleSessions(){return cleanupStaleSessions(this.sessions)}broadcast(u,O,k){return broadcast(this.sessions,u,O,k)}getSessionCount(){return getSessionCount(this.sessions)}getConnectedUserIds(){return getConnectedUserIds(this.sessions)}getUserSessions(u){return getUserSessions(this.sessions,u)}sendToUser(u,O,k){return sendToUser(this.sessions,u,O,k)}getStorage(){return getStorage(this.ctx)}}return Object.defineProperty(k,`name`,{value:O,writable:!1,configurable:!0}),k}export{PROTOCOL_VERSION,createActorHandler,decodeClientMessage,decodeFrame,decodeServerMessage,defineRoom,encodeClientMessage,encodeFrame,encodeServerMessage,restoreSessions,storeAttachment};
|