verani 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ const require_encode=require(`./encode-SjZrKIyU.cjs`);let __cloudflare_actors=require(`@cloudflare/actors`);function storeAttachment(e,f){e.serializeAttachment(f)}function encodeFrame(f){return require_encode.n(f)}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,f){super(`Failed to persist key "${e}": ${f.message}`),this.name=`PersistError`,this.originalCause=f}};function safeSerialize(e){let f=new WeakSet;return JSON.stringify(e,(e,p)=>{if(p instanceof Date)return{__type:`Date`,value:p.toISOString()};if(p instanceof RegExp)return{__type:`RegExp`,source:p.source,flags:p.flags};if(p instanceof Map)return{__type:`Map`,entries:Array.from(p.entries())};if(p instanceof Set)return{__type:`Set`,values:Array.from(p.values())};if(p instanceof Error)return{__type:`Error`,name:p.name,message:p.message};if(typeof p==`object`&&p){if(f.has(p))return;f.add(p)}if(!(typeof p==`function`||typeof p==`symbol`))return p})}function safeDeserialize(e){return JSON.parse(e,(e,f)=>{if(f&&typeof f==`object`&&f.__type)switch(f.__type){case`Date`:return new Date(f.value);case`RegExp`:return new RegExp(f.source,f.flags);case`Map`:return new Map(f.entries);case`Set`:return new Set(f.values);case`Error`:{let e=Error(f.message);return e.name=f.name,e}}return f})}function createShallowProxy(e,f,p){return new Proxy(e,{set(e,p,m){let h=Reflect.set(e,p,m);return h&&typeof p==`string`&&f(p,m),h},deleteProperty(e,f){let m=Reflect.deleteProperty(e,f);return m&&typeof f==`string`&&p(f),m}})}async function initializePersistedState(e,f,p=[],m={}){let{shallow:v=!0,throwOnError:b=!0}=m,x=e.ctx.storage,S=p.length>0?p.map(String):Object.keys(f),C={...f};for(let e of S)try{let f=await x.get(`_verani_persist:${e}`);if(f!==void 0)try{C[e]=safeDeserialize(f)}catch(f){if(b)throw new PersistError(e,f)}}catch(f){if(b&&!(f instanceof PersistError))throw new PersistError(e,f)}let w=async(f,p)=>{if(S.includes(f))try{let e=safeSerialize(p);await x.put(`_verani_persist:${f}`,e)}catch(p){let m=e[PERSIST_ERROR_HANDLER];if(m&&m(f,p),b)throw new PersistError(f,p)}},T=async f=>{if(S.includes(f))try{await x.delete(`_verani_persist:${f}`)}catch(p){let m=e[PERSIST_ERROR_HANDLER];if(m&&m(f,p),b)throw new PersistError(f,p)}},E;return E=v?createShallowProxy(C,(e,f)=>{w(String(e),f)},e=>{T(String(e))}):createDeepProxy(C,(e,f)=>{w(e,f)},e=>{T(e)},S),e[STATE_READY]=!0,e[PERSISTED_STATE]=E,E}function createDeepProxy(e,f,p,m,h,g){return new Proxy(e,{get(_,v){let y=Reflect.get(_,v);if(y&&typeof y==`object`&&!Array.isArray(y)){let _=g??String(v);if(m.includes(_)||g!==void 0)return createDeepProxy(y,f,p,m,h??e,_)}return y},set(e,p,_){let v=Reflect.set(e,p,_);if(v){let e=g??String(p);m.includes(e)&&(g&&h?f(g,h[g]):f(String(p),_))}return v},deleteProperty(e,_){let v=Reflect.deleteProperty(e,_);if(v){let e=g??String(_);m.includes(e)&&(g&&h?f(g,h[g]):p(String(_)))}return v}})}function isStateReady(e){return e[STATE_READY]===!0}function getPersistedState(e){if(!isStateReady(e))throw new PersistNotReadyError(`state`);return e[PERSISTED_STATE]}function setPersistErrorHandler(e,f){e[PERSIST_ERROR_HANDLER]=f}const setPeristErrorHandler=setPersistErrorHandler;async function persistKey(e,f,p){let m=safeSerialize(p);await e.ctx.storage.put(`_verani_persist:${f}`,m)}async function deletePersistedKey(e,f){await e.ctx.storage.delete(`_verani_persist:${f}`)}async function getPersistedKeys(e){let f=await e.ctx.storage.list({prefix:`_verani_persist:`});return Array.from(f.keys()).map(e=>e.replace(`_verani_persist:`,``))}async function clearPersistedState(e){let f=await e.ctx.storage.list({prefix:`_verani_persist:`}),p=Array.from(f.keys());await e.ctx.storage.delete(p)}const WS=Symbol(`WS`),META=Symbol(`META`),ROOMS=Symbol(`ROOMS`);function createConnectionHandler(e){let h=e.name||`VeraniConnectionDO`,v=e.websocketPath||`/ws`;class y extends __cloudflare_actors.Actor{constructor(...f){super(...f),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:v}}}getRoomBinding(f){if(!e.rooms)throw Error(`Cannot access room "${f}": no "rooms" config provided in ConnectionDefinition. Add rooms: { "${f}": "YourRoomBinding" } to your definition.`);let p=e.rooms[f];if(!p)throw Error(`Cannot access room "${f}": not found in "rooms" config. Available rooms: ${Object.keys(e.rooms).join(`, `)}`);let m=this.env[p];if(!m)throw Error(`Room "${f}" binding "${p}" not found in environment. Check your wrangler.toml durable_objects bindings.`);return m}getConnectionBinding(){if(!e.connectionBinding)throw Error(`Cannot resolve ConnectionDO: no "connectionBinding" provided in ConnectionDefinition. Add connectionBinding: "YourConnectionBinding" to your definition.`);let f=this.env[e.connectionBinding];if(!f)throw Error(`ConnectionDO binding "${e.connectionBinding}" not found in environment. Check your wrangler.toml durable_objects bindings.`);return f}createEmit(){let e=this;return{emit(f,p){e.sendToWebSocket(f,p)},to(f){return f.startsWith(`room:`)?e.createRoomEmitBuilder(f.slice(5)):e.createUserEmitBuilder(f)},toRoom(f){return e.createRoomEmitBuilder(f)},toUser(f){return e.createUserEmitBuilder(f)}}}createRoomEmitBuilder(e){let f=this;return{async emit(p,m){try{let h=f.getRoomBinding(e).get(e),g={exceptUserId:f[META]?.userId};return await h.broadcast(p,m,g)}catch{return 0}}}}createUserEmitBuilder(e){let f=this;return{async emit(p,m){try{return await f.getConnectionBinding().get(e).deliverMessage(p,m)?1:0}catch{return 0}}}}sendToWebSocket(e,f){let p=this[WS];if(!p||p.readyState!==WebSocket.OPEN)return!1;try{let h={type:`event`,channel:`default`,data:{type:e,...f}};return p.send(encodeFrame(h)),!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 f=await this.ctx.storage.get(`_connection_rooms`);if(f&&Array.isArray(f)&&(this[ROOMS]=new Map(f.map(e=>[e.roomName,e.metadata]))),e.handlers)for(let[f,p]of e.handlers.entries())this.handlers.set(f,p);let p=this.ctx.getWebSockets();if(p.length>0){let f=p[0];if(f.readyState===WebSocket.OPEN){this[WS]=f;let p=f.deserializeAttachment();p&&(this[META]=p,await this.rejoinRoomsAfterHibernation(),e.onHibernationRestore&&await e.onHibernationRestore(this))}}}shouldUpgradeSocket(e){return!0}onWebSocketUpgrade(e){let f=new WebSocketPair,[p,m]=Object.values(f);this.ctx.acceptWebSocket(m);let h=new Response(null,{status:101,webSocket:p});return Promise.resolve().then(()=>{this.onWebSocketConnect(m,e)}),h}async onWebSocketConnect(f,m){this[WS]&&this[WS].readyState===WebSocket.OPEN&&this[WS].close(1e3,`New connection established`);let h;if(h=e.extractMeta?await e.extractMeta(m):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(f,h),this[WS]=f,this[META]=h,e.onConnect)try{await e.onConnect(this.createContext())}catch(p){e.onError&&await e.onError(p,this.createContext()),f.close(1011,`Connection handler error`);return}}async onWebSocketMessage(f,p){if(this[META])try{let f=typeof p==`string`?p:p.toString(),m=JSON.parse(f),h=m,g=h.data,_=g?.type||h.type,v=this.handlers.get(_);if(v){await v(this.createContext(),g);return}e.onMessage&&await e.onMessage(this.createContext(),m)}catch(f){e.onError&&await e.onError(f,this.createContext())}}async onWebSocketDisconnect(f){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 deliverMessage(e,f){return this.sendToWebSocket(e,f)}async deliverSystemEvent(e,f){this.sendToWebSocket(`system:${e}`,f)}async getUserId(){return this[META]?.userId??null}async isConnected(){return this[WS]!==null&&this[WS].readyState===WebSocket.OPEN}async joinRoom(e,f){if(!this[META])throw Error(`Cannot join room: not connected`);await this.getRoomBinding(e).get(e).join(this[META].userId,f),this[ROOMS].set(e,f),await this.persistRooms()}async persistRooms(){let e=Array.from(this[ROOMS].entries()).map(([e,f])=>({roomName:e,metadata:f}));await this.ctx.storage.put(`_connection_rooms`,e)}async rejoinRoomsAfterHibernation(){if(this[ROOMS].size===0)return;let e=this[META]?.userId;if(!e)return;let f=[];for(let[p,m]of this[ROOMS].entries())try{await this.getRoomBinding(p).get(p).join(e,m)}catch{f.push(p)}if(f.length>0){for(let e of f)this[ROOMS].delete(e);await this.persistRooms()}}async leaveRoomInternal(e){this[META]&&await this.getRoomBinding(e).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,f){this.handlers.set(e,f)}off(e){this.handlers.delete(e)}async destroy(){for(let e of this[ROOMS].keys())try{await this.leaveRoomInternal(e)}catch{}this[WS]&&this[WS].readyState===WebSocket.OPEN&&this[WS].close(1e3,`Actor destroyed`),e.onDestroy&&this[META]&&await e.onDestroy(this.createContext()),await super.destroy()}}return Object.defineProperty(y,`name`,{value:h,writable:!1,configurable:!0}),y}function defineConnection(e){let f=new Map;return{...e,handlers:f,on(e,p){f.set(e,p)},off(e){f.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,`g`,{enumerable:!0,get:function(){return storeAttachment}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return setPersistErrorHandler}}),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 @@
1
+ import{n as encodeFrame$1}from"./encode-CFndA-BQ.mjs";import{Actor}from"@cloudflare/actors";function storeAttachment(e,f){e.serializeAttachment(f)}function encodeFrame(f){return encodeFrame$1(f)}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,f){super(`Failed to persist key "${e}": ${f.message}`),this.name=`PersistError`,this.originalCause=f}};function safeSerialize(e){let f=new WeakSet;return JSON.stringify(e,(e,p)=>{if(p instanceof Date)return{__type:`Date`,value:p.toISOString()};if(p instanceof RegExp)return{__type:`RegExp`,source:p.source,flags:p.flags};if(p instanceof Map)return{__type:`Map`,entries:Array.from(p.entries())};if(p instanceof Set)return{__type:`Set`,values:Array.from(p.values())};if(p instanceof Error)return{__type:`Error`,name:p.name,message:p.message};if(typeof p==`object`&&p){if(f.has(p))return;f.add(p)}if(!(typeof p==`function`||typeof p==`symbol`))return p})}function safeDeserialize(e){return JSON.parse(e,(e,f)=>{if(f&&typeof f==`object`&&f.__type)switch(f.__type){case`Date`:return new Date(f.value);case`RegExp`:return new RegExp(f.source,f.flags);case`Map`:return new Map(f.entries);case`Set`:return new Set(f.values);case`Error`:{let e=Error(f.message);return e.name=f.name,e}}return f})}function createShallowProxy(e,f,p){return new Proxy(e,{set(e,p,m){let h=Reflect.set(e,p,m);return h&&typeof p==`string`&&f(p,m),h},deleteProperty(e,f){let m=Reflect.deleteProperty(e,f);return m&&typeof f==`string`&&p(f),m}})}async function initializePersistedState(e,f,p=[],m={}){let{shallow:v=!0,throwOnError:b=!0}=m,x=e.ctx.storage,S=p.length>0?p.map(String):Object.keys(f),C={...f};for(let e of S)try{let f=await x.get(`_verani_persist:${e}`);if(f!==void 0)try{C[e]=safeDeserialize(f)}catch(f){if(b)throw new PersistError(e,f)}}catch(f){if(b&&!(f instanceof PersistError))throw new PersistError(e,f)}let w=async(f,p)=>{if(S.includes(f))try{let e=safeSerialize(p);await x.put(`_verani_persist:${f}`,e)}catch(p){let m=e[PERSIST_ERROR_HANDLER];if(m&&m(f,p),b)throw new PersistError(f,p)}},T=async f=>{if(S.includes(f))try{await x.delete(`_verani_persist:${f}`)}catch(p){let m=e[PERSIST_ERROR_HANDLER];if(m&&m(f,p),b)throw new PersistError(f,p)}},E;return E=v?createShallowProxy(C,(e,f)=>{w(String(e),f)},e=>{T(String(e))}):createDeepProxy(C,(e,f)=>{w(e,f)},e=>{T(e)},S),e[STATE_READY]=!0,e[PERSISTED_STATE]=E,E}function createDeepProxy(e,f,p,m,h,g){return new Proxy(e,{get(_,v){let y=Reflect.get(_,v);if(y&&typeof y==`object`&&!Array.isArray(y)){let _=g??String(v);if(m.includes(_)||g!==void 0)return createDeepProxy(y,f,p,m,h??e,_)}return y},set(e,p,_){let v=Reflect.set(e,p,_);if(v){let e=g??String(p);m.includes(e)&&(g&&h?f(g,h[g]):f(String(p),_))}return v},deleteProperty(e,_){let v=Reflect.deleteProperty(e,_);if(v){let e=g??String(_);m.includes(e)&&(g&&h?f(g,h[g]):p(String(_)))}return v}})}function isStateReady(e){return e[STATE_READY]===!0}function getPersistedState(e){if(!isStateReady(e))throw new PersistNotReadyError(`state`);return e[PERSISTED_STATE]}function setPersistErrorHandler(e,f){e[PERSIST_ERROR_HANDLER]=f}const setPeristErrorHandler=setPersistErrorHandler;async function persistKey(e,f,p){let m=safeSerialize(p);await e.ctx.storage.put(`_verani_persist:${f}`,m)}async function deletePersistedKey(e,f){await e.ctx.storage.delete(`_verani_persist:${f}`)}async function getPersistedKeys(e){let f=await e.ctx.storage.list({prefix:`_verani_persist:`});return Array.from(f.keys()).map(e=>e.replace(`_verani_persist:`,``))}async function clearPersistedState(e){let f=await e.ctx.storage.list({prefix:`_verani_persist:`}),p=Array.from(f.keys());await e.ctx.storage.delete(p)}const WS=Symbol(`WS`),META=Symbol(`META`),ROOMS=Symbol(`ROOMS`);function createConnectionHandler(e){let h=e.name||`VeraniConnectionDO`,v=e.websocketPath||`/ws`;class y extends Actor{constructor(...f){super(...f),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:v}}}getRoomBinding(f){if(!e.rooms)throw Error(`Cannot access room "${f}": no "rooms" config provided in ConnectionDefinition. Add rooms: { "${f}": "YourRoomBinding" } to your definition.`);let p=e.rooms[f];if(!p)throw Error(`Cannot access room "${f}": not found in "rooms" config. Available rooms: ${Object.keys(e.rooms).join(`, `)}`);let m=this.env[p];if(!m)throw Error(`Room "${f}" binding "${p}" not found in environment. Check your wrangler.toml durable_objects bindings.`);return m}getConnectionBinding(){if(!e.connectionBinding)throw Error(`Cannot resolve ConnectionDO: no "connectionBinding" provided in ConnectionDefinition. Add connectionBinding: "YourConnectionBinding" to your definition.`);let f=this.env[e.connectionBinding];if(!f)throw Error(`ConnectionDO binding "${e.connectionBinding}" not found in environment. Check your wrangler.toml durable_objects bindings.`);return f}createEmit(){let e=this;return{emit(f,p){e.sendToWebSocket(f,p)},to(f){return f.startsWith(`room:`)?e.createRoomEmitBuilder(f.slice(5)):e.createUserEmitBuilder(f)},toRoom(f){return e.createRoomEmitBuilder(f)},toUser(f){return e.createUserEmitBuilder(f)}}}createRoomEmitBuilder(e){let f=this;return{async emit(p,m){try{let h=f.getRoomBinding(e).get(e),g={exceptUserId:f[META]?.userId};return await h.broadcast(p,m,g)}catch{return 0}}}}createUserEmitBuilder(e){let f=this;return{async emit(p,m){try{return await f.getConnectionBinding().get(e).deliverMessage(p,m)?1:0}catch{return 0}}}}sendToWebSocket(e,f){let p=this[WS];if(!p||p.readyState!==WebSocket.OPEN)return!1;try{let h={type:`event`,channel:`default`,data:{type:e,...f}};return p.send(encodeFrame(h)),!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 f=await this.ctx.storage.get(`_connection_rooms`);if(f&&Array.isArray(f)&&(this[ROOMS]=new Map(f.map(e=>[e.roomName,e.metadata]))),e.handlers)for(let[f,p]of e.handlers.entries())this.handlers.set(f,p);let p=this.ctx.getWebSockets();if(p.length>0){let f=p[0];if(f.readyState===WebSocket.OPEN){this[WS]=f;let p=f.deserializeAttachment();p&&(this[META]=p,await this.rejoinRoomsAfterHibernation(),e.onHibernationRestore&&await e.onHibernationRestore(this))}}}shouldUpgradeSocket(e){return!0}onWebSocketUpgrade(e){let f=new WebSocketPair,[p,m]=Object.values(f);this.ctx.acceptWebSocket(m);let h=new Response(null,{status:101,webSocket:p});return Promise.resolve().then(()=>{this.onWebSocketConnect(m,e)}),h}async onWebSocketConnect(f,m){this[WS]&&this[WS].readyState===WebSocket.OPEN&&this[WS].close(1e3,`New connection established`);let h;if(h=e.extractMeta?await e.extractMeta(m):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(f,h),this[WS]=f,this[META]=h,e.onConnect)try{await e.onConnect(this.createContext())}catch(p){e.onError&&await e.onError(p,this.createContext()),f.close(1011,`Connection handler error`);return}}async onWebSocketMessage(f,p){if(this[META])try{let f=typeof p==`string`?p:p.toString(),m=JSON.parse(f),h=m,g=h.data,_=g?.type||h.type,v=this.handlers.get(_);if(v){await v(this.createContext(),g);return}e.onMessage&&await e.onMessage(this.createContext(),m)}catch(f){e.onError&&await e.onError(f,this.createContext())}}async onWebSocketDisconnect(f){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 deliverMessage(e,f){return this.sendToWebSocket(e,f)}async deliverSystemEvent(e,f){this.sendToWebSocket(`system:${e}`,f)}async getUserId(){return this[META]?.userId??null}async isConnected(){return this[WS]!==null&&this[WS].readyState===WebSocket.OPEN}async joinRoom(e,f){if(!this[META])throw Error(`Cannot join room: not connected`);await this.getRoomBinding(e).get(e).join(this[META].userId,f),this[ROOMS].set(e,f),await this.persistRooms()}async persistRooms(){let e=Array.from(this[ROOMS].entries()).map(([e,f])=>({roomName:e,metadata:f}));await this.ctx.storage.put(`_connection_rooms`,e)}async rejoinRoomsAfterHibernation(){if(this[ROOMS].size===0)return;let e=this[META]?.userId;if(!e)return;let f=[];for(let[p,m]of this[ROOMS].entries())try{await this.getRoomBinding(p).get(p).join(e,m)}catch{f.push(p)}if(f.length>0){for(let e of f)this[ROOMS].delete(e);await this.persistRooms()}}async leaveRoomInternal(e){this[META]&&await this.getRoomBinding(e).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,f){this.handlers.set(e,f)}off(e){this.handlers.delete(e)}async destroy(){for(let e of this[ROOMS].keys())try{await this.leaveRoomInternal(e)}catch{}this[WS]&&this[WS].readyState===WebSocket.OPEN&&this[WS].close(1e3,`Actor destroyed`),e.onDestroy&&this[META]&&await e.onDestroy(this.createContext()),await super.destroy()}}return Object.defineProperty(y,`name`,{value:h,writable:!1,configurable:!0}),y}function defineConnection(e){let f=new Map;return{...e,handlers:f,on(e,p){f.set(e,p)},off(e){f.delete(e)}}}export{clearPersistedState as a,getPersistedState as c,persistKey as d,safeDeserialize as f,storeAttachment as g,setPersistErrorHandler 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,400 @@
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
+ * Environment binding key for the ConnectionDO class.
53
+ * Must match the binding name in wrangler.toml/wrangler.jsonc.
54
+ * Required for broadcast message delivery to work.
55
+ *
56
+ * @example "UserConnection"
57
+ */
58
+ connectionBinding?: string;
59
+ /**
60
+ * Maximum consecutive delivery failures before a member is considered stale
61
+ * and automatically removed from the room. Default: 3
62
+ */
63
+ maxDeliveryFailures?: number;
64
+ /**
65
+ * Called when the RoomDO initializes or wakes from hibernation
66
+ */
67
+ onInit?(roomState: Record<string, unknown>): void | Promise<void>;
68
+ /**
69
+ * Called when a user joins this room
70
+ */
71
+ onJoin?(roomState: Record<string, unknown>, userId: string, metadata?: Record<string, unknown>): void | Promise<void>;
72
+ /**
73
+ * Called when a user leaves this room
74
+ */
75
+ onLeave?(roomState: Record<string, unknown>, userId: string): void | Promise<void>;
76
+ /**
77
+ * Called before the actor is destroyed and all storage is cleared.
78
+ * Use for cleanup logic (e.g., notifying other services).
79
+ */
80
+ onDestroy?(roomState: Record<string, unknown>): void | Promise<void>;
81
+ }
82
+ /**
83
+ * Stub interface for ConnectionDO - supports RPC calls from other DOs
84
+ *
85
+ * ConnectionDOs own a single WebSocket connection and receive messages
86
+ * from RoomDOs via RPC for delivery to their connected client.
87
+ */
88
+ interface ConnectionActorStub {
89
+ /**
90
+ * Standard fetch method for handling HTTP requests and WebSocket upgrades
91
+ */
92
+ fetch(request: Request): Promise<Response>;
93
+ /**
94
+ * Deliver a message to this connection's WebSocket (called via RPC from RoomDO)
95
+ * @param event - Event name
96
+ * @param data - Event data
97
+ * @returns Promise resolving to true if delivered, false if connection is closed
98
+ */
99
+ deliverMessage<TData = unknown>(event: string, data?: TData): Promise<boolean>;
100
+ /**
101
+ * Deliver a system event to this connection (presence updates, room events, etc.)
102
+ * @param type - System event type
103
+ * @param payload - Event payload
104
+ */
105
+ deliverSystemEvent<TPayload = unknown>(type: string, payload?: TPayload): Promise<void>;
106
+ /**
107
+ * Get the userId this connection belongs to
108
+ */
109
+ getUserId(): Promise<string | null>;
110
+ /**
111
+ * Check if this connection is still active
112
+ */
113
+ isConnected(): Promise<boolean>;
114
+ /**
115
+ * Join a room (registers with the RoomDO)
116
+ * @param roomName - Name of the room to join
117
+ * @param metadata - Optional metadata to include with membership
118
+ */
119
+ joinRoom(roomName: string, metadata?: Record<string, unknown>): Promise<void>;
120
+ /**
121
+ * Leave a room (unregisters from the RoomDO)
122
+ * @param roomName - Name of the room to leave
123
+ */
124
+ leaveRoom(roomName: string): Promise<void>;
125
+ /**
126
+ * Get list of rooms this connection is a member of
127
+ */
128
+ getRooms(): Promise<string[]>;
129
+ }
130
+ /**
131
+ * Environment type with Durable Object bindings for Verani.
132
+ * Users should extend this with their actual binding names from wrangler.toml.
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * interface Env extends VeraniEnv {
137
+ * UserConnection: DurableObjectNamespace;
138
+ * PresenceRoom: DurableObjectNamespace;
139
+ * ChatRoom: DurableObjectNamespace;
140
+ * }
141
+ * ```
142
+ */
143
+ interface VeraniEnv {
144
+ [key: string]: unknown;
145
+ }
146
+ /**
147
+ * ConnectionActor interface for single-WebSocket-per-DO model
148
+ *
149
+ * Each ConnectionActor owns exactly one WebSocket connection for a single user.
150
+ */
151
+ interface ConnectionActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> extends Actor<E> {
152
+ /**
153
+ * The single WebSocket connection owned by this DO (null if not connected)
154
+ */
155
+ ws: WebSocket | null;
156
+ /**
157
+ * Connection metadata for this user
158
+ */
159
+ meta: TMeta | null;
160
+ /**
161
+ * Set of room names this connection is a member of
162
+ */
163
+ rooms: Set<string>;
164
+ /**
165
+ * User-defined persisted state for this connection actor
166
+ */
167
+ connectionState: TState;
168
+ /**
169
+ * Check if this connection has an active WebSocket
170
+ */
171
+ isConnected(): boolean;
172
+ /**
173
+ * Deliver a message to this connection's WebSocket
174
+ * Called via RPC from RoomDO
175
+ */
176
+ deliverMessage<TData = unknown>(event: string, data?: TData): Promise<boolean>;
177
+ /**
178
+ * Deliver a system event (presence, room events, etc.)
179
+ */
180
+ deliverSystemEvent<TPayload = unknown>(type: string, payload?: TPayload): Promise<void>;
181
+ /**
182
+ * Join a room (register with RoomDO)
183
+ */
184
+ joinRoom(roomName: string, metadata?: Record<string, unknown>): Promise<void>;
185
+ /**
186
+ * Leave a room (unregister from RoomDO)
187
+ */
188
+ leaveRoom(roomName: string): Promise<void>;
189
+ /**
190
+ * Socket.io-like emit API for this connection
191
+ */
192
+ emit: ConnectionEmit<TMeta, E>;
193
+ /**
194
+ * Access the Durable Object storage API
195
+ */
196
+ getStorage(): DurableObjectStorage;
197
+ }
198
+ /**
199
+ * Connection-level emit API (for single-connection DO)
200
+ * Routes to appropriate RoomDO or ConnectionDO via RPC
201
+ */
202
+ interface ConnectionEmit<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
203
+ /**
204
+ * Emit to this connection's WebSocket
205
+ * @param event - Event name
206
+ * @param data - Event data
207
+ */
208
+ emit<TData = unknown>(event: string, data?: TData): void;
209
+ /**
210
+ * Target a specific room or user for emitting
211
+ * @param target - Room name (if starts with "room:") or userId
212
+ * @returns Builder for emitting to the target via RPC
213
+ */
214
+ to(target: string): AsyncEmitBuilder;
215
+ /**
216
+ * Target a specific room for broadcasting
217
+ * @param roomName - Room name
218
+ * @returns Builder for broadcasting to the room via RPC
219
+ */
220
+ toRoom(roomName: string): AsyncEmitBuilder;
221
+ /**
222
+ * Target a specific user for direct messaging
223
+ * @param userId - User ID
224
+ * @returns Builder for sending to the user via RPC
225
+ */
226
+ toUser(userId: string): AsyncEmitBuilder;
227
+ }
228
+ /**
229
+ * Async emit builder for RPC-based emit operations
230
+ */
231
+ interface AsyncEmitBuilder {
232
+ /**
233
+ * Emit to the targeted scope via RPC
234
+ * @param event - Event name
235
+ * @param data - Event data
236
+ * @returns Promise resolving to number of recipients
237
+ */
238
+ emit<TData = unknown>(event: string, data?: TData): Promise<number>;
239
+ }
240
+ //#endregion
241
+ //#region src/actor/connection-actor.d.ts
242
+ /**
243
+ * Definition for creating a ConnectionDO (per-user connection handler)
244
+ */
245
+ interface ConnectionDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
246
+ /** Optional name for debugging */
247
+ name?: string;
248
+ /** WebSocket upgrade path (default: "/ws") */
249
+ websocketPath?: string;
250
+ /**
251
+ * Map of room names to their environment binding keys.
252
+ * Must match binding names in wrangler.toml/wrangler.jsonc.
253
+ * Required for room features (joinRoom, toRoom, etc.).
254
+ *
255
+ * @example { "presence": "PresenceRoom", "chat": "ChatRoom" }
256
+ */
257
+ rooms?: Record<string, string>;
258
+ /**
259
+ * Environment binding key for the ConnectionDO class.
260
+ * Must match the binding name in wrangler.toml/wrangler.jsonc.
261
+ * Required for user-to-user messaging (toUser).
262
+ *
263
+ * @example "UserConnection"
264
+ */
265
+ connectionBinding?: string;
266
+ /**
267
+ * Extract metadata from the connection request
268
+ */
269
+ extractMeta?(req: Request): TMeta | Promise<TMeta>;
270
+ /**
271
+ * Called when a WebSocket connection is established
272
+ */
273
+ onConnect?(ctx: ConnectionContext<TMeta, E, TState>): void | Promise<void>;
274
+ /**
275
+ * Called when the WebSocket connection is closed
276
+ */
277
+ onDisconnect?(ctx: ConnectionContext<TMeta, E, TState>): void | Promise<void>;
278
+ /**
279
+ * Called when a message is received from the WebSocket
280
+ */
281
+ onMessage?(ctx: ConnectionContext<TMeta, E, TState>, frame: unknown): void | Promise<void>;
282
+ /**
283
+ * Called when an error occurs
284
+ */
285
+ onError?(error: Error, ctx: ConnectionContext<TMeta, E, TState>): void | Promise<void>;
286
+ /**
287
+ * Called after waking from hibernation
288
+ */
289
+ onHibernationRestore?(actor: ConnectionHandlerInstance<TMeta, E, TState>): void | Promise<void>;
290
+ /**
291
+ * Event handlers map (socket.io-like)
292
+ */
293
+ handlers?: Map<string, (ctx: ConnectionContext<TMeta, E, TState>, data: unknown) => void | Promise<void>>;
294
+ /**
295
+ * Initial state for this connection
296
+ */
297
+ state?: TState;
298
+ /**
299
+ * Keys to persist to storage
300
+ */
301
+ persistedKeys?: (string & keyof TState)[];
302
+ /**
303
+ * Persistence options
304
+ */
305
+ persistOptions?: {
306
+ shallow?: boolean;
307
+ throwOnError?: boolean;
308
+ };
309
+ /**
310
+ * Called when persistence fails
311
+ */
312
+ onPersistError?(key: string, error: Error): void;
313
+ /**
314
+ * Called before the actor is destroyed and all storage is cleared.
315
+ * Use for cleanup (e.g., leaving rooms, notifying services).
316
+ */
317
+ onDestroy?(ctx: ConnectionContext<TMeta, E, TState>): void | Promise<void>;
318
+ }
319
+ /**
320
+ * Instance interface for ConnectionHandler actors
321
+ * Used for typing the actor parameter in lifecycle hooks
322
+ */
323
+ interface ConnectionHandlerInstance<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
324
+ connectionState: TState;
325
+ isStateReady(): boolean;
326
+ getStorage(): DurableObjectStorage;
327
+ joinRoom(roomName: string, metadata?: Record<string, unknown>): Promise<void>;
328
+ leaveRoom(roomName: string): Promise<void>;
329
+ getRooms(): Promise<string[]>;
330
+ }
331
+ /**
332
+ * Context provided to connection lifecycle hooks
333
+ */
334
+ interface ConnectionContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
335
+ /** The connection actor instance */
336
+ actor: ConnectionHandlerInstance<TMeta, E, TState>;
337
+ /** The WebSocket connection (may be null after disconnect) */
338
+ ws: WebSocket | null;
339
+ /** Connection metadata */
340
+ meta: TMeta;
341
+ /** Socket.io-like emit API */
342
+ emit: ConnectionEmit<TMeta, E>;
343
+ /** Persisted state */
344
+ state: TState;
345
+ }
346
+ /**
347
+ * Return type for createConnectionHandler
348
+ */
349
+ type ConnectionHandlerClass<E = unknown> = {
350
+ new (state: any, env: E): Actor<E>;
351
+ get(userId: string): ConnectionActorStub;
352
+ configuration(request?: Request): ActorConfiguration;
353
+ };
354
+ /**
355
+ * Creates a Connection handler (per-user Durable Object)
356
+ *
357
+ * Each ConnectionDO owns exactly ONE WebSocket connection for a single user.
358
+ * Messages are received via RPC from RoomDOs and delivered to the WebSocket.
359
+ *
360
+ * @param definition - Connection definition with lifecycle hooks
361
+ * @returns ConnectionDO class for Cloudflare Workers
362
+ *
363
+ * @example
364
+ * ```typescript
365
+ * const UserConnection = createConnectionHandler({
366
+ * name: "user-connection",
367
+ * extractMeta: (req) => ({
368
+ * userId: extractUserIdFromToken(req),
369
+ * clientId: crypto.randomUUID(),
370
+ * channels: ["default"]
371
+ * }),
372
+ * onConnect: (ctx) => {
373
+ * console.log(`User ${ctx.meta.userId} connected`);
374
+ * ctx.actor.joinRoom("presence");
375
+ * },
376
+ * onDisconnect: (ctx) => {
377
+ * console.log(`User ${ctx.meta.userId} disconnected`);
378
+ * }
379
+ * });
380
+ *
381
+ * // In Worker:
382
+ * const userId = extractUserId(request);
383
+ * const stub = UserConnection.get(userId);
384
+ * return stub.fetch(request);
385
+ * ```
386
+ */
387
+ declare function createConnectionHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>>(definition: ConnectionDefinition<TMeta, E, TState>): ConnectionHandlerClass<E>;
388
+ /**
389
+ * Helper to define a connection with socket.io-like event handlers
390
+ */
391
+ interface ConnectionDefinitionWithHandlers<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> extends ConnectionDefinition<TMeta, E, TState> {
392
+ on<TData = unknown>(event: string, handler: (ctx: ConnectionContext<TMeta, E, TState>, data: TData) => void | Promise<void>): void;
393
+ off(event: string): void;
394
+ }
395
+ /**
396
+ * Define a connection with socket.io-like convenience methods
397
+ */
398
+ 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>;
399
+ //#endregion
400
+ 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 };