verani 0.1.2 → 0.1.4
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/verani.cjs +1 -1
- package/dist/verani.d.cts +1 -8
- package/dist/verani.d.mts +1 -8
- package/dist/verani.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ export const ChatRoom = createActorHandler(chatRoom);
|
|
|
86
86
|
The three-way relationship:
|
|
87
87
|
1. **Export** in `src/index.ts`: `export { ChatRoom }`
|
|
88
88
|
2. **Class name** in `wrangler.jsonc`: `"class_name": "ChatRoom"`
|
|
89
|
-
3. **Env binding**: Access via `env.
|
|
89
|
+
3. **Env binding**: Access via `env.ChatRoom` in your fetch handler
|
|
90
90
|
|
|
91
91
|
### Client Side
|
|
92
92
|
|
package/dist/verani.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let __cloudflare_actors=require(`@cloudflare/actors`);function extractUserId(e){let b=new URL(e.url),x=b.searchParams.get(`userId`)||b.searchParams.get(`user_id`);if(x)return x;let S=e.headers.get(`Authorization`);return S?.startsWith(`Bearer `)?S.substring(7):e.headers.get(`X-User-ID`)||`anonymous`}function extractClientId(e){let b=new URL(e.url);return b.searchParams.get(`clientId`)||b.searchParams.get(`client_id`)||e.headers.get(`X-Client-ID`)||crypto.randomUUID()}function defaultExtractMeta(e){let S=extractUserId(e),C=extractClientId(e),w=new URL(e.url).searchParams.get(`channels`);return{userId:S,clientId:C,channels:w?w.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){return{name:e.name,websocketPath:e.websocketPath,extractMeta:e.extractMeta||defaultExtractMeta,onConnect:e.onConnect,onDisconnect:e.onDisconnect,onMessage:e.onMessage,onError:e.onError}}function parseJWT(e){try{let b=e.split(`.`);if(b.length!==3)return null;let x=b[1],S=atob(x.replace(/-/g,`+`).replace(/_/g,`/`));return JSON.parse(S)}catch{return null}}function storeAttachment(e,b){e.serializeAttachment(b)}function restoreSessions(e){let b=0;for(let x of e.ctx.getWebSockets()){let S=x.deserializeAttachment();S&&(e.sessions.set(x,{ws:x,meta:S}),b++)}}function isValidFrame(e){return e&&typeof e==`object`&&typeof e.type==`string`&&(e.channel===void 0||typeof e.channel==`string`)}function decodeFrame(e){try{let b=typeof e==`string`?e:e.toString(),x=JSON.parse(b);return isValidFrame(x)?x:null}catch{return null}}function decodeClientMessage(e){return decodeFrame(e)}function decodeServerMessage(e){return decodeFrame(e)}function encodeFrame(e){try{return JSON.stringify(e)}catch(e){throw Error(`Failed to encode frame: ${e instanceof Error?e.message:`unknown error`}`)}}function encodeClientMessage(e){return encodeFrame(e)}function encodeServerMessage(e){return encodeFrame(e)}function decodeFrame$1(e){return decodeFrame(e)??{type:`invalid`}}function encodeFrame$1(e){return encodeFrame(e)}function sanitizeToClassName(e){return e.replace(/^\/+/,``).split(/[-_\/\s]+/).map(e=>e.replace(/[^a-zA-Z0-9]/g,``)).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createActorHandler(b){let x=sanitizeToClassName(b.name||b.websocketPath||`VeraniActor`);class S extends __cloudflare_actors.Actor{constructor(...e){super(...e),this.sessions=new Map}static configuration(e){return{locationHint:`me`,sockets:{upgradePath:b.websocketPath,autoResponse:{ping:`ping`,pong:`pong`}}}}async shouldUpgradeWebSocket(e){return!0}async fetch(e){let x=new URL(e.url),S=e.headers.get(`Upgrade`);return x.pathname===b.websocketPath&&S===`websocket`&&await this.shouldUpgradeWebSocket(e)?this.onWebSocketUpgrade(e):this.onRequest(e)}async onInit(){try{restoreSessions(this),b.onHibernationRestore&&this.sessions.size>0&&await b.onHibernationRestore(this)}catch{}}onWebSocketConnect(e,x){try{let S;if(S=b.extractMeta?b.extractMeta(x):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(e,S),this.sessions.set(e,{ws:e,meta:S}),b.onConnect){let x={actor:this,ws:e,meta:S};b.onConnect(x)}}catch(x){if(b.onError)try{b.onError(x,{actor:this,ws:e,meta:{userId:`unknown`,clientId:`unknown`,channels:[]}})}catch{}e.close(1011,`Internal server error`)}}onWebSocketMessage(e,x){let S;try{let C=decodeFrame$1(x);if(S=this.sessions.get(e),!S)return;if(b.onMessage){let x={actor:this,ws:e,meta:S.meta,frame:C};b.onMessage(x,C)}}catch(x){if(b.onError&&S)try{b.onError(x,{actor:this,ws:e,meta:S.meta})}catch{}}}onWebSocketDisconnect(e){try{let x=this.sessions.get(e);if(this.sessions.delete(e),x&&b.onDisconnect){let S={actor:this,ws:e,meta:x.meta};b.onDisconnect(S)}}catch{}}broadcast(e,b,x){let S=0,C=encodeFrame$1({type:`event`,channel:e,data:b});for(let{ws:b,meta:w}of this.sessions.values())if(w.channels.includes(e)&&!(x?.except&&b===x.except)&&!(x?.userIds&&!x.userIds.includes(w.userId))&&!(x?.clientIds&&!x.clientIds.includes(w.clientId)))try{b.send(C),S++}catch{}return S}getSessionCount(){return this.sessions.size}getConnectedUserIds(){let e=new Set;for(let{meta:b}of this.sessions.values())e.add(b.userId);return Array.from(e)}getUserSessions(e){let b=[];for(let{ws:x,meta:S}of this.sessions.values())S.userId===e&&b.push(x);return b}sendToUser(e,b,x){let S=0,C=encodeFrame$1({type:b,data:x});for(let{ws:b,meta:x}of this.sessions.values())if(x.userId===e)try{b.send(C),S++}catch{}return S}getStorage(){return this.ctx.storage}}return Object.defineProperty(S,`name`,{value:x,writable:!1,configurable:!0}),S}function encodeClientMessage$1(e){return encodeClientMessage(e)}function decodeServerMessage$1(e){return decodeServerMessage(e)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,b){this.config=e,this.onStateChange=b,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}setState(e){this.state!==e&&(this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}},VeraniClient=class{constructor(e,b={}){this.url=e,this.listeners=new Map,this.messageQueue=[],this.options={reconnection:{enabled:b.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:b.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:b.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:b.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:b.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:b.maxQueueSize??100,connectionTimeout:b.connectionTimeout??1e4},this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.connect()}connect(){try{this.connectionManager.setState(`connecting`),this.ws=new WebSocket(this.url);let e=setTimeout(()=>{this.connectionManager.getState()===`connecting`&&(this.ws?.close(),this.handleConnectionError(Error(`Connection timeout`)))},this.options.connectionTimeout);this.ws.addEventListener(`open`,()=>{clearTimeout(e),this.handleOpen()}),this.ws.addEventListener(`message`,e=>{this.handleMessage(e)}),this.ws.addEventListener(`close`,b=>{clearTimeout(e),this.handleClose(b)}),this.ws.addEventListener(`error`,b=>{clearTimeout(e),this.handleError(b)})}catch(e){this.handleConnectionError(e)}}handleOpen(){this.connectionManager.setState(`connected`),this.connectionManager.resetReconnection(),this.flushMessageQueue(),this.connectionResolve&&(this.connectionResolve(),this.connectionPromise=void 0,this.connectionResolve=void 0,this.connectionReject=void 0),this.onOpenCallback?.()}handleMessage(e){let b=decodeServerMessage$1(e.data);if(!b)return;let x=b.type,S=b.data;b.type===`event`&&b.data&&typeof b.data==`object`&&`type`in b.data&&(x=b.data.type,S=b.data);let C=this.listeners.get(x);if(C)for(let e of C)try{e(S)}catch{}}handleClose(e){this.connectionManager.setState(`disconnected`),this.connectionReject&&=(this.connectionReject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.onCloseCallback?.(e),e.code!==1e3&&e.code!==1001&&this.connectionManager.scheduleReconnect(()=>this.connect())}handleError(e){this.onErrorCallback?.(e)}handleConnectionError(e){this.connectionReject&&=(this.connectionReject(e),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.connectionManager.scheduleReconnect(()=>this.connect())}flushMessageQueue(){if(!(!this.ws||this.ws.readyState!==WebSocket.OPEN))for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();try{this.ws.send(encodeClientMessage$1(e))}catch{}}}getState(){return this.connectionManager.getState()}isConnected(){return this.ws?.readyState===WebSocket.OPEN}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromise||=new Promise((e,b)=>{this.connectionResolve=e,this.connectionReject=b}),this.connectionPromise)}on(e,b){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(b)}off(e,b){let x=this.listeners.get(e);x&&(x.delete(b),x.size===0&&this.listeners.delete(e))}once(e,b){let x=S=>{this.off(e,x),b(S)};this.on(e,x)}emit(e,b){let x={type:e,data:b};if(this.isConnected())try{this.ws.send(encodeClientMessage$1(x))}catch{this.queueMessage(x)}else this.queueMessage(x)}queueMessage(e){this.messageQueue.length>=this.options.maxQueueSize&&this.messageQueue.shift(),this.messageQueue.push(e)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.disconnect(),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.ws&&=(this.ws.close(1e3,`Client disconnect`),void 0)}close(){this.disconnect(),this.listeners.clear(),this.messageQueue=[],this.connectionManager.destroy()}};const PROTOCOL_VERSION=`1.0.0`;exports.ConnectionManager=ConnectionManager,exports.DEFAULT_RECONNECTION_CONFIG=DEFAULT_RECONNECTION_CONFIG,exports.PROTOCOL_VERSION=`1.0.0`,exports.VeraniClient=VeraniClient,exports.createActorHandler=createActorHandler,exports.decodeClientMessage=decodeClientMessage,exports.decodeFrame=decodeFrame,exports.decodeServerMessage=decodeServerMessage,exports.defineRoom=defineRoom,exports.encodeClientMessage=encodeClientMessage,exports.encodeFrame=encodeFrame,exports.encodeServerMessage=encodeServerMessage,exports.parseJWT=parseJWT,exports.restoreSessions=restoreSessions,exports.storeAttachment=storeAttachment;
|
|
1
|
+
let __cloudflare_actors=require(`@cloudflare/actors`);function defaultExtractMeta(e){let _=crypto.randomUUID(),v=crypto.randomUUID(),y=new URL(e.url).searchParams.get(`channels`);return{userId:_,clientId:v,channels:y?y.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){return{name:e.name,websocketPath:e.websocketPath,extractMeta:e.extractMeta||defaultExtractMeta,onConnect:e.onConnect,onDisconnect:e.onDisconnect,onMessage:e.onMessage,onError:e.onError}}function storeAttachment(e,_){e.serializeAttachment(_)}function restoreSessions(e){let _=0;for(let v of e.ctx.getWebSockets()){let y=v.deserializeAttachment();y&&(e.sessions.set(v,{ws:v,meta:y}),_++)}}function isValidFrame(e){return e&&typeof e==`object`&&typeof e.type==`string`&&(e.channel===void 0||typeof e.channel==`string`)}function decodeFrame(e){try{let _=typeof e==`string`?e:e.toString(),v=JSON.parse(_);return isValidFrame(v)?v:null}catch{return null}}function decodeClientMessage(e){return decodeFrame(e)}function decodeServerMessage(e){return decodeFrame(e)}function encodeFrame(e){try{return JSON.stringify(e)}catch(e){throw Error(`Failed to encode frame: ${e instanceof Error?e.message:`unknown error`}`)}}function encodeClientMessage(e){return encodeFrame(e)}function encodeServerMessage(e){return encodeFrame(e)}function decodeFrame$1(e){return decodeFrame(e)??{type:`invalid`}}function encodeFrame$1(e){return encodeFrame(e)}function sanitizeToClassName(e){return e.replace(/^\/+/,``).split(/[-_\/\s]+/).map(e=>e.replace(/[^a-zA-Z0-9]/g,``)).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createActorHandler(_){let v=sanitizeToClassName(_.name||_.websocketPath||`VeraniActor`);class x extends __cloudflare_actors.Actor{constructor(...e){super(...e),this.sessions=new Map}static configuration(e){return{locationHint:`me`,sockets:{upgradePath:_.websocketPath,autoResponse:{ping:`ping`,pong:`pong`}}}}async shouldUpgradeWebSocket(e){return!0}async fetch(e){let v=new URL(e.url),y=e.headers.get(`Upgrade`);return v.pathname===_.websocketPath&&y===`websocket`&&await this.shouldUpgradeWebSocket(e)?this.onWebSocketUpgrade(e):this.onRequest(e)}async onInit(){try{restoreSessions(this),_.onHibernationRestore&&this.sessions.size>0&&await _.onHibernationRestore(this)}catch{}}onWebSocketConnect(e,v){try{let b;if(b=_.extractMeta?_.extractMeta(v):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(e,b),this.sessions.set(e,{ws:e,meta:b}),_.onConnect){let v={actor:this,ws:e,meta:b};_.onConnect(v)}}catch(v){if(_.onError)try{_.onError(v,{actor:this,ws:e,meta:{userId:`unknown`,clientId:`unknown`,channels:[]}})}catch{}e.close(1011,`Internal server error`)}}onWebSocketMessage(e,v){let y;try{let b=decodeFrame$1(v);if(y=this.sessions.get(e),!y)return;if(_.onMessage){let v={actor:this,ws:e,meta:y.meta,frame:b};_.onMessage(v,b)}}catch(v){if(_.onError&&y)try{_.onError(v,{actor:this,ws:e,meta:y.meta})}catch{}}}onWebSocketDisconnect(e){try{let v=this.sessions.get(e);if(this.sessions.delete(e),v&&_.onDisconnect){let y={actor:this,ws:e,meta:v.meta};_.onDisconnect(y)}}catch{}}broadcast(e,_,v){let y=0,b=encodeFrame$1({type:`event`,channel:e,data:_});for(let{ws:_,meta:x}of this.sessions.values())if(x.channels.includes(e)&&!(v?.except&&_===v.except)&&!(v?.userIds&&!v.userIds.includes(x.userId))&&!(v?.clientIds&&!v.clientIds.includes(x.clientId)))try{_.send(b),y++}catch{}return y}getSessionCount(){return this.sessions.size}getConnectedUserIds(){let e=new Set;for(let{meta:_}of this.sessions.values())e.add(_.userId);return Array.from(e)}getUserSessions(e){let _=[];for(let{ws:v,meta:y}of this.sessions.values())y.userId===e&&_.push(v);return _}sendToUser(e,_,v){let y=0,b=encodeFrame$1({type:_,data:v});for(let{ws:_,meta:v}of this.sessions.values())if(v.userId===e)try{_.send(b),y++}catch{}return y}getStorage(){return this.ctx.storage}}return Object.defineProperty(x,`name`,{value:v,writable:!1,configurable:!0}),x}function encodeClientMessage$1(e){return encodeClientMessage(e)}function decodeServerMessage$1(e){return decodeServerMessage(e)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,_){this.config=e,this.onStateChange=_,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}setState(e){this.state!==e&&(this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}},VeraniClient=class{constructor(e,_={}){this.url=e,this.listeners=new Map,this.messageQueue=[],this.options={reconnection:{enabled:_.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:_.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:_.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:_.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:_.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:_.maxQueueSize??100,connectionTimeout:_.connectionTimeout??1e4},this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.connect()}connect(){try{this.connectionManager.setState(`connecting`),this.ws=new WebSocket(this.url);let e=setTimeout(()=>{this.connectionManager.getState()===`connecting`&&(this.ws?.close(),this.handleConnectionError(Error(`Connection timeout`)))},this.options.connectionTimeout);this.ws.addEventListener(`open`,()=>{clearTimeout(e),this.handleOpen()}),this.ws.addEventListener(`message`,e=>{this.handleMessage(e)}),this.ws.addEventListener(`close`,_=>{clearTimeout(e),this.handleClose(_)}),this.ws.addEventListener(`error`,_=>{clearTimeout(e),this.handleError(_)})}catch(e){this.handleConnectionError(e)}}handleOpen(){this.connectionManager.setState(`connected`),this.connectionManager.resetReconnection(),this.flushMessageQueue(),this.connectionResolve&&(this.connectionResolve(),this.connectionPromise=void 0,this.connectionResolve=void 0,this.connectionReject=void 0),this.onOpenCallback?.()}handleMessage(e){let _=decodeServerMessage$1(e.data);if(!_)return;let v=_.type,y=_.data;_.type===`event`&&_.data&&typeof _.data==`object`&&`type`in _.data&&(v=_.data.type,y=_.data);let b=this.listeners.get(v);if(b)for(let e of b)try{e(y)}catch{}}handleClose(e){this.connectionManager.setState(`disconnected`),this.connectionReject&&=(this.connectionReject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.onCloseCallback?.(e),e.code!==1e3&&e.code!==1001&&this.connectionManager.scheduleReconnect(()=>this.connect())}handleError(e){this.onErrorCallback?.(e)}handleConnectionError(e){this.connectionReject&&=(this.connectionReject(e),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.connectionManager.scheduleReconnect(()=>this.connect())}flushMessageQueue(){if(!(!this.ws||this.ws.readyState!==WebSocket.OPEN))for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();try{this.ws.send(encodeClientMessage$1(e))}catch{}}}getState(){return this.connectionManager.getState()}isConnected(){return this.ws?.readyState===WebSocket.OPEN}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromise||=new Promise((e,_)=>{this.connectionResolve=e,this.connectionReject=_}),this.connectionPromise)}on(e,_){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(_)}off(e,_){let v=this.listeners.get(e);v&&(v.delete(_),v.size===0&&this.listeners.delete(e))}once(e,_){let v=y=>{this.off(e,v),_(y)};this.on(e,v)}emit(e,_){let v={type:e,data:_};if(this.isConnected())try{this.ws.send(encodeClientMessage$1(v))}catch{this.queueMessage(v)}else this.queueMessage(v)}queueMessage(e){this.messageQueue.length>=this.options.maxQueueSize&&this.messageQueue.shift(),this.messageQueue.push(e)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.disconnect(),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.ws&&=(this.ws.close(1e3,`Client disconnect`),void 0)}close(){this.disconnect(),this.listeners.clear(),this.messageQueue=[],this.connectionManager.destroy()}};const PROTOCOL_VERSION=`1.0.0`;exports.ConnectionManager=ConnectionManager,exports.DEFAULT_RECONNECTION_CONFIG=DEFAULT_RECONNECTION_CONFIG,exports.PROTOCOL_VERSION=`1.0.0`,exports.VeraniClient=VeraniClient,exports.createActorHandler=createActorHandler,exports.decodeClientMessage=decodeClientMessage,exports.decodeFrame=decodeFrame,exports.decodeServerMessage=decodeServerMessage,exports.defineRoom=defineRoom,exports.encodeClientMessage=encodeClientMessage,exports.encodeFrame=encodeFrame,exports.encodeServerMessage=encodeServerMessage,exports.restoreSessions=restoreSessions,exports.storeAttachment=storeAttachment;
|
package/dist/verani.d.cts
CHANGED
|
@@ -120,13 +120,6 @@ interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
120
120
|
* @returns Normalized room definition with defaults
|
|
121
121
|
*/
|
|
122
122
|
declare function defineRoom<TMeta extends ConnectionMeta = ConnectionMeta>(def: RoomDefinition<TMeta>): RoomDefinition<TMeta>;
|
|
123
|
-
/**
|
|
124
|
-
* Helper to parse JWT tokens (basic implementation)
|
|
125
|
-
* In production, use a proper JWT library
|
|
126
|
-
* @param token - JWT token string
|
|
127
|
-
* @returns Decoded payload or null if invalid
|
|
128
|
-
*/
|
|
129
|
-
declare function parseJWT(token: string): any;
|
|
130
123
|
//#endregion
|
|
131
124
|
//#region src/actor/actor-runtime.d.ts
|
|
132
125
|
/**
|
|
@@ -396,4 +389,4 @@ declare function decodeClientMessage(raw: any): MessageFrame | null;
|
|
|
396
389
|
*/
|
|
397
390
|
declare function decodeServerMessage(raw: any): MessageFrame | null;
|
|
398
391
|
//#endregion
|
|
399
|
-
export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, ConnectionManager, type ConnectionMeta, type ConnectionState, DEFAULT_RECONNECTION_CONFIG, type MessageContext, type MessageFrame, PROTOCOL_VERSION, type ReconnectionConfig, type RoomContext, type RoomDefinition, type ServerMessage, type VeraniActor, VeraniClient, type VeraniClientOptions, type VeraniMessage, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, encodeClientMessage, encodeFrame, encodeServerMessage,
|
|
392
|
+
export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, ConnectionManager, type ConnectionMeta, type ConnectionState, DEFAULT_RECONNECTION_CONFIG, type MessageContext, type MessageFrame, PROTOCOL_VERSION, type ReconnectionConfig, type RoomContext, type RoomDefinition, type ServerMessage, type VeraniActor, VeraniClient, type VeraniClientOptions, type VeraniMessage, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, encodeClientMessage, encodeFrame, encodeServerMessage, restoreSessions, storeAttachment };
|
package/dist/verani.d.mts
CHANGED
|
@@ -120,13 +120,6 @@ interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
120
120
|
* @returns Normalized room definition with defaults
|
|
121
121
|
*/
|
|
122
122
|
declare function defineRoom<TMeta extends ConnectionMeta = ConnectionMeta>(def: RoomDefinition<TMeta>): RoomDefinition<TMeta>;
|
|
123
|
-
/**
|
|
124
|
-
* Helper to parse JWT tokens (basic implementation)
|
|
125
|
-
* In production, use a proper JWT library
|
|
126
|
-
* @param token - JWT token string
|
|
127
|
-
* @returns Decoded payload or null if invalid
|
|
128
|
-
*/
|
|
129
|
-
declare function parseJWT(token: string): any;
|
|
130
123
|
//#endregion
|
|
131
124
|
//#region src/actor/actor-runtime.d.ts
|
|
132
125
|
/**
|
|
@@ -396,4 +389,4 @@ declare function decodeClientMessage(raw: any): MessageFrame | null;
|
|
|
396
389
|
*/
|
|
397
390
|
declare function decodeServerMessage(raw: any): MessageFrame | null;
|
|
398
391
|
//#endregion
|
|
399
|
-
export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, ConnectionManager, type ConnectionMeta, type ConnectionState, DEFAULT_RECONNECTION_CONFIG, type MessageContext, type MessageFrame, PROTOCOL_VERSION, type ReconnectionConfig, type RoomContext, type RoomDefinition, type ServerMessage, type VeraniActor, VeraniClient, type VeraniClientOptions, type VeraniMessage, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, encodeClientMessage, encodeFrame, encodeServerMessage,
|
|
392
|
+
export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, ConnectionManager, type ConnectionMeta, type ConnectionState, DEFAULT_RECONNECTION_CONFIG, type MessageContext, type MessageFrame, PROTOCOL_VERSION, type ReconnectionConfig, type RoomContext, type RoomDefinition, type ServerMessage, type VeraniActor, VeraniClient, type VeraniClientOptions, type VeraniMessage, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, encodeClientMessage, encodeFrame, encodeServerMessage, restoreSessions, storeAttachment };
|
package/dist/verani.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Actor}from"@cloudflare/actors";function extractUserId(e){let b=new URL(e.url),x=b.searchParams.get(`userId`)||b.searchParams.get(`user_id`);if(x)return x;let S=e.headers.get(`Authorization`);return S?.startsWith(`Bearer `)?S.substring(7):e.headers.get(`X-User-ID`)||`anonymous`}function extractClientId(e){let b=new URL(e.url);return b.searchParams.get(`clientId`)||b.searchParams.get(`client_id`)||e.headers.get(`X-Client-ID`)||crypto.randomUUID()}function defaultExtractMeta(e){let S=extractUserId(e),C=extractClientId(e),w=new URL(e.url).searchParams.get(`channels`);return{userId:S,clientId:C,channels:w?w.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){return{name:e.name,websocketPath:e.websocketPath,extractMeta:e.extractMeta||defaultExtractMeta,onConnect:e.onConnect,onDisconnect:e.onDisconnect,onMessage:e.onMessage,onError:e.onError}}function parseJWT(e){try{let b=e.split(`.`);if(b.length!==3)return null;let x=b[1],S=atob(x.replace(/-/g,`+`).replace(/_/g,`/`));return JSON.parse(S)}catch{return null}}function storeAttachment(e,b){e.serializeAttachment(b)}function restoreSessions(e){let b=0;for(let x of e.ctx.getWebSockets()){let S=x.deserializeAttachment();S&&(e.sessions.set(x,{ws:x,meta:S}),b++)}}function isValidFrame(e){return e&&typeof e==`object`&&typeof e.type==`string`&&(e.channel===void 0||typeof e.channel==`string`)}function decodeFrame(e){try{let b=typeof e==`string`?e:e.toString(),x=JSON.parse(b);return isValidFrame(x)?x:null}catch{return null}}function decodeClientMessage(e){return decodeFrame(e)}function decodeServerMessage(e){return decodeFrame(e)}function encodeFrame(e){try{return JSON.stringify(e)}catch(e){throw Error(`Failed to encode frame: ${e instanceof Error?e.message:`unknown error`}`)}}function encodeClientMessage(e){return encodeFrame(e)}function encodeServerMessage(e){return encodeFrame(e)}function decodeFrame$1(e){return decodeFrame(e)??{type:`invalid`}}function encodeFrame$1(e){return encodeFrame(e)}function sanitizeToClassName(e){return e.replace(/^\/+/,``).split(/[-_\/\s]+/).map(e=>e.replace(/[^a-zA-Z0-9]/g,``)).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createActorHandler(b){let x=sanitizeToClassName(b.name||b.websocketPath||`VeraniActor`);class S extends Actor{constructor(...e){super(...e),this.sessions=new Map}static configuration(e){return{locationHint:`me`,sockets:{upgradePath:b.websocketPath,autoResponse:{ping:`ping`,pong:`pong`}}}}async shouldUpgradeWebSocket(e){return!0}async fetch(e){let x=new URL(e.url),S=e.headers.get(`Upgrade`);return x.pathname===b.websocketPath&&S===`websocket`&&await this.shouldUpgradeWebSocket(e)?this.onWebSocketUpgrade(e):this.onRequest(e)}async onInit(){try{restoreSessions(this),b.onHibernationRestore&&this.sessions.size>0&&await b.onHibernationRestore(this)}catch{}}onWebSocketConnect(e,x){try{let S;if(S=b.extractMeta?b.extractMeta(x):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(e,S),this.sessions.set(e,{ws:e,meta:S}),b.onConnect){let x={actor:this,ws:e,meta:S};b.onConnect(x)}}catch(x){if(b.onError)try{b.onError(x,{actor:this,ws:e,meta:{userId:`unknown`,clientId:`unknown`,channels:[]}})}catch{}e.close(1011,`Internal server error`)}}onWebSocketMessage(e,x){let S;try{let C=decodeFrame$1(x);if(S=this.sessions.get(e),!S)return;if(b.onMessage){let x={actor:this,ws:e,meta:S.meta,frame:C};b.onMessage(x,C)}}catch(x){if(b.onError&&S)try{b.onError(x,{actor:this,ws:e,meta:S.meta})}catch{}}}onWebSocketDisconnect(e){try{let x=this.sessions.get(e);if(this.sessions.delete(e),x&&b.onDisconnect){let S={actor:this,ws:e,meta:x.meta};b.onDisconnect(S)}}catch{}}broadcast(e,b,x){let S=0,C=encodeFrame$1({type:`event`,channel:e,data:b});for(let{ws:b,meta:w}of this.sessions.values())if(w.channels.includes(e)&&!(x?.except&&b===x.except)&&!(x?.userIds&&!x.userIds.includes(w.userId))&&!(x?.clientIds&&!x.clientIds.includes(w.clientId)))try{b.send(C),S++}catch{}return S}getSessionCount(){return this.sessions.size}getConnectedUserIds(){let e=new Set;for(let{meta:b}of this.sessions.values())e.add(b.userId);return Array.from(e)}getUserSessions(e){let b=[];for(let{ws:x,meta:S}of this.sessions.values())S.userId===e&&b.push(x);return b}sendToUser(e,b,x){let S=0,C=encodeFrame$1({type:b,data:x});for(let{ws:b,meta:x}of this.sessions.values())if(x.userId===e)try{b.send(C),S++}catch{}return S}getStorage(){return this.ctx.storage}}return Object.defineProperty(S,`name`,{value:x,writable:!1,configurable:!0}),S}function encodeClientMessage$1(e){return encodeClientMessage(e)}function decodeServerMessage$1(e){return decodeServerMessage(e)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,b){this.config=e,this.onStateChange=b,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}setState(e){this.state!==e&&(this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}},VeraniClient=class{constructor(e,b={}){this.url=e,this.listeners=new Map,this.messageQueue=[],this.options={reconnection:{enabled:b.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:b.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:b.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:b.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:b.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:b.maxQueueSize??100,connectionTimeout:b.connectionTimeout??1e4},this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.connect()}connect(){try{this.connectionManager.setState(`connecting`),this.ws=new WebSocket(this.url);let e=setTimeout(()=>{this.connectionManager.getState()===`connecting`&&(this.ws?.close(),this.handleConnectionError(Error(`Connection timeout`)))},this.options.connectionTimeout);this.ws.addEventListener(`open`,()=>{clearTimeout(e),this.handleOpen()}),this.ws.addEventListener(`message`,e=>{this.handleMessage(e)}),this.ws.addEventListener(`close`,b=>{clearTimeout(e),this.handleClose(b)}),this.ws.addEventListener(`error`,b=>{clearTimeout(e),this.handleError(b)})}catch(e){this.handleConnectionError(e)}}handleOpen(){this.connectionManager.setState(`connected`),this.connectionManager.resetReconnection(),this.flushMessageQueue(),this.connectionResolve&&(this.connectionResolve(),this.connectionPromise=void 0,this.connectionResolve=void 0,this.connectionReject=void 0),this.onOpenCallback?.()}handleMessage(e){let b=decodeServerMessage$1(e.data);if(!b)return;let x=b.type,S=b.data;b.type===`event`&&b.data&&typeof b.data==`object`&&`type`in b.data&&(x=b.data.type,S=b.data);let C=this.listeners.get(x);if(C)for(let e of C)try{e(S)}catch{}}handleClose(e){this.connectionManager.setState(`disconnected`),this.connectionReject&&=(this.connectionReject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.onCloseCallback?.(e),e.code!==1e3&&e.code!==1001&&this.connectionManager.scheduleReconnect(()=>this.connect())}handleError(e){this.onErrorCallback?.(e)}handleConnectionError(e){this.connectionReject&&=(this.connectionReject(e),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.connectionManager.scheduleReconnect(()=>this.connect())}flushMessageQueue(){if(!(!this.ws||this.ws.readyState!==WebSocket.OPEN))for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();try{this.ws.send(encodeClientMessage$1(e))}catch{}}}getState(){return this.connectionManager.getState()}isConnected(){return this.ws?.readyState===WebSocket.OPEN}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromise||=new Promise((e,b)=>{this.connectionResolve=e,this.connectionReject=b}),this.connectionPromise)}on(e,b){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(b)}off(e,b){let x=this.listeners.get(e);x&&(x.delete(b),x.size===0&&this.listeners.delete(e))}once(e,b){let x=S=>{this.off(e,x),b(S)};this.on(e,x)}emit(e,b){let x={type:e,data:b};if(this.isConnected())try{this.ws.send(encodeClientMessage$1(x))}catch{this.queueMessage(x)}else this.queueMessage(x)}queueMessage(e){this.messageQueue.length>=this.options.maxQueueSize&&this.messageQueue.shift(),this.messageQueue.push(e)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.disconnect(),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.ws&&=(this.ws.close(1e3,`Client disconnect`),void 0)}close(){this.disconnect(),this.listeners.clear(),this.messageQueue=[],this.connectionManager.destroy()}};const PROTOCOL_VERSION=`1.0.0`;export{ConnectionManager,DEFAULT_RECONNECTION_CONFIG,PROTOCOL_VERSION,VeraniClient,createActorHandler,decodeClientMessage,decodeFrame,decodeServerMessage,defineRoom,encodeClientMessage,encodeFrame,encodeServerMessage,parseJWT,restoreSessions,storeAttachment};
|
|
1
|
+
import{Actor}from"@cloudflare/actors";function defaultExtractMeta(e){let _=crypto.randomUUID(),v=crypto.randomUUID(),y=new URL(e.url).searchParams.get(`channels`);return{userId:_,clientId:v,channels:y?y.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){return{name:e.name,websocketPath:e.websocketPath,extractMeta:e.extractMeta||defaultExtractMeta,onConnect:e.onConnect,onDisconnect:e.onDisconnect,onMessage:e.onMessage,onError:e.onError}}function storeAttachment(e,_){e.serializeAttachment(_)}function restoreSessions(e){let _=0;for(let v of e.ctx.getWebSockets()){let y=v.deserializeAttachment();y&&(e.sessions.set(v,{ws:v,meta:y}),_++)}}function isValidFrame(e){return e&&typeof e==`object`&&typeof e.type==`string`&&(e.channel===void 0||typeof e.channel==`string`)}function decodeFrame(e){try{let _=typeof e==`string`?e:e.toString(),v=JSON.parse(_);return isValidFrame(v)?v:null}catch{return null}}function decodeClientMessage(e){return decodeFrame(e)}function decodeServerMessage(e){return decodeFrame(e)}function encodeFrame(e){try{return JSON.stringify(e)}catch(e){throw Error(`Failed to encode frame: ${e instanceof Error?e.message:`unknown error`}`)}}function encodeClientMessage(e){return encodeFrame(e)}function encodeServerMessage(e){return encodeFrame(e)}function decodeFrame$1(e){return decodeFrame(e)??{type:`invalid`}}function encodeFrame$1(e){return encodeFrame(e)}function sanitizeToClassName(e){return e.replace(/^\/+/,``).split(/[-_\/\s]+/).map(e=>e.replace(/[^a-zA-Z0-9]/g,``)).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createActorHandler(_){let v=sanitizeToClassName(_.name||_.websocketPath||`VeraniActor`);class x extends Actor{constructor(...e){super(...e),this.sessions=new Map}static configuration(e){return{locationHint:`me`,sockets:{upgradePath:_.websocketPath,autoResponse:{ping:`ping`,pong:`pong`}}}}async shouldUpgradeWebSocket(e){return!0}async fetch(e){let v=new URL(e.url),y=e.headers.get(`Upgrade`);return v.pathname===_.websocketPath&&y===`websocket`&&await this.shouldUpgradeWebSocket(e)?this.onWebSocketUpgrade(e):this.onRequest(e)}async onInit(){try{restoreSessions(this),_.onHibernationRestore&&this.sessions.size>0&&await _.onHibernationRestore(this)}catch{}}onWebSocketConnect(e,v){try{let b;if(b=_.extractMeta?_.extractMeta(v):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(e,b),this.sessions.set(e,{ws:e,meta:b}),_.onConnect){let v={actor:this,ws:e,meta:b};_.onConnect(v)}}catch(v){if(_.onError)try{_.onError(v,{actor:this,ws:e,meta:{userId:`unknown`,clientId:`unknown`,channels:[]}})}catch{}e.close(1011,`Internal server error`)}}onWebSocketMessage(e,v){let y;try{let b=decodeFrame$1(v);if(y=this.sessions.get(e),!y)return;if(_.onMessage){let v={actor:this,ws:e,meta:y.meta,frame:b};_.onMessage(v,b)}}catch(v){if(_.onError&&y)try{_.onError(v,{actor:this,ws:e,meta:y.meta})}catch{}}}onWebSocketDisconnect(e){try{let v=this.sessions.get(e);if(this.sessions.delete(e),v&&_.onDisconnect){let y={actor:this,ws:e,meta:v.meta};_.onDisconnect(y)}}catch{}}broadcast(e,_,v){let y=0,b=encodeFrame$1({type:`event`,channel:e,data:_});for(let{ws:_,meta:x}of this.sessions.values())if(x.channels.includes(e)&&!(v?.except&&_===v.except)&&!(v?.userIds&&!v.userIds.includes(x.userId))&&!(v?.clientIds&&!v.clientIds.includes(x.clientId)))try{_.send(b),y++}catch{}return y}getSessionCount(){return this.sessions.size}getConnectedUserIds(){let e=new Set;for(let{meta:_}of this.sessions.values())e.add(_.userId);return Array.from(e)}getUserSessions(e){let _=[];for(let{ws:v,meta:y}of this.sessions.values())y.userId===e&&_.push(v);return _}sendToUser(e,_,v){let y=0,b=encodeFrame$1({type:_,data:v});for(let{ws:_,meta:v}of this.sessions.values())if(v.userId===e)try{_.send(b),y++}catch{}return y}getStorage(){return this.ctx.storage}}return Object.defineProperty(x,`name`,{value:v,writable:!1,configurable:!0}),x}function encodeClientMessage$1(e){return encodeClientMessage(e)}function decodeServerMessage$1(e){return decodeServerMessage(e)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,_){this.config=e,this.onStateChange=_,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}setState(e){this.state!==e&&(this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}},VeraniClient=class{constructor(e,_={}){this.url=e,this.listeners=new Map,this.messageQueue=[],this.options={reconnection:{enabled:_.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:_.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:_.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:_.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:_.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:_.maxQueueSize??100,connectionTimeout:_.connectionTimeout??1e4},this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.connect()}connect(){try{this.connectionManager.setState(`connecting`),this.ws=new WebSocket(this.url);let e=setTimeout(()=>{this.connectionManager.getState()===`connecting`&&(this.ws?.close(),this.handleConnectionError(Error(`Connection timeout`)))},this.options.connectionTimeout);this.ws.addEventListener(`open`,()=>{clearTimeout(e),this.handleOpen()}),this.ws.addEventListener(`message`,e=>{this.handleMessage(e)}),this.ws.addEventListener(`close`,_=>{clearTimeout(e),this.handleClose(_)}),this.ws.addEventListener(`error`,_=>{clearTimeout(e),this.handleError(_)})}catch(e){this.handleConnectionError(e)}}handleOpen(){this.connectionManager.setState(`connected`),this.connectionManager.resetReconnection(),this.flushMessageQueue(),this.connectionResolve&&(this.connectionResolve(),this.connectionPromise=void 0,this.connectionResolve=void 0,this.connectionReject=void 0),this.onOpenCallback?.()}handleMessage(e){let _=decodeServerMessage$1(e.data);if(!_)return;let v=_.type,y=_.data;_.type===`event`&&_.data&&typeof _.data==`object`&&`type`in _.data&&(v=_.data.type,y=_.data);let b=this.listeners.get(v);if(b)for(let e of b)try{e(y)}catch{}}handleClose(e){this.connectionManager.setState(`disconnected`),this.connectionReject&&=(this.connectionReject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.onCloseCallback?.(e),e.code!==1e3&&e.code!==1001&&this.connectionManager.scheduleReconnect(()=>this.connect())}handleError(e){this.onErrorCallback?.(e)}handleConnectionError(e){this.connectionReject&&=(this.connectionReject(e),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.connectionManager.scheduleReconnect(()=>this.connect())}flushMessageQueue(){if(!(!this.ws||this.ws.readyState!==WebSocket.OPEN))for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();try{this.ws.send(encodeClientMessage$1(e))}catch{}}}getState(){return this.connectionManager.getState()}isConnected(){return this.ws?.readyState===WebSocket.OPEN}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromise||=new Promise((e,_)=>{this.connectionResolve=e,this.connectionReject=_}),this.connectionPromise)}on(e,_){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(_)}off(e,_){let v=this.listeners.get(e);v&&(v.delete(_),v.size===0&&this.listeners.delete(e))}once(e,_){let v=y=>{this.off(e,v),_(y)};this.on(e,v)}emit(e,_){let v={type:e,data:_};if(this.isConnected())try{this.ws.send(encodeClientMessage$1(v))}catch{this.queueMessage(v)}else this.queueMessage(v)}queueMessage(e){this.messageQueue.length>=this.options.maxQueueSize&&this.messageQueue.shift(),this.messageQueue.push(e)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.disconnect(),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.ws&&=(this.ws.close(1e3,`Client disconnect`),void 0)}close(){this.disconnect(),this.listeners.clear(),this.messageQueue=[],this.connectionManager.destroy()}};const PROTOCOL_VERSION=`1.0.0`;export{ConnectionManager,DEFAULT_RECONNECTION_CONFIG,PROTOCOL_VERSION,VeraniClient,createActorHandler,decodeClientMessage,decodeFrame,decodeServerMessage,defineRoom,encodeClientMessage,encodeFrame,encodeServerMessage,restoreSessions,storeAttachment};
|