verani 0.6.1 → 0.8.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/{actor-runtime-D_xHVOcH.d.mts → actor-runtime-Dkz-u49p.d.mts} +173 -21
- package/dist/actor-runtime-Fr0n1XGB.cjs +1 -0
- package/dist/{actor-runtime-98JPmjaf.d.cts → actor-runtime-gMSqKdY5.d.cts} +173 -21
- package/dist/actor-runtime-mfMYeKoB.mjs +1 -0
- package/dist/typed.cjs +1 -1
- package/dist/typed.d.cts +6 -6
- package/dist/typed.d.mts +6 -6
- package/dist/typed.mjs +1 -1
- package/dist/verani.cjs +1 -1
- package/dist/verani.d.cts +6 -6
- package/dist/verani.d.mts +6 -6
- package/dist/verani.mjs +1 -1
- package/package.json +1 -1
- package/dist/actor-runtime-DamIMiBw.mjs +0 -1
- package/dist/actor-runtime-v4YHMnw9.cjs +0 -1
|
@@ -1,8 +1,104 @@
|
|
|
1
1
|
import { n as ConnectionMeta, r as MessageFrame } from "./types-Ds_V-GWZ.mjs";
|
|
2
2
|
import { Actor, ActorConfiguration } from "@cloudflare/actors";
|
|
3
3
|
|
|
4
|
-
//#region src/actor/
|
|
4
|
+
//#region src/actor/persist.d.ts
|
|
5
5
|
|
|
6
|
+
declare const PERSIST_ERROR_HANDLER: unique symbol;
|
|
7
|
+
declare const PERSISTED_STATE: unique symbol;
|
|
8
|
+
declare const STATE_READY: unique symbol;
|
|
9
|
+
/**
|
|
10
|
+
* Error thrown when persisted state is accessed before initialization
|
|
11
|
+
*/
|
|
12
|
+
declare class PersistNotReadyError extends Error {
|
|
13
|
+
constructor(key: string);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Error thrown when persistence operation fails
|
|
17
|
+
*/
|
|
18
|
+
declare class PersistError extends Error {
|
|
19
|
+
readonly originalCause: Error;
|
|
20
|
+
constructor(key: string, cause: Error);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Options for SafePersist behavior
|
|
24
|
+
*/
|
|
25
|
+
interface SafePersistOptions {
|
|
26
|
+
/**
|
|
27
|
+
* If true, only track top-level property changes (no deep proxy).
|
|
28
|
+
* This is safer and more predictable. Default: true
|
|
29
|
+
*/
|
|
30
|
+
shallow?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* If true, throw errors instead of swallowing them. Default: true
|
|
33
|
+
*/
|
|
34
|
+
throwOnError?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Safely serialize a value for storage.
|
|
38
|
+
* Handles circular references and special types gracefully.
|
|
39
|
+
*/
|
|
40
|
+
declare function safeSerialize(value: unknown): string;
|
|
41
|
+
/**
|
|
42
|
+
* Safely deserialize a value from storage.
|
|
43
|
+
* Restores special types that were serialized.
|
|
44
|
+
*/
|
|
45
|
+
declare function safeDeserialize(json: string): unknown;
|
|
46
|
+
/**
|
|
47
|
+
* Interface for actors that support safe persistence
|
|
48
|
+
*/
|
|
49
|
+
interface PersistableActor {
|
|
50
|
+
/** Durable Object storage interface */
|
|
51
|
+
ctx: {
|
|
52
|
+
storage: DurableObjectStorage;
|
|
53
|
+
};
|
|
54
|
+
/** Whether the state has been initialized from storage */
|
|
55
|
+
[STATE_READY]?: boolean;
|
|
56
|
+
/** The persisted state object */
|
|
57
|
+
[PERSISTED_STATE]?: Record<string, unknown>;
|
|
58
|
+
/** Error handler for persistence failures */
|
|
59
|
+
[PERSIST_ERROR_HANDLER]?: (key: string, error: Error) => void;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Initialize persisted state from Durable Object storage.
|
|
63
|
+
* Call this in the actor's onInit or constructor after storage is available.
|
|
64
|
+
*
|
|
65
|
+
* @param actor - The actor instance
|
|
66
|
+
* @param initialState - Default state values
|
|
67
|
+
* @param persistedKeys - Keys to persist (empty = all keys)
|
|
68
|
+
* @param options - Persistence options
|
|
69
|
+
*/
|
|
70
|
+
declare function initializePersistedState<T extends Record<string, unknown>>(actor: PersistableActor, initialState: T, persistedKeys?: (keyof T)[], options?: SafePersistOptions): Promise<T>;
|
|
71
|
+
/**
|
|
72
|
+
* Helper to check if state is ready for access
|
|
73
|
+
*/
|
|
74
|
+
declare function isStateReady(actor: PersistableActor): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Helper to get persisted state with type safety.
|
|
77
|
+
* Throws if state is not yet initialized.
|
|
78
|
+
*/
|
|
79
|
+
declare function getPersistedState<T extends Record<string, unknown>>(actor: PersistableActor): T;
|
|
80
|
+
/**
|
|
81
|
+
* Set the error handler for persistence failures
|
|
82
|
+
*/
|
|
83
|
+
declare function setPeristErrorHandler(actor: PersistableActor, handler: (key: string, error: Error) => void): void;
|
|
84
|
+
/**
|
|
85
|
+
* Manually persist a specific key (useful for batch updates)
|
|
86
|
+
*/
|
|
87
|
+
declare function persistKey(actor: PersistableActor, key: string, value: unknown): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Manually delete a persisted key
|
|
90
|
+
*/
|
|
91
|
+
declare function deletePersistedKey(actor: PersistableActor, key: string): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Get all persisted keys from storage
|
|
94
|
+
*/
|
|
95
|
+
declare function getPersistedKeys(actor: PersistableActor): Promise<string[]>;
|
|
96
|
+
/**
|
|
97
|
+
* Clear all persisted state
|
|
98
|
+
*/
|
|
99
|
+
declare function clearPersistedState(actor: PersistableActor): Promise<void>;
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/actor/types.d.ts
|
|
6
102
|
/**
|
|
7
103
|
* Options for broadcasting messages to connections
|
|
8
104
|
*/
|
|
@@ -101,7 +197,7 @@ interface ActorStub {
|
|
|
101
197
|
/**
|
|
102
198
|
* Extended Actor interface with Verani-specific methods
|
|
103
199
|
*/
|
|
104
|
-
interface VeraniActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> extends Actor<E> {
|
|
200
|
+
interface VeraniActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> extends Actor<E> {
|
|
105
201
|
/**
|
|
106
202
|
* Map of active WebSocket sessions keyed by their WebSocket instance.
|
|
107
203
|
* Each entry contains the WebSocket and its associated metadata.
|
|
@@ -111,6 +207,17 @@ interface VeraniActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown
|
|
|
111
207
|
ws: WebSocket;
|
|
112
208
|
meta: TMeta;
|
|
113
209
|
}>;
|
|
210
|
+
/**
|
|
211
|
+
* User-defined persisted state for this actor.
|
|
212
|
+
* Access this after onInit completes. Changes to tracked keys are automatically persisted.
|
|
213
|
+
* @see RoomDefinition.state and RoomDefinition.persistedKeys
|
|
214
|
+
*/
|
|
215
|
+
roomState: TState;
|
|
216
|
+
/**
|
|
217
|
+
* Check if the persisted state has been initialized.
|
|
218
|
+
* Returns true after onInit completes and state is loaded from storage.
|
|
219
|
+
*/
|
|
220
|
+
isStateReady(): boolean;
|
|
114
221
|
/**
|
|
115
222
|
* Broadcast a message to all connections in a channel.
|
|
116
223
|
* Performs channel, userId, clientId, and exclusion filtering.
|
|
@@ -161,37 +268,37 @@ interface VeraniActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown
|
|
|
161
268
|
/**
|
|
162
269
|
* Event handler function type for socket.io-like event handling
|
|
163
270
|
*/
|
|
164
|
-
type EventHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> = (ctx: MessageContext<TMeta, E>, data: any) => void | Promise<void>;
|
|
271
|
+
type EventHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> = (ctx: MessageContext<TMeta, E, TState>, data: any) => void | Promise<void>;
|
|
165
272
|
/**
|
|
166
273
|
* Event emitter interface for room-level event handling
|
|
167
274
|
*/
|
|
168
|
-
interface RoomEventEmitter<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
275
|
+
interface RoomEventEmitter<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
|
|
169
276
|
/**
|
|
170
277
|
* Register an event handler
|
|
171
278
|
* @param event - Event name (supports wildcard "*")
|
|
172
279
|
* @param handler - Handler function
|
|
173
280
|
*/
|
|
174
|
-
on(event: string, handler: EventHandler<TMeta, E>): void;
|
|
281
|
+
on(event: string, handler: EventHandler<TMeta, E, TState>): void;
|
|
175
282
|
/**
|
|
176
283
|
* Remove an event handler
|
|
177
284
|
* @param event - Event name
|
|
178
285
|
* @param handler - Optional specific handler to remove, or remove all handlers for event
|
|
179
286
|
*/
|
|
180
|
-
off(event: string, handler?: EventHandler<TMeta, E>): void;
|
|
287
|
+
off(event: string, handler?: EventHandler<TMeta, E, TState>): void;
|
|
181
288
|
/**
|
|
182
289
|
* Emit an event to registered handlers
|
|
183
290
|
* @param event - Event name
|
|
184
291
|
* @param ctx - Message context
|
|
185
292
|
* @param data - Event data
|
|
186
293
|
*/
|
|
187
|
-
emit(event: string, ctx: MessageContext<TMeta, E>, data: any): Promise<void>;
|
|
294
|
+
emit(event: string, ctx: MessageContext<TMeta, E, TState>, data: any): Promise<void>;
|
|
188
295
|
/**
|
|
189
296
|
* Rebuild handlers from static storage.
|
|
190
297
|
* Called after hibernation to restore handlers from the room definition.
|
|
191
298
|
* This method MUST be implemented by all event emitter implementations.
|
|
192
299
|
* @param staticHandlers - Map of event names to handler sets from static storage
|
|
193
300
|
*/
|
|
194
|
-
rebuildHandlers(staticHandlers: Map<string, Set<EventHandler<TMeta, E>>>): void;
|
|
301
|
+
rebuildHandlers(staticHandlers: Map<string, Set<EventHandler<TMeta, E, TState>>>): void;
|
|
195
302
|
}
|
|
196
303
|
/**
|
|
197
304
|
* Builder interface for targeting specific scopes when emitting
|
|
@@ -245,9 +352,9 @@ interface ActorEmit<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown>
|
|
|
245
352
|
/**
|
|
246
353
|
* Context provided to room lifecycle hooks
|
|
247
354
|
*/
|
|
248
|
-
interface RoomContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
355
|
+
interface RoomContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
|
|
249
356
|
/** The actor instance handling this connection */
|
|
250
|
-
actor: VeraniActor<TMeta, E>;
|
|
357
|
+
actor: VeraniActor<TMeta, E, TState>;
|
|
251
358
|
/** The WebSocket connection */
|
|
252
359
|
ws: WebSocket;
|
|
253
360
|
/** Connection metadata */
|
|
@@ -258,7 +365,7 @@ interface RoomContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown
|
|
|
258
365
|
/**
|
|
259
366
|
* Context for onMessage hook with frame included
|
|
260
367
|
*/
|
|
261
|
-
interface MessageContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> extends RoomContext<TMeta, E> {
|
|
368
|
+
interface MessageContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> extends RoomContext<TMeta, E, TState> {
|
|
262
369
|
/** The received message frame */
|
|
263
370
|
frame: MessageFrame;
|
|
264
371
|
}
|
|
@@ -268,8 +375,12 @@ interface MessageContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
268
375
|
* **Important:** All lifecycle hooks are properly awaited if they return a Promise.
|
|
269
376
|
* This ensures async operations complete before the actor proceeds to the next step
|
|
270
377
|
* or potentially enters hibernation.
|
|
378
|
+
*
|
|
379
|
+
* @template TMeta - Connection metadata type
|
|
380
|
+
* @template E - Environment type
|
|
381
|
+
* @template TState - Room state type (for persistence)
|
|
271
382
|
*/
|
|
272
|
-
interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
383
|
+
interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
|
|
273
384
|
/** Optional room name for debugging */
|
|
274
385
|
name?: string;
|
|
275
386
|
/** WebSocket upgrade path (default: "/ws") */
|
|
@@ -285,13 +396,13 @@ interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
285
396
|
* sessions map after this hook completes successfully. If this hook throws, the
|
|
286
397
|
* connection is closed and no orphaned session is created.
|
|
287
398
|
*/
|
|
288
|
-
onConnect?(ctx: RoomContext<TMeta, E>): void | Promise<void>;
|
|
399
|
+
onConnect?(ctx: RoomContext<TMeta, E, TState>): void | Promise<void>;
|
|
289
400
|
/**
|
|
290
401
|
* Called when a WebSocket connection is closed.
|
|
291
402
|
* This hook is awaited if it returns a Promise. The session is removed from the
|
|
292
403
|
* sessions map before this hook is called.
|
|
293
404
|
*/
|
|
294
|
-
onDisconnect?(ctx: RoomContext<TMeta, E>): void | Promise<void>;
|
|
405
|
+
onDisconnect?(ctx: RoomContext<TMeta, E, TState>): void | Promise<void>;
|
|
295
406
|
/**
|
|
296
407
|
* Called when a message is received from a connection.
|
|
297
408
|
* This hook is awaited if it returns a Promise. The actor will not process
|
|
@@ -300,30 +411,71 @@ interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
300
411
|
* **Note:** If event handlers are registered via `eventEmitter`, they take priority.
|
|
301
412
|
* This hook is used as a fallback when no matching event handler is found.
|
|
302
413
|
*/
|
|
303
|
-
onMessage?(ctx: MessageContext<TMeta, E>, frame: MessageFrame): void | Promise<void>;
|
|
414
|
+
onMessage?(ctx: MessageContext<TMeta, E, TState>, frame: MessageFrame): void | Promise<void>;
|
|
304
415
|
/**
|
|
305
416
|
* Called when an error occurs in a lifecycle hook.
|
|
306
417
|
* This hook is also awaited if it returns a Promise.
|
|
307
418
|
*/
|
|
308
|
-
onError?(error: Error, ctx: RoomContext<TMeta, E>): void | Promise<void>;
|
|
419
|
+
onError?(error: Error, ctx: RoomContext<TMeta, E, TState>): void | Promise<void>;
|
|
309
420
|
/**
|
|
310
421
|
* Called after actor wakes from hibernation and sessions are restored.
|
|
311
422
|
* This hook is awaited if it returns a Promise. It is called even if some
|
|
312
423
|
* sessions failed to restore, allowing you to handle partial restoration scenarios.
|
|
313
424
|
*/
|
|
314
|
-
onHibernationRestore?(actor: VeraniActor<TMeta, E>): void | Promise<void>;
|
|
425
|
+
onHibernationRestore?(actor: VeraniActor<TMeta, E, TState>): void | Promise<void>;
|
|
315
426
|
/**
|
|
316
427
|
* Event emitter for socket.io-like event handling.
|
|
317
428
|
* If provided, event handlers registered here will be called for matching message types.
|
|
318
429
|
* If not provided, a default event emitter will be created.
|
|
319
430
|
*/
|
|
320
|
-
eventEmitter?: RoomEventEmitter<TMeta, E>;
|
|
431
|
+
eventEmitter?: RoomEventEmitter<TMeta, E, TState>;
|
|
321
432
|
/**
|
|
322
433
|
* Static handler storage that persists across hibernation.
|
|
323
434
|
* Handlers registered via room.on() are stored here and rebuilt in onInit.
|
|
324
435
|
* @internal
|
|
325
436
|
*/
|
|
326
|
-
_staticHandlers?: Map<string, Set<EventHandler<TMeta, E>>>;
|
|
437
|
+
_staticHandlers?: Map<string, Set<EventHandler<TMeta, E, TState>>>;
|
|
438
|
+
/**
|
|
439
|
+
* Initial state for this room. This object defines the default values
|
|
440
|
+
* for your room's state. Access via `actor.roomState` in lifecycle hooks.
|
|
441
|
+
*
|
|
442
|
+
* @example
|
|
443
|
+
* ```typescript
|
|
444
|
+
* const room = defineRoom({
|
|
445
|
+
* state: {
|
|
446
|
+
* messageCount: 0,
|
|
447
|
+
* lastActivity: null as Date | null,
|
|
448
|
+
* settings: { maxUsers: 100 }
|
|
449
|
+
* },
|
|
450
|
+
* persistedKeys: ['messageCount', 'settings'],
|
|
451
|
+
* // ...
|
|
452
|
+
* });
|
|
453
|
+
* ```
|
|
454
|
+
*/
|
|
455
|
+
state?: TState;
|
|
456
|
+
/**
|
|
457
|
+
* Keys from `state` to persist to Durable Object storage.
|
|
458
|
+
* If empty or undefined, no state is persisted.
|
|
459
|
+
* Changes to these keys are automatically saved and restored on hibernation wake.
|
|
460
|
+
*
|
|
461
|
+
* @example
|
|
462
|
+
* ```typescript
|
|
463
|
+
* persistedKeys: ['messageCount', 'settings'] // Only these keys are persisted
|
|
464
|
+
* ```
|
|
465
|
+
*/
|
|
466
|
+
persistedKeys?: (string & keyof TState)[];
|
|
467
|
+
/**
|
|
468
|
+
* Options for state persistence behavior.
|
|
469
|
+
*/
|
|
470
|
+
persistOptions?: SafePersistOptions;
|
|
471
|
+
/**
|
|
472
|
+
* Called when persistence fails for a key.
|
|
473
|
+
* Use this to handle errors gracefully (e.g., notify admins, fallback behavior).
|
|
474
|
+
*
|
|
475
|
+
* @param key - The state key that failed to persist
|
|
476
|
+
* @param error - The error that occurred
|
|
477
|
+
*/
|
|
478
|
+
onPersistError?(key: string, error: Error): void;
|
|
327
479
|
}
|
|
328
480
|
//#endregion
|
|
329
481
|
//#region src/actor/actor-runtime.d.ts
|
|
@@ -340,6 +492,6 @@ type ActorHandlerClass<E = unknown> = {
|
|
|
340
492
|
* @param room - The room definition with lifecycle hooks
|
|
341
493
|
* @returns Actor class for Cloudflare Workers (extends DurableObject)
|
|
342
494
|
*/
|
|
343
|
-
declare function createActorHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown>(room: RoomDefinition<TMeta, E>): ActorHandlerClass<E>;
|
|
495
|
+
declare function createActorHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>>(room: RoomDefinition<TMeta, E, TState>): ActorHandlerClass<E>;
|
|
344
496
|
//#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 };
|
|
497
|
+
export { safeSerialize as C, safeDeserialize as S, getPersistedKeys as _, EventHandler as a, isStateReady as b, RoomDefinition as c, PersistError as d, PersistNotReadyError as f, deletePersistedKey as g, clearPersistedState as h, BroadcastOptions as i, RpcBroadcastOptions as l, SafePersistOptions as m, createActorHandler as n, MessageContext as o, PersistableActor as p, ActorStub as r, RoomContext as s, ActorHandlerClass as t, VeraniActor as u, getPersistedState as v, setPeristErrorHandler as w, persistKey as x, initializePersistedState as y };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const require_encode=require(`./encode-BYFW_6TI.cjs`);let __cloudflare_actors=require(`@cloudflare/actors`);var RoomEventEmitterImpl=class{constructor(){this.handlers=new Map}on(u,I){this.handlers.has(u)||this.handlers.set(u,new Set),this.handlers.get(u).add(I)}off(u,I){let L=this.handlers.get(u);L&&(I?(L.delete(I),L.size===0&&this.handlers.delete(u)):this.handlers.delete(u))}async emit(u,I,L){let R=this.handlers.get(u);if(R&&R.size>0){let u=[];for(let z of R)try{let R=z(I,L);R instanceof Promise&&u.push(R)}catch{}await Promise.all(u)}let z=this.handlers.get(`*`);if(z&&z.size>0){let u=[];for(let R of z)try{let z=R(I,L);z instanceof Promise&&u.push(z)}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[I,L]of u.entries())this.handlers.set(I,new Set(L))}};function createRoomEventEmitter(){return new RoomEventEmitterImpl}function defaultExtractMeta(u){let I=crypto.randomUUID(),L=crypto.randomUUID(),R=new URL(u.url).searchParams.get(`channels`);return{userId:I,clientId:L,channels:R?R.split(`,`).map(u=>u.trim()).filter(Boolean):[`default`]}}function defineRoom(u){let I=u.eventEmitter||createRoomEventEmitter(),L=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:I,_staticHandlers:L,state:u.state,persistedKeys:u.persistedKeys,persistOptions:u.persistOptions,onPersistError:u.onPersistError,on(u,R){I.on(u,R),L.has(u)||L.set(u,new Set),L.get(u).add(R)},off(u,R){I.off(u,R);let z=L.get(u);z&&(R?(z.delete(R),z.size===0&&L.delete(u)):L.delete(u))}}}function cleanupStaleSessions(u){let I=0,L=[];for(let[I,R]of u.entries())I.readyState!==WebSocket.OPEN&&L.push(I);for(let R of L)u.delete(R),I++;return I}function decodeFrame(I){return require_encode.a(I)??{type:`invalid`}}function encodeFrame(I){return require_encode.n(I)}function broadcast(u,I,L,R){let z=0,B=encodeFrame({type:`event`,channel:I,data:L}),V=[];for(let{ws:L,meta:H}of u.values())if(H.channels.includes(I)&&!(R?.except&&L===R.except)&&!(R?.userIds&&!R.userIds.includes(H.userId))&&!(R?.clientIds&&!R.clientIds.includes(H.clientId))){if(L.readyState!==WebSocket.OPEN){V.push(L);continue}try{L.send(B),z++}catch{V.push(L)}}for(let I of V)u.delete(I);return V.length,z}function sendToUser(u,I,L,R){let z=0,B=encodeFrame({type:`event`,channel:L,data:R}),V=[];for(let{ws:R,meta:H}of u.values())if(H.userId===I&&H.channels.includes(L)){if(R.readyState!==WebSocket.OPEN){V.push(R);continue}try{R.send(B),z++}catch{V.push(R)}}for(let I of V)u.delete(I);return V.length,z}function getSessionCount(u){return u.size}function getConnectedUserIds(u){let I=new Set;for(let{meta:L}of u.values())I.add(L.userId);return Array.from(I)}function getUserSessions(u,I){let L=[];for(let{ws:R,meta:z}of u.values())z.userId===I&&L.push(R);return L}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(I){return{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,I){u.serializeAttachment(I)}function restoreSessions(u){let I=0,L=0;for(let R of u.ctx.getWebSockets()){if(R.readyState!==WebSocket.OPEN){L++;continue}let z=R.deserializeAttachment();if(!z){L++;continue}if(!isValidConnectionMeta(z)){L++;continue}u.sessions.set(R,{ws:R,meta:z}),I++}}async function onInit(u,I){if(I.eventEmitter&&I._staticHandlers)try{I.eventEmitter.rebuildHandlers(I._staticHandlers)}catch{}try{restoreSessions(u)}catch{}if(I.onHibernationRestore&&u.sessions.size>0)try{await I.onHibernationRestore(u)}catch{}else I.onHibernationRestore&&u.sessions.size}function createUserEmitBuilder(u,I,L){return{emit(R,z){return sendToUser(I,u,L,{type:R,...z})}}}function createChannelEmitBuilder(u,I,L){return{emit(R,z){return broadcast(I,u,{type:R,...z},L)}}}function createSocketEmit(u){let I=u.meta.channels[0]||`default`;return{emit(L,R){if(u.ws.readyState===WebSocket.OPEN)try{let z={type:`event`,channel:I,data:{type:L,...R}};u.ws.send(encodeFrame(z))}catch{}},to(L){return u.meta.channels.includes(L)?createChannelEmitBuilder(L,u.actor.sessions,{except:u.ws}):createUserEmitBuilder(L,u.actor.sessions,I)}}}function createActorEmit(u){return{emit(I,L){let R={type:I,...L};return broadcast(u.sessions,`default`,R)},to(I){return createChannelEmitBuilder(I,u.sessions)}}}async function onWebSocketConnect(u,I,L,R){let z;try{z=I.extractMeta?await I.extractMeta(R):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(L,z);let B={actor:u,ws:L,meta:z,frame:{type:`connect`}};if(I.onConnect){let R={actor:u,ws:L,meta:z,emit:createSocketEmit(B)};await I.onConnect(R)}u.sessions.set(L,{ws:L,meta:z})}catch(R){if(I.onError&&z)try{let B={actor:u,ws:L,meta:z,frame:{type:`error`}};await I.onError(R,{actor:u,ws:L,meta:z,emit:createSocketEmit(B)})}catch{}L.close(1011,`Internal server error`)}}async function onWebSocketMessage(u,I,L,R){let z;try{let B=decodeFrame(R);if(B&&B.type===`ping`){if(L.readyState===WebSocket.OPEN)try{L.send(encodeFrame({type:`pong`}))}catch{}return}if(!B||B.type===`invalid`||(z=u.sessions.get(L),!z))return;let V={actor:u,ws:L,meta:z.meta,frame:B,emit:createSocketEmit({actor:u,ws:L,meta:z.meta,frame:B})},W=I.eventEmitter;W&&W.hasHandlers&&W.hasHandlers(B.type)?await W.emit(B.type,V,B.data||{}):I.onMessage&&await I.onMessage(V,B)}catch(R){if(I.onError&&z)try{await I.onError(R,{actor:u,ws:L,meta:z.meta,emit:createSocketEmit({actor:u,ws:L,meta:z.meta,frame:{type:`error`}})})}catch{}}}async function onWebSocketDisconnect(u,I,L){try{let R=u.sessions.get(L);if(u.sessions.delete(L),R&&I.onDisconnect){let z={actor:u,ws:L,meta:R.meta,frame:{type:`disconnect`}},B={actor:u,ws:L,meta:R.meta,emit:createSocketEmit(z)};await I.onDisconnect(B)}}catch{}}function createFetch(u,I){return async function(L){let R=new URL(L.url),z=L.headers.get(`Upgrade`);return R.pathname===u.websocketPath&&z===`websocket`&&await I.shouldUpgradeWebSocket(L)?I.onWebSocketUpgrade(L):I.onRequest(L)}}const PERSIST_ERROR_HANDLER=Symbol(`PERSIST_ERROR_HANDLER`),PERSISTED_STATE=Symbol(`PERSISTED_STATE`),STATE_READY=Symbol(`STATE_READY`);var PersistNotReadyError=class extends Error{constructor(u){super(`Cannot access persisted state key "${u}" before storage is initialized. Wait for onInit to complete or check actor.isStateReady().`),this.name=`PersistNotReadyError`}},PersistError=class extends Error{constructor(u,I){super(`Failed to persist key "${u}": ${I.message}`),this.name=`PersistError`,this.originalCause=I}};function safeSerialize(u){let I=new WeakSet;return JSON.stringify(u,(u,L)=>{if(L instanceof Date)return{__type:`Date`,value:L.toISOString()};if(L instanceof RegExp)return{__type:`RegExp`,source:L.source,flags:L.flags};if(L instanceof Map)return{__type:`Map`,entries:Array.from(L.entries())};if(L instanceof Set)return{__type:`Set`,values:Array.from(L.values())};if(L instanceof Error)return{__type:`Error`,name:L.name,message:L.message};if(typeof L==`object`&&L){if(I.has(L))return;I.add(L)}if(!(typeof L==`function`||typeof L==`symbol`))return L})}function safeDeserialize(u){return JSON.parse(u,(u,I)=>{if(I&&typeof I==`object`&&I.__type)switch(I.__type){case`Date`:return new Date(I.value);case`RegExp`:return new RegExp(I.source,I.flags);case`Map`:return new Map(I.entries);case`Set`:return new Set(I.values);case`Error`:{let u=Error(I.message);return u.name=I.name,u}}return I})}function createShallowProxy(u,I,L){return new Proxy(u,{set(u,L,R){let z=Reflect.set(u,L,R);return z&&typeof L==`string`&&I(L,R),z},deleteProperty(u,I){let R=Reflect.deleteProperty(u,I);return R&&typeof I==`string`&&L(I),R}})}async function initializePersistedState(u,I,L=[],R={}){let{shallow:z=!0,throwOnError:B=!0}=R,V=u.ctx.storage,H=L.length>0?L.map(String):Object.keys(I),U={...I};for(let u of H)try{let I=await V.get(`_verani_persist:${u}`);if(I!==void 0)try{U[u]=safeDeserialize(I)}catch(I){if(B)throw new PersistError(u,I)}}catch(I){if(B&&!(I instanceof PersistError))throw new PersistError(u,I)}let W=async(I,L)=>{if(H.includes(I))try{let u=safeSerialize(L);await V.put(`_verani_persist:${I}`,u)}catch(L){let R=u[PERSIST_ERROR_HANDLER];if(R&&R(I,L),B)throw new PersistError(I,L)}},G=async I=>{if(H.includes(I))try{await V.delete(`_verani_persist:${I}`)}catch(L){let R=u[PERSIST_ERROR_HANDLER];if(R&&R(I,L),B)throw new PersistError(I,L)}},K;return K=z?createShallowProxy(U,(u,I)=>{W(String(u),I)},u=>{G(String(u))}):createDeepProxy(U,(u,I)=>{W(u,I)},u=>{G(u)},H),u[STATE_READY]=!0,u[PERSISTED_STATE]=K,K}function createDeepProxy(u,I,L,R,z){return new Proxy(u,{get(u,B){let V=Reflect.get(u,B);if(V&&typeof V==`object`&&!Array.isArray(V)){let u=z??String(B);if(R.includes(u)||z!==void 0)return createDeepProxy(V,I,L,R,u)}return V},set(u,L,B){let V=Reflect.set(u,L,B);if(V){let u=z??String(L);R.includes(u)&&(z?I(z,void 0):I(String(L),B))}return V},deleteProperty(u,B){let V=Reflect.deleteProperty(u,B);if(V){let u=z??String(B);R.includes(u)&&(z?I(z,void 0):L(String(B)))}return V}})}function isStateReady(u){return u[STATE_READY]===!0}function getPersistedState(u){if(!isStateReady(u))throw new PersistNotReadyError(`state`);return u[PERSISTED_STATE]}function setPeristErrorHandler(u,I){u[PERSIST_ERROR_HANDLER]=I}async function persistKey(u,I,L){let R=u.ctx.storage,z=safeSerialize(L);await R.put(`_verani_persist:${I}`,z)}async function deletePersistedKey(u,I){await u.ctx.storage.delete(`_verani_persist:${I}`)}async function getPersistedKeys(u){let I=await u.ctx.storage.list({prefix:`_verani_persist:`});return Array.from(I.keys()).map(u=>u.replace(`_verani_persist:`,``))}async function clearPersistedState(u){let I=u.ctx.storage,L=await I.list({prefix:`_verani_persist:`}),R=Array.from(L.keys());await I.delete(R)}function createActorHandler(u){let L=sanitizeToClassName(u.name||u.websocketPath||`VeraniActor`),R=u;class z extends __cloudflare_actors.Actor{constructor(...I){super(...I),this.sessions=new Map,this.emit=createActorEmit(this),this[STATE_READY]=!1,this[PERSISTED_STATE]=u.state?{...u.state}:{},this.fetch=createFetch(R,this)}get roomState(){return this[PERSISTED_STATE]}isStateReady(){return isStateReady(this)}static{this.configuration=createConfiguration(R)}async shouldUpgradeWebSocket(u){return!0}async onInit(){if(u.state){u.onPersistError&&setPeristErrorHandler(this,u.onPersistError);let I=u.persistedKeys;this[PERSISTED_STATE]=await initializePersistedState(this,u.state,I,u.persistOptions)}await onInit(this,R)}async onWebSocketConnect(u,I){await onWebSocketConnect(this,R,u,I)}async onWebSocketMessage(u,I){await onWebSocketMessage(this,R,u,I)}async onWebSocketDisconnect(u){await onWebSocketDisconnect(this,R,u)}cleanupStaleSessions(){return cleanupStaleSessions(this.sessions)}broadcast(u,I,L){return broadcast(this.sessions,u,I,L)}getSessionCount(){return getSessionCount(this.sessions)}getConnectedUserIds(){return getConnectedUserIds(this.sessions)}getUserSessions(u){return getUserSessions(this.sessions,u)}sendToUser(u,I,L){return sendToUser(this.sessions,u,I,L)}emitToChannel(u,I,L){let R={type:I,...L};return broadcast(this.sessions,u,R)}emitToUser(u,I,L){let R=encodeFrame({type:`event`,channel:`default`,data:{type:I,...L}}),z=0,B=[];for(let{ws:I,meta:L}of this.sessions.values())if(L.userId===u){if(I.readyState!==WebSocket.OPEN){B.push(I);continue}try{I.send(R),z++}catch{B.push(I)}}for(let u of B)this.sessions.delete(u);return z}getStorage(){return getStorage(this.ctx)}}return Object.defineProperty(z,`name`,{value:L,writable:!1,configurable:!0}),z}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return deletePersistedKey}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return initializePersistedState}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return safeDeserialize}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return safeSerialize}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return defineRoom}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return storeAttachment}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return clearPersistedState}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return isStateReady}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return restoreSessions}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return PersistError}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return getPersistedKeys}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return setPeristErrorHandler}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return PersistNotReadyError}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return getPersistedState}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return createActorHandler}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return persistKey}});
|
|
@@ -1,8 +1,104 @@
|
|
|
1
1
|
import { n as ConnectionMeta, r as MessageFrame } from "./types-BefWc1M_.cjs";
|
|
2
2
|
import { Actor, ActorConfiguration } from "@cloudflare/actors";
|
|
3
3
|
|
|
4
|
-
//#region src/actor/
|
|
4
|
+
//#region src/actor/persist.d.ts
|
|
5
5
|
|
|
6
|
+
declare const PERSIST_ERROR_HANDLER: unique symbol;
|
|
7
|
+
declare const PERSISTED_STATE: unique symbol;
|
|
8
|
+
declare const STATE_READY: unique symbol;
|
|
9
|
+
/**
|
|
10
|
+
* Error thrown when persisted state is accessed before initialization
|
|
11
|
+
*/
|
|
12
|
+
declare class PersistNotReadyError extends Error {
|
|
13
|
+
constructor(key: string);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Error thrown when persistence operation fails
|
|
17
|
+
*/
|
|
18
|
+
declare class PersistError extends Error {
|
|
19
|
+
readonly originalCause: Error;
|
|
20
|
+
constructor(key: string, cause: Error);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Options for SafePersist behavior
|
|
24
|
+
*/
|
|
25
|
+
interface SafePersistOptions {
|
|
26
|
+
/**
|
|
27
|
+
* If true, only track top-level property changes (no deep proxy).
|
|
28
|
+
* This is safer and more predictable. Default: true
|
|
29
|
+
*/
|
|
30
|
+
shallow?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* If true, throw errors instead of swallowing them. Default: true
|
|
33
|
+
*/
|
|
34
|
+
throwOnError?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Safely serialize a value for storage.
|
|
38
|
+
* Handles circular references and special types gracefully.
|
|
39
|
+
*/
|
|
40
|
+
declare function safeSerialize(value: unknown): string;
|
|
41
|
+
/**
|
|
42
|
+
* Safely deserialize a value from storage.
|
|
43
|
+
* Restores special types that were serialized.
|
|
44
|
+
*/
|
|
45
|
+
declare function safeDeserialize(json: string): unknown;
|
|
46
|
+
/**
|
|
47
|
+
* Interface for actors that support safe persistence
|
|
48
|
+
*/
|
|
49
|
+
interface PersistableActor {
|
|
50
|
+
/** Durable Object storage interface */
|
|
51
|
+
ctx: {
|
|
52
|
+
storage: DurableObjectStorage;
|
|
53
|
+
};
|
|
54
|
+
/** Whether the state has been initialized from storage */
|
|
55
|
+
[STATE_READY]?: boolean;
|
|
56
|
+
/** The persisted state object */
|
|
57
|
+
[PERSISTED_STATE]?: Record<string, unknown>;
|
|
58
|
+
/** Error handler for persistence failures */
|
|
59
|
+
[PERSIST_ERROR_HANDLER]?: (key: string, error: Error) => void;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Initialize persisted state from Durable Object storage.
|
|
63
|
+
* Call this in the actor's onInit or constructor after storage is available.
|
|
64
|
+
*
|
|
65
|
+
* @param actor - The actor instance
|
|
66
|
+
* @param initialState - Default state values
|
|
67
|
+
* @param persistedKeys - Keys to persist (empty = all keys)
|
|
68
|
+
* @param options - Persistence options
|
|
69
|
+
*/
|
|
70
|
+
declare function initializePersistedState<T extends Record<string, unknown>>(actor: PersistableActor, initialState: T, persistedKeys?: (keyof T)[], options?: SafePersistOptions): Promise<T>;
|
|
71
|
+
/**
|
|
72
|
+
* Helper to check if state is ready for access
|
|
73
|
+
*/
|
|
74
|
+
declare function isStateReady(actor: PersistableActor): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Helper to get persisted state with type safety.
|
|
77
|
+
* Throws if state is not yet initialized.
|
|
78
|
+
*/
|
|
79
|
+
declare function getPersistedState<T extends Record<string, unknown>>(actor: PersistableActor): T;
|
|
80
|
+
/**
|
|
81
|
+
* Set the error handler for persistence failures
|
|
82
|
+
*/
|
|
83
|
+
declare function setPeristErrorHandler(actor: PersistableActor, handler: (key: string, error: Error) => void): void;
|
|
84
|
+
/**
|
|
85
|
+
* Manually persist a specific key (useful for batch updates)
|
|
86
|
+
*/
|
|
87
|
+
declare function persistKey(actor: PersistableActor, key: string, value: unknown): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Manually delete a persisted key
|
|
90
|
+
*/
|
|
91
|
+
declare function deletePersistedKey(actor: PersistableActor, key: string): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Get all persisted keys from storage
|
|
94
|
+
*/
|
|
95
|
+
declare function getPersistedKeys(actor: PersistableActor): Promise<string[]>;
|
|
96
|
+
/**
|
|
97
|
+
* Clear all persisted state
|
|
98
|
+
*/
|
|
99
|
+
declare function clearPersistedState(actor: PersistableActor): Promise<void>;
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/actor/types.d.ts
|
|
6
102
|
/**
|
|
7
103
|
* Options for broadcasting messages to connections
|
|
8
104
|
*/
|
|
@@ -101,7 +197,7 @@ interface ActorStub {
|
|
|
101
197
|
/**
|
|
102
198
|
* Extended Actor interface with Verani-specific methods
|
|
103
199
|
*/
|
|
104
|
-
interface VeraniActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> extends Actor<E> {
|
|
200
|
+
interface VeraniActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> extends Actor<E> {
|
|
105
201
|
/**
|
|
106
202
|
* Map of active WebSocket sessions keyed by their WebSocket instance.
|
|
107
203
|
* Each entry contains the WebSocket and its associated metadata.
|
|
@@ -111,6 +207,17 @@ interface VeraniActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown
|
|
|
111
207
|
ws: WebSocket;
|
|
112
208
|
meta: TMeta;
|
|
113
209
|
}>;
|
|
210
|
+
/**
|
|
211
|
+
* User-defined persisted state for this actor.
|
|
212
|
+
* Access this after onInit completes. Changes to tracked keys are automatically persisted.
|
|
213
|
+
* @see RoomDefinition.state and RoomDefinition.persistedKeys
|
|
214
|
+
*/
|
|
215
|
+
roomState: TState;
|
|
216
|
+
/**
|
|
217
|
+
* Check if the persisted state has been initialized.
|
|
218
|
+
* Returns true after onInit completes and state is loaded from storage.
|
|
219
|
+
*/
|
|
220
|
+
isStateReady(): boolean;
|
|
114
221
|
/**
|
|
115
222
|
* Broadcast a message to all connections in a channel.
|
|
116
223
|
* Performs channel, userId, clientId, and exclusion filtering.
|
|
@@ -161,37 +268,37 @@ interface VeraniActor<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown
|
|
|
161
268
|
/**
|
|
162
269
|
* Event handler function type for socket.io-like event handling
|
|
163
270
|
*/
|
|
164
|
-
type EventHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> = (ctx: MessageContext<TMeta, E>, data: any) => void | Promise<void>;
|
|
271
|
+
type EventHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> = (ctx: MessageContext<TMeta, E, TState>, data: any) => void | Promise<void>;
|
|
165
272
|
/**
|
|
166
273
|
* Event emitter interface for room-level event handling
|
|
167
274
|
*/
|
|
168
|
-
interface RoomEventEmitter<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
275
|
+
interface RoomEventEmitter<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
|
|
169
276
|
/**
|
|
170
277
|
* Register an event handler
|
|
171
278
|
* @param event - Event name (supports wildcard "*")
|
|
172
279
|
* @param handler - Handler function
|
|
173
280
|
*/
|
|
174
|
-
on(event: string, handler: EventHandler<TMeta, E>): void;
|
|
281
|
+
on(event: string, handler: EventHandler<TMeta, E, TState>): void;
|
|
175
282
|
/**
|
|
176
283
|
* Remove an event handler
|
|
177
284
|
* @param event - Event name
|
|
178
285
|
* @param handler - Optional specific handler to remove, or remove all handlers for event
|
|
179
286
|
*/
|
|
180
|
-
off(event: string, handler?: EventHandler<TMeta, E>): void;
|
|
287
|
+
off(event: string, handler?: EventHandler<TMeta, E, TState>): void;
|
|
181
288
|
/**
|
|
182
289
|
* Emit an event to registered handlers
|
|
183
290
|
* @param event - Event name
|
|
184
291
|
* @param ctx - Message context
|
|
185
292
|
* @param data - Event data
|
|
186
293
|
*/
|
|
187
|
-
emit(event: string, ctx: MessageContext<TMeta, E>, data: any): Promise<void>;
|
|
294
|
+
emit(event: string, ctx: MessageContext<TMeta, E, TState>, data: any): Promise<void>;
|
|
188
295
|
/**
|
|
189
296
|
* Rebuild handlers from static storage.
|
|
190
297
|
* Called after hibernation to restore handlers from the room definition.
|
|
191
298
|
* This method MUST be implemented by all event emitter implementations.
|
|
192
299
|
* @param staticHandlers - Map of event names to handler sets from static storage
|
|
193
300
|
*/
|
|
194
|
-
rebuildHandlers(staticHandlers: Map<string, Set<EventHandler<TMeta, E>>>): void;
|
|
301
|
+
rebuildHandlers(staticHandlers: Map<string, Set<EventHandler<TMeta, E, TState>>>): void;
|
|
195
302
|
}
|
|
196
303
|
/**
|
|
197
304
|
* Builder interface for targeting specific scopes when emitting
|
|
@@ -245,9 +352,9 @@ interface ActorEmit<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown>
|
|
|
245
352
|
/**
|
|
246
353
|
* Context provided to room lifecycle hooks
|
|
247
354
|
*/
|
|
248
|
-
interface RoomContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
355
|
+
interface RoomContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
|
|
249
356
|
/** The actor instance handling this connection */
|
|
250
|
-
actor: VeraniActor<TMeta, E>;
|
|
357
|
+
actor: VeraniActor<TMeta, E, TState>;
|
|
251
358
|
/** The WebSocket connection */
|
|
252
359
|
ws: WebSocket;
|
|
253
360
|
/** Connection metadata */
|
|
@@ -258,7 +365,7 @@ interface RoomContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown
|
|
|
258
365
|
/**
|
|
259
366
|
* Context for onMessage hook with frame included
|
|
260
367
|
*/
|
|
261
|
-
interface MessageContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> extends RoomContext<TMeta, E> {
|
|
368
|
+
interface MessageContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> extends RoomContext<TMeta, E, TState> {
|
|
262
369
|
/** The received message frame */
|
|
263
370
|
frame: MessageFrame;
|
|
264
371
|
}
|
|
@@ -268,8 +375,12 @@ interface MessageContext<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
268
375
|
* **Important:** All lifecycle hooks are properly awaited if they return a Promise.
|
|
269
376
|
* This ensures async operations complete before the actor proceeds to the next step
|
|
270
377
|
* or potentially enters hibernation.
|
|
378
|
+
*
|
|
379
|
+
* @template TMeta - Connection metadata type
|
|
380
|
+
* @template E - Environment type
|
|
381
|
+
* @template TState - Room state type (for persistence)
|
|
271
382
|
*/
|
|
272
|
-
interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
383
|
+
interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> {
|
|
273
384
|
/** Optional room name for debugging */
|
|
274
385
|
name?: string;
|
|
275
386
|
/** WebSocket upgrade path (default: "/ws") */
|
|
@@ -285,13 +396,13 @@ interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
285
396
|
* sessions map after this hook completes successfully. If this hook throws, the
|
|
286
397
|
* connection is closed and no orphaned session is created.
|
|
287
398
|
*/
|
|
288
|
-
onConnect?(ctx: RoomContext<TMeta, E>): void | Promise<void>;
|
|
399
|
+
onConnect?(ctx: RoomContext<TMeta, E, TState>): void | Promise<void>;
|
|
289
400
|
/**
|
|
290
401
|
* Called when a WebSocket connection is closed.
|
|
291
402
|
* This hook is awaited if it returns a Promise. The session is removed from the
|
|
292
403
|
* sessions map before this hook is called.
|
|
293
404
|
*/
|
|
294
|
-
onDisconnect?(ctx: RoomContext<TMeta, E>): void | Promise<void>;
|
|
405
|
+
onDisconnect?(ctx: RoomContext<TMeta, E, TState>): void | Promise<void>;
|
|
295
406
|
/**
|
|
296
407
|
* Called when a message is received from a connection.
|
|
297
408
|
* This hook is awaited if it returns a Promise. The actor will not process
|
|
@@ -300,30 +411,71 @@ interface RoomDefinition<TMeta extends ConnectionMeta = ConnectionMeta, E = unkn
|
|
|
300
411
|
* **Note:** If event handlers are registered via `eventEmitter`, they take priority.
|
|
301
412
|
* This hook is used as a fallback when no matching event handler is found.
|
|
302
413
|
*/
|
|
303
|
-
onMessage?(ctx: MessageContext<TMeta, E>, frame: MessageFrame): void | Promise<void>;
|
|
414
|
+
onMessage?(ctx: MessageContext<TMeta, E, TState>, frame: MessageFrame): void | Promise<void>;
|
|
304
415
|
/**
|
|
305
416
|
* Called when an error occurs in a lifecycle hook.
|
|
306
417
|
* This hook is also awaited if it returns a Promise.
|
|
307
418
|
*/
|
|
308
|
-
onError?(error: Error, ctx: RoomContext<TMeta, E>): void | Promise<void>;
|
|
419
|
+
onError?(error: Error, ctx: RoomContext<TMeta, E, TState>): void | Promise<void>;
|
|
309
420
|
/**
|
|
310
421
|
* Called after actor wakes from hibernation and sessions are restored.
|
|
311
422
|
* This hook is awaited if it returns a Promise. It is called even if some
|
|
312
423
|
* sessions failed to restore, allowing you to handle partial restoration scenarios.
|
|
313
424
|
*/
|
|
314
|
-
onHibernationRestore?(actor: VeraniActor<TMeta, E>): void | Promise<void>;
|
|
425
|
+
onHibernationRestore?(actor: VeraniActor<TMeta, E, TState>): void | Promise<void>;
|
|
315
426
|
/**
|
|
316
427
|
* Event emitter for socket.io-like event handling.
|
|
317
428
|
* If provided, event handlers registered here will be called for matching message types.
|
|
318
429
|
* If not provided, a default event emitter will be created.
|
|
319
430
|
*/
|
|
320
|
-
eventEmitter?: RoomEventEmitter<TMeta, E>;
|
|
431
|
+
eventEmitter?: RoomEventEmitter<TMeta, E, TState>;
|
|
321
432
|
/**
|
|
322
433
|
* Static handler storage that persists across hibernation.
|
|
323
434
|
* Handlers registered via room.on() are stored here and rebuilt in onInit.
|
|
324
435
|
* @internal
|
|
325
436
|
*/
|
|
326
|
-
_staticHandlers?: Map<string, Set<EventHandler<TMeta, E>>>;
|
|
437
|
+
_staticHandlers?: Map<string, Set<EventHandler<TMeta, E, TState>>>;
|
|
438
|
+
/**
|
|
439
|
+
* Initial state for this room. This object defines the default values
|
|
440
|
+
* for your room's state. Access via `actor.roomState` in lifecycle hooks.
|
|
441
|
+
*
|
|
442
|
+
* @example
|
|
443
|
+
* ```typescript
|
|
444
|
+
* const room = defineRoom({
|
|
445
|
+
* state: {
|
|
446
|
+
* messageCount: 0,
|
|
447
|
+
* lastActivity: null as Date | null,
|
|
448
|
+
* settings: { maxUsers: 100 }
|
|
449
|
+
* },
|
|
450
|
+
* persistedKeys: ['messageCount', 'settings'],
|
|
451
|
+
* // ...
|
|
452
|
+
* });
|
|
453
|
+
* ```
|
|
454
|
+
*/
|
|
455
|
+
state?: TState;
|
|
456
|
+
/**
|
|
457
|
+
* Keys from `state` to persist to Durable Object storage.
|
|
458
|
+
* If empty or undefined, no state is persisted.
|
|
459
|
+
* Changes to these keys are automatically saved and restored on hibernation wake.
|
|
460
|
+
*
|
|
461
|
+
* @example
|
|
462
|
+
* ```typescript
|
|
463
|
+
* persistedKeys: ['messageCount', 'settings'] // Only these keys are persisted
|
|
464
|
+
* ```
|
|
465
|
+
*/
|
|
466
|
+
persistedKeys?: (string & keyof TState)[];
|
|
467
|
+
/**
|
|
468
|
+
* Options for state persistence behavior.
|
|
469
|
+
*/
|
|
470
|
+
persistOptions?: SafePersistOptions;
|
|
471
|
+
/**
|
|
472
|
+
* Called when persistence fails for a key.
|
|
473
|
+
* Use this to handle errors gracefully (e.g., notify admins, fallback behavior).
|
|
474
|
+
*
|
|
475
|
+
* @param key - The state key that failed to persist
|
|
476
|
+
* @param error - The error that occurred
|
|
477
|
+
*/
|
|
478
|
+
onPersistError?(key: string, error: Error): void;
|
|
327
479
|
}
|
|
328
480
|
//#endregion
|
|
329
481
|
//#region src/actor/actor-runtime.d.ts
|
|
@@ -340,6 +492,6 @@ type ActorHandlerClass<E = unknown> = {
|
|
|
340
492
|
* @param room - The room definition with lifecycle hooks
|
|
341
493
|
* @returns Actor class for Cloudflare Workers (extends DurableObject)
|
|
342
494
|
*/
|
|
343
|
-
declare function createActorHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown>(room: RoomDefinition<TMeta, E>): ActorHandlerClass<E>;
|
|
495
|
+
declare function createActorHandler<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>>(room: RoomDefinition<TMeta, E, TState>): ActorHandlerClass<E>;
|
|
344
496
|
//#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 };
|
|
497
|
+
export { safeSerialize as C, safeDeserialize as S, getPersistedKeys as _, EventHandler as a, isStateReady as b, RoomDefinition as c, PersistError as d, PersistNotReadyError as f, deletePersistedKey as g, clearPersistedState as h, BroadcastOptions as i, RpcBroadcastOptions as l, SafePersistOptions as m, createActorHandler as n, MessageContext as o, PersistableActor as p, ActorStub as r, RoomContext as s, ActorHandlerClass as t, VeraniActor as u, getPersistedState as v, setPeristErrorHandler as w, persistKey as x, initializePersistedState as y };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as decodeFrame$1,n as encodeFrame$1}from"./encode-BhJqnsto.mjs";import{Actor}from"@cloudflare/actors";var RoomEventEmitterImpl=class{constructor(){this.handlers=new Map}on(p,L){this.handlers.has(p)||this.handlers.set(p,new Set),this.handlers.get(p).add(L)}off(p,L){let R=this.handlers.get(p);R&&(L?(R.delete(L),R.size===0&&this.handlers.delete(p)):this.handlers.delete(p))}async emit(p,L,R){let z=this.handlers.get(p);if(z&&z.size>0){let p=[];for(let B of z)try{let z=B(L,R);z instanceof Promise&&p.push(z)}catch{}await Promise.all(p)}let B=this.handlers.get(`*`);if(B&&B.size>0){let p=[];for(let z of B)try{let B=z(L,R);B instanceof Promise&&p.push(B)}catch{}await Promise.all(p)}}hasHandlers(p){return this.handlers.has(p)&&this.handlers.get(p).size>0||this.handlers.has(`*`)&&this.handlers.get(`*`).size>0}getEventNames(){return Array.from(this.handlers.keys())}rebuildHandlers(p){this.handlers.clear();for(let[L,R]of p.entries())this.handlers.set(L,new Set(R))}};function createRoomEventEmitter(){return new RoomEventEmitterImpl}function defaultExtractMeta(p){let L=crypto.randomUUID(),R=crypto.randomUUID(),z=new URL(p.url).searchParams.get(`channels`);return{userId:L,clientId:R,channels:z?z.split(`,`).map(p=>p.trim()).filter(Boolean):[`default`]}}function defineRoom(p){let L=p.eventEmitter||createRoomEventEmitter(),R=p._staticHandlers||new Map;return{name:p.name,websocketPath:p.websocketPath,extractMeta:p.extractMeta||(p=>defaultExtractMeta(p)),onConnect:p.onConnect,onDisconnect:p.onDisconnect,onMessage:p.onMessage,onError:p.onError,onHibernationRestore:p.onHibernationRestore,eventEmitter:L,_staticHandlers:R,state:p.state,persistedKeys:p.persistedKeys,persistOptions:p.persistOptions,onPersistError:p.onPersistError,on(p,z){L.on(p,z),R.has(p)||R.set(p,new Set),R.get(p).add(z)},off(p,z){L.off(p,z);let B=R.get(p);B&&(z?(B.delete(z),B.size===0&&R.delete(p)):R.delete(p))}}}function cleanupStaleSessions(p){let L=0,R=[];for(let[L,z]of p.entries())L.readyState!==WebSocket.OPEN&&R.push(L);for(let z of R)p.delete(z),L++;return L}function decodeFrame(L){return decodeFrame$1(L)??{type:`invalid`}}function encodeFrame(p){return encodeFrame$1(p)}function broadcast(p,L,R,z){let B=0,V=encodeFrame({type:`event`,channel:L,data:R}),H=[];for(let{ws:R,meta:U}of p.values())if(U.channels.includes(L)&&!(z?.except&&R===z.except)&&!(z?.userIds&&!z.userIds.includes(U.userId))&&!(z?.clientIds&&!z.clientIds.includes(U.clientId))){if(R.readyState!==WebSocket.OPEN){H.push(R);continue}try{R.send(V),B++}catch{H.push(R)}}for(let L of H)p.delete(L);return H.length,B}function sendToUser(p,L,R,z){let B=0,V=encodeFrame({type:`event`,channel:R,data:z}),H=[];for(let{ws:z,meta:U}of p.values())if(U.userId===L&&U.channels.includes(R)){if(z.readyState!==WebSocket.OPEN){H.push(z);continue}try{z.send(V),B++}catch{H.push(z)}}for(let L of H)p.delete(L);return H.length,B}function getSessionCount(p){return p.size}function getConnectedUserIds(p){let L=new Set;for(let{meta:R}of p.values())L.add(R.userId);return Array.from(L)}function getUserSessions(p,L){let R=[];for(let{ws:z,meta:B}of p.values())B.userId===L&&R.push(z);return R}function getStorage(p){return p.storage}function sanitizeToClassName(p){return p.replace(/^\/+/,``).split(/[-_\/\s]+/).map(p=>p.replace(/[^a-zA-Z0-9]/g,``)).filter(p=>p.length>0).map(p=>p.charAt(0).toUpperCase()+p.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createConfiguration(p){return function(L){return{sockets:{upgradePath:p.websocketPath}}}}function isValidConnectionMeta(p){return!(!p||typeof p!=`object`||typeof p.userId!=`string`||!p.userId||typeof p.clientId!=`string`||!p.clientId||!Array.isArray(p.channels)||!p.channels.every(p=>typeof p==`string`))}function storeAttachment(p,L){p.serializeAttachment(L)}function restoreSessions(p){let L=0,R=0;for(let z of p.ctx.getWebSockets()){if(z.readyState!==WebSocket.OPEN){R++;continue}let B=z.deserializeAttachment();if(!B){R++;continue}if(!isValidConnectionMeta(B)){R++;continue}p.sessions.set(z,{ws:z,meta:B}),L++}}async function onInit(p,L){if(L.eventEmitter&&L._staticHandlers)try{L.eventEmitter.rebuildHandlers(L._staticHandlers)}catch{}try{restoreSessions(p)}catch{}if(L.onHibernationRestore&&p.sessions.size>0)try{await L.onHibernationRestore(p)}catch{}else L.onHibernationRestore&&p.sessions.size}function createUserEmitBuilder(p,L,R){return{emit(z,B){return sendToUser(L,p,R,{type:z,...B})}}}function createChannelEmitBuilder(p,L,R){return{emit(z,B){return broadcast(L,p,{type:z,...B},R)}}}function createSocketEmit(p){let L=p.meta.channels[0]||`default`;return{emit(R,z){if(p.ws.readyState===WebSocket.OPEN)try{let B={type:`event`,channel:L,data:{type:R,...z}};p.ws.send(encodeFrame(B))}catch{}},to(R){return p.meta.channels.includes(R)?createChannelEmitBuilder(R,p.actor.sessions,{except:p.ws}):createUserEmitBuilder(R,p.actor.sessions,L)}}}function createActorEmit(p){return{emit(L,R){let z={type:L,...R};return broadcast(p.sessions,`default`,z)},to(L){return createChannelEmitBuilder(L,p.sessions)}}}async function onWebSocketConnect(p,L,R,z){let B;try{B=L.extractMeta?await L.extractMeta(z):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(R,B);let V={actor:p,ws:R,meta:B,frame:{type:`connect`}};if(L.onConnect){let z={actor:p,ws:R,meta:B,emit:createSocketEmit(V)};await L.onConnect(z)}p.sessions.set(R,{ws:R,meta:B})}catch(z){if(L.onError&&B)try{let V={actor:p,ws:R,meta:B,frame:{type:`error`}};await L.onError(z,{actor:p,ws:R,meta:B,emit:createSocketEmit(V)})}catch{}R.close(1011,`Internal server error`)}}async function onWebSocketMessage(p,L,R,z){let B;try{let V=decodeFrame(z);if(V&&V.type===`ping`){if(R.readyState===WebSocket.OPEN)try{R.send(encodeFrame({type:`pong`}))}catch{}return}if(!V||V.type===`invalid`||(B=p.sessions.get(R),!B))return;let H={actor:p,ws:R,meta:B.meta,frame:V,emit:createSocketEmit({actor:p,ws:R,meta:B.meta,frame:V})},U=L.eventEmitter;U&&U.hasHandlers&&U.hasHandlers(V.type)?await U.emit(V.type,H,V.data||{}):L.onMessage&&await L.onMessage(H,V)}catch(z){if(L.onError&&B)try{await L.onError(z,{actor:p,ws:R,meta:B.meta,emit:createSocketEmit({actor:p,ws:R,meta:B.meta,frame:{type:`error`}})})}catch{}}}async function onWebSocketDisconnect(p,L,R){try{let z=p.sessions.get(R);if(p.sessions.delete(R),z&&L.onDisconnect){let B={actor:p,ws:R,meta:z.meta,frame:{type:`disconnect`}},V={actor:p,ws:R,meta:z.meta,emit:createSocketEmit(B)};await L.onDisconnect(V)}}catch{}}function createFetch(p,L){return async function(R){let z=new URL(R.url),B=R.headers.get(`Upgrade`);return z.pathname===p.websocketPath&&B===`websocket`&&await L.shouldUpgradeWebSocket(R)?L.onWebSocketUpgrade(R):L.onRequest(R)}}const PERSIST_ERROR_HANDLER=Symbol(`PERSIST_ERROR_HANDLER`),PERSISTED_STATE=Symbol(`PERSISTED_STATE`),STATE_READY=Symbol(`STATE_READY`);var PersistNotReadyError=class extends Error{constructor(p){super(`Cannot access persisted state key "${p}" before storage is initialized. Wait for onInit to complete or check actor.isStateReady().`),this.name=`PersistNotReadyError`}},PersistError=class extends Error{constructor(p,L){super(`Failed to persist key "${p}": ${L.message}`),this.name=`PersistError`,this.originalCause=L}};function safeSerialize(p){let L=new WeakSet;return JSON.stringify(p,(p,R)=>{if(R instanceof Date)return{__type:`Date`,value:R.toISOString()};if(R instanceof RegExp)return{__type:`RegExp`,source:R.source,flags:R.flags};if(R instanceof Map)return{__type:`Map`,entries:Array.from(R.entries())};if(R instanceof Set)return{__type:`Set`,values:Array.from(R.values())};if(R instanceof Error)return{__type:`Error`,name:R.name,message:R.message};if(typeof R==`object`&&R){if(L.has(R))return;L.add(R)}if(!(typeof R==`function`||typeof R==`symbol`))return R})}function safeDeserialize(p){return JSON.parse(p,(p,L)=>{if(L&&typeof L==`object`&&L.__type)switch(L.__type){case`Date`:return new Date(L.value);case`RegExp`:return new RegExp(L.source,L.flags);case`Map`:return new Map(L.entries);case`Set`:return new Set(L.values);case`Error`:{let p=Error(L.message);return p.name=L.name,p}}return L})}function createShallowProxy(p,L,R){return new Proxy(p,{set(p,R,z){let B=Reflect.set(p,R,z);return B&&typeof R==`string`&&L(R,z),B},deleteProperty(p,L){let z=Reflect.deleteProperty(p,L);return z&&typeof L==`string`&&R(L),z}})}async function initializePersistedState(p,L,R=[],z={}){let{shallow:B=!0,throwOnError:V=!0}=z,H=p.ctx.storage,U=R.length>0?R.map(String):Object.keys(L),W={...L};for(let p of U)try{let L=await H.get(`_verani_persist:${p}`);if(L!==void 0)try{W[p]=safeDeserialize(L)}catch(L){if(V)throw new PersistError(p,L)}}catch(L){if(V&&!(L instanceof PersistError))throw new PersistError(p,L)}let G=async(L,R)=>{if(U.includes(L))try{let p=safeSerialize(R);await H.put(`_verani_persist:${L}`,p)}catch(R){let z=p[PERSIST_ERROR_HANDLER];if(z&&z(L,R),V)throw new PersistError(L,R)}},K=async L=>{if(U.includes(L))try{await H.delete(`_verani_persist:${L}`)}catch(R){let z=p[PERSIST_ERROR_HANDLER];if(z&&z(L,R),V)throw new PersistError(L,R)}},q;return q=B?createShallowProxy(W,(p,L)=>{G(String(p),L)},p=>{K(String(p))}):createDeepProxy(W,(p,L)=>{G(p,L)},p=>{K(p)},U),p[STATE_READY]=!0,p[PERSISTED_STATE]=q,q}function createDeepProxy(p,L,R,z,B){return new Proxy(p,{get(p,V){let H=Reflect.get(p,V);if(H&&typeof H==`object`&&!Array.isArray(H)){let p=B??String(V);if(z.includes(p)||B!==void 0)return createDeepProxy(H,L,R,z,p)}return H},set(p,R,V){let H=Reflect.set(p,R,V);if(H){let p=B??String(R);z.includes(p)&&(B?L(B,void 0):L(String(R),V))}return H},deleteProperty(p,V){let H=Reflect.deleteProperty(p,V);if(H){let p=B??String(V);z.includes(p)&&(B?L(B,void 0):R(String(V)))}return H}})}function isStateReady(p){return p[STATE_READY]===!0}function getPersistedState(p){if(!isStateReady(p))throw new PersistNotReadyError(`state`);return p[PERSISTED_STATE]}function setPeristErrorHandler(p,L){p[PERSIST_ERROR_HANDLER]=L}async function persistKey(p,L,R){let z=p.ctx.storage,B=safeSerialize(R);await z.put(`_verani_persist:${L}`,B)}async function deletePersistedKey(p,L){await p.ctx.storage.delete(`_verani_persist:${L}`)}async function getPersistedKeys(p){let L=await p.ctx.storage.list({prefix:`_verani_persist:`});return Array.from(L.keys()).map(p=>p.replace(`_verani_persist:`,``))}async function clearPersistedState(p){let L=p.ctx.storage,R=await L.list({prefix:`_verani_persist:`}),z=Array.from(R.keys());await L.delete(z)}function createActorHandler(p){let L=sanitizeToClassName(p.name||p.websocketPath||`VeraniActor`),z=p;class B extends Actor{constructor(...L){super(...L),this.sessions=new Map,this.emit=createActorEmit(this),this[STATE_READY]=!1,this[PERSISTED_STATE]=p.state?{...p.state}:{},this.fetch=createFetch(z,this)}get roomState(){return this[PERSISTED_STATE]}isStateReady(){return isStateReady(this)}static{this.configuration=createConfiguration(z)}async shouldUpgradeWebSocket(p){return!0}async onInit(){if(p.state){p.onPersistError&&setPeristErrorHandler(this,p.onPersistError);let L=p.persistedKeys;this[PERSISTED_STATE]=await initializePersistedState(this,p.state,L,p.persistOptions)}await onInit(this,z)}async onWebSocketConnect(p,L){await onWebSocketConnect(this,z,p,L)}async onWebSocketMessage(p,L){await onWebSocketMessage(this,z,p,L)}async onWebSocketDisconnect(p){await onWebSocketDisconnect(this,z,p)}cleanupStaleSessions(){return cleanupStaleSessions(this.sessions)}broadcast(p,L,R){return broadcast(this.sessions,p,L,R)}getSessionCount(){return getSessionCount(this.sessions)}getConnectedUserIds(){return getConnectedUserIds(this.sessions)}getUserSessions(p){return getUserSessions(this.sessions,p)}sendToUser(p,L,R){return sendToUser(this.sessions,p,L,R)}emitToChannel(p,L,R){let z={type:L,...R};return broadcast(this.sessions,p,z)}emitToUser(p,L,R){let z=encodeFrame({type:`event`,channel:`default`,data:{type:L,...R}}),B=0,V=[];for(let{ws:L,meta:R}of this.sessions.values())if(R.userId===p){if(L.readyState!==WebSocket.OPEN){V.push(L);continue}try{L.send(z),B++}catch{V.push(L)}}for(let p of V)this.sessions.delete(p);return B}getStorage(){return getStorage(this.ctx)}}return Object.defineProperty(B,`name`,{value:L,writable:!1,configurable:!0}),B}export{deletePersistedKey as a,initializePersistedState as c,safeDeserialize as d,safeSerialize as f,defineRoom as g,storeAttachment as h,clearPersistedState as i,isStateReady as l,restoreSessions as m,PersistError as n,getPersistedKeys as o,setPeristErrorHandler as p,PersistNotReadyError as r,getPersistedState as s,createActorHandler as t,persistKey as u};
|
package/dist/typed.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const require_actor_runtime=require(`./actor-runtime-
|
|
1
|
+
const require_actor_runtime=require(`./actor-runtime-Fr0n1XGB.cjs`);require(`./encode-BYFW_6TI.cjs`);const require_validation=require(`./validation-DvWPGkXK.cjs`);function createTypedSocketEmit(e){let n=((n,r)=>{e.emit(n,r)});return n.to=n=>{let r=e.to(n);return{emit:(e,n)=>r.emit(e,n)}},n}function createTypedActorEmit(e){let n=((n,r)=>e.emit.emit(n,r));return n.to=n=>{let r=e.emit.to(n);return{emit:(e,n)=>r.emit(e,n)}},n}function wrapContext(e){return{actor:Object.assign({},e.actor,{emit:createTypedActorEmit(e.actor)}),ws:e.ws,meta:e.meta,emit:createTypedSocketEmit(e.emit)}}function wrapMessageContext(e){return{...wrapContext(e),frame:e.frame}}function createTypedRoom(r,i){let o=require_actor_runtime.g({name:i.name,websocketPath:i.websocketPath??`/ws`,extractMeta:i.extractMeta,onConnect:i.onConnect?e=>i.onConnect(wrapContext(e)):void 0,onDisconnect:i.onDisconnect?e=>i.onDisconnect(wrapContext(e)):void 0,onError:i.onError?(e,n)=>i.onError(e,wrapContext(n)):void 0,onHibernationRestore:i.onHibernationRestore}),s=require_validation.o(r),c=s?require_validation.a(r):void 0;return{on(e,i){o.on(e,(a,o)=>{let l=wrapMessageContext(a);if(s){let a=require_validation.r(r,e);if(a){let r=require_validation.s(a,o,e,`client`,c);return r===void 0?void 0:i(l,r)}}return i(l,o)})},off(e,n){o.off(e)},get definition(){return o},contract:r}}exports.createActorHandler=require_actor_runtime.t,exports.createTypedRoom=createTypedRoom,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.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as ConnectionMeta } from "./types-BefWc1M_.cjs";
|
|
2
2
|
import { A as TypedServerEmit, B as payload, C as InferServerEvents, D as ServerPayloadMap, E as ServerPayload, F as EventPayloads, I as ExtractPayload, L as PayloadMarker, M as Contract, N as ContractDefinition, O as TypedClientEmit, P as EventMap, R as defineContract, S as InferClientEvents, T as ServerEventNames, _ as ClientEventHandler, a as ValidationResult, b as ClientPayloadMap, c as createValidatedHandler, f as getValidationErrorHandler, g as AllEventNames, h as withValidation, i as ValidationIssue, j as TypedServerListener, k as TypedClientListener, m as validateData, n as ValidationConfig, o as Validator, p as isValidatedContract, r as ValidationError, s as ValidatorMap, t as ValidatedContract, u as getClientValidator, v as ClientEventNames, w as ServerEventHandler, x as InferChannels, y as ClientPayload, z as isContract } from "./validation-Dw-KMz9Q.cjs";
|
|
3
|
-
import { c as RoomDefinition, n as createActorHandler, r as ActorStub, u as VeraniActor } from "./actor-runtime-
|
|
3
|
+
import { c as RoomDefinition, n as createActorHandler, r as ActorStub, u as VeraniActor } from "./actor-runtime-gMSqKdY5.cjs";
|
|
4
4
|
|
|
5
5
|
//#region src/typed/server.d.ts
|
|
6
6
|
|
|
@@ -110,7 +110,7 @@ interface TypedRoomConfig<C extends Contract, TMeta extends ConnectionMeta = Con
|
|
|
110
110
|
*/
|
|
111
111
|
type TypedEventHandler<C extends Contract, E extends ClientEventNames<C>, TMeta extends ConnectionMeta = ConnectionMeta, TEnv = unknown> = (ctx: TypedMessageContext<C, TMeta, TEnv>, data: ClientPayload<C, E>) => void | Promise<void>;
|
|
112
112
|
/**
|
|
113
|
-
* Typed room with contract-aware
|
|
113
|
+
* Typed room with contract-aware event handling (Socket.io-like API).
|
|
114
114
|
*/
|
|
115
115
|
interface TypedRoom<C extends Contract, TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
116
116
|
/**
|
|
@@ -122,13 +122,13 @@ interface TypedRoom<C extends Contract, TMeta extends ConnectionMeta = Connectio
|
|
|
122
122
|
*
|
|
123
123
|
* @example
|
|
124
124
|
* ```typescript
|
|
125
|
-
* room.
|
|
125
|
+
* room.on("message.send", (ctx, data) => {
|
|
126
126
|
* // data: { text: string } - inferred from contract!
|
|
127
127
|
* ctx.emit("chat.message", { from: ctx.meta.userId, text: data.text });
|
|
128
128
|
* });
|
|
129
129
|
* ```
|
|
130
130
|
*/
|
|
131
|
-
|
|
131
|
+
on<TEvent extends ClientEventNames<C>>(event: TEvent, handler: TypedEventHandler<C, TEvent, TMeta, E>): void;
|
|
132
132
|
/**
|
|
133
133
|
* Remove an event handler.
|
|
134
134
|
* @param event - Event name
|
|
@@ -148,7 +148,7 @@ interface TypedRoom<C extends Contract, TMeta extends ConnectionMeta = Connectio
|
|
|
148
148
|
* Creates a type-safe room based on a contract definition.
|
|
149
149
|
*
|
|
150
150
|
* This wraps the base `defineRoom()` function and adds:
|
|
151
|
-
* - Typed `
|
|
151
|
+
* - Typed `on()` method for registering event handlers
|
|
152
152
|
* - Typed `emit` on context for sending server events
|
|
153
153
|
* - Compile-time validation of event names and payloads
|
|
154
154
|
*
|
|
@@ -174,7 +174,7 @@ interface TypedRoom<C extends Contract, TMeta extends ConnectionMeta = Connectio
|
|
|
174
174
|
* },
|
|
175
175
|
* });
|
|
176
176
|
*
|
|
177
|
-
* room.
|
|
177
|
+
* room.on("message.send", (ctx, data) => {
|
|
178
178
|
* ctx.emit("chat.message", { from: ctx.meta.userId, text: data.text });
|
|
179
179
|
* });
|
|
180
180
|
*
|
package/dist/typed.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as ConnectionMeta } from "./types-Ds_V-GWZ.mjs";
|
|
2
2
|
import { A as TypedServerEmit, B as payload, C as InferServerEvents, D as ServerPayloadMap, E as ServerPayload, F as EventPayloads, I as ExtractPayload, L as PayloadMarker, M as Contract, N as ContractDefinition, O as TypedClientEmit, P as EventMap, R as defineContract, S as InferClientEvents, T as ServerEventNames, _ as ClientEventHandler, a as ValidationResult, b as ClientPayloadMap, c as createValidatedHandler, f as getValidationErrorHandler, g as AllEventNames, h as withValidation, i as ValidationIssue, j as TypedServerListener, k as TypedClientListener, m as validateData, n as ValidationConfig, o as Validator, p as isValidatedContract, r as ValidationError, s as ValidatorMap, t as ValidatedContract, u as getClientValidator, v as ClientEventNames, w as ServerEventHandler, x as InferChannels, y as ClientPayload, z as isContract } from "./validation-DCongzPL.mjs";
|
|
3
|
-
import { c as RoomDefinition, n as createActorHandler, r as ActorStub, u as VeraniActor } from "./actor-runtime-
|
|
3
|
+
import { c as RoomDefinition, n as createActorHandler, r as ActorStub, u as VeraniActor } from "./actor-runtime-Dkz-u49p.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/typed/server.d.ts
|
|
6
6
|
|
|
@@ -110,7 +110,7 @@ interface TypedRoomConfig<C extends Contract, TMeta extends ConnectionMeta = Con
|
|
|
110
110
|
*/
|
|
111
111
|
type TypedEventHandler<C extends Contract, E extends ClientEventNames<C>, TMeta extends ConnectionMeta = ConnectionMeta, TEnv = unknown> = (ctx: TypedMessageContext<C, TMeta, TEnv>, data: ClientPayload<C, E>) => void | Promise<void>;
|
|
112
112
|
/**
|
|
113
|
-
* Typed room with contract-aware
|
|
113
|
+
* Typed room with contract-aware event handling (Socket.io-like API).
|
|
114
114
|
*/
|
|
115
115
|
interface TypedRoom<C extends Contract, TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> {
|
|
116
116
|
/**
|
|
@@ -122,13 +122,13 @@ interface TypedRoom<C extends Contract, TMeta extends ConnectionMeta = Connectio
|
|
|
122
122
|
*
|
|
123
123
|
* @example
|
|
124
124
|
* ```typescript
|
|
125
|
-
* room.
|
|
125
|
+
* room.on("message.send", (ctx, data) => {
|
|
126
126
|
* // data: { text: string } - inferred from contract!
|
|
127
127
|
* ctx.emit("chat.message", { from: ctx.meta.userId, text: data.text });
|
|
128
128
|
* });
|
|
129
129
|
* ```
|
|
130
130
|
*/
|
|
131
|
-
|
|
131
|
+
on<TEvent extends ClientEventNames<C>>(event: TEvent, handler: TypedEventHandler<C, TEvent, TMeta, E>): void;
|
|
132
132
|
/**
|
|
133
133
|
* Remove an event handler.
|
|
134
134
|
* @param event - Event name
|
|
@@ -148,7 +148,7 @@ interface TypedRoom<C extends Contract, TMeta extends ConnectionMeta = Connectio
|
|
|
148
148
|
* Creates a type-safe room based on a contract definition.
|
|
149
149
|
*
|
|
150
150
|
* This wraps the base `defineRoom()` function and adds:
|
|
151
|
-
* - Typed `
|
|
151
|
+
* - Typed `on()` method for registering event handlers
|
|
152
152
|
* - Typed `emit` on context for sending server events
|
|
153
153
|
* - Compile-time validation of event names and payloads
|
|
154
154
|
*
|
|
@@ -174,7 +174,7 @@ interface TypedRoom<C extends Contract, TMeta extends ConnectionMeta = Connectio
|
|
|
174
174
|
* },
|
|
175
175
|
* });
|
|
176
176
|
*
|
|
177
|
-
* room.
|
|
177
|
+
* room.on("message.send", (ctx, data) => {
|
|
178
178
|
* ctx.emit("chat.message", { from: ctx.meta.userId, text: data.text });
|
|
179
179
|
* });
|
|
180
180
|
*
|
package/dist/typed.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{g as defineRoom,t as createActorHandler}from"./actor-runtime-mfMYeKoB.mjs";import"./encode-BhJqnsto.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 createTypedSocketEmit(e){let l=((l,u)=>{e.emit(l,u)});return l.to=l=>{let u=e.to(l);return{emit:(e,l)=>u.emit(e,l)}},l}function createTypedActorEmit(e){let l=((l,u)=>e.emit.emit(l,u));return l.to=l=>{let u=e.emit.to(l);return{emit:(e,l)=>u.emit(e,l)}},l}function wrapContext(e){return{actor:Object.assign({},e.actor,{emit:createTypedActorEmit(e.actor)}),ws:e.ws,meta:e.meta,emit:createTypedSocketEmit(e.emit)}}function wrapMessageContext(e){return{...wrapContext(e),frame:e.frame}}function createTypedRoom(l,d){let f=defineRoom({name:d.name,websocketPath:d.websocketPath??`/ws`,extractMeta:d.extractMeta,onConnect:d.onConnect?e=>d.onConnect(wrapContext(e)):void 0,onDisconnect:d.onDisconnect?e=>d.onDisconnect(wrapContext(e)):void 0,onError:d.onError?(e,l)=>d.onError(e,wrapContext(l)):void 0,onHibernationRestore:d.onHibernationRestore}),p=isValidatedContract(l),h=p?getValidationErrorHandler(l):void 0;return{on(e,u){f.on(e,(d,f)=>{let m=wrapMessageContext(d);if(p){let d=getClientValidator(l,e);if(d){let l=validateData(d,f,e,`client`,h);return l===void 0?void 0:u(m,l)}}return u(m,f)})},off(e,l){f.off(e)},get definition(){return f},contract:l}}export{createActorHandler,createTypedRoom,createValidatedHandler,defineContract,getClientValidator,getValidationErrorHandler,isContract,isValidatedContract,payload,validateData,withValidation};
|
package/dist/verani.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const require_actor_runtime=require(`./actor-runtime-
|
|
1
|
+
const require_actor_runtime=require(`./actor-runtime-Fr0n1XGB.cjs`),require_encode=require(`./encode-BYFW_6TI.cjs`),require_types=require(`./types-DUO6RVw1.cjs`);exports.PROTOCOL_VERSION=require_types.t,exports.PersistError=require_actor_runtime.n,exports.PersistNotReadyError=require_actor_runtime.r,exports.clearPersistedState=require_actor_runtime.i,exports.createActorHandler=require_actor_runtime.t,exports.decodeClientMessage=require_encode.i,exports.decodeFrame=require_encode.a,exports.decodeServerMessage=require_encode.o,exports.defineRoom=require_actor_runtime.g,exports.deletePersistedKey=require_actor_runtime.a,exports.encodeClientMessage=require_encode.t,exports.encodeFrame=require_encode.n,exports.encodeServerMessage=require_encode.r,exports.getPersistedKeys=require_actor_runtime.o,exports.getPersistedState=require_actor_runtime.s,exports.initializePersistedState=require_actor_runtime.c,exports.isStateReady=require_actor_runtime.l,exports.persistKey=require_actor_runtime.u,exports.restoreSessions=require_actor_runtime.m,exports.safeDeserialize=require_actor_runtime.d,exports.safeSerialize=require_actor_runtime.f,exports.setPeristErrorHandler=require_actor_runtime.p,exports.storeAttachment=require_actor_runtime.h;
|
package/dist/verani.d.cts
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
import { a as ServerMessage, i as PROTOCOL_VERSION, n as ConnectionMeta, o as VeraniMessage, r as MessageFrame, t as ClientMessage } from "./types-BefWc1M_.cjs";
|
|
2
2
|
import { a as encodeFrame, i as encodeClientMessage, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, t as decodeClientMessage } from "./decode-D5ZPxqwe.cjs";
|
|
3
|
-
import { a as EventHandler, c as RoomDefinition, i as BroadcastOptions, l as RpcBroadcastOptions, n as createActorHandler, o as MessageContext, r as ActorStub, s as RoomContext, t as ActorHandlerClass, u as VeraniActor } from "./actor-runtime-
|
|
3
|
+
import { C as safeSerialize, S as safeDeserialize, _ as getPersistedKeys, a as EventHandler, b as isStateReady, c as RoomDefinition, d as PersistError, f as PersistNotReadyError, g as deletePersistedKey, h as clearPersistedState, i as BroadcastOptions, l as RpcBroadcastOptions, m as SafePersistOptions, n as createActorHandler, o as MessageContext, p as PersistableActor, r as ActorStub, s as RoomContext, t as ActorHandlerClass, u as VeraniActor, v as getPersistedState, w as setPeristErrorHandler, x as persistKey, y as initializePersistedState } from "./actor-runtime-gMSqKdY5.cjs";
|
|
4
4
|
|
|
5
5
|
//#region src/actor/router.d.ts
|
|
6
6
|
/**
|
|
7
7
|
* Extended room definition with socket.io-like convenience methods
|
|
8
8
|
*/
|
|
9
|
-
interface RoomDefinitionWithHandlers<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> extends RoomDefinition<TMeta, E> {
|
|
9
|
+
interface RoomDefinitionWithHandlers<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> extends RoomDefinition<TMeta, E, TState> {
|
|
10
10
|
/**
|
|
11
11
|
* Register an event handler (socket.io-like API)
|
|
12
12
|
* @param event - Event name
|
|
13
13
|
* @param handler - Handler function
|
|
14
14
|
*/
|
|
15
|
-
on(event: string, handler: EventHandler<TMeta, E>): void;
|
|
15
|
+
on(event: string, handler: EventHandler<TMeta, E, TState>): void;
|
|
16
16
|
/**
|
|
17
17
|
* Remove an event handler (socket.io-like API)
|
|
18
18
|
* @param event - Event name
|
|
19
19
|
* @param handler - Optional specific handler to remove
|
|
20
20
|
*/
|
|
21
|
-
off(event: string, handler?: EventHandler<TMeta, E>): void;
|
|
21
|
+
off(event: string, handler?: EventHandler<TMeta, E, TState>): void;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Defines a room with lifecycle hooks and metadata extraction
|
|
25
25
|
* @param def - Room definition with optional hooks
|
|
26
26
|
* @returns Normalized room definition with defaults and socket.io-like event handler methods
|
|
27
27
|
*/
|
|
28
|
-
declare function defineRoom<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown>(def: RoomDefinition<TMeta, E>): RoomDefinitionWithHandlers<TMeta, E>;
|
|
28
|
+
declare function defineRoom<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>>(def: RoomDefinition<TMeta, E, TState>): RoomDefinitionWithHandlers<TMeta, E, TState>;
|
|
29
29
|
//#endregion
|
|
30
30
|
//#region src/actor/attachment.d.ts
|
|
31
31
|
declare function storeAttachment(ws: WebSocket, meta: ConnectionMeta): void;
|
|
32
32
|
declare function restoreSessions(actor: any): void;
|
|
33
33
|
//#endregion
|
|
34
|
-
export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, type ConnectionMeta, type MessageContext, type MessageFrame, PROTOCOL_VERSION, type RoomContext, type RoomDefinition, type RpcBroadcastOptions, type ServerMessage, type VeraniActor, type VeraniMessage, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, encodeClientMessage, encodeFrame, encodeServerMessage, restoreSessions, storeAttachment };
|
|
34
|
+
export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, type ConnectionMeta, type MessageContext, type MessageFrame, PROTOCOL_VERSION, PersistError, PersistNotReadyError, type PersistableActor, type RoomContext, type RoomDefinition, type RpcBroadcastOptions, type SafePersistOptions, type ServerMessage, type VeraniActor, type VeraniMessage, clearPersistedState, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, deletePersistedKey, encodeClientMessage, encodeFrame, encodeServerMessage, getPersistedKeys, getPersistedState, initializePersistedState, isStateReady, persistKey, restoreSessions, safeDeserialize, safeSerialize, setPeristErrorHandler, storeAttachment };
|
package/dist/verani.d.mts
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
import { a as ServerMessage, i as PROTOCOL_VERSION, n as ConnectionMeta, o as VeraniMessage, r as MessageFrame, t as ClientMessage } from "./types-Ds_V-GWZ.mjs";
|
|
2
2
|
import { a as encodeFrame, i as encodeClientMessage, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, t as decodeClientMessage } from "./decode-E39NapB5.mjs";
|
|
3
|
-
import { a as EventHandler, c as RoomDefinition, i as BroadcastOptions, l as RpcBroadcastOptions, n as createActorHandler, o as MessageContext, r as ActorStub, s as RoomContext, t as ActorHandlerClass, u as VeraniActor } from "./actor-runtime-
|
|
3
|
+
import { C as safeSerialize, S as safeDeserialize, _ as getPersistedKeys, a as EventHandler, b as isStateReady, c as RoomDefinition, d as PersistError, f as PersistNotReadyError, g as deletePersistedKey, h as clearPersistedState, i as BroadcastOptions, l as RpcBroadcastOptions, m as SafePersistOptions, n as createActorHandler, o as MessageContext, p as PersistableActor, r as ActorStub, s as RoomContext, t as ActorHandlerClass, u as VeraniActor, v as getPersistedState, w as setPeristErrorHandler, x as persistKey, y as initializePersistedState } from "./actor-runtime-Dkz-u49p.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/actor/router.d.ts
|
|
6
6
|
/**
|
|
7
7
|
* Extended room definition with socket.io-like convenience methods
|
|
8
8
|
*/
|
|
9
|
-
interface RoomDefinitionWithHandlers<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown> extends RoomDefinition<TMeta, E> {
|
|
9
|
+
interface RoomDefinitionWithHandlers<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>> extends RoomDefinition<TMeta, E, TState> {
|
|
10
10
|
/**
|
|
11
11
|
* Register an event handler (socket.io-like API)
|
|
12
12
|
* @param event - Event name
|
|
13
13
|
* @param handler - Handler function
|
|
14
14
|
*/
|
|
15
|
-
on(event: string, handler: EventHandler<TMeta, E>): void;
|
|
15
|
+
on(event: string, handler: EventHandler<TMeta, E, TState>): void;
|
|
16
16
|
/**
|
|
17
17
|
* Remove an event handler (socket.io-like API)
|
|
18
18
|
* @param event - Event name
|
|
19
19
|
* @param handler - Optional specific handler to remove
|
|
20
20
|
*/
|
|
21
|
-
off(event: string, handler?: EventHandler<TMeta, E>): void;
|
|
21
|
+
off(event: string, handler?: EventHandler<TMeta, E, TState>): void;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Defines a room with lifecycle hooks and metadata extraction
|
|
25
25
|
* @param def - Room definition with optional hooks
|
|
26
26
|
* @returns Normalized room definition with defaults and socket.io-like event handler methods
|
|
27
27
|
*/
|
|
28
|
-
declare function defineRoom<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown>(def: RoomDefinition<TMeta, E>): RoomDefinitionWithHandlers<TMeta, E>;
|
|
28
|
+
declare function defineRoom<TMeta extends ConnectionMeta = ConnectionMeta, E = unknown, TState extends Record<string, unknown> = Record<string, unknown>>(def: RoomDefinition<TMeta, E, TState>): RoomDefinitionWithHandlers<TMeta, E, TState>;
|
|
29
29
|
//#endregion
|
|
30
30
|
//#region src/actor/attachment.d.ts
|
|
31
31
|
declare function storeAttachment(ws: WebSocket, meta: ConnectionMeta): void;
|
|
32
32
|
declare function restoreSessions(actor: any): void;
|
|
33
33
|
//#endregion
|
|
34
|
-
export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, type ConnectionMeta, type MessageContext, type MessageFrame, PROTOCOL_VERSION, type RoomContext, type RoomDefinition, type RpcBroadcastOptions, type ServerMessage, type VeraniActor, type VeraniMessage, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, encodeClientMessage, encodeFrame, encodeServerMessage, restoreSessions, storeAttachment };
|
|
34
|
+
export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, type ConnectionMeta, type MessageContext, type MessageFrame, PROTOCOL_VERSION, PersistError, PersistNotReadyError, type PersistableActor, type RoomContext, type RoomDefinition, type RpcBroadcastOptions, type SafePersistOptions, type ServerMessage, type VeraniActor, type VeraniMessage, clearPersistedState, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, deletePersistedKey, encodeClientMessage, encodeFrame, encodeServerMessage, getPersistedKeys, getPersistedState, initializePersistedState, isStateReady, persistKey, restoreSessions, safeDeserialize, safeSerialize, setPeristErrorHandler, storeAttachment };
|
package/dist/verani.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{a as deletePersistedKey,c as initializePersistedState,d as safeDeserialize,f as safeSerialize,g as defineRoom,h as storeAttachment,i as clearPersistedState,l as isStateReady,m as restoreSessions,n as PersistError,o as getPersistedKeys,p as setPeristErrorHandler,r as PersistNotReadyError,s as getPersistedState,t as createActorHandler,u as persistKey}from"./actor-runtime-mfMYeKoB.mjs";import{a as decodeFrame,i as decodeClientMessage,n as encodeFrame,o as decodeServerMessage,r as encodeServerMessage,t as encodeClientMessage}from"./encode-BhJqnsto.mjs";import{t as PROTOCOL_VERSION}from"./types-rZsftNPE.mjs";export{PROTOCOL_VERSION,PersistError,PersistNotReadyError,clearPersistedState,createActorHandler,decodeClientMessage,decodeFrame,decodeServerMessage,defineRoom,deletePersistedKey,encodeClientMessage,encodeFrame,encodeServerMessage,getPersistedKeys,getPersistedState,initializePersistedState,isStateReady,persistKey,restoreSessions,safeDeserialize,safeSerialize,setPeristErrorHandler,storeAttachment};
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as decodeFrame$1,n as encodeFrame$1}from"./encode-BhJqnsto.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};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const require_encode=require(`./encode-BYFW_6TI.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(S){return require_encode.a(S)??{type:`invalid`}}function encodeFrame(S){return require_encode.n(S)}function broadcast(e,S,C,w){let T=0,E=encodeFrame({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({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(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(w);if(E&&E.type===`ping`){if(C.readyState===WebSocket.OPEN)try{C.send(encodeFrame({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=encodeFrame({type:`event`,channel:`default`,data:{type:S,...C}}),T=0,E=[];for(let{ws:S,meta:C}of this.sessions.values())if(C.userId===e){if(S.readyState!==WebSocket.OPEN){E.push(S);continue}try{S.send(w),T++}catch{E.push(S)}}for(let e of E)this.sessions.delete(e);return T}getStorage(){return getStorage(this.ctx)}}return Object.defineProperty(w,`name`,{value:C,writable:!1,configurable:!0}),w}Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return defineRoom}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return restoreSessions}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return storeAttachment}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return createActorHandler}});
|