verani 0.12.4 → 0.13.2
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/dist/connection-actor-CmhbORTK.cjs +1 -0
- package/dist/connection-actor-DnuN0Hm0.mjs +1 -0
- package/dist/typed.cjs +1 -1
- package/dist/typed.mjs +1 -1
- package/dist/verani.cjs +3 -3
- package/dist/verani.mjs +3 -3
- package/package.json +6 -3
- package/dist/connection-actor-B4dOVR_6.cjs +0 -1
- package/dist/connection-actor-CzDt7vFj.mjs +0 -1
|
@@ -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._initPromise=null,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]??e.rooms[f.split(`:`,1)[0]];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,p){return e.createRoomEmitBuilder(f,p)},toUser(f){return e.createUserEmitBuilder(f)}}}createRoomEmitBuilder(e,f){let p=this;return{async emit(m,h){let g=p.getRoomBinding(e),_=g.get(g.idFromName(e)),v=f?.includeSelf||!p[META]?.userId?void 0:{exceptUserId:p[META].userId};return await _.broadcast(m,h,v)}}}createUserEmitBuilder(e){let f=this;return{async emit(p,m){let h=f.getConnectionBinding();return await h.get(h.idFromName(e)).deliverMessage(p,m)?1:0}}}restoreWebSocketIfNeeded(){if(this[WS])return;let f=this.ctx.getWebSockets();if(f.length>0&&f[0].readyState===WebSocket.OPEN){this[WS]=f[0];let e=f[0].deserializeAttachment();e&&(this[META]=e)}if(e.handlers&&this.handlers.size===0)for(let[f,p]of e.handlers.entries())this.handlers.set(f,p)}async restoreFullStateIfNeeded(){if(this._initPromise){await this._initPromise;return}if(this.restoreWebSocketIfNeeded(),this[ROOMS].size===0){let e=await this.ctx.storage.get(`_connection_rooms`);e&&Array.isArray(e)&&(this[ROOMS]=new Map(e.map(e=>[e.roomName,e.metadata])))}e.state&&!this[STATE_READY]&&(e.onPersistError&&setPeristErrorHandler(this,e.onPersistError),this[PERSISTED_STATE]=await initializePersistedState(this,e.state,e.persistedKeys,e.persistOptions))}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(){if(!this[META])throw Error(`Cannot create context: connection metadata not available`);return{actor:this,ws:this[WS],meta:this[META],emit:this.createEmit(),state:this.connectionState}}async onInit(){this._initPromise=this._doInit(),await this._initPromise}async _doInit(){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 e=p[0];if(e.readyState===WebSocket.OPEN){this[WS]=e;let f=e.deserializeAttachment();f&&(this[META]=f)}}this[META]&&this[ROOMS].size>0&&await this.rejoinRoomsAfterHibernation(),this[META]&&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]||await this.restoreFullStateIfNeeded(),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.restoreWebSocketIfNeeded(),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.restoreWebSocketIfNeeded(),this.sendToWebSocket(e,f)}async deliverSystemEvent(e,f){this.restoreWebSocketIfNeeded(),this.sendToWebSocket(`system:${e}`,f)}async getUserId(){return this.restoreWebSocketIfNeeded(),this[META]?.userId??null}async isConnected(){return this.restoreWebSocketIfNeeded(),this[WS]!==null&&this[WS].readyState===WebSocket.OPEN}async joinRoom(e,f){if(!this[META])throw Error(`Cannot join room: not connected`);let p=this.getRoomBinding(e);await p.get(p.idFromName(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{let f=this.getRoomBinding(p);await f.get(f.idFromName(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){if(!this[META])return;let f=this.getRoomBinding(e);await f.get(f.idFromName(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._initPromise=null,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]??e.rooms[f.split(`:`,1)[0]];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,p){return e.createRoomEmitBuilder(f,p)},toUser(f){return e.createUserEmitBuilder(f)}}}createRoomEmitBuilder(e,f){let p=this;return{async emit(m,h){let g=p.getRoomBinding(e),_=g.get(g.idFromName(e)),v=f?.includeSelf||!p[META]?.userId?void 0:{exceptUserId:p[META].userId};return await _.broadcast(m,h,v)}}}createUserEmitBuilder(e){let f=this;return{async emit(p,m){let h=f.getConnectionBinding();return await h.get(h.idFromName(e)).deliverMessage(p,m)?1:0}}}restoreWebSocketIfNeeded(){if(this[WS])return;let f=this.ctx.getWebSockets();if(f.length>0&&f[0].readyState===WebSocket.OPEN){this[WS]=f[0];let e=f[0].deserializeAttachment();e&&(this[META]=e)}if(e.handlers&&this.handlers.size===0)for(let[f,p]of e.handlers.entries())this.handlers.set(f,p)}async restoreFullStateIfNeeded(){if(this._initPromise){await this._initPromise;return}if(this.restoreWebSocketIfNeeded(),this[ROOMS].size===0){let e=await this.ctx.storage.get(`_connection_rooms`);e&&Array.isArray(e)&&(this[ROOMS]=new Map(e.map(e=>[e.roomName,e.metadata])))}e.state&&!this[STATE_READY]&&(e.onPersistError&&setPeristErrorHandler(this,e.onPersistError),this[PERSISTED_STATE]=await initializePersistedState(this,e.state,e.persistedKeys,e.persistOptions))}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(){if(!this[META])throw Error(`Cannot create context: connection metadata not available`);return{actor:this,ws:this[WS],meta:this[META],emit:this.createEmit(),state:this.connectionState}}async onInit(){this._initPromise=this._doInit(),await this._initPromise}async _doInit(){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 e=p[0];if(e.readyState===WebSocket.OPEN){this[WS]=e;let f=e.deserializeAttachment();f&&(this[META]=f)}}this[META]&&this[ROOMS].size>0&&await this.rejoinRoomsAfterHibernation(),this[META]&&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]||await this.restoreFullStateIfNeeded(),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.restoreWebSocketIfNeeded(),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.restoreWebSocketIfNeeded(),this.sendToWebSocket(e,f)}async deliverSystemEvent(e,f){this.restoreWebSocketIfNeeded(),this.sendToWebSocket(`system:${e}`,f)}async getUserId(){return this.restoreWebSocketIfNeeded(),this[META]?.userId??null}async isConnected(){return this.restoreWebSocketIfNeeded(),this[WS]!==null&&this[WS].readyState===WebSocket.OPEN}async joinRoom(e,f){if(!this[META])throw Error(`Cannot join room: not connected`);let p=this.getRoomBinding(e);await p.get(p.idFromName(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{let f=this.getRoomBinding(p);await f.get(f.idFromName(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){if(!this[META])return;let f=this.getRoomBinding(e);await f.get(f.idFromName(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};
|
package/dist/typed.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const require_connection_actor=require(`./connection-actor-
|
|
1
|
+
const require_connection_actor=require(`./connection-actor-CmhbORTK.cjs`);require(`./encode-SjZrKIyU.cjs`);const require_validation=require(`./validation-DvWPGkXK.cjs`);function createTypedConnectionEmit(e){let t=((t,n)=>{e.emit(t,n)});return t.to=t=>{let n=e.to(t);return{emit:(e,t)=>n.emit(e,t)}},t.toRoom=(t,n)=>{let r=e.toRoom(t,n);return{emit:(e,t)=>r.emit(e,t)}},t.toUser=t=>{let n=e.toUser(t);return{emit:(e,t)=>n.emit(e,t)}},t}function wrapContext(e){return{actor:e.actor,ws:e.ws,meta:e.meta,emit:createTypedConnectionEmit(e.emit),state:e.state}}function createTypedConnection(n,i){let a=require_connection_actor.n({name:i.name,websocketPath:i.websocketPath??`/ws`,rooms:i.rooms,connectionBinding:i.connectionBinding,extractMeta:i.extractMeta,state:i.state,persistedKeys:i.persistedKeys,onConnect:i.onConnect?e=>i.onConnect(wrapContext(e)):void 0,onDisconnect:i.onDisconnect?e=>i.onDisconnect(wrapContext(e)):void 0,onError:i.onError?(e,t)=>i.onError(e,wrapContext(t)):void 0,onHibernationRestore:i.onHibernationRestore}),o=require_validation.o(n),s=o?require_validation.a(n):void 0;return{on(e,i){a.on(e,(a,c)=>{let l={...wrapContext(a),frame:{type:e,data:c}};if(o){let r=require_validation.r(n,e);if(r){let n=require_validation.s(r,c,e,`client`,s);return n===void 0?void 0:i(l,n)}}return i(l,c)})},off(e,t){a.off(e)},get definition(){return a},contract:n}}exports.createConnectionHandler=require_connection_actor.t,exports.createTypedConnection=createTypedConnection,exports.createTypedRoom=createTypedConnection,exports.createValidatedHandler=require_validation.t,exports.defineContract=require_validation.l,exports.getClientValidator=require_validation.r,exports.getValidationErrorHandler=require_validation.a,exports.isContract=require_validation.u,exports.isValidatedContract=require_validation.o,exports.payload=require_validation.d,exports.validateData=require_validation.s,exports.withValidation=require_validation.c;
|
package/dist/typed.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as defineConnection,t as createConnectionHandler}from"./connection-actor-
|
|
1
|
+
import{n as defineConnection,t as createConnectionHandler}from"./connection-actor-DnuN0Hm0.mjs";import"./encode-CFndA-BQ.mjs";import{a as getValidationErrorHandler,c as withValidation,d as payload,l as defineContract,o as isValidatedContract,r as getClientValidator,s as validateData,t as createValidatedHandler,u as isContract}from"./validation-NB7a2Sf1.mjs";function createTypedConnectionEmit(e){let s=((s,c)=>{e.emit(s,c)});return s.to=s=>{let c=e.to(s);return{emit:(e,s)=>c.emit(e,s)}},s.toRoom=(s,c)=>{let l=e.toRoom(s,c);return{emit:(e,s)=>l.emit(e,s)}},s.toUser=s=>{let c=e.toUser(s);return{emit:(e,s)=>c.emit(e,s)}},s}function wrapContext(e){return{actor:e.actor,ws:e.ws,meta:e.meta,emit:createTypedConnectionEmit(e.emit),state:e.state}}function createTypedConnection(s,l){let u=defineConnection({name:l.name,websocketPath:l.websocketPath??`/ws`,rooms:l.rooms,connectionBinding:l.connectionBinding,extractMeta:l.extractMeta,state:l.state,persistedKeys:l.persistedKeys,onConnect:l.onConnect?e=>l.onConnect(wrapContext(e)):void 0,onDisconnect:l.onDisconnect?e=>l.onDisconnect(wrapContext(e)):void 0,onError:l.onError?(e,s)=>l.onError(e,wrapContext(s)):void 0,onHibernationRestore:l.onHibernationRestore}),d=isValidatedContract(s),p=d?getValidationErrorHandler(s):void 0;return{on(e,c){u.on(e,(l,u)=>{let f={...wrapContext(l),frame:{type:e,data:u}};if(d){let l=getClientValidator(s,e);if(l){let s=validateData(l,u,e,`client`,p);return s===void 0?void 0:c(f,s)}}return c(f,u)})},off(e,s){u.off(e)},get definition(){return u},contract:s}}export{createConnectionHandler,createTypedConnection,createTypedConnection as createTypedRoom,createValidatedHandler,defineContract,getClientValidator,getValidationErrorHandler,isContract,isValidatedContract,payload,validateData,withValidation};
|
package/dist/verani.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_connection_actor=require(`./connection-actor-
|
|
1
|
+
const require_connection_actor=require(`./connection-actor-CmhbORTK.cjs`),require_encode=require(`./encode-SjZrKIyU.cjs`),require_types=require(`./types-DUO6RVw1.cjs`);let __cloudflare_actors=require(`@cloudflare/actors`);const MEMBERS=Symbol(`MEMBERS`),ROOM_NAME=Symbol(`ROOM_NAME`),DELIVERY_FAILURES=Symbol(`DELIVERY_FAILURES`),INITIALIZED=Symbol(`INITIALIZED`);function createRoomHandler(e={}){let n=e.name||`VeraniRoomDO`;class r extends __cloudflare_actors.Actor{constructor(...e){super(...e),this[MEMBERS]=new Map,this[ROOM_NAME]=``,this[DELIVERY_FAILURES]=new Map,this[INITIALIZED]=!1,this.roomState={}}getConnectionBinding(){if(!e.connectionBinding)throw Error(`Cannot resolve ConnectionDO: no "connectionBinding" provided in RoomCoordinatorDefinition. Add connectionBinding: "YourConnectionBinding" to your definition.`);let n=this.env[e.connectionBinding];if(!n)throw Error(`ConnectionDO binding "${e.connectionBinding}" not found in environment. Check your wrangler.toml durable_objects bindings.`);return n}async onInit(){await this.ensureReady(),e.onInit&&await e.onInit(this.roomState)}async ensureReady(){this[INITIALIZED]||(this[INITIALIZED]=!0,this.ensureTables(),await this.migrateFromKV(),this.loadMembers(),this.loadRoomState())}ensureTables(){this.sql`CREATE TABLE IF NOT EXISTS room_members (
|
|
2
2
|
user_id TEXT PRIMARY KEY,
|
|
3
3
|
joined_at INTEGER NOT NULL,
|
|
4
4
|
metadata TEXT
|
|
@@ -7,5 +7,5 @@ const require_connection_actor=require(`./connection-actor-B4dOVR_6.cjs`),requir
|
|
|
7
7
|
value TEXT
|
|
8
8
|
)`}async migrateFromKV(){let e=this.ctx.storage,n=await e.list({prefix:`_room_member:`});if(n.size>0){for(let[e,r]of n.entries()){let n=e.replace(`_room_member:`,``),i=r.metadata?JSON.stringify(r.metadata):null;this.sql`INSERT OR REPLACE INTO room_members (user_id, joined_at, metadata)
|
|
9
9
|
VALUES (${n}, ${r.joinedAt}, ${i})`}await e.delete(Array.from(n.keys()))}let r=await e.list({prefix:`_room_state:`});if(r.size>0){for(let[e,n]of r.entries()){let r=e.replace(`_room_state:`,``);this.sql`INSERT OR REPLACE INTO room_state (key, value)
|
|
10
|
-
VALUES (${r}, ${JSON.stringify(n)})`}await e.delete(Array.from(r.keys()))}}loadMembers(){this[MEMBERS].clear();let e=this.sql`SELECT user_id, joined_at, metadata FROM room_members`;for(let n of e)this[MEMBERS].set(n.user_id,{userId:n.user_id,joinedAt:n.joined_at,metadata:n.metadata?JSON.parse(n.metadata):void 0})}loadRoomState(){this.roomState={};let e=this.sql`SELECT key, value FROM room_state`;for(let n of e)this.roomState[n.key]=JSON.parse(n.value)}async join(n,r={}){this.ensureReady();let i=Date.now(),a={userId:n,joinedAt:i,metadata:r},o=Object.keys(r).length>0?JSON.stringify(r):null;this[MEMBERS].set(n,a),this.sql`INSERT OR REPLACE INTO room_members (user_id, joined_at, metadata)
|
|
11
|
-
VALUES (${n}, ${i}, ${o})`,e.onJoin&&await e.onJoin(this.roomState,n,r)}async leave(n){this.ensureReady(),this[MEMBERS].has(n)&&(this[MEMBERS].delete(n),this.sql`DELETE FROM room_members WHERE user_id = ${n}`,e.onLeave&&await e.onLeave(this.roomState,n))}async broadcast(n,r,i){this.ensureReady();let a=this.getConnectionBinding(),s=Array.from(this[MEMBERS].entries()).filter(([e])=>!(i?.userIds&&!i.userIds.includes(e)||i?.exceptUserId===e)),c=await Promise.all(s.map(async([e])=>{try{return await a.get(a.idFromName(e)).deliverMessage(n,r),{userId:e,ok:!0}}catch(n){return{userId:e,ok:!1,error:n}}})),l=e.maxDeliveryFailures??3,u=0,d=[];for(let e of c)if(e.ok)u++,this[DELIVERY_FAILURES].delete(e.userId);else{let n=(this[DELIVERY_FAILURES].get(e.userId)??0)+1;this[DELIVERY_FAILURES].set(e.userId,n),n>=l&&d.push(e.userId)}for(let e of d)this[DELIVERY_FAILURES].delete(e),await this.leave(e);return u}async getMembers(){return this.ensureReady(),Array.from(this[MEMBERS].values())}async getMemberCount(){return this.ensureReady(),this[MEMBERS].size}async hasMember(e){return this.ensureReady(),this[MEMBERS].has(e)}async updateMemberMetadata(e,n){this.ensureReady();let r=this[MEMBERS].get(e);if(!r)return;r.metadata={...r.metadata,...n},this[MEMBERS].set(e,r);let i=JSON.stringify(r.metadata);this.sql`UPDATE room_members SET metadata = ${i} WHERE user_id = ${e}`}async getRoomState(){return this.ensureReady(),{...this.roomState}}async setRoomState(e,n){this.ensureReady(),this.roomState[e]=n;let r=JSON.stringify(n);this.sql`INSERT OR REPLACE INTO room_state (key, value) VALUES (${e}, ${r})`}async destroy(){e.onDestroy&&await e.onDestroy(this.roomState),await super.destroy()}}return Object.defineProperty(r,`name`,{value:n,writable:!1,configurable:!0}),r}let _enabled=!1;function enableDebug(e){}exports.PROTOCOL_VERSION=require_types.t,exports.PersistError=require_connection_actor.r,exports.PersistNotReadyError=require_connection_actor.i,exports.clearPersistedState=require_connection_actor.a,exports.createConnectionHandler=require_connection_actor.t,exports.createRoomHandler=createRoomHandler,exports.decodeClientMessage=require_encode.i,exports.decodeFrame=require_encode.a,exports.decodeServerMessage=require_encode.o,exports.defineConnection=require_connection_actor.n,exports.deletePersistedKey=require_connection_actor.o,exports.enableDebug=enableDebug,exports.encodeClientMessage=require_encode.t,exports.encodeFrame=require_encode.n,exports.encodeServerMessage=require_encode.r,exports.getPersistedKeys=require_connection_actor.s,exports.getPersistedState=require_connection_actor.c,exports.initializePersistedState=require_connection_actor.l,exports.isStateReady=require_connection_actor.u,exports.persistKey=require_connection_actor.d,exports.safeDeserialize=require_connection_actor.f,exports.safeSerialize=require_connection_actor.p,exports.setPeristErrorHandler=require_connection_actor.m,exports.setPersistErrorHandler=require_connection_actor.h,exports.storeAttachment=require_connection_actor.g;
|
|
10
|
+
VALUES (${r}, ${JSON.stringify(n)})`}await e.delete(Array.from(r.keys()))}}loadMembers(){this[MEMBERS].clear();let e=this.sql`SELECT user_id, joined_at, metadata FROM room_members`;for(let n of e)this[MEMBERS].set(n.user_id,{userId:n.user_id,joinedAt:n.joined_at,metadata:n.metadata?JSON.parse(n.metadata):void 0})}loadRoomState(){this.roomState={};let e=this.sql`SELECT key, value FROM room_state`;for(let n of e)this.roomState[n.key]=JSON.parse(n.value)}async join(n,r={}){await this.ensureReady();let i=Date.now(),a={userId:n,joinedAt:i,metadata:r},o=Object.keys(r).length>0?JSON.stringify(r):null;this[MEMBERS].set(n,a),this.sql`INSERT OR REPLACE INTO room_members (user_id, joined_at, metadata)
|
|
11
|
+
VALUES (${n}, ${i}, ${o})`,e.onJoin&&await e.onJoin(this.roomState,n,r)}async leave(n){await this.ensureReady(),this[MEMBERS].has(n)&&(this[MEMBERS].delete(n),this.sql`DELETE FROM room_members WHERE user_id = ${n}`,e.onLeave&&await e.onLeave(this.roomState,n))}async broadcast(n,r,i){await this.ensureReady();let a=this.getConnectionBinding(),s=Array.from(this[MEMBERS].entries()).filter(([e])=>!(i?.userIds&&!i.userIds.includes(e)||i?.exceptUserId===e)),c=await Promise.all(s.map(async([e])=>{try{return await a.get(a.idFromName(e)).deliverMessage(n,r),{userId:e,ok:!0}}catch(n){return{userId:e,ok:!1,error:n}}})),l=e.maxDeliveryFailures??3,u=0,d=[];for(let e of c)if(e.ok)u++,this[DELIVERY_FAILURES].delete(e.userId);else{let n=(this[DELIVERY_FAILURES].get(e.userId)??0)+1;this[DELIVERY_FAILURES].set(e.userId,n),n>=l&&d.push(e.userId)}for(let e of d)this[DELIVERY_FAILURES].delete(e),await this.leave(e);return u}async getMembers(){return await this.ensureReady(),Array.from(this[MEMBERS].values())}async getMemberCount(){return await this.ensureReady(),this[MEMBERS].size}async hasMember(e){return await this.ensureReady(),this[MEMBERS].has(e)}async updateMemberMetadata(e,n){await this.ensureReady();let r=this[MEMBERS].get(e);if(!r)return;r.metadata={...r.metadata,...n},this[MEMBERS].set(e,r);let i=JSON.stringify(r.metadata);this.sql`UPDATE room_members SET metadata = ${i} WHERE user_id = ${e}`}async getRoomState(){return await this.ensureReady(),{...this.roomState}}async setRoomState(e,n){await this.ensureReady(),this.roomState[e]=n;let r=JSON.stringify(n);this.sql`INSERT OR REPLACE INTO room_state (key, value) VALUES (${e}, ${r})`}async destroy(){e.onDestroy&&await e.onDestroy(this.roomState),await super.destroy()}}return Object.defineProperty(r,`name`,{value:n,writable:!1,configurable:!0}),r}let _enabled=!1;function enableDebug(e){}exports.PROTOCOL_VERSION=require_types.t,exports.PersistError=require_connection_actor.r,exports.PersistNotReadyError=require_connection_actor.i,exports.clearPersistedState=require_connection_actor.a,exports.createConnectionHandler=require_connection_actor.t,exports.createRoomHandler=createRoomHandler,exports.decodeClientMessage=require_encode.i,exports.decodeFrame=require_encode.a,exports.decodeServerMessage=require_encode.o,exports.defineConnection=require_connection_actor.n,exports.deletePersistedKey=require_connection_actor.o,exports.enableDebug=enableDebug,exports.encodeClientMessage=require_encode.t,exports.encodeFrame=require_encode.n,exports.encodeServerMessage=require_encode.r,exports.getPersistedKeys=require_connection_actor.s,exports.getPersistedState=require_connection_actor.c,exports.initializePersistedState=require_connection_actor.l,exports.isStateReady=require_connection_actor.u,exports.persistKey=require_connection_actor.d,exports.safeDeserialize=require_connection_actor.f,exports.safeSerialize=require_connection_actor.p,exports.setPeristErrorHandler=require_connection_actor.m,exports.setPersistErrorHandler=require_connection_actor.h,exports.storeAttachment=require_connection_actor.g;
|
package/dist/verani.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as clearPersistedState,c as getPersistedState,d as persistKey,f as safeDeserialize,g as storeAttachment,h as setPersistErrorHandler,i as PersistNotReadyError,l as initializePersistedState,m as setPeristErrorHandler,n as defineConnection,o as deletePersistedKey,p as safeSerialize,r as PersistError,s as getPersistedKeys,t as createConnectionHandler,u as isStateReady}from"./connection-actor-
|
|
1
|
+
import{a as clearPersistedState,c as getPersistedState,d as persistKey,f as safeDeserialize,g as storeAttachment,h as setPersistErrorHandler,i as PersistNotReadyError,l as initializePersistedState,m as setPeristErrorHandler,n as defineConnection,o as deletePersistedKey,p as safeSerialize,r as PersistError,s as getPersistedKeys,t as createConnectionHandler,u as isStateReady}from"./connection-actor-DnuN0Hm0.mjs";import{a as decodeFrame,i as decodeClientMessage,n as encodeFrame,o as decodeServerMessage,r as encodeServerMessage,t as encodeClientMessage}from"./encode-CFndA-BQ.mjs";import{t as PROTOCOL_VERSION}from"./types-rZsftNPE.mjs";import{Actor}from"@cloudflare/actors";const MEMBERS=Symbol(`MEMBERS`),ROOM_NAME=Symbol(`ROOM_NAME`),DELIVERY_FAILURES=Symbol(`DELIVERY_FAILURES`),INITIALIZED=Symbol(`INITIALIZED`);function createRoomHandler(e={}){let x=e.name||`VeraniRoomDO`;class S extends Actor{constructor(...e){super(...e),this[MEMBERS]=new Map,this[ROOM_NAME]=``,this[DELIVERY_FAILURES]=new Map,this[INITIALIZED]=!1,this.roomState={}}getConnectionBinding(){if(!e.connectionBinding)throw Error(`Cannot resolve ConnectionDO: no "connectionBinding" provided in RoomCoordinatorDefinition. Add connectionBinding: "YourConnectionBinding" to your definition.`);let x=this.env[e.connectionBinding];if(!x)throw Error(`ConnectionDO binding "${e.connectionBinding}" not found in environment. Check your wrangler.toml durable_objects bindings.`);return x}async onInit(){await this.ensureReady(),e.onInit&&await e.onInit(this.roomState)}async ensureReady(){this[INITIALIZED]||(this[INITIALIZED]=!0,this.ensureTables(),await this.migrateFromKV(),this.loadMembers(),this.loadRoomState())}ensureTables(){this.sql`CREATE TABLE IF NOT EXISTS room_members (
|
|
2
2
|
user_id TEXT PRIMARY KEY,
|
|
3
3
|
joined_at INTEGER NOT NULL,
|
|
4
4
|
metadata TEXT
|
|
@@ -7,5 +7,5 @@ import{a as clearPersistedState,c as getPersistedState,d as persistKey,f as safe
|
|
|
7
7
|
value TEXT
|
|
8
8
|
)`}async migrateFromKV(){let e=this.ctx.storage,x=await e.list({prefix:`_room_member:`});if(x.size>0){for(let[e,S]of x.entries()){let x=e.replace(`_room_member:`,``),C=S.metadata?JSON.stringify(S.metadata):null;this.sql`INSERT OR REPLACE INTO room_members (user_id, joined_at, metadata)
|
|
9
9
|
VALUES (${x}, ${S.joinedAt}, ${C})`}await e.delete(Array.from(x.keys()))}let S=await e.list({prefix:`_room_state:`});if(S.size>0){for(let[e,x]of S.entries()){let S=e.replace(`_room_state:`,``);this.sql`INSERT OR REPLACE INTO room_state (key, value)
|
|
10
|
-
VALUES (${S}, ${JSON.stringify(x)})`}await e.delete(Array.from(S.keys()))}}loadMembers(){this[MEMBERS].clear();let e=this.sql`SELECT user_id, joined_at, metadata FROM room_members`;for(let x of e)this[MEMBERS].set(x.user_id,{userId:x.user_id,joinedAt:x.joined_at,metadata:x.metadata?JSON.parse(x.metadata):void 0})}loadRoomState(){this.roomState={};let e=this.sql`SELECT key, value FROM room_state`;for(let x of e)this.roomState[x.key]=JSON.parse(x.value)}async join(x,S={}){this.ensureReady();let C=Date.now(),w={userId:x,joinedAt:C,metadata:S},T=Object.keys(S).length>0?JSON.stringify(S):null;this[MEMBERS].set(x,w),this.sql`INSERT OR REPLACE INTO room_members (user_id, joined_at, metadata)
|
|
11
|
-
VALUES (${x}, ${C}, ${T})`,e.onJoin&&await e.onJoin(this.roomState,x,S)}async leave(x){this.ensureReady(),this[MEMBERS].has(x)&&(this[MEMBERS].delete(x),this.sql`DELETE FROM room_members WHERE user_id = ${x}`,e.onLeave&&await e.onLeave(this.roomState,x))}async broadcast(x,S,C){this.ensureReady();let w=this.getConnectionBinding(),T=Array.from(this[MEMBERS].entries()).filter(([e])=>!(C?.userIds&&!C.userIds.includes(e)||C?.exceptUserId===e)),E=await Promise.all(T.map(async([e])=>{try{return await w.get(w.idFromName(e)).deliverMessage(x,S),{userId:e,ok:!0}}catch(x){return{userId:e,ok:!1,error:x}}})),D=e.maxDeliveryFailures??3,O=0,k=[];for(let e of E)if(e.ok)O++,this[DELIVERY_FAILURES].delete(e.userId);else{let x=(this[DELIVERY_FAILURES].get(e.userId)??0)+1;this[DELIVERY_FAILURES].set(e.userId,x),x>=D&&k.push(e.userId)}for(let e of k)this[DELIVERY_FAILURES].delete(e),await this.leave(e);return O}async getMembers(){return this.ensureReady(),Array.from(this[MEMBERS].values())}async getMemberCount(){return this.ensureReady(),this[MEMBERS].size}async hasMember(e){return this.ensureReady(),this[MEMBERS].has(e)}async updateMemberMetadata(e,x){this.ensureReady();let S=this[MEMBERS].get(e);if(!S)return;S.metadata={...S.metadata,...x},this[MEMBERS].set(e,S);let C=JSON.stringify(S.metadata);this.sql`UPDATE room_members SET metadata = ${C} WHERE user_id = ${e}`}async getRoomState(){return this.ensureReady(),{...this.roomState}}async setRoomState(e,x){this.ensureReady(),this.roomState[e]=x;let S=JSON.stringify(x);this.sql`INSERT OR REPLACE INTO room_state (key, value) VALUES (${e}, ${S})`}async destroy(){e.onDestroy&&await e.onDestroy(this.roomState),await super.destroy()}}return Object.defineProperty(S,`name`,{value:x,writable:!1,configurable:!0}),S}function enableDebug(e){}export{PROTOCOL_VERSION,PersistError,PersistNotReadyError,clearPersistedState,createConnectionHandler,createRoomHandler,decodeClientMessage,decodeFrame,decodeServerMessage,defineConnection,deletePersistedKey,enableDebug,encodeClientMessage,encodeFrame,encodeServerMessage,getPersistedKeys,getPersistedState,initializePersistedState,isStateReady,persistKey,safeDeserialize,safeSerialize,setPeristErrorHandler,setPersistErrorHandler,storeAttachment};
|
|
10
|
+
VALUES (${S}, ${JSON.stringify(x)})`}await e.delete(Array.from(S.keys()))}}loadMembers(){this[MEMBERS].clear();let e=this.sql`SELECT user_id, joined_at, metadata FROM room_members`;for(let x of e)this[MEMBERS].set(x.user_id,{userId:x.user_id,joinedAt:x.joined_at,metadata:x.metadata?JSON.parse(x.metadata):void 0})}loadRoomState(){this.roomState={};let e=this.sql`SELECT key, value FROM room_state`;for(let x of e)this.roomState[x.key]=JSON.parse(x.value)}async join(x,S={}){await this.ensureReady();let C=Date.now(),w={userId:x,joinedAt:C,metadata:S},T=Object.keys(S).length>0?JSON.stringify(S):null;this[MEMBERS].set(x,w),this.sql`INSERT OR REPLACE INTO room_members (user_id, joined_at, metadata)
|
|
11
|
+
VALUES (${x}, ${C}, ${T})`,e.onJoin&&await e.onJoin(this.roomState,x,S)}async leave(x){await this.ensureReady(),this[MEMBERS].has(x)&&(this[MEMBERS].delete(x),this.sql`DELETE FROM room_members WHERE user_id = ${x}`,e.onLeave&&await e.onLeave(this.roomState,x))}async broadcast(x,S,C){await this.ensureReady();let w=this.getConnectionBinding(),T=Array.from(this[MEMBERS].entries()).filter(([e])=>!(C?.userIds&&!C.userIds.includes(e)||C?.exceptUserId===e)),E=await Promise.all(T.map(async([e])=>{try{return await w.get(w.idFromName(e)).deliverMessage(x,S),{userId:e,ok:!0}}catch(x){return{userId:e,ok:!1,error:x}}})),D=e.maxDeliveryFailures??3,O=0,k=[];for(let e of E)if(e.ok)O++,this[DELIVERY_FAILURES].delete(e.userId);else{let x=(this[DELIVERY_FAILURES].get(e.userId)??0)+1;this[DELIVERY_FAILURES].set(e.userId,x),x>=D&&k.push(e.userId)}for(let e of k)this[DELIVERY_FAILURES].delete(e),await this.leave(e);return O}async getMembers(){return await this.ensureReady(),Array.from(this[MEMBERS].values())}async getMemberCount(){return await this.ensureReady(),this[MEMBERS].size}async hasMember(e){return await this.ensureReady(),this[MEMBERS].has(e)}async updateMemberMetadata(e,x){await this.ensureReady();let S=this[MEMBERS].get(e);if(!S)return;S.metadata={...S.metadata,...x},this[MEMBERS].set(e,S);let C=JSON.stringify(S.metadata);this.sql`UPDATE room_members SET metadata = ${C} WHERE user_id = ${e}`}async getRoomState(){return await this.ensureReady(),{...this.roomState}}async setRoomState(e,x){await this.ensureReady(),this.roomState[e]=x;let S=JSON.stringify(x);this.sql`INSERT OR REPLACE INTO room_state (key, value) VALUES (${e}, ${S})`}async destroy(){e.onDestroy&&await e.onDestroy(this.roomState),await super.destroy()}}return Object.defineProperty(S,`name`,{value:x,writable:!1,configurable:!0}),S}function enableDebug(e){}export{PROTOCOL_VERSION,PersistError,PersistNotReadyError,clearPersistedState,createConnectionHandler,createRoomHandler,decodeClientMessage,decodeFrame,decodeServerMessage,defineConnection,deletePersistedKey,enableDebug,encodeClientMessage,encodeFrame,encodeServerMessage,getPersistedKeys,getPersistedState,initializePersistedState,isStateReady,persistKey,safeDeserialize,safeSerialize,setPeristErrorHandler,setPersistErrorHandler,storeAttachment};
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "verani",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "A simple, focused realtime SDK for Cloudflare Actors with Socket.io-like semantics",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"workspaces": [
|
|
7
|
-
"site"
|
|
7
|
+
"site",
|
|
8
|
+
"examples/tanstack-chat"
|
|
8
9
|
],
|
|
9
10
|
"keywords": [
|
|
10
11
|
"cloudflare",
|
|
@@ -82,7 +83,9 @@
|
|
|
82
83
|
"start": "wrangler dev",
|
|
83
84
|
"test": "vitest",
|
|
84
85
|
"test:unit": "vitest run --config vitest.unit.config.mts",
|
|
85
|
-
"cf-typegen": "wrangler types"
|
|
86
|
+
"cf-typegen": "wrangler types",
|
|
87
|
+
"example:basic-chat": "cd examples/basic-chat && wrangler dev",
|
|
88
|
+
"example:tanstack-chat": "cd examples/tanstack-chat && npm run dev"
|
|
86
89
|
},
|
|
87
90
|
"devDependencies": {
|
|
88
91
|
"@cloudflare/vitest-pool-workers": "^0.8.19",
|
|
@@ -1 +0,0 @@
|
|
|
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]??e.rooms[f.split(`:`,1)[0]];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,p){return e.createRoomEmitBuilder(f,p)},toUser(f){return e.createUserEmitBuilder(f)}}}createRoomEmitBuilder(e,f){let p=this;return{async emit(m,h){let g=p.getRoomBinding(e),_=g.get(g.idFromName(e)),v=f?.includeSelf||!p[META]?.userId?void 0:{exceptUserId:p[META].userId};return await _.broadcast(m,h,v)}}}createUserEmitBuilder(e){let f=this;return{async emit(p,m){let h=f.getConnectionBinding();return await h.get(h.idFromName(e)).deliverMessage(p,m)?1:0}}}restoreWebSocketIfNeeded(){if(this[WS])return;let e=this.ctx.getWebSockets();if(e.length>0&&e[0].readyState===WebSocket.OPEN){this[WS]=e[0];let f=e[0].deserializeAttachment();f&&(this[META]=f)}}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.restoreWebSocketIfNeeded(),this.sendToWebSocket(e,f)}async deliverSystemEvent(e,f){this.restoreWebSocketIfNeeded(),this.sendToWebSocket(`system:${e}`,f)}async getUserId(){return this.restoreWebSocketIfNeeded(),this[META]?.userId??null}async isConnected(){return this.restoreWebSocketIfNeeded(),this[WS]!==null&&this[WS].readyState===WebSocket.OPEN}async joinRoom(e,f){if(!this[META])throw Error(`Cannot join room: not connected`);let p=this.getRoomBinding(e);await p.get(p.idFromName(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{let f=this.getRoomBinding(p);await f.get(f.idFromName(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){if(!this[META])return;let f=this.getRoomBinding(e);await f.get(f.idFromName(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}});
|
|
@@ -1 +0,0 @@
|
|
|
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]??e.rooms[f.split(`:`,1)[0]];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,p){return e.createRoomEmitBuilder(f,p)},toUser(f){return e.createUserEmitBuilder(f)}}}createRoomEmitBuilder(e,f){let p=this;return{async emit(m,h){let g=p.getRoomBinding(e),_=g.get(g.idFromName(e)),v=f?.includeSelf||!p[META]?.userId?void 0:{exceptUserId:p[META].userId};return await _.broadcast(m,h,v)}}}createUserEmitBuilder(e){let f=this;return{async emit(p,m){let h=f.getConnectionBinding();return await h.get(h.idFromName(e)).deliverMessage(p,m)?1:0}}}restoreWebSocketIfNeeded(){if(this[WS])return;let e=this.ctx.getWebSockets();if(e.length>0&&e[0].readyState===WebSocket.OPEN){this[WS]=e[0];let f=e[0].deserializeAttachment();f&&(this[META]=f)}}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.restoreWebSocketIfNeeded(),this.sendToWebSocket(e,f)}async deliverSystemEvent(e,f){this.restoreWebSocketIfNeeded(),this.sendToWebSocket(`system:${e}`,f)}async getUserId(){return this.restoreWebSocketIfNeeded(),this[META]?.userId??null}async isConnected(){return this.restoreWebSocketIfNeeded(),this[WS]!==null&&this[WS].readyState===WebSocket.OPEN}async joinRoom(e,f){if(!this[META])throw Error(`Cannot join room: not connected`);let p=this.getRoomBinding(e);await p.get(p.idFromName(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{let f=this.getRoomBinding(p);await f.get(f.idFromName(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){if(!this[META])return;let f=this.getRoomBinding(e);await f.get(f.idFromName(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};
|