verani 0.4.5 → 0.5.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.
package/dist/verani.cjs CHANGED
@@ -1 +1 @@
1
- const require_types=require(`./types-083oWz55.cjs`);let __cloudflare_actors=require(`@cloudflare/actors`);var RoomEventEmitterImpl=class{constructor(){this.handlers=new Map}on(e,S){this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(S)}off(e,S){let C=this.handlers.get(e);C&&(S?(C.delete(S),C.size===0&&this.handlers.delete(e)):this.handlers.delete(e))}async emit(e,S,C){let w=this.handlers.get(e);if(w&&w.size>0){let e=[];for(let T of w)try{let w=T(S,C);w instanceof Promise&&e.push(w)}catch{}await Promise.all(e)}let T=this.handlers.get(`*`);if(T&&T.size>0){let e=[];for(let w of T)try{let T=w(S,C);T instanceof Promise&&e.push(T)}catch{}await Promise.all(e)}}hasHandlers(e){return this.handlers.has(e)&&this.handlers.get(e).size>0||this.handlers.has(`*`)&&this.handlers.get(`*`).size>0}getEventNames(){return Array.from(this.handlers.keys())}rebuildHandlers(e){this.handlers.clear();for(let[S,C]of e.entries())this.handlers.set(S,new Set(C))}};function createRoomEventEmitter(){return new RoomEventEmitterImpl}function defaultExtractMeta(e){let S=crypto.randomUUID(),C=crypto.randomUUID(),w=new URL(e.url).searchParams.get(`channels`);return{userId:S,clientId:C,channels:w?w.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){let S=e.eventEmitter||createRoomEventEmitter(),C=e._staticHandlers||new Map;return{name:e.name,websocketPath:e.websocketPath,extractMeta:e.extractMeta||(e=>defaultExtractMeta(e)),onConnect:e.onConnect,onDisconnect:e.onDisconnect,onMessage:e.onMessage,onError:e.onError,onHibernationRestore:e.onHibernationRestore,eventEmitter:S,_staticHandlers:C,on(e,w){S.on(e,w),C.has(e)||C.set(e,new Set),C.get(e).add(w)},off(e,w){S.off(e,w);let T=C.get(e);T&&(w?(T.delete(w),T.size===0&&C.delete(e)):C.delete(e))}}}function cleanupStaleSessions(e){let S=0,C=[];for(let[S,w]of e.entries())S.readyState!==WebSocket.OPEN&&C.push(S);for(let w of C)e.delete(w),S++;return S}function decodeFrame$1(S){return require_types.o(S)??{type:`invalid`}}function encodeFrame$1(S){return require_types.r(S)}function broadcast(e,S,C,w){let T=0,E=encodeFrame$1({type:`event`,channel:S,data:C}),D=[];for(let{ws:C,meta:O}of e.values())if(O.channels.includes(S)&&!(w?.except&&C===w.except)&&!(w?.userIds&&!w.userIds.includes(O.userId))&&!(w?.clientIds&&!w.clientIds.includes(O.clientId))){if(C.readyState!==WebSocket.OPEN){D.push(C);continue}try{C.send(E),T++}catch{D.push(C)}}for(let S of D)e.delete(S);return D.length,T}function sendToUser(e,S,C,w){let T=0,E=encodeFrame$1({type:`event`,channel:C,data:w}),D=[];for(let{ws:w,meta:O}of e.values())if(O.userId===S&&O.channels.includes(C)){if(w.readyState!==WebSocket.OPEN){D.push(w);continue}try{w.send(E),T++}catch{D.push(w)}}for(let S of D)e.delete(S);return D.length,T}function getSessionCount(e){return e.size}function getConnectedUserIds(e){let S=new Set;for(let{meta:C}of e.values())S.add(C.userId);return Array.from(S)}function getUserSessions(e,S){let C=[];for(let{ws:w,meta:T}of e.values())T.userId===S&&C.push(w);return C}function getStorage(e){return e.storage}function sanitizeToClassName(e){return e.replace(/^\/+/,``).split(/[-_\/\s]+/).map(e=>e.replace(/[^a-zA-Z0-9]/g,``)).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createConfiguration(e){return function(S){return{locationHint:`me`,sockets:{upgradePath:e.websocketPath}}}}function isValidConnectionMeta(e){return!(!e||typeof e!=`object`||typeof e.userId!=`string`||!e.userId||typeof e.clientId!=`string`||!e.clientId||!Array.isArray(e.channels)||!e.channels.every(e=>typeof e==`string`))}function storeAttachment(e,S){e.serializeAttachment(S)}function restoreSessions(e){let S=0,C=0;for(let w of e.ctx.getWebSockets()){if(w.readyState!==WebSocket.OPEN){C++;continue}let T=w.deserializeAttachment();if(!T){C++;continue}if(!isValidConnectionMeta(T)){C++;continue}e.sessions.set(w,{ws:w,meta:T}),S++}}async function onInit(e,S){if(S.eventEmitter&&S._staticHandlers)try{S.eventEmitter.rebuildHandlers(S._staticHandlers)}catch{}try{restoreSessions(e)}catch{}if(S.onHibernationRestore&&e.sessions.size>0)try{await S.onHibernationRestore(e)}catch{}else S.onHibernationRestore&&e.sessions.size}function createUserEmitBuilder(e,S,C){return{emit(w,T){return sendToUser(S,e,C,{type:w,...T})}}}function createChannelEmitBuilder(e,S,C){return{emit(w,T){return broadcast(S,e,{type:w,...T},C)}}}function createSocketEmit(e){let S=e.meta.channels[0]||`default`;return{emit(C,w){if(e.ws.readyState===WebSocket.OPEN)try{let T={type:`event`,channel:S,data:{type:C,...w}};e.ws.send(encodeFrame$1(T))}catch{}},to(C){return e.meta.channels.includes(C)?createChannelEmitBuilder(C,e.actor.sessions,{except:e.ws}):createUserEmitBuilder(C,e.actor.sessions,S)}}}function createActorEmit(e){return{emit(S,C){let w={type:S,...C};return broadcast(e.sessions,`default`,w)},to(S){return createChannelEmitBuilder(S,e.sessions)}}}async function onWebSocketConnect(e,S,C,w){let T;try{T=S.extractMeta?await S.extractMeta(w):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(C,T);let E={actor:e,ws:C,meta:T,frame:{type:`connect`}};if(S.onConnect){let w={actor:e,ws:C,meta:T,emit:createSocketEmit(E)};await S.onConnect(w)}e.sessions.set(C,{ws:C,meta:T})}catch(w){if(S.onError&&T)try{let E={actor:e,ws:C,meta:T,frame:{type:`error`}};await S.onError(w,{actor:e,ws:C,meta:T,emit:createSocketEmit(E)})}catch{}C.close(1011,`Internal server error`)}}async function onWebSocketMessage(e,S,C,w){let T;try{let E=decodeFrame$1(w);if(E&&E.type===`ping`){if(C.readyState===WebSocket.OPEN)try{C.send(encodeFrame$1({type:`pong`}))}catch{}return}if(!E||E.type===`invalid`||(T=e.sessions.get(C),!T))return;let D={actor:e,ws:C,meta:T.meta,frame:E,emit:createSocketEmit({actor:e,ws:C,meta:T.meta,frame:E})},k=S.eventEmitter;k&&k.hasHandlers&&k.hasHandlers(E.type)?await k.emit(E.type,D,E.data||{}):S.onMessage&&await S.onMessage(D,E)}catch(w){if(S.onError&&T)try{await S.onError(w,{actor:e,ws:C,meta:T.meta,emit:createSocketEmit({actor:e,ws:C,meta:T.meta,frame:{type:`error`}})})}catch{}}}async function onWebSocketDisconnect(e,S,C){try{let w=e.sessions.get(C);if(e.sessions.delete(C),w&&S.onDisconnect){let T={actor:e,ws:C,meta:w.meta,frame:{type:`disconnect`}},E={actor:e,ws:C,meta:w.meta,emit:createSocketEmit(T)};await S.onDisconnect(E)}}catch{}}function createFetch(e,S){return async function(C){let w=new URL(C.url),T=C.headers.get(`Upgrade`);return w.pathname===e.websocketPath&&T===`websocket`&&await S.shouldUpgradeWebSocket(C)?S.onWebSocketUpgrade(C):S.onRequest(C)}}function createActorHandler(e){let C=sanitizeToClassName(e.name||e.websocketPath||`VeraniActor`);class w extends __cloudflare_actors.Actor{constructor(...S){super(...S),this.sessions=new Map,this.emit=createActorEmit(this),this.fetch=createFetch(e,this)}static{this.configuration=createConfiguration(e)}async shouldUpgradeWebSocket(e){return!0}async onInit(){await onInit(this,e)}async onWebSocketConnect(S,C){await onWebSocketConnect(this,e,S,C)}async onWebSocketMessage(S,C){await onWebSocketMessage(this,e,S,C)}async onWebSocketDisconnect(S){await onWebSocketDisconnect(this,e,S)}cleanupStaleSessions(){return cleanupStaleSessions(this.sessions)}broadcast(e,S,C){return broadcast(this.sessions,e,S,C)}getSessionCount(){return getSessionCount(this.sessions)}getConnectedUserIds(){return getConnectedUserIds(this.sessions)}getUserSessions(e){return getUserSessions(this.sessions,e)}sendToUser(e,S,C){return sendToUser(this.sessions,e,S,C)}getStorage(){return getStorage(this.ctx)}}return Object.defineProperty(w,`name`,{value:C,writable:!1,configurable:!0}),w}exports.PROTOCOL_VERSION=require_types.t,exports.createActorHandler=createActorHandler,exports.decodeClientMessage=require_types.a,exports.decodeFrame=require_types.o,exports.decodeServerMessage=require_types.s,exports.defineRoom=defineRoom,exports.encodeClientMessage=require_types.n,exports.encodeFrame=require_types.r,exports.encodeServerMessage=require_types.i,exports.restoreSessions=restoreSessions,exports.storeAttachment=storeAttachment;
1
+ const require_types=require(`./types-083oWz55.cjs`);let __cloudflare_actors=require(`@cloudflare/actors`);var RoomEventEmitterImpl=class{constructor(){this.handlers=new Map}on(e,S){this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(S)}off(e,S){let C=this.handlers.get(e);C&&(S?(C.delete(S),C.size===0&&this.handlers.delete(e)):this.handlers.delete(e))}async emit(e,S,C){let w=this.handlers.get(e);if(w&&w.size>0){let e=[];for(let T of w)try{let w=T(S,C);w instanceof Promise&&e.push(w)}catch{}await Promise.all(e)}let T=this.handlers.get(`*`);if(T&&T.size>0){let e=[];for(let w of T)try{let T=w(S,C);T instanceof Promise&&e.push(T)}catch{}await Promise.all(e)}}hasHandlers(e){return this.handlers.has(e)&&this.handlers.get(e).size>0||this.handlers.has(`*`)&&this.handlers.get(`*`).size>0}getEventNames(){return Array.from(this.handlers.keys())}rebuildHandlers(e){this.handlers.clear();for(let[S,C]of e.entries())this.handlers.set(S,new Set(C))}};function createRoomEventEmitter(){return new RoomEventEmitterImpl}function defaultExtractMeta(e){let S=crypto.randomUUID(),C=crypto.randomUUID(),w=new URL(e.url).searchParams.get(`channels`);return{userId:S,clientId:C,channels:w?w.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){let S=e.eventEmitter||createRoomEventEmitter(),C=e._staticHandlers||new Map;return{name:e.name,websocketPath:e.websocketPath,extractMeta:e.extractMeta||(e=>defaultExtractMeta(e)),onConnect:e.onConnect,onDisconnect:e.onDisconnect,onMessage:e.onMessage,onError:e.onError,onHibernationRestore:e.onHibernationRestore,eventEmitter:S,_staticHandlers:C,on(e,w){S.on(e,w),C.has(e)||C.set(e,new Set),C.get(e).add(w)},off(e,w){S.off(e,w);let T=C.get(e);T&&(w?(T.delete(w),T.size===0&&C.delete(e)):C.delete(e))}}}function cleanupStaleSessions(e){let S=0,C=[];for(let[S,w]of e.entries())S.readyState!==WebSocket.OPEN&&C.push(S);for(let w of C)e.delete(w),S++;return S}function decodeFrame$1(S){return require_types.o(S)??{type:`invalid`}}function encodeFrame$1(S){return require_types.r(S)}function broadcast(e,S,C,w){let T=0,E=encodeFrame$1({type:`event`,channel:S,data:C}),D=[];for(let{ws:C,meta:O}of e.values())if(O.channels.includes(S)&&!(w?.except&&C===w.except)&&!(w?.userIds&&!w.userIds.includes(O.userId))&&!(w?.clientIds&&!w.clientIds.includes(O.clientId))){if(C.readyState!==WebSocket.OPEN){D.push(C);continue}try{C.send(E),T++}catch{D.push(C)}}for(let S of D)e.delete(S);return D.length,T}function sendToUser(e,S,C,w){let T=0,E=encodeFrame$1({type:`event`,channel:C,data:w}),D=[];for(let{ws:w,meta:O}of e.values())if(O.userId===S&&O.channels.includes(C)){if(w.readyState!==WebSocket.OPEN){D.push(w);continue}try{w.send(E),T++}catch{D.push(w)}}for(let S of D)e.delete(S);return D.length,T}function getSessionCount(e){return e.size}function getConnectedUserIds(e){let S=new Set;for(let{meta:C}of e.values())S.add(C.userId);return Array.from(S)}function getUserSessions(e,S){let C=[];for(let{ws:w,meta:T}of e.values())T.userId===S&&C.push(w);return C}function getStorage(e){return e.storage}function sanitizeToClassName(e){return e.replace(/^\/+/,``).split(/[-_\/\s]+/).map(e=>e.replace(/[^a-zA-Z0-9]/g,``)).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createConfiguration(e){return function(S){return{locationHint:`me`,sockets:{upgradePath:e.websocketPath}}}}function isValidConnectionMeta(e){return!(!e||typeof e!=`object`||typeof e.userId!=`string`||!e.userId||typeof e.clientId!=`string`||!e.clientId||!Array.isArray(e.channels)||!e.channels.every(e=>typeof e==`string`))}function storeAttachment(e,S){e.serializeAttachment(S)}function restoreSessions(e){let S=0,C=0;for(let w of e.ctx.getWebSockets()){if(w.readyState!==WebSocket.OPEN){C++;continue}let T=w.deserializeAttachment();if(!T){C++;continue}if(!isValidConnectionMeta(T)){C++;continue}e.sessions.set(w,{ws:w,meta:T}),S++}}async function onInit(e,S){if(S.eventEmitter&&S._staticHandlers)try{S.eventEmitter.rebuildHandlers(S._staticHandlers)}catch{}try{restoreSessions(e)}catch{}if(S.onHibernationRestore&&e.sessions.size>0)try{await S.onHibernationRestore(e)}catch{}else S.onHibernationRestore&&e.sessions.size}function createUserEmitBuilder(e,S,C){return{emit(w,T){return sendToUser(S,e,C,{type:w,...T})}}}function createChannelEmitBuilder(e,S,C){return{emit(w,T){return broadcast(S,e,{type:w,...T},C)}}}function createSocketEmit(e){let S=e.meta.channels[0]||`default`;return{emit(C,w){if(e.ws.readyState===WebSocket.OPEN)try{let T={type:`event`,channel:S,data:{type:C,...w}};e.ws.send(encodeFrame$1(T))}catch{}},to(C){return e.meta.channels.includes(C)?createChannelEmitBuilder(C,e.actor.sessions,{except:e.ws}):createUserEmitBuilder(C,e.actor.sessions,S)}}}function createActorEmit(e){return{emit(S,C){let w={type:S,...C};return broadcast(e.sessions,`default`,w)},to(S){return createChannelEmitBuilder(S,e.sessions)}}}async function onWebSocketConnect(e,S,C,w){let T;try{T=S.extractMeta?await S.extractMeta(w):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(C,T);let E={actor:e,ws:C,meta:T,frame:{type:`connect`}};if(S.onConnect){let w={actor:e,ws:C,meta:T,emit:createSocketEmit(E)};await S.onConnect(w)}e.sessions.set(C,{ws:C,meta:T})}catch(w){if(S.onError&&T)try{let E={actor:e,ws:C,meta:T,frame:{type:`error`}};await S.onError(w,{actor:e,ws:C,meta:T,emit:createSocketEmit(E)})}catch{}C.close(1011,`Internal server error`)}}async function onWebSocketMessage(e,S,C,w){let T;try{let E=decodeFrame$1(w);if(E&&E.type===`ping`){if(C.readyState===WebSocket.OPEN)try{C.send(encodeFrame$1({type:`pong`}))}catch{}return}if(!E||E.type===`invalid`||(T=e.sessions.get(C),!T))return;let D={actor:e,ws:C,meta:T.meta,frame:E,emit:createSocketEmit({actor:e,ws:C,meta:T.meta,frame:E})},k=S.eventEmitter;k&&k.hasHandlers&&k.hasHandlers(E.type)?await k.emit(E.type,D,E.data||{}):S.onMessage&&await S.onMessage(D,E)}catch(w){if(S.onError&&T)try{await S.onError(w,{actor:e,ws:C,meta:T.meta,emit:createSocketEmit({actor:e,ws:C,meta:T.meta,frame:{type:`error`}})})}catch{}}}async function onWebSocketDisconnect(e,S,C){try{let w=e.sessions.get(C);if(e.sessions.delete(C),w&&S.onDisconnect){let T={actor:e,ws:C,meta:w.meta,frame:{type:`disconnect`}},E={actor:e,ws:C,meta:w.meta,emit:createSocketEmit(T)};await S.onDisconnect(E)}}catch{}}function createFetch(e,S){return async function(C){let w=new URL(C.url),T=C.headers.get(`Upgrade`);return w.pathname===e.websocketPath&&T===`websocket`&&await S.shouldUpgradeWebSocket(C)?S.onWebSocketUpgrade(C):S.onRequest(C)}}function createActorHandler(e){let C=sanitizeToClassName(e.name||e.websocketPath||`VeraniActor`);class w extends __cloudflare_actors.Actor{constructor(...S){super(...S),this.sessions=new Map,this.emit=createActorEmit(this),this.fetch=createFetch(e,this)}static{this.configuration=createConfiguration(e)}async shouldUpgradeWebSocket(e){return!0}async onInit(){await onInit(this,e)}async onWebSocketConnect(S,C){await onWebSocketConnect(this,e,S,C)}async onWebSocketMessage(S,C){await onWebSocketMessage(this,e,S,C)}async onWebSocketDisconnect(S){await onWebSocketDisconnect(this,e,S)}cleanupStaleSessions(){return cleanupStaleSessions(this.sessions)}broadcast(e,S,C){return broadcast(this.sessions,e,S,C)}getSessionCount(){return getSessionCount(this.sessions)}getConnectedUserIds(){return getConnectedUserIds(this.sessions)}getUserSessions(e){return getUserSessions(this.sessions,e)}sendToUser(e,S,C){return sendToUser(this.sessions,e,S,C)}emitToChannel(e,S,C){let w={type:S,...C};return broadcast(this.sessions,e,w)}emitToUser(e,S,C){let w={type:S,...C};return sendToUser(this.sessions,e,`default`,w)}getStorage(){return getStorage(this.ctx)}}return Object.defineProperty(w,`name`,{value:C,writable:!1,configurable:!0}),w}exports.PROTOCOL_VERSION=require_types.t,exports.createActorHandler=createActorHandler,exports.decodeClientMessage=require_types.a,exports.decodeFrame=require_types.o,exports.decodeServerMessage=require_types.s,exports.defineRoom=defineRoom,exports.encodeClientMessage=require_types.n,exports.encodeFrame=require_types.r,exports.encodeServerMessage=require_types.i,exports.restoreSessions=restoreSessions,exports.storeAttachment=storeAttachment;
package/dist/verani.d.cts CHANGED
@@ -292,6 +292,31 @@ interface ActorStub {
292
292
  */
293
293
  fetch(request: Request): Promise<Response>;
294
294
  /**
295
+ * Socket.IO-like emit API: Emit an event to a specific channel via RPC.
296
+ * @param channel - Channel name to emit to
297
+ * @param event - Event name
298
+ * @param data - Event data
299
+ * @returns Promise resolving to the number of connections that received the message
300
+ * @example
301
+ * ```typescript
302
+ * await stub.emitToChannel("default", "announcement", { text: "Hello!" });
303
+ * ```
304
+ */
305
+ emitToChannel(channel: string, event: string, data?: any): Promise<number>;
306
+ /**
307
+ * Socket.IO-like emit API: Emit an event to a specific user (all their sessions) via RPC.
308
+ * @param userId - User ID to emit to
309
+ * @param event - Event name
310
+ * @param data - Event data
311
+ * @returns Promise resolving to the number of sessions that received the message
312
+ * @example
313
+ * ```typescript
314
+ * await stub.emitToUser("alice", "notification", { message: "Hello!" });
315
+ * ```
316
+ */
317
+ emitToUser(userId: string, event: string, data?: any): Promise<number>;
318
+ /**
319
+ * @deprecated Use `emitToUser()` instead for Socket.IO-like API.
295
320
  * Sends a message to a specific user (all their sessions) via RPC.
296
321
  * @param userId - The user ID to send to
297
322
  * @param channel - The channel to send to
@@ -300,6 +325,7 @@ interface ActorStub {
300
325
  */
301
326
  sendToUser(userId: string, channel: string, data?: any): Promise<number>;
302
327
  /**
328
+ * @deprecated Use `emitToChannel()` instead for Socket.IO-like API.
303
329
  * Broadcasts a message to all connections in a channel via RPC.
304
330
  * Note: The `except` option from BroadcastOptions is not available over RPC
305
331
  * since WebSocket cannot be serialized.
package/dist/verani.d.mts CHANGED
@@ -292,6 +292,31 @@ interface ActorStub {
292
292
  */
293
293
  fetch(request: Request): Promise<Response>;
294
294
  /**
295
+ * Socket.IO-like emit API: Emit an event to a specific channel via RPC.
296
+ * @param channel - Channel name to emit to
297
+ * @param event - Event name
298
+ * @param data - Event data
299
+ * @returns Promise resolving to the number of connections that received the message
300
+ * @example
301
+ * ```typescript
302
+ * await stub.emitToChannel("default", "announcement", { text: "Hello!" });
303
+ * ```
304
+ */
305
+ emitToChannel(channel: string, event: string, data?: any): Promise<number>;
306
+ /**
307
+ * Socket.IO-like emit API: Emit an event to a specific user (all their sessions) via RPC.
308
+ * @param userId - User ID to emit to
309
+ * @param event - Event name
310
+ * @param data - Event data
311
+ * @returns Promise resolving to the number of sessions that received the message
312
+ * @example
313
+ * ```typescript
314
+ * await stub.emitToUser("alice", "notification", { message: "Hello!" });
315
+ * ```
316
+ */
317
+ emitToUser(userId: string, event: string, data?: any): Promise<number>;
318
+ /**
319
+ * @deprecated Use `emitToUser()` instead for Socket.IO-like API.
295
320
  * Sends a message to a specific user (all their sessions) via RPC.
296
321
  * @param userId - The user ID to send to
297
322
  * @param channel - The channel to send to
@@ -300,6 +325,7 @@ interface ActorStub {
300
325
  */
301
326
  sendToUser(userId: string, channel: string, data?: any): Promise<number>;
302
327
  /**
328
+ * @deprecated Use `emitToChannel()` instead for Socket.IO-like API.
303
329
  * Broadcasts a message to all connections in a channel via RPC.
304
330
  * Note: The `except` option from BroadcastOptions is not available over RPC
305
331
  * since WebSocket cannot be serialized.
package/dist/verani.mjs CHANGED
@@ -1 +1 @@
1
- import{a as decodeClientMessage,i as encodeServerMessage,n as encodeClientMessage,o as decodeFrame,r as encodeFrame,s as decodeServerMessage,t as PROTOCOL_VERSION}from"./types-CJLnZrA8.mjs";import{Actor}from"@cloudflare/actors";var RoomEventEmitterImpl=class{constructor(){this.handlers=new Map}on(u,k){this.handlers.has(u)||this.handlers.set(u,new Set),this.handlers.get(u).add(k)}off(u,k){let A=this.handlers.get(u);A&&(k?(A.delete(k),A.size===0&&this.handlers.delete(u)):this.handlers.delete(u))}async emit(u,k,A){let j=this.handlers.get(u);if(j&&j.size>0){let u=[];for(let M of j)try{let j=M(k,A);j instanceof Promise&&u.push(j)}catch{}await Promise.all(u)}let M=this.handlers.get(`*`);if(M&&M.size>0){let u=[];for(let j of M)try{let M=j(k,A);M instanceof Promise&&u.push(M)}catch{}await Promise.all(u)}}hasHandlers(u){return this.handlers.has(u)&&this.handlers.get(u).size>0||this.handlers.has(`*`)&&this.handlers.get(`*`).size>0}getEventNames(){return Array.from(this.handlers.keys())}rebuildHandlers(u){this.handlers.clear();for(let[k,A]of u.entries())this.handlers.set(k,new Set(A))}};function createRoomEventEmitter(){return new RoomEventEmitterImpl}function defaultExtractMeta(u){let k=crypto.randomUUID(),A=crypto.randomUUID(),j=new URL(u.url).searchParams.get(`channels`);return{userId:k,clientId:A,channels:j?j.split(`,`).map(u=>u.trim()).filter(Boolean):[`default`]}}function defineRoom(u){let k=u.eventEmitter||createRoomEventEmitter(),A=u._staticHandlers||new Map;return{name:u.name,websocketPath:u.websocketPath,extractMeta:u.extractMeta||(u=>defaultExtractMeta(u)),onConnect:u.onConnect,onDisconnect:u.onDisconnect,onMessage:u.onMessage,onError:u.onError,onHibernationRestore:u.onHibernationRestore,eventEmitter:k,_staticHandlers:A,on(u,j){k.on(u,j),A.has(u)||A.set(u,new Set),A.get(u).add(j)},off(u,j){k.off(u,j);let M=A.get(u);M&&(j?(M.delete(j),M.size===0&&A.delete(u)):A.delete(u))}}}function cleanupStaleSessions(u){let k=0,A=[];for(let[k,j]of u.entries())k.readyState!==WebSocket.OPEN&&A.push(k);for(let j of A)u.delete(j),k++;return k}function decodeFrame$1(u){return decodeFrame(u)??{type:`invalid`}}function encodeFrame$1(u){return encodeFrame(u)}function broadcast(u,k,A,j){let M=0,N=encodeFrame$1({type:`event`,channel:k,data:A}),P=[];for(let{ws:A,meta:F}of u.values())if(F.channels.includes(k)&&!(j?.except&&A===j.except)&&!(j?.userIds&&!j.userIds.includes(F.userId))&&!(j?.clientIds&&!j.clientIds.includes(F.clientId))){if(A.readyState!==WebSocket.OPEN){P.push(A);continue}try{A.send(N),M++}catch{P.push(A)}}for(let k of P)u.delete(k);return P.length,M}function sendToUser(u,k,A,j){let M=0,N=encodeFrame$1({type:`event`,channel:A,data:j}),P=[];for(let{ws:j,meta:F}of u.values())if(F.userId===k&&F.channels.includes(A)){if(j.readyState!==WebSocket.OPEN){P.push(j);continue}try{j.send(N),M++}catch{P.push(j)}}for(let k of P)u.delete(k);return P.length,M}function getSessionCount(u){return u.size}function getConnectedUserIds(u){let k=new Set;for(let{meta:A}of u.values())k.add(A.userId);return Array.from(k)}function getUserSessions(u,k){let A=[];for(let{ws:j,meta:M}of u.values())M.userId===k&&A.push(j);return A}function getStorage(u){return u.storage}function sanitizeToClassName(u){return u.replace(/^\/+/,``).split(/[-_\/\s]+/).map(u=>u.replace(/[^a-zA-Z0-9]/g,``)).filter(u=>u.length>0).map(u=>u.charAt(0).toUpperCase()+u.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createConfiguration(u){return function(k){return{locationHint:`me`,sockets:{upgradePath:u.websocketPath}}}}function isValidConnectionMeta(u){return!(!u||typeof u!=`object`||typeof u.userId!=`string`||!u.userId||typeof u.clientId!=`string`||!u.clientId||!Array.isArray(u.channels)||!u.channels.every(u=>typeof u==`string`))}function storeAttachment(u,k){u.serializeAttachment(k)}function restoreSessions(u){let k=0,A=0;for(let j of u.ctx.getWebSockets()){if(j.readyState!==WebSocket.OPEN){A++;continue}let M=j.deserializeAttachment();if(!M){A++;continue}if(!isValidConnectionMeta(M)){A++;continue}u.sessions.set(j,{ws:j,meta:M}),k++}}async function onInit(u,k){if(k.eventEmitter&&k._staticHandlers)try{k.eventEmitter.rebuildHandlers(k._staticHandlers)}catch{}try{restoreSessions(u)}catch{}if(k.onHibernationRestore&&u.sessions.size>0)try{await k.onHibernationRestore(u)}catch{}else k.onHibernationRestore&&u.sessions.size}function createUserEmitBuilder(u,k,A){return{emit(j,M){return sendToUser(k,u,A,{type:j,...M})}}}function createChannelEmitBuilder(u,k,A){return{emit(j,M){return broadcast(k,u,{type:j,...M},A)}}}function createSocketEmit(u){let k=u.meta.channels[0]||`default`;return{emit(A,j){if(u.ws.readyState===WebSocket.OPEN)try{let M={type:`event`,channel:k,data:{type:A,...j}};u.ws.send(encodeFrame$1(M))}catch{}},to(A){return u.meta.channels.includes(A)?createChannelEmitBuilder(A,u.actor.sessions,{except:u.ws}):createUserEmitBuilder(A,u.actor.sessions,k)}}}function createActorEmit(u){return{emit(k,A){let j={type:k,...A};return broadcast(u.sessions,`default`,j)},to(k){return createChannelEmitBuilder(k,u.sessions)}}}async function onWebSocketConnect(u,k,A,j){let M;try{M=k.extractMeta?await k.extractMeta(j):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(A,M);let N={actor:u,ws:A,meta:M,frame:{type:`connect`}};if(k.onConnect){let j={actor:u,ws:A,meta:M,emit:createSocketEmit(N)};await k.onConnect(j)}u.sessions.set(A,{ws:A,meta:M})}catch(j){if(k.onError&&M)try{let N={actor:u,ws:A,meta:M,frame:{type:`error`}};await k.onError(j,{actor:u,ws:A,meta:M,emit:createSocketEmit(N)})}catch{}A.close(1011,`Internal server error`)}}async function onWebSocketMessage(u,k,A,j){let M;try{let N=decodeFrame$1(j);if(N&&N.type===`ping`){if(A.readyState===WebSocket.OPEN)try{A.send(encodeFrame$1({type:`pong`}))}catch{}return}if(!N||N.type===`invalid`||(M=u.sessions.get(A),!M))return;let P={actor:u,ws:A,meta:M.meta,frame:N,emit:createSocketEmit({actor:u,ws:A,meta:M.meta,frame:N})},F=k.eventEmitter;F&&F.hasHandlers&&F.hasHandlers(N.type)?await F.emit(N.type,P,N.data||{}):k.onMessage&&await k.onMessage(P,N)}catch(j){if(k.onError&&M)try{await k.onError(j,{actor:u,ws:A,meta:M.meta,emit:createSocketEmit({actor:u,ws:A,meta:M.meta,frame:{type:`error`}})})}catch{}}}async function onWebSocketDisconnect(u,k,A){try{let j=u.sessions.get(A);if(u.sessions.delete(A),j&&k.onDisconnect){let M={actor:u,ws:A,meta:j.meta,frame:{type:`disconnect`}},N={actor:u,ws:A,meta:j.meta,emit:createSocketEmit(M)};await k.onDisconnect(N)}}catch{}}function createFetch(u,k){return async function(A){let j=new URL(A.url),M=A.headers.get(`Upgrade`);return j.pathname===u.websocketPath&&M===`websocket`&&await k.shouldUpgradeWebSocket(A)?k.onWebSocketUpgrade(A):k.onRequest(A)}}function createActorHandler(u){let k=sanitizeToClassName(u.name||u.websocketPath||`VeraniActor`);class A extends Actor{constructor(...k){super(...k),this.sessions=new Map,this.emit=createActorEmit(this),this.fetch=createFetch(u,this)}static{this.configuration=createConfiguration(u)}async shouldUpgradeWebSocket(u){return!0}async onInit(){await onInit(this,u)}async onWebSocketConnect(k,A){await onWebSocketConnect(this,u,k,A)}async onWebSocketMessage(k,A){await onWebSocketMessage(this,u,k,A)}async onWebSocketDisconnect(k){await onWebSocketDisconnect(this,u,k)}cleanupStaleSessions(){return cleanupStaleSessions(this.sessions)}broadcast(u,k,A){return broadcast(this.sessions,u,k,A)}getSessionCount(){return getSessionCount(this.sessions)}getConnectedUserIds(){return getConnectedUserIds(this.sessions)}getUserSessions(u){return getUserSessions(this.sessions,u)}sendToUser(u,k,A){return sendToUser(this.sessions,u,k,A)}getStorage(){return getStorage(this.ctx)}}return Object.defineProperty(A,`name`,{value:k,writable:!1,configurable:!0}),A}export{PROTOCOL_VERSION,createActorHandler,decodeClientMessage,decodeFrame,decodeServerMessage,defineRoom,encodeClientMessage,encodeFrame,encodeServerMessage,restoreSessions,storeAttachment};
1
+ import{a as decodeClientMessage,i as encodeServerMessage,n as encodeClientMessage,o as decodeFrame,r as encodeFrame,s as decodeServerMessage,t as PROTOCOL_VERSION}from"./types-CJLnZrA8.mjs";import{Actor}from"@cloudflare/actors";var RoomEventEmitterImpl=class{constructor(){this.handlers=new Map}on(u,k){this.handlers.has(u)||this.handlers.set(u,new Set),this.handlers.get(u).add(k)}off(u,k){let A=this.handlers.get(u);A&&(k?(A.delete(k),A.size===0&&this.handlers.delete(u)):this.handlers.delete(u))}async emit(u,k,A){let j=this.handlers.get(u);if(j&&j.size>0){let u=[];for(let M of j)try{let j=M(k,A);j instanceof Promise&&u.push(j)}catch{}await Promise.all(u)}let M=this.handlers.get(`*`);if(M&&M.size>0){let u=[];for(let j of M)try{let M=j(k,A);M instanceof Promise&&u.push(M)}catch{}await Promise.all(u)}}hasHandlers(u){return this.handlers.has(u)&&this.handlers.get(u).size>0||this.handlers.has(`*`)&&this.handlers.get(`*`).size>0}getEventNames(){return Array.from(this.handlers.keys())}rebuildHandlers(u){this.handlers.clear();for(let[k,A]of u.entries())this.handlers.set(k,new Set(A))}};function createRoomEventEmitter(){return new RoomEventEmitterImpl}function defaultExtractMeta(u){let k=crypto.randomUUID(),A=crypto.randomUUID(),j=new URL(u.url).searchParams.get(`channels`);return{userId:k,clientId:A,channels:j?j.split(`,`).map(u=>u.trim()).filter(Boolean):[`default`]}}function defineRoom(u){let k=u.eventEmitter||createRoomEventEmitter(),A=u._staticHandlers||new Map;return{name:u.name,websocketPath:u.websocketPath,extractMeta:u.extractMeta||(u=>defaultExtractMeta(u)),onConnect:u.onConnect,onDisconnect:u.onDisconnect,onMessage:u.onMessage,onError:u.onError,onHibernationRestore:u.onHibernationRestore,eventEmitter:k,_staticHandlers:A,on(u,j){k.on(u,j),A.has(u)||A.set(u,new Set),A.get(u).add(j)},off(u,j){k.off(u,j);let M=A.get(u);M&&(j?(M.delete(j),M.size===0&&A.delete(u)):A.delete(u))}}}function cleanupStaleSessions(u){let k=0,A=[];for(let[k,j]of u.entries())k.readyState!==WebSocket.OPEN&&A.push(k);for(let j of A)u.delete(j),k++;return k}function decodeFrame$1(u){return decodeFrame(u)??{type:`invalid`}}function encodeFrame$1(u){return encodeFrame(u)}function broadcast(u,k,A,j){let M=0,N=encodeFrame$1({type:`event`,channel:k,data:A}),P=[];for(let{ws:A,meta:F}of u.values())if(F.channels.includes(k)&&!(j?.except&&A===j.except)&&!(j?.userIds&&!j.userIds.includes(F.userId))&&!(j?.clientIds&&!j.clientIds.includes(F.clientId))){if(A.readyState!==WebSocket.OPEN){P.push(A);continue}try{A.send(N),M++}catch{P.push(A)}}for(let k of P)u.delete(k);return P.length,M}function sendToUser(u,k,A,j){let M=0,N=encodeFrame$1({type:`event`,channel:A,data:j}),P=[];for(let{ws:j,meta:F}of u.values())if(F.userId===k&&F.channels.includes(A)){if(j.readyState!==WebSocket.OPEN){P.push(j);continue}try{j.send(N),M++}catch{P.push(j)}}for(let k of P)u.delete(k);return P.length,M}function getSessionCount(u){return u.size}function getConnectedUserIds(u){let k=new Set;for(let{meta:A}of u.values())k.add(A.userId);return Array.from(k)}function getUserSessions(u,k){let A=[];for(let{ws:j,meta:M}of u.values())M.userId===k&&A.push(j);return A}function getStorage(u){return u.storage}function sanitizeToClassName(u){return u.replace(/^\/+/,``).split(/[-_\/\s]+/).map(u=>u.replace(/[^a-zA-Z0-9]/g,``)).filter(u=>u.length>0).map(u=>u.charAt(0).toUpperCase()+u.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createConfiguration(u){return function(k){return{locationHint:`me`,sockets:{upgradePath:u.websocketPath}}}}function isValidConnectionMeta(u){return!(!u||typeof u!=`object`||typeof u.userId!=`string`||!u.userId||typeof u.clientId!=`string`||!u.clientId||!Array.isArray(u.channels)||!u.channels.every(u=>typeof u==`string`))}function storeAttachment(u,k){u.serializeAttachment(k)}function restoreSessions(u){let k=0,A=0;for(let j of u.ctx.getWebSockets()){if(j.readyState!==WebSocket.OPEN){A++;continue}let M=j.deserializeAttachment();if(!M){A++;continue}if(!isValidConnectionMeta(M)){A++;continue}u.sessions.set(j,{ws:j,meta:M}),k++}}async function onInit(u,k){if(k.eventEmitter&&k._staticHandlers)try{k.eventEmitter.rebuildHandlers(k._staticHandlers)}catch{}try{restoreSessions(u)}catch{}if(k.onHibernationRestore&&u.sessions.size>0)try{await k.onHibernationRestore(u)}catch{}else k.onHibernationRestore&&u.sessions.size}function createUserEmitBuilder(u,k,A){return{emit(j,M){return sendToUser(k,u,A,{type:j,...M})}}}function createChannelEmitBuilder(u,k,A){return{emit(j,M){return broadcast(k,u,{type:j,...M},A)}}}function createSocketEmit(u){let k=u.meta.channels[0]||`default`;return{emit(A,j){if(u.ws.readyState===WebSocket.OPEN)try{let M={type:`event`,channel:k,data:{type:A,...j}};u.ws.send(encodeFrame$1(M))}catch{}},to(A){return u.meta.channels.includes(A)?createChannelEmitBuilder(A,u.actor.sessions,{except:u.ws}):createUserEmitBuilder(A,u.actor.sessions,k)}}}function createActorEmit(u){return{emit(k,A){let j={type:k,...A};return broadcast(u.sessions,`default`,j)},to(k){return createChannelEmitBuilder(k,u.sessions)}}}async function onWebSocketConnect(u,k,A,j){let M;try{M=k.extractMeta?await k.extractMeta(j):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(A,M);let N={actor:u,ws:A,meta:M,frame:{type:`connect`}};if(k.onConnect){let j={actor:u,ws:A,meta:M,emit:createSocketEmit(N)};await k.onConnect(j)}u.sessions.set(A,{ws:A,meta:M})}catch(j){if(k.onError&&M)try{let N={actor:u,ws:A,meta:M,frame:{type:`error`}};await k.onError(j,{actor:u,ws:A,meta:M,emit:createSocketEmit(N)})}catch{}A.close(1011,`Internal server error`)}}async function onWebSocketMessage(u,k,A,j){let M;try{let N=decodeFrame$1(j);if(N&&N.type===`ping`){if(A.readyState===WebSocket.OPEN)try{A.send(encodeFrame$1({type:`pong`}))}catch{}return}if(!N||N.type===`invalid`||(M=u.sessions.get(A),!M))return;let P={actor:u,ws:A,meta:M.meta,frame:N,emit:createSocketEmit({actor:u,ws:A,meta:M.meta,frame:N})},F=k.eventEmitter;F&&F.hasHandlers&&F.hasHandlers(N.type)?await F.emit(N.type,P,N.data||{}):k.onMessage&&await k.onMessage(P,N)}catch(j){if(k.onError&&M)try{await k.onError(j,{actor:u,ws:A,meta:M.meta,emit:createSocketEmit({actor:u,ws:A,meta:M.meta,frame:{type:`error`}})})}catch{}}}async function onWebSocketDisconnect(u,k,A){try{let j=u.sessions.get(A);if(u.sessions.delete(A),j&&k.onDisconnect){let M={actor:u,ws:A,meta:j.meta,frame:{type:`disconnect`}},N={actor:u,ws:A,meta:j.meta,emit:createSocketEmit(M)};await k.onDisconnect(N)}}catch{}}function createFetch(u,k){return async function(A){let j=new URL(A.url),M=A.headers.get(`Upgrade`);return j.pathname===u.websocketPath&&M===`websocket`&&await k.shouldUpgradeWebSocket(A)?k.onWebSocketUpgrade(A):k.onRequest(A)}}function createActorHandler(u){let k=sanitizeToClassName(u.name||u.websocketPath||`VeraniActor`);class A extends Actor{constructor(...k){super(...k),this.sessions=new Map,this.emit=createActorEmit(this),this.fetch=createFetch(u,this)}static{this.configuration=createConfiguration(u)}async shouldUpgradeWebSocket(u){return!0}async onInit(){await onInit(this,u)}async onWebSocketConnect(k,A){await onWebSocketConnect(this,u,k,A)}async onWebSocketMessage(k,A){await onWebSocketMessage(this,u,k,A)}async onWebSocketDisconnect(k){await onWebSocketDisconnect(this,u,k)}cleanupStaleSessions(){return cleanupStaleSessions(this.sessions)}broadcast(u,k,A){return broadcast(this.sessions,u,k,A)}getSessionCount(){return getSessionCount(this.sessions)}getConnectedUserIds(){return getConnectedUserIds(this.sessions)}getUserSessions(u){return getUserSessions(this.sessions,u)}sendToUser(u,k,A){return sendToUser(this.sessions,u,k,A)}emitToChannel(u,k,A){let j={type:k,...A};return broadcast(this.sessions,u,j)}emitToUser(u,k,A){let j={type:k,...A};return sendToUser(this.sessions,u,`default`,j)}getStorage(){return getStorage(this.ctx)}}return Object.defineProperty(A,`name`,{value:k,writable:!1,configurable:!0}),A}export{PROTOCOL_VERSION,createActorHandler,decodeClientMessage,decodeFrame,decodeServerMessage,defineRoom,encodeClientMessage,encodeFrame,encodeServerMessage,restoreSessions,storeAttachment};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "verani",
3
- "version": "0.4.5",
3
+ "version": "0.5.0",
4
4
  "description": "A simple, focused realtime SDK for Cloudflare Actors with Socket.io-like semantics",
5
5
  "license": "ISC",
6
6
  "keywords": [