verani 0.5.5 → 0.6.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.
Files changed (38) hide show
  1. package/dist/actor-runtime-CbquUZdU.d.mts +345 -0
  2. package/dist/actor-runtime-PyNnwtah.mjs +1 -0
  3. package/dist/actor-runtime-crxBqW3H.d.cts +345 -0
  4. package/dist/actor-runtime-v4YHMnw9.cjs +1 -0
  5. package/dist/client-8ubwq_k4.d.mts +204 -0
  6. package/dist/client-BNppil3J.d.cts +204 -0
  7. package/dist/client-DaUp7q-P.mjs +1 -0
  8. package/dist/client-ofEyep6Z.cjs +1 -0
  9. package/dist/client.cjs +1 -1
  10. package/dist/client.d.cts +3 -205
  11. package/dist/client.d.mts +3 -205
  12. package/dist/client.mjs +1 -1
  13. package/dist/{decode-9DerwlQ1.d.mts → decode-Cetz5jkZ.d.cts} +4 -46
  14. package/dist/{decode-DMA_rBWB.d.cts → decode-Cs8CEYAY.d.mts} +4 -46
  15. package/dist/{types-CJLnZrA8.mjs → encode-BEipaz10.mjs} +1 -1
  16. package/dist/encode-BYFW_6TI.cjs +1 -0
  17. package/dist/typed-client.cjs +1 -0
  18. package/dist/typed-client.d.cts +165 -0
  19. package/dist/typed-client.d.mts +165 -0
  20. package/dist/typed-client.mjs +1 -0
  21. package/dist/typed.cjs +1 -0
  22. package/dist/typed.d.cts +186 -0
  23. package/dist/typed.d.mts +186 -0
  24. package/dist/typed.mjs +1 -0
  25. package/dist/types-B5NT7bya.mjs +1 -0
  26. package/dist/types-Bs7hBgfQ.d.mts +46 -0
  27. package/dist/types-DUO6RVw1.cjs +1 -0
  28. package/dist/types-cugB3Mcz.d.cts +46 -0
  29. package/dist/validation-CSmvG882.d.cts +383 -0
  30. package/dist/validation-D1xWZI_t.d.mts +383 -0
  31. package/dist/validation-DRz-ayMa.mjs +1 -0
  32. package/dist/validation-DvWPGkXK.cjs +1 -0
  33. package/dist/verani.cjs +1 -1
  34. package/dist/verani.d.cts +3 -343
  35. package/dist/verani.d.mts +3 -343
  36. package/dist/verani.mjs +1 -1
  37. package/package.json +21 -1
  38. package/dist/types-083oWz55.cjs +0 -1
@@ -0,0 +1,345 @@
1
+ import { n as ConnectionMeta, r as MessageFrame } from "./types-Bs7hBgfQ.mjs";
2
+ import { Actor, ActorConfiguration } from "@cloudflare/actors";
3
+
4
+ //#region src/actor/types.d.ts
5
+
6
+ /**
7
+ * Options for broadcasting messages to connections
8
+ */
9
+ interface BroadcastOptions {
10
+ /** Exclude specific WebSocket from receiving the broadcast */
11
+ except?: WebSocket;
12
+ /** Only send to specific user IDs */
13
+ userIds?: string[];
14
+ /** Only send to specific client IDs */
15
+ clientIds?: string[];
16
+ }
17
+ /**
18
+ * RPC-safe version of BroadcastOptions for use over RPC calls.
19
+ * Excludes the `except` field since WebSocket cannot be serialized over RPC.
20
+ */
21
+ interface RpcBroadcastOptions {
22
+ /** Only send to specific user IDs */
23
+ userIds?: string[];
24
+ /** Only send to specific client IDs */
25
+ clientIds?: string[];
26
+ }
27
+ /**
28
+ * Actor stub interface returned by .get() method.
29
+ * Provides RPC access to actor methods that can be called remotely.
30
+ *
31
+ * Note: RPC methods return Promises even if the underlying method is synchronous.
32
+ * Methods that return non-serializable types (like WebSocket[] or DurableObjectStorage)
33
+ * are excluded from this interface.
34
+ */
35
+ interface ActorStub {
36
+ /**
37
+ * Standard fetch method for handling HTTP requests and WebSocket upgrades
38
+ */
39
+ fetch(request: Request): Promise<Response>;
40
+ /**
41
+ * Socket.IO-like emit API: Emit an event to a specific channel via RPC.
42
+ * @param channel - Channel name to emit to
43
+ * @param event - Event name
44
+ * @param data - Event data
45
+ * @returns Promise resolving to the number of connections that received the message
46
+ * @example
47
+ * ```typescript
48
+ * await stub.emitToChannel("default", "announcement", { text: "Hello!" });
49
+ * ```
50
+ */
51
+ emitToChannel(channel: string, event: string, data?: any): Promise<number>;
52
+ /**
53
+ * Socket.IO-like emit API: Emit an event to a specific user (all their sessions) via RPC.
54
+ * @param userId - User ID to emit to
55
+ * @param event - Event name
56
+ * @param data - Event data
57
+ * @returns Promise resolving to the number of sessions that received the message
58
+ * @example
59
+ * ```typescript
60
+ * await stub.emitToUser("alice", "notification", { message: "Hello!" });
61
+ * ```
62
+ */
63
+ emitToUser(userId: string, event: string, data?: any): Promise<number>;
64
+ /**
65
+ * @deprecated Use `emitToUser()` instead for Socket.IO-like API.
66
+ * Sends a message to a specific user (all their sessions) via RPC.
67
+ * @param userId - The user ID to send to
68
+ * @param channel - The channel to send to
69
+ * @param data - Message data
70
+ * @returns Promise resolving to the number of sessions that received the message
71
+ */
72
+ sendToUser(userId: string, channel: string, data?: any): Promise<number>;
73
+ /**
74
+ * @deprecated Use `emitToChannel()` instead for Socket.IO-like API.
75
+ * Broadcasts a message to all connections in a channel via RPC.
76
+ * Note: The `except` option from BroadcastOptions is not available over RPC
77
+ * since WebSocket cannot be serialized.
78
+ * @param channel - The channel to broadcast to
79
+ * @param data - The data to send
80
+ * @param opts - Broadcast options (filtering by userIds or clientIds)
81
+ * @returns Promise resolving to the number of connections that received the message
82
+ */
83
+ broadcast(channel: string, data: any, opts?: RpcBroadcastOptions): Promise<number>;
84
+ /**
85
+ * Gets the total number of active sessions via RPC.
86
+ * @returns Promise resolving to the number of connected WebSockets
87
+ */
88
+ getSessionCount(): Promise<number>;
89
+ /**
90
+ * Gets all unique user IDs currently connected via RPC.
91
+ * @returns Promise resolving to an array of unique user IDs
92
+ */
93
+ getConnectedUserIds(): Promise<string[]>;
94
+ /**
95
+ * Removes all WebSocket sessions that are not in OPEN state via RPC.
96
+ * This prevents stale connections from accumulating in memory.
97
+ * @returns Promise resolving to the number of sessions cleaned up
98
+ */
99
+ cleanupStaleSessions(): Promise<number>;
100
+ }
101
+ /**
102
+ * Extended Actor interface with Verani-specific methods
103
+ */
104
+ interface VeraniActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> extends Actor<E> {
105
+ /**
106
+ * Map of active WebSocket sessions keyed by their WebSocket instance.
107
+ * Each entry contains the WebSocket and its associated metadata.
108
+ * See: @src/actor/actor-runtime.ts usage for session management.
109
+ */
110
+ sessions: Map<WebSocket, {
111
+ ws: WebSocket;
112
+ meta: TMeta;
113
+ }>;
114
+ /**
115
+ * Broadcast a message to all connections in a channel.
116
+ * Performs channel, userId, clientId, and exclusion filtering.
117
+ * Returns the number of connections the message was sent to.
118
+ * @see @src/actor/actor-runtime.ts broadcast()
119
+ */
120
+ broadcast(channel: string, data: any, opts?: BroadcastOptions): number;
121
+ /**
122
+ * Returns the number of currently connected WebSocket sessions.
123
+ * @see @src/actor/actor-runtime.ts getSessionCount()
124
+ */
125
+ getSessionCount(): number;
126
+ /**
127
+ * Get all unique user IDs currently connected to this actor.
128
+ * @see @src/actor/actor-runtime.ts getConnectedUserIds()
129
+ */
130
+ getConnectedUserIds(): string[];
131
+ /**
132
+ * Get all WebSocket sessions for a given user ID.
133
+ * @see @src/actor/actor-runtime.ts getUserSessions()
134
+ */
135
+ getUserSessions(userId: string): WebSocket[];
136
+ /**
137
+ * Send a message to all sessions belonging to a user ID in a given channel.
138
+ * Message will only be sent to sessions where the user's channels include the given channel.
139
+ * Returns the number of sessions the message was sent to.
140
+ * The message "type" is always "event" (see src/actor/actor-runtime.ts).
141
+ * @see @src/actor/actor-runtime.ts sendToUser()
142
+ */
143
+ sendToUser(userId: string, channel: string, data?: any): number;
144
+ /**
145
+ * Validates and removes stale WebSocket sessions.
146
+ * Called automatically during broadcast/send operations, but can be called manually.
147
+ * Returns the number of stale sessions removed.
148
+ * @see @src/actor/actor-runtime.ts cleanupStaleSessions()
149
+ */
150
+ cleanupStaleSessions(): number;
151
+ /**
152
+ * Access the Durable Object storage API for this actor instance.
153
+ * @see @src/actor/actor-runtime.ts getStorage()
154
+ */
155
+ getStorage(): DurableObjectStorage;
156
+ /**
157
+ * Socket.io-like emit API for actor-level broadcasting
158
+ */
159
+ emit: ActorEmit<TMeta, E>;
160
+ }
161
+ /**
162
+ * Event handler function type for socket.io-like event handling
163
+ */
164
+ type EventHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> = (ctx: MessageContext<TMeta, E>, data: any) => void | Promise<void>;
165
+ /**
166
+ * Event emitter interface for room-level event handling
167
+ */
168
+ interface RoomEventEmitter<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
169
+ /**
170
+ * Register an event handler
171
+ * @param event - Event name (supports wildcard "*")
172
+ * @param handler - Handler function
173
+ */
174
+ on(event: string, handler: EventHandler<TMeta, E>): void;
175
+ /**
176
+ * Remove an event handler
177
+ * @param event - Event name
178
+ * @param handler - Optional specific handler to remove, or remove all handlers for event
179
+ */
180
+ off(event: string, handler?: EventHandler<TMeta, E>): void;
181
+ /**
182
+ * Emit an event to registered handlers
183
+ * @param event - Event name
184
+ * @param ctx - Message context
185
+ * @param data - Event data
186
+ */
187
+ emit(event: string, ctx: MessageContext<TMeta, E>, data: any): Promise<void>;
188
+ /**
189
+ * Rebuild handlers from static storage.
190
+ * Called after hibernation to restore handlers from the room definition.
191
+ * This method MUST be implemented by all event emitter implementations.
192
+ * @param staticHandlers - Map of event names to handler sets from static storage
193
+ */
194
+ rebuildHandlers(staticHandlers: Map<string, Set<EventHandler<TMeta, E>>>): void;
195
+ }
196
+ /**
197
+ * Builder interface for targeting specific scopes when emitting
198
+ */
199
+ interface EmitBuilder<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
200
+ /**
201
+ * Emit to the targeted scope
202
+ * @param event - Event name
203
+ * @param data - Event data
204
+ * @returns Number of connections that received the message
205
+ */
206
+ emit(event: string, data?: any): number;
207
+ }
208
+ /**
209
+ * Socket-level emit API (available on context)
210
+ * Allows emitting to current socket, user, or channel
211
+ */
212
+ interface SocketEmit<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
213
+ /**
214
+ * Emit to the current socket
215
+ * @param event - Event name
216
+ * @param data - Event data
217
+ */
218
+ emit(event: string, data?: any): void;
219
+ /**
220
+ * Target a specific user or channel for emitting
221
+ * @param target - User ID or channel name
222
+ * @returns Builder for emitting to the target
223
+ */
224
+ to(target: string): EmitBuilder<TMeta, E>;
225
+ }
226
+ /**
227
+ * Actor-level emit API (available on actor)
228
+ * Allows broadcasting to channels
229
+ */
230
+ interface ActorEmit<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
231
+ /**
232
+ * Broadcast to default channel
233
+ * @param event - Event name
234
+ * @param data - Event data
235
+ * @returns Number of connections that received the message
236
+ */
237
+ emit(event: string, data?: any): number;
238
+ /**
239
+ * Target a specific channel for broadcasting
240
+ * @param channel - Channel name
241
+ * @returns Builder for emitting to the channel
242
+ */
243
+ to(channel: string): EmitBuilder<TMeta, E>;
244
+ }
245
+ /**
246
+ * Context provided to room lifecycle hooks
247
+ */
248
+ interface RoomContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
249
+ /** The actor instance handling this connection */
250
+ actor: VeraniActor<TMeta, E>;
251
+ /** The WebSocket connection */
252
+ ws: WebSocket;
253
+ /** Connection metadata */
254
+ meta: TMeta;
255
+ /** Socket.io-like emit API for this connection */
256
+ emit: SocketEmit<TMeta, E>;
257
+ }
258
+ /**
259
+ * Context for onMessage hook with frame included
260
+ */
261
+ interface MessageContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> extends RoomContext<TMeta, E> {
262
+ /** The received message frame */
263
+ frame: MessageFrame;
264
+ }
265
+ /**
266
+ * Room definition with lifecycle hooks
267
+ *
268
+ * **Important:** All lifecycle hooks are properly awaited if they return a Promise.
269
+ * This ensures async operations complete before the actor proceeds to the next step
270
+ * or potentially enters hibernation.
271
+ */
272
+ interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
273
+ /** Optional room name for debugging */
274
+ name?: string;
275
+ /** WebSocket upgrade path (default: "/ws") */
276
+ websocketPath: string;
277
+ /**
278
+ * Extract metadata from the connection request.
279
+ * This function is awaited if it returns a Promise.
280
+ */
281
+ extractMeta?(req: Request): TMeta | Promise<TMeta>;
282
+ /**
283
+ * Called when a new WebSocket connection is established.
284
+ * This hook is awaited if it returns a Promise. The session is only added to the
285
+ * sessions map after this hook completes successfully. If this hook throws, the
286
+ * connection is closed and no orphaned session is created.
287
+ */
288
+ onConnect?(ctx: RoomContext<TMeta, E>): void | Promise<void>;
289
+ /**
290
+ * Called when a WebSocket connection is closed.
291
+ * This hook is awaited if it returns a Promise. The session is removed from the
292
+ * sessions map before this hook is called.
293
+ */
294
+ onDisconnect?(ctx: RoomContext<TMeta, E>): void | Promise<void>;
295
+ /**
296
+ * Called when a message is received from a connection.
297
+ * This hook is awaited if it returns a Promise. The actor will not process
298
+ * other messages from this connection until this hook completes.
299
+ *
300
+ * **Note:** If event handlers are registered via `eventEmitter`, they take priority.
301
+ * This hook is used as a fallback when no matching event handler is found.
302
+ */
303
+ onMessage?(ctx: MessageContext<TMeta, E>, frame: MessageFrame): void | Promise<void>;
304
+ /**
305
+ * Called when an error occurs in a lifecycle hook.
306
+ * This hook is also awaited if it returns a Promise.
307
+ */
308
+ onError?(error: Error, ctx: RoomContext<TMeta, E>): void | Promise<void>;
309
+ /**
310
+ * Called after actor wakes from hibernation and sessions are restored.
311
+ * This hook is awaited if it returns a Promise. It is called even if some
312
+ * sessions failed to restore, allowing you to handle partial restoration scenarios.
313
+ */
314
+ onHibernationRestore?(actor: VeraniActor<TMeta, E>): void | Promise<void>;
315
+ /**
316
+ * Event emitter for socket.io-like event handling.
317
+ * If provided, event handlers registered here will be called for matching message types.
318
+ * If not provided, a default event emitter will be created.
319
+ */
320
+ eventEmitter?: RoomEventEmitter<TMeta, E>;
321
+ /**
322
+ * Static handler storage that persists across hibernation.
323
+ * Handlers registered via room.on() are stored here and rebuilt in onInit.
324
+ * @internal
325
+ */
326
+ _staticHandlers?: Map<string, Set<EventHandler<TMeta, E>>>;
327
+ }
328
+ //#endregion
329
+ //#region src/actor/actor-runtime.d.ts
330
+ /**
331
+ * Return type for createActorHandler - represents an Actor class constructor
332
+ */
333
+ type ActorHandlerClass<E = unknown> = {
334
+ new (state: any, env: E): Actor<E>;
335
+ get(id: string): ActorStub;
336
+ configuration(request?: Request): ActorConfiguration;
337
+ };
338
+ /**
339
+ * Creates an Actor handler from a room definition
340
+ * @param room - The room definition with lifecycle hooks
341
+ * @returns Actor class for Cloudflare Workers (extends DurableObject)
342
+ */
343
+ declare function createActorHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown>(room: RoomDefinition<TMeta, E>): ActorHandlerClass<E>;
344
+ //#endregion
345
+ export { EventHandler as a, RoomDefinition as c, BroadcastOptions as i, RpcBroadcastOptions as l, createActorHandler as n, MessageContext as o, ActorStub as r, RoomContext as s, ActorHandlerClass as t, VeraniActor as u };
@@ -0,0 +1 @@
1
+ import{a as decodeFrame$1,n as encodeFrame$1}from"./encode-BEipaz10.mjs";import{Actor}from"@cloudflare/actors";var RoomEventEmitterImpl=class{constructor(){this.handlers=new Map}on(e,w){this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(w)}off(e,w){let T=this.handlers.get(e);T&&(w?(T.delete(w),T.size===0&&this.handlers.delete(e)):this.handlers.delete(e))}async emit(e,w,T){let E=this.handlers.get(e);if(E&&E.size>0){let e=[];for(let D of E)try{let E=D(w,T);E instanceof Promise&&e.push(E)}catch{}await Promise.all(e)}let D=this.handlers.get(`*`);if(D&&D.size>0){let e=[];for(let E of D)try{let D=E(w,T);D instanceof Promise&&e.push(D)}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[w,T]of e.entries())this.handlers.set(w,new Set(T))}};function createRoomEventEmitter(){return new RoomEventEmitterImpl}function defaultExtractMeta(e){let w=crypto.randomUUID(),T=crypto.randomUUID(),E=new URL(e.url).searchParams.get(`channels`);return{userId:w,clientId:T,channels:E?E.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){let w=e.eventEmitter||createRoomEventEmitter(),T=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:w,_staticHandlers:T,on(e,E){w.on(e,E),T.has(e)||T.set(e,new Set),T.get(e).add(E)},off(e,E){w.off(e,E);let D=T.get(e);D&&(E?(D.delete(E),D.size===0&&T.delete(e)):T.delete(e))}}}function cleanupStaleSessions(e){let w=0,T=[];for(let[w,E]of e.entries())w.readyState!==WebSocket.OPEN&&T.push(w);for(let E of T)e.delete(E),w++;return w}function decodeFrame(w){return decodeFrame$1(w)??{type:`invalid`}}function encodeFrame(e){return encodeFrame$1(e)}function broadcast(e,w,T,E){let D=0,O=encodeFrame({type:`event`,channel:w,data:T}),k=[];for(let{ws:T,meta:A}of e.values())if(A.channels.includes(w)&&!(E?.except&&T===E.except)&&!(E?.userIds&&!E.userIds.includes(A.userId))&&!(E?.clientIds&&!E.clientIds.includes(A.clientId))){if(T.readyState!==WebSocket.OPEN){k.push(T);continue}try{T.send(O),D++}catch{k.push(T)}}for(let w of k)e.delete(w);return k.length,D}function sendToUser(e,w,T,E){let D=0,O=encodeFrame({type:`event`,channel:T,data:E}),k=[];for(let{ws:E,meta:A}of e.values())if(A.userId===w&&A.channels.includes(T)){if(E.readyState!==WebSocket.OPEN){k.push(E);continue}try{E.send(O),D++}catch{k.push(E)}}for(let w of k)e.delete(w);return k.length,D}function getSessionCount(e){return e.size}function getConnectedUserIds(e){let w=new Set;for(let{meta:T}of e.values())w.add(T.userId);return Array.from(w)}function getUserSessions(e,w){let T=[];for(let{ws:E,meta:D}of e.values())D.userId===w&&T.push(E);return T}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(w){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,w){e.serializeAttachment(w)}function restoreSessions(e){let w=0,T=0;for(let E of e.ctx.getWebSockets()){if(E.readyState!==WebSocket.OPEN){T++;continue}let D=E.deserializeAttachment();if(!D){T++;continue}if(!isValidConnectionMeta(D)){T++;continue}e.sessions.set(E,{ws:E,meta:D}),w++}}async function onInit(e,w){if(w.eventEmitter&&w._staticHandlers)try{w.eventEmitter.rebuildHandlers(w._staticHandlers)}catch{}try{restoreSessions(e)}catch{}if(w.onHibernationRestore&&e.sessions.size>0)try{await w.onHibernationRestore(e)}catch{}else w.onHibernationRestore&&e.sessions.size}function createUserEmitBuilder(e,w,T){return{emit(E,D){return sendToUser(w,e,T,{type:E,...D})}}}function createChannelEmitBuilder(e,w,T){return{emit(E,D){return broadcast(w,e,{type:E,...D},T)}}}function createSocketEmit(e){let w=e.meta.channels[0]||`default`;return{emit(T,E){if(e.ws.readyState===WebSocket.OPEN)try{let D={type:`event`,channel:w,data:{type:T,...E}};e.ws.send(encodeFrame(D))}catch{}},to(T){return e.meta.channels.includes(T)?createChannelEmitBuilder(T,e.actor.sessions,{except:e.ws}):createUserEmitBuilder(T,e.actor.sessions,w)}}}function createActorEmit(e){return{emit(w,T){let E={type:w,...T};return broadcast(e.sessions,`default`,E)},to(w){return createChannelEmitBuilder(w,e.sessions)}}}async function onWebSocketConnect(e,w,T,E){let D;try{D=w.extractMeta?await w.extractMeta(E):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(T,D);let O={actor:e,ws:T,meta:D,frame:{type:`connect`}};if(w.onConnect){let E={actor:e,ws:T,meta:D,emit:createSocketEmit(O)};await w.onConnect(E)}e.sessions.set(T,{ws:T,meta:D})}catch(E){if(w.onError&&D)try{let O={actor:e,ws:T,meta:D,frame:{type:`error`}};await w.onError(E,{actor:e,ws:T,meta:D,emit:createSocketEmit(O)})}catch{}T.close(1011,`Internal server error`)}}async function onWebSocketMessage(e,w,T,E){let D;try{let O=decodeFrame(E);if(O&&O.type===`ping`){if(T.readyState===WebSocket.OPEN)try{T.send(encodeFrame({type:`pong`}))}catch{}return}if(!O||O.type===`invalid`||(D=e.sessions.get(T),!D))return;let k={actor:e,ws:T,meta:D.meta,frame:O,emit:createSocketEmit({actor:e,ws:T,meta:D.meta,frame:O})},A=w.eventEmitter;A&&A.hasHandlers&&A.hasHandlers(O.type)?await A.emit(O.type,k,O.data||{}):w.onMessage&&await w.onMessage(k,O)}catch(E){if(w.onError&&D)try{await w.onError(E,{actor:e,ws:T,meta:D.meta,emit:createSocketEmit({actor:e,ws:T,meta:D.meta,frame:{type:`error`}})})}catch{}}}async function onWebSocketDisconnect(e,w,T){try{let E=e.sessions.get(T);if(e.sessions.delete(T),E&&w.onDisconnect){let D={actor:e,ws:T,meta:E.meta,frame:{type:`disconnect`}},O={actor:e,ws:T,meta:E.meta,emit:createSocketEmit(D)};await w.onDisconnect(O)}}catch{}}function createFetch(e,w){return async function(T){let E=new URL(T.url),D=T.headers.get(`Upgrade`);return E.pathname===e.websocketPath&&D===`websocket`&&await w.shouldUpgradeWebSocket(T)?w.onWebSocketUpgrade(T):w.onRequest(T)}}function createActorHandler(e){let w=sanitizeToClassName(e.name||e.websocketPath||`VeraniActor`);class E extends Actor{constructor(...w){super(...w),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(w,T){await onWebSocketConnect(this,e,w,T)}async onWebSocketMessage(w,T){await onWebSocketMessage(this,e,w,T)}async onWebSocketDisconnect(w){await onWebSocketDisconnect(this,e,w)}cleanupStaleSessions(){return cleanupStaleSessions(this.sessions)}broadcast(e,w,T){return broadcast(this.sessions,e,w,T)}getSessionCount(){return getSessionCount(this.sessions)}getConnectedUserIds(){return getConnectedUserIds(this.sessions)}getUserSessions(e){return getUserSessions(this.sessions,e)}sendToUser(e,w,T){return sendToUser(this.sessions,e,w,T)}emitToChannel(e,w,T){let E={type:w,...T};return broadcast(this.sessions,e,E)}emitToUser(e,w,T){let E=encodeFrame({type:`event`,channel:`default`,data:{type:w,...T}}),D=0,O=[];for(let{ws:w,meta:T}of this.sessions.values())if(T.userId===e){if(w.readyState!==WebSocket.OPEN){O.push(w);continue}try{w.send(E),D++}catch{O.push(w)}}for(let e of O)this.sessions.delete(e);return D}getStorage(){return getStorage(this.ctx)}}return Object.defineProperty(E,`name`,{value:w,writable:!1,configurable:!0}),E}export{defineRoom as i,restoreSessions as n,storeAttachment as r,createActorHandler as t};