verani 0.9.0 → 0.10.1

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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/client-Cf_oqaqp.cjs +1 -0
  3. package/dist/{client-C3BlxAY_.d.cts → client-D7P17bMd.d.mts} +4 -4
  4. package/dist/{client-CV5RrpZX.d.mts → client-JCkzlxJL.d.cts} +4 -4
  5. package/dist/client-oy-w-Ky5.mjs +1 -0
  6. package/dist/client.cjs +1 -1
  7. package/dist/client.d.cts +3 -3
  8. package/dist/client.d.mts +3 -3
  9. package/dist/client.mjs +1 -1
  10. package/dist/connection-actor-BPsGLJoj.mjs +1 -0
  11. package/dist/connection-actor-BUVlHA-z.cjs +1 -0
  12. package/dist/connection-actor-BZtdpBVP.d.mts +355 -0
  13. package/dist/connection-actor-lJotemJR.d.cts +355 -0
  14. package/dist/{decode--RaAAv0U.d.cts → decode-DwdLTFvb.d.mts} +4 -4
  15. package/dist/{decode-BUzd77kA.d.mts → decode-otE_3S_-.d.cts} +4 -4
  16. package/dist/encode-CFndA-BQ.mjs +1 -0
  17. package/dist/encode-SjZrKIyU.cjs +1 -0
  18. package/dist/typed-client.cjs +1 -1
  19. package/dist/typed-client.d.cts +1 -1
  20. package/dist/typed-client.d.mts +1 -1
  21. package/dist/typed-client.mjs +1 -1
  22. package/dist/typed.cjs +1 -1
  23. package/dist/typed.d.cts +66 -64
  24. package/dist/typed.d.mts +66 -64
  25. package/dist/typed.mjs +1 -1
  26. package/dist/{types-DOI6EHQJ.d.cts → types-6m1L8QLb.d.mts} +16 -13
  27. package/dist/{types-_41fL9Dn.d.mts → types-DIIeb2YQ.d.cts} +16 -13
  28. package/dist/verani.cjs +1 -1
  29. package/dist/verani.d.cts +99 -179
  30. package/dist/verani.d.mts +99 -179
  31. package/dist/verani.mjs +1 -1
  32. package/package.json +2 -1
  33. package/dist/actor-runtime-83AF97II.mjs +0 -1
  34. package/dist/actor-runtime-BqF6_7z_.d.mts +0 -696
  35. package/dist/actor-runtime-CCoZUVrQ.cjs +0 -1
  36. package/dist/actor-runtime-gFDsFVsU.d.cts +0 -696
  37. package/dist/client-C3K_PiKE.cjs +0 -1
  38. package/dist/client-DQnA1Oel.mjs +0 -1
  39. package/dist/encode-BYFW_6TI.cjs +0 -1
  40. package/dist/encode-BhJqnsto.mjs +0 -1
package/README.md CHANGED
@@ -126,7 +126,7 @@ Update `wrangler.jsonc`:
126
126
  {
127
127
  "name": "my-verani-app",
128
128
  "main": "src/index.ts",
129
- "compatibility_date": "2024-01-01",
129
+ "compatibility_date": "2025-11-26",
130
130
 
131
131
  "durable_objects": {
132
132
  "bindings": [
@@ -0,0 +1 @@
1
+ const require_encode=require(`./encode-SjZrKIyU.cjs`);function encodeClientMessage(l){return require_encode.t(l)}function decodeServerMessage(l){return require_encode.o(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(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){this.options=e,this.getWebSocket=l,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(this.createPingInterval(),this.options.pingInterval))}createPingInterval(){return()=>{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({type:`ping`}))}catch{}}}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({type:`ping`}))}catch{}this.lastPongReceived=Date.now(),this.pingInterval=setInterval(this.createPingInterval(),this.options.pingInterval)}}stopPingInterval(){this.pingInterval!==void 0&&(clearInterval(this.pingInterval),this.pingInterval=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(e.data);if(!f)return;if(f.type===`pong`){l.recordPong();return}let p=f.type,m=f.data,h=f.data;f.type===`event`&&h&&typeof h==`object`&&`type`in h&&(p=h.type,m=h),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()),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(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()}};Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return ConnectionManager}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return DEFAULT_RECONNECTION_CONFIG}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return VeraniClient}});
@@ -180,25 +180,25 @@ declare class VeraniClient {
180
180
  * @param event - Event type to listen for
181
181
  * @param callback - Callback function to invoke when event is received
182
182
  */
183
- on(event: string, callback: (data: any) => void): void;
183
+ on<TData = unknown>(event: string, callback: (data: TData) => void): void;
184
184
  /**
185
185
  * Removes an event listener
186
186
  * @param event - Event type to remove listener from
187
187
  * @param callback - Callback function to remove
188
188
  */
189
- off(event: string, callback: (data: any) => void): void;
189
+ off<TData = unknown>(event: string, callback: (data: TData) => void): void;
190
190
  /**
191
191
  * Registers a one-time event listener
192
192
  * @param event - Event type to listen for
193
193
  * @param callback - Callback function to invoke once
194
194
  */
195
- once(event: string, callback: (data: any) => void): void;
195
+ once<TData = unknown>(event: string, callback: (data: TData) => void): void;
196
196
  /**
197
197
  * Sends a message to the server
198
198
  * @param type - Message type
199
199
  * @param data - Optional message data
200
200
  */
201
- emit(type: string, data?: any): void;
201
+ emit<TData = unknown>(type: string, data?: TData): void;
202
202
  /**
203
203
  * Registers lifecycle callback for connection open
204
204
  */
@@ -180,25 +180,25 @@ declare class VeraniClient {
180
180
  * @param event - Event type to listen for
181
181
  * @param callback - Callback function to invoke when event is received
182
182
  */
183
- on(event: string, callback: (data: any) => void): void;
183
+ on<TData = unknown>(event: string, callback: (data: TData) => void): void;
184
184
  /**
185
185
  * Removes an event listener
186
186
  * @param event - Event type to remove listener from
187
187
  * @param callback - Callback function to remove
188
188
  */
189
- off(event: string, callback: (data: any) => void): void;
189
+ off<TData = unknown>(event: string, callback: (data: TData) => void): void;
190
190
  /**
191
191
  * Registers a one-time event listener
192
192
  * @param event - Event type to listen for
193
193
  * @param callback - Callback function to invoke once
194
194
  */
195
- once(event: string, callback: (data: any) => void): void;
195
+ once<TData = unknown>(event: string, callback: (data: TData) => void): void;
196
196
  /**
197
197
  * Sends a message to the server
198
198
  * @param type - Message type
199
199
  * @param data - Optional message data
200
200
  */
201
- emit(type: string, data?: any): void;
201
+ emit<TData = unknown>(type: string, data?: TData): void;
202
202
  /**
203
203
  * Registers lifecycle callback for connection open
204
204
  */
@@ -0,0 +1 @@
1
+ import{o as decodeServerMessage$1,t as encodeClientMessage$1}from"./encode-CFndA-BQ.mjs";function encodeClientMessage(e){return encodeClientMessage$1(e)}function decodeServerMessage(u){return decodeServerMessage$1(u)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,u){this.config=e,this.onStateChange=u,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}isValidStateTransition(e,u){return{disconnected:[`connecting`,`reconnecting`],connecting:[`connected`,`disconnected`,`error`,`reconnecting`],connected:[`disconnected`,`reconnecting`],reconnecting:[`connecting`,`disconnected`,`error`],error:[`reconnecting`,`disconnected`,`connecting`]}[e]?.includes(u)??!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(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 u=getDocument();if(!u)return null;let d=()=>{e(isPageVisible())};return u.addEventListener(`visibilitychange`,d),()=>{u.removeEventListener(`visibilitychange`,d)}}var KeepaliveManager=class{constructor(e,u){this.options=e,this.getWebSocket=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(this.createPingInterval(),this.options.pingInterval))}createPingInterval(){return()=>{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({type:`ping`}))}catch{}}}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({type:`ping`}))}catch{}this.lastPongReceived=Date.now(),this.pingInterval=setInterval(this.createPingInterval(),this.options.pingInterval)}}stopPingInterval(){this.pingInterval!==void 0&&(clearInterval(this.pingInterval),this.pingInterval=void 0),this.visibilityCleanup&&=(this.visibilityCleanup(),void 0)}recordPong(){this.lastPongReceived=Date.now()}},EventEmitter=class{constructor(){this.listeners=new Map}on(e,u){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(u)}off(e,u){let d=this.listeners.get(e);d&&(d.delete(u),d.size===0&&this.listeners.delete(e))}once(e,u){let d=f=>{this.off(e,d),u(f)};this.on(e,d)}emitLifecycleEvent(e,u){let d=this.listeners.get(e);if(d)for(let e of d)try{e(u)}catch{}}dispatch(e,u){let d=this.listeners.get(e);if(d)for(let e of d)try{e(u)}catch{}}clear(){this.listeners.clear()}};function handleWebSocketOpen(e,u,d,f,p,m,h,g){e.clear(),u.setState(`connected`),u.resetReconnection(),d.startPingInterval(),f.flushMessageQueue(p),m.resolve&&(m.resolve(),m.clear()),h.emitLifecycleEvent(`open`),h.emitLifecycleEvent(`connected`),g?.()}function handleWebSocketMessage(e,u,d){let p=decodeServerMessage(e.data);if(!p)return;if(p.type===`pong`){u.recordPong();return}let m=p.type,h=p.data,g=p.data;p.type===`event`&&g&&typeof g==`object`&&`type`in g&&(m=g.type,h=g),d.dispatch(m,h)}function handleWebSocketClose(e,u,d,f,p,m,h,g){h&&(h.value=!1),u.clear(),d.setState(`disconnected`),f.reject&&(f.reject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),f.clear()),p.emitLifecycleEvent(`close`,e),p.emitLifecycleEvent(`disconnected`,e),g?.(e),e.code!==1e3&&e.code!==1001&&d.scheduleReconnect(m)&&p.emitLifecycleEvent(`reconnecting`)}function handleWebSocketError(e,u,d,f,p,m){p&&(p.value=!1),u.clear(),d.emitLifecycleEvent(`error`,e),m?.(e),f(Error(`WebSocket error`))}function handleConnectionError(e,u,d,f,p,m,h){h&&(h.value=!1),u.clear(),d.reject&&(d.reject(e),d.clear()),p.emitLifecycleEvent(`error`,e),f.scheduleReconnect(m)&&p.emitLifecycleEvent(`reconnecting`)}var ConnectionHandler=class{constructor(e,u,d,f,p,m,h,g,_,v,y,b){this.url=e,this.options=u,this.connectionManager=d,this.keepalive=f,this.eventEmitter=p,this.messageQueue=m,this.connectionPromise=h,this.isConnectingRef=g,this.isConnectedFn=_,this.onOpenCallback=v,this.onCloseCallback=y,this.onErrorCallback=b,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`,u=>{this.connectionId===e&&handleWebSocketMessage(u,this.keepalive,this.eventEmitter)}),this.ws.addEventListener(`close`,u=>{this.connectionId===e&&handleWebSocketClose(u,this.connectionTimeoutState,this.connectionManager,this.connectionPromise,this.eventEmitter,()=>this.connect(),this.isConnectingRef,this.onCloseCallback)}),this.ws.addEventListener(`error`,u=>{this.connectionId===e&&handleWebSocketError(u,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,u={}){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(u),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()),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,u)=>{this.connectionPromiseState.resolve=e,this.connectionPromiseState.reject=u;let d=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(d)})})),this.connectionPromiseState.promise)}on(e,u){this.eventEmitter.on(e,u)}off(e,u){this.eventEmitter.off(e,u)}once(e,u){this.eventEmitter.once(e,u)}emit(e,u){let f={type:e,data:u};if(this.isConnected()){let e=this.connectionHandler.getWebSocket();if(e)try{e.send(encodeClientMessage(f))}catch{this.messageQueue.queueMessage(f)}}else this.messageQueue.queueMessage(f)}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 as n,DEFAULT_RECONNECTION_CONFIG as r,VeraniClient as t};
package/dist/client.cjs CHANGED
@@ -1 +1 @@
1
- const require_encode=require(`./encode-BYFW_6TI.cjs`),require_types=require(`./types-DUO6RVw1.cjs`),require_client=require(`./client-C3K_PiKE.cjs`);exports.ConnectionManager=require_client.n,exports.DEFAULT_RECONNECTION_CONFIG=require_client.r,exports.PROTOCOL_VERSION=require_types.t,exports.VeraniClient=require_client.t,exports.decodeClientMessage=require_encode.i,exports.decodeFrame=require_encode.a,exports.decodeServerMessage=require_encode.o,exports.encodeClientMessage=require_encode.t,exports.encodeFrame=require_encode.n,exports.encodeServerMessage=require_encode.r;
1
+ const require_encode=require(`./encode-SjZrKIyU.cjs`),require_types=require(`./types-DUO6RVw1.cjs`),require_client=require(`./client-Cf_oqaqp.cjs`);exports.ConnectionManager=require_client.n,exports.DEFAULT_RECONNECTION_CONFIG=require_client.r,exports.PROTOCOL_VERSION=require_types.t,exports.VeraniClient=require_client.t,exports.decodeClientMessage=require_encode.i,exports.decodeFrame=require_encode.a,exports.decodeServerMessage=require_encode.o,exports.encodeClientMessage=require_encode.t,exports.encodeFrame=require_encode.n,exports.encodeServerMessage=require_encode.r;
package/dist/client.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as DEFAULT_RECONNECTION_CONFIG, i as ConnectionState, n as VeraniClientOptions, o as ReconnectionConfig, r as ConnectionManager, t as VeraniClient } from "./client-C3BlxAY_.cjs";
2
- import { a as ServerMessage, i as PROTOCOL_VERSION, o as VeraniMessage, t as ClientMessage } from "./types-DOI6EHQJ.cjs";
3
- import { a as encodeFrame, i as encodeClientMessage, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, t as decodeClientMessage } from "./decode--RaAAv0U.cjs";
1
+ import { a as DEFAULT_RECONNECTION_CONFIG, i as ConnectionState, n as VeraniClientOptions, o as ReconnectionConfig, r as ConnectionManager, t as VeraniClient } from "./client-JCkzlxJL.cjs";
2
+ import { a as ServerMessage, i as PROTOCOL_VERSION, o as VeraniMessage, t as ClientMessage } from "./types-DIIeb2YQ.cjs";
3
+ import { a as encodeFrame, i as encodeClientMessage, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, t as decodeClientMessage } from "./decode-otE_3S_-.cjs";
4
4
  export { type ClientMessage, ConnectionManager, type ConnectionState, DEFAULT_RECONNECTION_CONFIG, PROTOCOL_VERSION, type ReconnectionConfig, type ServerMessage, VeraniClient, type VeraniClientOptions, type VeraniMessage, decodeClientMessage, decodeFrame, decodeServerMessage, encodeClientMessage, encodeFrame, encodeServerMessage };
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as DEFAULT_RECONNECTION_CONFIG, i as ConnectionState, n as VeraniClientOptions, o as ReconnectionConfig, r as ConnectionManager, t as VeraniClient } from "./client-CV5RrpZX.mjs";
2
- import { a as ServerMessage, i as PROTOCOL_VERSION, o as VeraniMessage, t as ClientMessage } from "./types-_41fL9Dn.mjs";
3
- import { a as encodeFrame, i as encodeClientMessage, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, t as decodeClientMessage } from "./decode-BUzd77kA.mjs";
1
+ import { a as DEFAULT_RECONNECTION_CONFIG, i as ConnectionState, n as VeraniClientOptions, o as ReconnectionConfig, r as ConnectionManager, t as VeraniClient } from "./client-D7P17bMd.mjs";
2
+ import { a as ServerMessage, i as PROTOCOL_VERSION, o as VeraniMessage, t as ClientMessage } from "./types-6m1L8QLb.mjs";
3
+ import { a as encodeFrame, i as encodeClientMessage, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, t as decodeClientMessage } from "./decode-DwdLTFvb.mjs";
4
4
  export { type ClientMessage, ConnectionManager, type ConnectionState, DEFAULT_RECONNECTION_CONFIG, PROTOCOL_VERSION, type ReconnectionConfig, type ServerMessage, VeraniClient, type VeraniClientOptions, type VeraniMessage, decodeClientMessage, decodeFrame, decodeServerMessage, encodeClientMessage, encodeFrame, encodeServerMessage };
package/dist/client.mjs CHANGED
@@ -1 +1 @@
1
- import{a as decodeFrame,i as decodeClientMessage,n as encodeFrame,o as decodeServerMessage,r as encodeServerMessage,t as encodeClientMessage}from"./encode-BhJqnsto.mjs";import{t as PROTOCOL_VERSION}from"./types-rZsftNPE.mjs";import{n as ConnectionManager,r as DEFAULT_RECONNECTION_CONFIG,t as VeraniClient}from"./client-DQnA1Oel.mjs";export{ConnectionManager,DEFAULT_RECONNECTION_CONFIG,PROTOCOL_VERSION,VeraniClient,decodeClientMessage,decodeFrame,decodeServerMessage,encodeClientMessage,encodeFrame,encodeServerMessage};
1
+ import{a as decodeFrame,i as decodeClientMessage,n as encodeFrame,o as decodeServerMessage,r as encodeServerMessage,t as encodeClientMessage}from"./encode-CFndA-BQ.mjs";import{t as PROTOCOL_VERSION}from"./types-rZsftNPE.mjs";import{n as ConnectionManager,r as DEFAULT_RECONNECTION_CONFIG,t as VeraniClient}from"./client-oy-w-Ky5.mjs";export{ConnectionManager,DEFAULT_RECONNECTION_CONFIG,PROTOCOL_VERSION,VeraniClient,decodeClientMessage,decodeFrame,decodeServerMessage,encodeClientMessage,encodeFrame,encodeServerMessage};
@@ -0,0 +1 @@
1
+ import{n as encodeFrame$1}from"./encode-CFndA-BQ.mjs";import{Actor}from"@cloudflare/actors";function storeAttachment(e,d){e.serializeAttachment(d)}function encodeFrame(d){return encodeFrame$1(d)}const PERSIST_ERROR_HANDLER=Symbol(`PERSIST_ERROR_HANDLER`),PERSISTED_STATE=Symbol(`PERSISTED_STATE`),STATE_READY=Symbol(`STATE_READY`);var PersistNotReadyError=class extends Error{constructor(e){super(`Cannot access persisted state key "${e}" before storage is initialized. Wait for onInit to complete or check actor.isStateReady().`),this.name=`PersistNotReadyError`}},PersistError=class extends Error{constructor(e,d){super(`Failed to persist key "${e}": ${d.message}`),this.name=`PersistError`,this.originalCause=d}};function safeSerialize(e){let d=new WeakSet;return JSON.stringify(e,(e,f)=>{if(f instanceof Date)return{__type:`Date`,value:f.toISOString()};if(f instanceof RegExp)return{__type:`RegExp`,source:f.source,flags:f.flags};if(f instanceof Map)return{__type:`Map`,entries:Array.from(f.entries())};if(f instanceof Set)return{__type:`Set`,values:Array.from(f.values())};if(f instanceof Error)return{__type:`Error`,name:f.name,message:f.message};if(typeof f==`object`&&f){if(d.has(f))return;d.add(f)}if(!(typeof f==`function`||typeof f==`symbol`))return f})}function safeDeserialize(e){return JSON.parse(e,(e,d)=>{if(d&&typeof d==`object`&&d.__type)switch(d.__type){case`Date`:return new Date(d.value);case`RegExp`:return new RegExp(d.source,d.flags);case`Map`:return new Map(d.entries);case`Set`:return new Set(d.values);case`Error`:{let e=Error(d.message);return e.name=d.name,e}}return d})}function createShallowProxy(e,d,f){return new Proxy(e,{set(e,f,p){let m=Reflect.set(e,f,p);return m&&typeof f==`string`&&d(f,p),m},deleteProperty(e,d){let p=Reflect.deleteProperty(e,d);return p&&typeof d==`string`&&f(d),p}})}async function initializePersistedState(e,d,f=[],p={}){let{shallow:_=!0,throwOnError:y=!0}=p,b=e.ctx.storage,x=f.length>0?f.map(String):Object.keys(d),S={...d};for(let e of x)try{let d=await b.get(`_verani_persist:${e}`);if(d!==void 0)try{S[e]=safeDeserialize(d)}catch(d){if(y)throw new PersistError(e,d)}}catch(d){if(y&&!(d instanceof PersistError))throw new PersistError(e,d)}let C=async(d,f)=>{if(x.includes(d))try{let e=safeSerialize(f);await b.put(`_verani_persist:${d}`,e)}catch(f){let p=e[PERSIST_ERROR_HANDLER];if(p&&p(d,f),y)throw new PersistError(d,f)}},w=async d=>{if(x.includes(d))try{await b.delete(`_verani_persist:${d}`)}catch(f){let p=e[PERSIST_ERROR_HANDLER];if(p&&p(d,f),y)throw new PersistError(d,f)}},T;return T=_?createShallowProxy(S,(e,d)=>{C(String(e),d)},e=>{w(String(e))}):createDeepProxy(S,(e,d)=>{C(e,d)},e=>{w(e)},x),e[STATE_READY]=!0,e[PERSISTED_STATE]=T,T}function createDeepProxy(e,d,f,p,m){return new Proxy(e,{get(e,h){let g=Reflect.get(e,h);if(g&&typeof g==`object`&&!Array.isArray(g)){let e=m??String(h);if(p.includes(e)||m!==void 0)return createDeepProxy(g,d,f,p,e)}return g},set(e,f,h){let g=Reflect.set(e,f,h);if(g){let e=m??String(f);p.includes(e)&&(m?d(m,void 0):d(String(f),h))}return g},deleteProperty(e,h){let g=Reflect.deleteProperty(e,h);if(g){let e=m??String(h);p.includes(e)&&(m?d(m,void 0):f(String(h)))}return g}})}function isStateReady(e){return e[STATE_READY]===!0}function getPersistedState(e){if(!isStateReady(e))throw new PersistNotReadyError(`state`);return e[PERSISTED_STATE]}function setPeristErrorHandler(e,d){e[PERSIST_ERROR_HANDLER]=d}async function persistKey(e,d,f){let p=e.ctx.storage,m=safeSerialize(f);await p.put(`_verani_persist:${d}`,m)}async function deletePersistedKey(e,d){await e.ctx.storage.delete(`_verani_persist:${d}`)}async function getPersistedKeys(e){let d=await e.ctx.storage.list({prefix:`_verani_persist:`});return Array.from(d.keys()).map(e=>e.replace(`_verani_persist:`,``))}async function clearPersistedState(e){let d=e.ctx.storage,f=await d.list({prefix:`_verani_persist:`}),p=Array.from(f.keys());await d.delete(p)}const WS=Symbol(`WS`),META=Symbol(`META`),ROOMS=Symbol(`ROOMS`);function createConnectionHandler(e){let m=e.name||`VeraniConnectionDO`,_=e.websocketPath||`/ws`;class v extends Actor{constructor(...d){super(...d),this[WS]=null,this[META]=null,this[ROOMS]=new Map,this[STATE_READY]=!1,this[PERSISTED_STATE]=e.state?{...e.state}:{},this.handlers=new Map}get connectionState(){return this[PERSISTED_STATE]}isStateReady(){return isStateReady(this)}static configuration(e){return{sockets:{upgradePath:_}}}getRoomDO(){let e=this.env;return e.ROOM_DO||e.RoomDO||e.VERANI_ROOM}getConnectionDO(){let e=this.env;return e.CONNECTION_DO||e.ConnectionDO||e.VERANI_CONNECTION}createEmit(){let e=this;return{emit(d,f){e.sendToWebSocket(d,f)},to(d){return d.startsWith(`room:`)?e.createRoomEmitBuilder(d.slice(5)):e.createUserEmitBuilder(d)},toRoom(d){return e.createRoomEmitBuilder(d)},toUser(d){return e.createUserEmitBuilder(d)}}}createRoomEmitBuilder(e){let d=this;return{async emit(f,p){let m=d.getRoomDO();if(!m)return 0;try{let h=m.get(e),g={exceptUserId:d[META]?.userId};return await h.broadcast(f,p,g)}catch{return 0}}}}createUserEmitBuilder(e){let d=this;return{async emit(f,p){let m=d.getConnectionDO();if(!m)return 0;try{return await m.get(e).deliverMessage(f,p)?1:0}catch{return 0}}}}sendToWebSocket(e,d){let f=this[WS];if(!f||f.readyState!==WebSocket.OPEN)return!1;try{let m={type:`event`,channel:`default`,data:{type:e,...d}};return f.send(encodeFrame(m)),!0}catch{return!1}}createContext(){return{actor:this,ws:this[WS],meta:this[META],emit:this.createEmit(),state:this.connectionState}}async onInit(){e.state&&(e.onPersistError&&setPeristErrorHandler(this,e.onPersistError),this[PERSISTED_STATE]=await initializePersistedState(this,e.state,e.persistedKeys,e.persistOptions));let d=await this.ctx.storage.get(`_connection_rooms`);if(d&&Array.isArray(d)&&(this[ROOMS]=new Map(d.map(e=>[e.roomName,e.metadata]))),e.handlers)for(let[d,f]of e.handlers.entries())this.handlers.set(d,f);let f=this.ctx.getWebSockets();if(f.length>0){let d=f[0];if(d.readyState===WebSocket.OPEN){this[WS]=d;let f=d.deserializeAttachment();f&&(this[META]=f,await this.rejoinRoomsAfterHibernation(),e.onHibernationRestore&&await e.onHibernationRestore(this))}}}async shouldUpgradeWebSocket(e){return!0}async onWebSocketConnect(d,p){this[WS]&&this[WS].readyState===WebSocket.OPEN&&this[WS].close(1e3,`New connection established`);let m;if(m=e.extractMeta?await e.extractMeta(p):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(d,m),this[WS]=d,this[META]=m,e.onConnect)try{await e.onConnect(this.createContext())}catch(f){e.onError&&await e.onError(f,this.createContext()),d.close(1011,`Connection handler error`);return}}async onWebSocketMessage(d,f){if(this[META])try{let d=typeof f==`string`?f:f.toString(),p=JSON.parse(d),m=p,h=m.data,g=h?.type||m.type,_=this.handlers.get(g);if(_){await _(this.createContext(),h);return}e.onMessage&&await e.onMessage(this.createContext(),p)}catch(d){e.onError&&await e.onError(d,this.createContext())}}async onWebSocketDisconnect(d){if(this[META]&&e.onDisconnect)try{await e.onDisconnect(this.createContext())}catch{}for(let e of this[ROOMS].keys())try{await this.leaveRoomInternal(e)}catch{}this[WS]=null}async fetch(e){let d=new URL(e.url),f=e.headers.get(`Upgrade`);return d.pathname===_&&f===`websocket`&&await this.shouldUpgradeWebSocket(e)?this.onWebSocketUpgrade(e):this.onRequest(e)}async deliverMessage(e,d){return this.sendToWebSocket(e,d)}async deliverSystemEvent(e,d){this.sendToWebSocket(`system:${e}`,d)}async getUserId(){return this[META]?.userId??null}async isConnected(){return this[WS]!==null&&this[WS].readyState===WebSocket.OPEN}async joinRoom(e,d){if(!this[META])throw Error(`Cannot join room: not connected`);let f=this.getRoomDO();if(!f)throw Error(`RoomDO binding not found`);await f.get(e).join(this[META].userId,d),this[ROOMS].set(e,d),await this.persistRooms()}async persistRooms(){let e=Array.from(this[ROOMS].entries()).map(([e,d])=>({roomName:e,metadata:d}));await this.ctx.storage.put(`_connection_rooms`,e)}async rejoinRoomsAfterHibernation(){if(this[ROOMS].size===0)return;let e=this.getRoomDO();if(!e)return;let d=this[META]?.userId;if(!d)return;let f=[];for(let[p,m]of this[ROOMS].entries())try{await e.get(p).join(d,m)}catch{f.push(p)}if(f.length>0){for(let e of f)this[ROOMS].delete(e);await this.persistRooms()}}async leaveRoomInternal(e){if(!this[META])return;let d=this.getRoomDO();d&&await d.get(e).leave(this[META].userId)}async leaveRoom(e){if(!this[META])throw Error(`Cannot leave room: not connected`);await this.leaveRoomInternal(e),this[ROOMS].delete(e),await this.persistRooms()}async getRooms(){return Array.from(this[ROOMS].keys())}getStorage(){return this.ctx.storage}on(e,d){this.handlers.set(e,d)}off(e){this.handlers.delete(e)}}return Object.defineProperty(v,`name`,{value:m,writable:!1,configurable:!0}),v}function defineConnection(e){let d=new Map;return{...e,handlers:d,on(e,f){d.set(e,f)},off(e){d.delete(e)}}}export{clearPersistedState as a,getPersistedState as c,persistKey as d,safeDeserialize as f,storeAttachment as h,PersistNotReadyError as i,initializePersistedState as l,setPeristErrorHandler as m,defineConnection as n,deletePersistedKey as o,safeSerialize as p,PersistError as r,getPersistedKeys as s,createConnectionHandler as t,isStateReady as u};
@@ -0,0 +1 @@
1
+ const require_encode=require(`./encode-SjZrKIyU.cjs`);let __cloudflare_actors=require(`@cloudflare/actors`);function storeAttachment(e,d){e.serializeAttachment(d)}function encodeFrame(d){return require_encode.n(d)}const PERSIST_ERROR_HANDLER=Symbol(`PERSIST_ERROR_HANDLER`),PERSISTED_STATE=Symbol(`PERSISTED_STATE`),STATE_READY=Symbol(`STATE_READY`);var PersistNotReadyError=class extends Error{constructor(e){super(`Cannot access persisted state key "${e}" before storage is initialized. Wait for onInit to complete or check actor.isStateReady().`),this.name=`PersistNotReadyError`}},PersistError=class extends Error{constructor(e,d){super(`Failed to persist key "${e}": ${d.message}`),this.name=`PersistError`,this.originalCause=d}};function safeSerialize(e){let d=new WeakSet;return JSON.stringify(e,(e,f)=>{if(f instanceof Date)return{__type:`Date`,value:f.toISOString()};if(f instanceof RegExp)return{__type:`RegExp`,source:f.source,flags:f.flags};if(f instanceof Map)return{__type:`Map`,entries:Array.from(f.entries())};if(f instanceof Set)return{__type:`Set`,values:Array.from(f.values())};if(f instanceof Error)return{__type:`Error`,name:f.name,message:f.message};if(typeof f==`object`&&f){if(d.has(f))return;d.add(f)}if(!(typeof f==`function`||typeof f==`symbol`))return f})}function safeDeserialize(e){return JSON.parse(e,(e,d)=>{if(d&&typeof d==`object`&&d.__type)switch(d.__type){case`Date`:return new Date(d.value);case`RegExp`:return new RegExp(d.source,d.flags);case`Map`:return new Map(d.entries);case`Set`:return new Set(d.values);case`Error`:{let e=Error(d.message);return e.name=d.name,e}}return d})}function createShallowProxy(e,d,f){return new Proxy(e,{set(e,f,p){let m=Reflect.set(e,f,p);return m&&typeof f==`string`&&d(f,p),m},deleteProperty(e,d){let p=Reflect.deleteProperty(e,d);return p&&typeof d==`string`&&f(d),p}})}async function initializePersistedState(e,d,f=[],p={}){let{shallow:_=!0,throwOnError:y=!0}=p,b=e.ctx.storage,x=f.length>0?f.map(String):Object.keys(d),S={...d};for(let e of x)try{let d=await b.get(`_verani_persist:${e}`);if(d!==void 0)try{S[e]=safeDeserialize(d)}catch(d){if(y)throw new PersistError(e,d)}}catch(d){if(y&&!(d instanceof PersistError))throw new PersistError(e,d)}let C=async(d,f)=>{if(x.includes(d))try{let e=safeSerialize(f);await b.put(`_verani_persist:${d}`,e)}catch(f){let p=e[PERSIST_ERROR_HANDLER];if(p&&p(d,f),y)throw new PersistError(d,f)}},w=async d=>{if(x.includes(d))try{await b.delete(`_verani_persist:${d}`)}catch(f){let p=e[PERSIST_ERROR_HANDLER];if(p&&p(d,f),y)throw new PersistError(d,f)}},T;return T=_?createShallowProxy(S,(e,d)=>{C(String(e),d)},e=>{w(String(e))}):createDeepProxy(S,(e,d)=>{C(e,d)},e=>{w(e)},x),e[STATE_READY]=!0,e[PERSISTED_STATE]=T,T}function createDeepProxy(e,d,f,p,m){return new Proxy(e,{get(e,h){let g=Reflect.get(e,h);if(g&&typeof g==`object`&&!Array.isArray(g)){let e=m??String(h);if(p.includes(e)||m!==void 0)return createDeepProxy(g,d,f,p,e)}return g},set(e,f,h){let g=Reflect.set(e,f,h);if(g){let e=m??String(f);p.includes(e)&&(m?d(m,void 0):d(String(f),h))}return g},deleteProperty(e,h){let g=Reflect.deleteProperty(e,h);if(g){let e=m??String(h);p.includes(e)&&(m?d(m,void 0):f(String(h)))}return g}})}function isStateReady(e){return e[STATE_READY]===!0}function getPersistedState(e){if(!isStateReady(e))throw new PersistNotReadyError(`state`);return e[PERSISTED_STATE]}function setPeristErrorHandler(e,d){e[PERSIST_ERROR_HANDLER]=d}async function persistKey(e,d,f){let p=e.ctx.storage,m=safeSerialize(f);await p.put(`_verani_persist:${d}`,m)}async function deletePersistedKey(e,d){await e.ctx.storage.delete(`_verani_persist:${d}`)}async function getPersistedKeys(e){let d=await e.ctx.storage.list({prefix:`_verani_persist:`});return Array.from(d.keys()).map(e=>e.replace(`_verani_persist:`,``))}async function clearPersistedState(e){let d=e.ctx.storage,f=await d.list({prefix:`_verani_persist:`}),p=Array.from(f.keys());await d.delete(p)}const WS=Symbol(`WS`),META=Symbol(`META`),ROOMS=Symbol(`ROOMS`);function createConnectionHandler(e){let m=e.name||`VeraniConnectionDO`,_=e.websocketPath||`/ws`;class v extends __cloudflare_actors.Actor{constructor(...d){super(...d),this[WS]=null,this[META]=null,this[ROOMS]=new Map,this[STATE_READY]=!1,this[PERSISTED_STATE]=e.state?{...e.state}:{},this.handlers=new Map}get connectionState(){return this[PERSISTED_STATE]}isStateReady(){return isStateReady(this)}static configuration(e){return{sockets:{upgradePath:_}}}getRoomDO(){let e=this.env;return e.ROOM_DO||e.RoomDO||e.VERANI_ROOM}getConnectionDO(){let e=this.env;return e.CONNECTION_DO||e.ConnectionDO||e.VERANI_CONNECTION}createEmit(){let e=this;return{emit(d,f){e.sendToWebSocket(d,f)},to(d){return d.startsWith(`room:`)?e.createRoomEmitBuilder(d.slice(5)):e.createUserEmitBuilder(d)},toRoom(d){return e.createRoomEmitBuilder(d)},toUser(d){return e.createUserEmitBuilder(d)}}}createRoomEmitBuilder(e){let d=this;return{async emit(f,p){let m=d.getRoomDO();if(!m)return 0;try{let h=m.get(e),g={exceptUserId:d[META]?.userId};return await h.broadcast(f,p,g)}catch{return 0}}}}createUserEmitBuilder(e){let d=this;return{async emit(f,p){let m=d.getConnectionDO();if(!m)return 0;try{return await m.get(e).deliverMessage(f,p)?1:0}catch{return 0}}}}sendToWebSocket(e,d){let f=this[WS];if(!f||f.readyState!==WebSocket.OPEN)return!1;try{let m={type:`event`,channel:`default`,data:{type:e,...d}};return f.send(encodeFrame(m)),!0}catch{return!1}}createContext(){return{actor:this,ws:this[WS],meta:this[META],emit:this.createEmit(),state:this.connectionState}}async onInit(){e.state&&(e.onPersistError&&setPeristErrorHandler(this,e.onPersistError),this[PERSISTED_STATE]=await initializePersistedState(this,e.state,e.persistedKeys,e.persistOptions));let d=await this.ctx.storage.get(`_connection_rooms`);if(d&&Array.isArray(d)&&(this[ROOMS]=new Map(d.map(e=>[e.roomName,e.metadata]))),e.handlers)for(let[d,f]of e.handlers.entries())this.handlers.set(d,f);let f=this.ctx.getWebSockets();if(f.length>0){let d=f[0];if(d.readyState===WebSocket.OPEN){this[WS]=d;let f=d.deserializeAttachment();f&&(this[META]=f,await this.rejoinRoomsAfterHibernation(),e.onHibernationRestore&&await e.onHibernationRestore(this))}}}async shouldUpgradeWebSocket(e){return!0}async onWebSocketConnect(d,p){this[WS]&&this[WS].readyState===WebSocket.OPEN&&this[WS].close(1e3,`New connection established`);let m;if(m=e.extractMeta?await e.extractMeta(p):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(d,m),this[WS]=d,this[META]=m,e.onConnect)try{await e.onConnect(this.createContext())}catch(f){e.onError&&await e.onError(f,this.createContext()),d.close(1011,`Connection handler error`);return}}async onWebSocketMessage(d,f){if(this[META])try{let d=typeof f==`string`?f:f.toString(),p=JSON.parse(d),m=p,h=m.data,g=h?.type||m.type,_=this.handlers.get(g);if(_){await _(this.createContext(),h);return}e.onMessage&&await e.onMessage(this.createContext(),p)}catch(d){e.onError&&await e.onError(d,this.createContext())}}async onWebSocketDisconnect(d){if(this[META]&&e.onDisconnect)try{await e.onDisconnect(this.createContext())}catch{}for(let e of this[ROOMS].keys())try{await this.leaveRoomInternal(e)}catch{}this[WS]=null}async fetch(e){let d=new URL(e.url),f=e.headers.get(`Upgrade`);return d.pathname===_&&f===`websocket`&&await this.shouldUpgradeWebSocket(e)?this.onWebSocketUpgrade(e):this.onRequest(e)}async deliverMessage(e,d){return this.sendToWebSocket(e,d)}async deliverSystemEvent(e,d){this.sendToWebSocket(`system:${e}`,d)}async getUserId(){return this[META]?.userId??null}async isConnected(){return this[WS]!==null&&this[WS].readyState===WebSocket.OPEN}async joinRoom(e,d){if(!this[META])throw Error(`Cannot join room: not connected`);let f=this.getRoomDO();if(!f)throw Error(`RoomDO binding not found`);await f.get(e).join(this[META].userId,d),this[ROOMS].set(e,d),await this.persistRooms()}async persistRooms(){let e=Array.from(this[ROOMS].entries()).map(([e,d])=>({roomName:e,metadata:d}));await this.ctx.storage.put(`_connection_rooms`,e)}async rejoinRoomsAfterHibernation(){if(this[ROOMS].size===0)return;let e=this.getRoomDO();if(!e)return;let d=this[META]?.userId;if(!d)return;let f=[];for(let[p,m]of this[ROOMS].entries())try{await e.get(p).join(d,m)}catch{f.push(p)}if(f.length>0){for(let e of f)this[ROOMS].delete(e);await this.persistRooms()}}async leaveRoomInternal(e){if(!this[META])return;let d=this.getRoomDO();d&&await d.get(e).leave(this[META].userId)}async leaveRoom(e){if(!this[META])throw Error(`Cannot leave room: not connected`);await this.leaveRoomInternal(e),this[ROOMS].delete(e),await this.persistRooms()}async getRooms(){return Array.from(this[ROOMS].keys())}getStorage(){return this.ctx.storage}on(e,d){this.handlers.set(e,d)}off(e){this.handlers.delete(e)}}return Object.defineProperty(v,`name`,{value:m,writable:!1,configurable:!0}),v}function defineConnection(e){let d=new Map;return{...e,handlers:d,on(e,f){d.set(e,f)},off(e){d.delete(e)}}}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return clearPersistedState}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return getPersistedState}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return persistKey}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return safeDeserialize}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return storeAttachment}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return PersistNotReadyError}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return initializePersistedState}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return setPeristErrorHandler}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return defineConnection}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return deletePersistedKey}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return safeSerialize}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return PersistError}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return getPersistedKeys}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return createConnectionHandler}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return isStateReady}});
@@ -0,0 +1,355 @@
1
+ import { n as ConnectionMeta } from "./types-6m1L8QLb.mjs";
2
+ import { Actor, ActorConfiguration } from "@cloudflare/actors";
3
+
4
+ //#region src/actor/types.d.ts
5
+
6
+ /**
7
+ * Options for broadcasting messages to connections
8
+ */
9
+ interface BroadcastOptions {
10
+ /** Exclude specific WebSocket from receiving the broadcast (local only) */
11
+ except?: WebSocket;
12
+ /** Exclude a specific userId from receiving the broadcast (RPC-safe) */
13
+ exceptUserId?: string;
14
+ /** Only send to specific user IDs */
15
+ userIds?: string[];
16
+ /** Only send to specific client IDs */
17
+ clientIds?: string[];
18
+ }
19
+ /**
20
+ * RPC-safe version of BroadcastOptions for use over RPC calls.
21
+ * Excludes the `except` field since WebSocket cannot be serialized over RPC.
22
+ */
23
+ interface RpcBroadcastOptions {
24
+ /** Exclude a specific userId from receiving the broadcast */
25
+ exceptUserId?: string;
26
+ /** Only send to specific user IDs */
27
+ userIds?: string[];
28
+ /** Only send to specific client IDs */
29
+ clientIds?: string[];
30
+ }
31
+ /**
32
+ * Represents a member in a RoomDO
33
+ */
34
+ interface RoomMember {
35
+ /** The user's unique identifier */
36
+ userId: string;
37
+ /** Timestamp when the user joined this room */
38
+ joinedAt: number;
39
+ /** Optional metadata associated with this member */
40
+ metadata?: Record<string, unknown>;
41
+ }
42
+ /**
43
+ * Definition for creating a RoomDO (coordination Durable Object)
44
+ *
45
+ * RoomDOs manage room membership and coordinate message delivery between
46
+ * ConnectionDOs. They do NOT hold WebSocket connections directly.
47
+ */
48
+ interface RoomCoordinatorDefinition<E = unknown> {
49
+ /** Optional room name for debugging */
50
+ name?: string;
51
+ /**
52
+ * Called when the RoomDO initializes or wakes from hibernation
53
+ */
54
+ onInit?(roomState: Record<string, unknown>): void | Promise<void>;
55
+ /**
56
+ * Called when a user joins this room
57
+ */
58
+ onJoin?(roomState: Record<string, unknown>, userId: string, metadata?: Record<string, unknown>): void | Promise<void>;
59
+ /**
60
+ * Called when a user leaves this room
61
+ */
62
+ onLeave?(roomState: Record<string, unknown>, userId: string): void | Promise<void>;
63
+ }
64
+ /**
65
+ * Stub interface for ConnectionDO - supports RPC calls from other DOs
66
+ *
67
+ * ConnectionDOs own a single WebSocket connection and receive messages
68
+ * from RoomDOs via RPC for delivery to their connected client.
69
+ */
70
+ interface ConnectionActorStub {
71
+ /**
72
+ * Standard fetch method for handling HTTP requests and WebSocket upgrades
73
+ */
74
+ fetch(request: Request): Promise<Response>;
75
+ /**
76
+ * Deliver a message to this connection's WebSocket (called via RPC from RoomDO)
77
+ * @param event - Event name
78
+ * @param data - Event data
79
+ * @returns Promise resolving to true if delivered, false if connection is closed
80
+ */
81
+ deliverMessage<TData = unknown>(event: string, data?: TData): Promise<boolean>;
82
+ /**
83
+ * Deliver a system event to this connection (presence updates, room events, etc.)
84
+ * @param type - System event type
85
+ * @param payload - Event payload
86
+ */
87
+ deliverSystemEvent<TPayload = unknown>(type: string, payload?: TPayload): Promise<void>;
88
+ /**
89
+ * Get the userId this connection belongs to
90
+ */
91
+ getUserId(): Promise<string | null>;
92
+ /**
93
+ * Check if this connection is still active
94
+ */
95
+ isConnected(): Promise<boolean>;
96
+ /**
97
+ * Join a room (registers with the RoomDO)
98
+ * @param roomName - Name of the room to join
99
+ * @param metadata - Optional metadata to include with membership
100
+ */
101
+ joinRoom(roomName: string, metadata?: Record<string, unknown>): Promise<void>;
102
+ /**
103
+ * Leave a room (unregisters from the RoomDO)
104
+ * @param roomName - Name of the room to leave
105
+ */
106
+ leaveRoom(roomName: string): Promise<void>;
107
+ /**
108
+ * Get list of rooms this connection is a member of
109
+ */
110
+ getRooms(): Promise<string[]>;
111
+ }
112
+ /**
113
+ * Environment type with Durable Object bindings for Verani
114
+ * Users should extend this with their own bindings
115
+ */
116
+ interface VeraniEnv {
117
+ /** ConnectionDO binding - required for per-user connection routing */
118
+ CONNECTION_DO?: DurableObjectNamespace;
119
+ /** RoomDO binding - required for room coordination */
120
+ ROOM_DO?: DurableObjectNamespace;
121
+ }
122
+ /**
123
+ * ConnectionActor interface for single-WebSocket-per-DO model
124
+ *
125
+ * Each ConnectionActor owns exactly one WebSocket connection for a single user.
126
+ */
127
+ interface ConnectionActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> extends Actor<E> {
128
+ /**
129
+ * The single WebSocket connection owned by this DO (null if not connected)
130
+ */
131
+ ws: WebSocket | null;
132
+ /**
133
+ * Connection metadata for this user
134
+ */
135
+ meta: TMeta | null;
136
+ /**
137
+ * Set of room names this connection is a member of
138
+ */
139
+ rooms: Set<string>;
140
+ /**
141
+ * User-defined persisted state for this connection actor
142
+ */
143
+ connectionState: TState;
144
+ /**
145
+ * Check if this connection has an active WebSocket
146
+ */
147
+ isConnected(): boolean;
148
+ /**
149
+ * Deliver a message to this connection's WebSocket
150
+ * Called via RPC from RoomDO
151
+ */
152
+ deliverMessage<TData = unknown>(event: string, data?: TData): Promise<boolean>;
153
+ /**
154
+ * Deliver a system event (presence, room events, etc.)
155
+ */
156
+ deliverSystemEvent<TPayload = unknown>(type: string, payload?: TPayload): Promise<void>;
157
+ /**
158
+ * Join a room (register with RoomDO)
159
+ */
160
+ joinRoom(roomName: string, metadata?: Record<string, unknown>): Promise<void>;
161
+ /**
162
+ * Leave a room (unregister from RoomDO)
163
+ */
164
+ leaveRoom(roomName: string): Promise<void>;
165
+ /**
166
+ * Socket.io-like emit API for this connection
167
+ */
168
+ emit: ConnectionEmit<TMeta, E>;
169
+ /**
170
+ * Access the Durable Object storage API
171
+ */
172
+ getStorage(): DurableObjectStorage;
173
+ }
174
+ /**
175
+ * Connection-level emit API (for single-connection DO)
176
+ * Routes to appropriate RoomDO or ConnectionDO via RPC
177
+ */
178
+ interface ConnectionEmit<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
179
+ /**
180
+ * Emit to this connection's WebSocket
181
+ * @param event - Event name
182
+ * @param data - Event data
183
+ */
184
+ emit<TData = unknown>(event: string, data?: TData): void;
185
+ /**
186
+ * Target a specific room or user for emitting
187
+ * @param target - Room name (if starts with "room:") or userId
188
+ * @returns Builder for emitting to the target via RPC
189
+ */
190
+ to(target: string): AsyncEmitBuilder;
191
+ /**
192
+ * Target a specific room for broadcasting
193
+ * @param roomName - Room name
194
+ * @returns Builder for broadcasting to the room via RPC
195
+ */
196
+ toRoom(roomName: string): AsyncEmitBuilder;
197
+ /**
198
+ * Target a specific user for direct messaging
199
+ * @param userId - User ID
200
+ * @returns Builder for sending to the user via RPC
201
+ */
202
+ toUser(userId: string): AsyncEmitBuilder;
203
+ }
204
+ /**
205
+ * Async emit builder for RPC-based emit operations
206
+ */
207
+ interface AsyncEmitBuilder {
208
+ /**
209
+ * Emit to the targeted scope via RPC
210
+ * @param event - Event name
211
+ * @param data - Event data
212
+ * @returns Promise resolving to number of recipients
213
+ */
214
+ emit<TData = unknown>(event: string, data?: TData): Promise<number>;
215
+ }
216
+ //#endregion
217
+ //#region src/actor/connection-actor.d.ts
218
+ /**
219
+ * Definition for creating a ConnectionDO (per-user connection handler)
220
+ */
221
+ interface ConnectionDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
222
+ /** Optional name for debugging */
223
+ name?: string;
224
+ /** WebSocket upgrade path (default: "/ws") */
225
+ websocketPath?: string;
226
+ /**
227
+ * Extract metadata from the connection request
228
+ */
229
+ extractMeta?(req: Request): TMeta | Promise<TMeta>;
230
+ /**
231
+ * Called when a WebSocket connection is established
232
+ */
233
+ onConnect?(ctx: ConnectionContext<TMeta, E, TState>): void | Promise<void>;
234
+ /**
235
+ * Called when the WebSocket connection is closed
236
+ */
237
+ onDisconnect?(ctx: ConnectionContext<TMeta, E, TState>): void | Promise<void>;
238
+ /**
239
+ * Called when a message is received from the WebSocket
240
+ */
241
+ onMessage?(ctx: ConnectionContext<TMeta, E, TState>, frame: unknown): void | Promise<void>;
242
+ /**
243
+ * Called when an error occurs
244
+ */
245
+ onError?(error: Error, ctx: ConnectionContext<TMeta, E, TState>): void | Promise<void>;
246
+ /**
247
+ * Called after waking from hibernation
248
+ */
249
+ onHibernationRestore?(actor: ConnectionHandlerInstance<TMeta, E, TState>): void | Promise<void>;
250
+ /**
251
+ * Event handlers map (socket.io-like)
252
+ */
253
+ handlers?: Map<string, (ctx: ConnectionContext<TMeta, E, TState>, data: unknown) => void | Promise<void>>;
254
+ /**
255
+ * Initial state for this connection
256
+ */
257
+ state?: TState;
258
+ /**
259
+ * Keys to persist to storage
260
+ */
261
+ persistedKeys?: (string & keyof TState)[];
262
+ /**
263
+ * Persistence options
264
+ */
265
+ persistOptions?: {
266
+ shallow?: boolean;
267
+ throwOnError?: boolean;
268
+ };
269
+ /**
270
+ * Called when persistence fails
271
+ */
272
+ onPersistError?(key: string, error: Error): void;
273
+ }
274
+ /**
275
+ * Instance interface for ConnectionHandler actors
276
+ * Used for typing the actor parameter in lifecycle hooks
277
+ */
278
+ interface ConnectionHandlerInstance<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
279
+ connectionState: TState;
280
+ isStateReady(): boolean;
281
+ getStorage(): DurableObjectStorage;
282
+ joinRoom(roomName: string, metadata?: Record<string, unknown>): Promise<void>;
283
+ leaveRoom(roomName: string): Promise<void>;
284
+ getRooms(): Promise<string[]>;
285
+ }
286
+ /**
287
+ * Context provided to connection lifecycle hooks
288
+ */
289
+ interface ConnectionContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
290
+ /** The connection actor instance */
291
+ actor: ConnectionHandlerInstance<TMeta, E, TState>;
292
+ /** The WebSocket connection (may be null after disconnect) */
293
+ ws: WebSocket | null;
294
+ /** Connection metadata */
295
+ meta: TMeta;
296
+ /** Socket.io-like emit API */
297
+ emit: ConnectionEmit<TMeta, E>;
298
+ /** Persisted state */
299
+ state: TState;
300
+ }
301
+ /**
302
+ * Return type for createConnectionHandler
303
+ */
304
+ type ConnectionHandlerClass<E = unknown> = {
305
+ new (state: any, env: E): Actor<E>;
306
+ get(userId: string): ConnectionActorStub;
307
+ configuration(request?: Request): ActorConfiguration;
308
+ };
309
+ /**
310
+ * Creates a Connection handler (per-user Durable Object)
311
+ *
312
+ * Each ConnectionDO owns exactly ONE WebSocket connection for a single user.
313
+ * Messages are received via RPC from RoomDOs and delivered to the WebSocket.
314
+ *
315
+ * @param definition - Connection definition with lifecycle hooks
316
+ * @returns ConnectionDO class for Cloudflare Workers
317
+ *
318
+ * @example
319
+ * ```typescript
320
+ * const UserConnection = createConnectionHandler({
321
+ * name: "user-connection",
322
+ * extractMeta: (req) => ({
323
+ * userId: extractUserIdFromToken(req),
324
+ * clientId: crypto.randomUUID(),
325
+ * channels: ["default"]
326
+ * }),
327
+ * onConnect: (ctx) => {
328
+ * console.log(`User ${ctx.meta.userId} connected`);
329
+ * ctx.actor.joinRoom("presence");
330
+ * },
331
+ * onDisconnect: (ctx) => {
332
+ * console.log(`User ${ctx.meta.userId} disconnected`);
333
+ * }
334
+ * });
335
+ *
336
+ * // In Worker:
337
+ * const userId = extractUserId(request);
338
+ * const stub = UserConnection.get(userId);
339
+ * return stub.fetch(request);
340
+ * ```
341
+ */
342
+ declare function createConnectionHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>>(definition: ConnectionDefinition<TMeta, E, TState>): ConnectionHandlerClass<E>;
343
+ /**
344
+ * Helper to define a connection with socket.io-like event handlers
345
+ */
346
+ interface ConnectionDefinitionWithHandlers<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> extends ConnectionDefinition<TMeta, E, TState> {
347
+ on<TData = unknown>(event: string, handler: (ctx: ConnectionContext<TMeta, E, TState>, data: TData) => void | Promise<void>): void;
348
+ off(event: string): void;
349
+ }
350
+ /**
351
+ * Define a connection with socket.io-like convenience methods
352
+ */
353
+ declare function defineConnection<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>>(def: ConnectionDefinition<TMeta, E, TState>): ConnectionDefinitionWithHandlers<TMeta, E, TState>;
354
+ //#endregion
355
+ export { ConnectionHandlerInstance as a, AsyncEmitBuilder as c, ConnectionActorStub as d, ConnectionEmit as f, VeraniEnv as g, RpcBroadcastOptions as h, ConnectionHandlerClass as i, BroadcastOptions as l, RoomMember as m, ConnectionDefinition as n, createConnectionHandler as o, RoomCoordinatorDefinition as p, ConnectionDefinitionWithHandlers as r, defineConnection as s, ConnectionContext as t, ConnectionActor as u };