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 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.CHAT` in your fetch handler
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, parseJWT, restoreSessions, storeAttachment };
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, parseJWT, restoreSessions, storeAttachment };
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "verani",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "A simple, focused realtime SDK for Cloudflare Actors with Socket.io-like semantics",
5
5
  "license": "ISC",
6
6
  "keywords": [