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.
- package/README.md +1 -1
- package/dist/client-Cf_oqaqp.cjs +1 -0
- package/dist/{client-C3BlxAY_.d.cts → client-D7P17bMd.d.mts} +4 -4
- package/dist/{client-CV5RrpZX.d.mts → client-JCkzlxJL.d.cts} +4 -4
- package/dist/client-oy-w-Ky5.mjs +1 -0
- package/dist/client.cjs +1 -1
- package/dist/client.d.cts +3 -3
- package/dist/client.d.mts +3 -3
- package/dist/client.mjs +1 -1
- package/dist/connection-actor-BPsGLJoj.mjs +1 -0
- package/dist/connection-actor-BUVlHA-z.cjs +1 -0
- package/dist/connection-actor-BZtdpBVP.d.mts +355 -0
- package/dist/connection-actor-lJotemJR.d.cts +355 -0
- package/dist/{decode--RaAAv0U.d.cts → decode-DwdLTFvb.d.mts} +4 -4
- package/dist/{decode-BUzd77kA.d.mts → decode-otE_3S_-.d.cts} +4 -4
- package/dist/encode-CFndA-BQ.mjs +1 -0
- package/dist/encode-SjZrKIyU.cjs +1 -0
- package/dist/typed-client.cjs +1 -1
- package/dist/typed-client.d.cts +1 -1
- package/dist/typed-client.d.mts +1 -1
- package/dist/typed-client.mjs +1 -1
- package/dist/typed.cjs +1 -1
- package/dist/typed.d.cts +66 -64
- package/dist/typed.d.mts +66 -64
- package/dist/typed.mjs +1 -1
- package/dist/{types-DOI6EHQJ.d.cts → types-6m1L8QLb.d.mts} +16 -13
- package/dist/{types-_41fL9Dn.d.mts → types-DIIeb2YQ.d.cts} +16 -13
- package/dist/verani.cjs +1 -1
- package/dist/verani.d.cts +99 -179
- package/dist/verani.d.mts +99 -179
- package/dist/verani.mjs +1 -1
- package/package.json +2 -1
- package/dist/actor-runtime-83AF97II.mjs +0 -1
- package/dist/actor-runtime-BqF6_7z_.d.mts +0 -696
- package/dist/actor-runtime-CCoZUVrQ.cjs +0 -1
- package/dist/actor-runtime-gFDsFVsU.d.cts +0 -696
- package/dist/client-C3K_PiKE.cjs +0 -1
- package/dist/client-DQnA1Oel.mjs +0 -1
- package/dist/encode-BYFW_6TI.cjs +0 -1
- package/dist/encode-BhJqnsto.mjs +0 -1
package/README.md
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
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?:
|
|
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:
|
|
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:
|
|
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:
|
|
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?:
|
|
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-
|
|
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-
|
|
2
|
-
import { a as ServerMessage, i as PROTOCOL_VERSION, o as VeraniMessage, t as ClientMessage } from "./types-
|
|
3
|
-
import { a as encodeFrame, i as encodeClientMessage, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, t as decodeClientMessage } from "./decode
|
|
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-
|
|
2
|
-
import { a as ServerMessage, i as PROTOCOL_VERSION, o as VeraniMessage, t as ClientMessage } from "./types-
|
|
3
|
-
import { a as encodeFrame, i as encodeClientMessage, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, t as decodeClientMessage } from "./decode-
|
|
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-
|
|
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 };
|