@pluv/io 3.2.2 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["authorize","doc","sessionId","__PLUV_VERSION","__PLUV_VERSION","authorize"],"sources":["../src/AbstractPersistence.ts","../src/Persistence.ts","../src/AbstractPubSub.ts","../src/PubSub.ts","../src/AbstractPlatform.ts","../src/AbstractWebSocket.ts","../src/authorize.ts","../src/PluvProcedure.ts","../src/PluvRouter.ts","../src/constants.ts","../src/utils/oneLine.ts","../src/utils/pickBy.ts","../src/IORoom.ts","../src/version.generated.ts","../src/PluvServer.ts","../src/PluvIO.ts","../src/createIO.ts"],"sourcesContent":["import type { JsonObject } from \"@pluv/types\";\n\nexport abstract class AbstractPersistence {\n public abstract addUser(\n room: string,\n connectionId: string,\n user: JsonObject | null,\n ): Promise<void>;\n\n public abstract deleteStorageState(room: string): Promise<void>;\n\n public abstract deleteUser(room: string, connectionId: string): Promise<void>;\n\n public abstract deleteUsers(room: string): Promise<void>;\n\n public abstract getStorageState(room: string): Promise<string | null>;\n\n public abstract getUser(room: string, connectionId: string): Promise<JsonObject | null>;\n\n public abstract getUsers(\n room: string,\n ): Promise<Map<[connectionId: string][0], JsonObject | null>>;\n\n public abstract getUsersSize(room: string): Promise<number>;\n\n public abstract initialize(roomContext: any): typeof this;\n\n public abstract setStorageState(room: string, state: string): Promise<void>;\n}\n","import type { JsonObject } from \"@pluv/types\";\nimport { AbstractPersistence } from \"./AbstractPersistence\";\n\nexport class Persistence extends AbstractPersistence {\n private _storages = new Map<string, string>();\n private _users = new Map<string, Map<string, JsonObject | null>>();\n\n public addUser(room: string, connectionId: string, user: JsonObject | null): Promise<void> {\n const users = this._users.get(room) ?? new Map<string, JsonObject | null>();\n\n this._users.set(room, users);\n users.set(connectionId, user);\n\n return Promise.resolve();\n }\n\n public deleteStorageState(room: string): Promise<void> {\n this._storages.delete(room);\n\n return Promise.resolve();\n }\n\n public async deleteUser(room: string, connectionId: string): Promise<void> {\n const users = this._users.get(room);\n\n if (!users) return;\n\n users.delete(connectionId);\n\n if (!users.size) {\n await this.deleteUsers(room);\n await this.deleteStorageState(room);\n }\n\n return Promise.resolve();\n }\n\n public deleteUsers(room: string): Promise<void> {\n this._users.delete(room);\n\n return Promise.resolve();\n }\n\n public getSize(room: string): Promise<number> {\n return Promise.resolve(this._users.get(room)?.size ?? 0);\n }\n\n public getStorageState(room: string): Promise<string | null> {\n const storage = this._storages.get(room) ?? null;\n\n return Promise.resolve(storage);\n }\n\n public getUser(room: string, connectionId: string): Promise<JsonObject | null> {\n const user = this._users.get(room)?.get(connectionId);\n\n return Promise.resolve(user ?? null);\n }\n\n public async getUsers(room: string): Promise<Map<string, JsonObject | null>> {\n const users = this._users.get(room);\n\n if (!users) return new Map();\n\n return users;\n }\n\n public async getUsersSize(room: string): Promise<number> {\n return this._users.size;\n }\n\n public initialize(roomContext: any): this {\n return this;\n }\n\n public setStorageState(room: string, state: string): Promise<void> {\n this._storages.set(room, state);\n\n return Promise.resolve();\n }\n}\n","import type { IOEventMessage, IOLike, InferIOOutput } from \"@pluv/types\";\nimport type { SendMessageOptions } from \"./types\";\n\nexport type IOPubSubEventMessage<\n TIO extends IOLike,\n TEvent extends keyof InferIOOutput<TIO> = keyof InferIOOutput<TIO>,\n> = IOEventMessage<TIO, TEvent> & {\n options?: SendMessageOptions;\n};\n\nexport abstract class AbstractPubSub {\n public abstract publish(roomName: string, payload: IOPubSubEventMessage<any>): Promise<void>;\n\n public abstract subscribe(\n roomName: string,\n onMessage: (message: IOPubSubEventMessage<any>) => void,\n ): Promise<number>;\n\n public abstract unsubscribe(subscriptionId: number): void;\n}\n","import type { Subject } from \"wonka\";\nimport { makeSubject, subscribe } from \"wonka\";\nimport type { IOPubSubEventMessage } from \"./AbstractPubSub\";\nimport { AbstractPubSub } from \"./AbstractPubSub\";\n\nexport class PubSub extends AbstractPubSub {\n private _currentSubscriptionId: number = 0;\n // Map of roomName to subjects\n private _subjects = new Map<string, Subject<IOPubSubEventMessage<any>>>();\n // Map of roomName to subscription ids\n private _subscriptionsRefs = new Map<string, readonly number[]>();\n // Map of\n private _unsubscriptions = new Map<number, [roomName: string, unsubscribe: () => void]>();\n\n public async publish(roomName: string, payload: IOPubSubEventMessage<any>): Promise<void> {\n const subject = this._subjects.get(roomName);\n\n if (!subject) return;\n\n Promise.resolve(subject.next(payload));\n }\n\n public subscribe(\n roomName: string,\n onMessage: (message: IOPubSubEventMessage<any>) => void,\n ): Promise<number> {\n const id = this._currentSubscriptionId++;\n const refs = this._subscriptionsRefs.get(roomName) ?? [];\n\n const subject = makeSubject<IOPubSubEventMessage<any>>();\n\n this._subjects.set(roomName, subject);\n this._subscriptionsRefs.set(roomName, [...refs, id]);\n\n const unsubscribe = subscribe(onMessage)(subject.source).unsubscribe;\n\n this._unsubscriptions.set(id, [roomName, unsubscribe]);\n\n return Promise.resolve(id);\n }\n\n public unsubscribe(subscriptionId: number): void {\n const [roomName, unsubscribe] = this._unsubscriptions.get(subscriptionId) ?? [];\n\n if (!roomName || !unsubscribe) return;\n\n const refs = this._subscriptionsRefs.get(roomName);\n\n if (!refs) return;\n\n if (refs.length === 1) {\n this._subjects.delete(roomName);\n this._subscriptionsRefs.delete(roomName);\n this._unsubscriptions.delete(subscriptionId);\n\n return;\n }\n\n const index = refs.indexOf(subscriptionId);\n\n this._subscriptionsRefs.set(\n roomName,\n index === -1 ? refs : [...refs.slice(0, index), ...refs.slice(index + 1)],\n );\n\n unsubscribe();\n\n this._unsubscriptions.delete(subscriptionId);\n }\n}\n","import type { Maybe } from \"@pluv/types\";\nimport type { AbstractPersistence } from \"./AbstractPersistence\";\nimport type { AbstractPubSub } from \"./AbstractPubSub\";\nimport type { AbstractWebSocket, InferWebSocketSource } from \"./AbstractWebSocket\";\nimport type { JWTEncodeParams } from \"./authorize\";\nimport { Persistence } from \"./Persistence\";\nimport { PubSub } from \"./PubSub\";\nimport type { PlatformConfig, ResolvedPluvIOAuthorize, WebSocketSerializedState } from \"./types\";\n\nexport type InferPlatformWebSocketType<TPlatform extends AbstractPlatform> =\n TPlatform extends AbstractPlatform<infer IAbstractWebSocket> ? IAbstractWebSocket : never;\n\nexport type InferPlatformWebSocketSource<TPlatform> =\n TPlatform extends AbstractPlatform<infer IAbstractWebSocket>\n ? InferWebSocketSource<IAbstractWebSocket>\n : never;\n\nexport type InferInitContextType<TPlatform extends AbstractPlatform> =\n TPlatform extends AbstractPlatform<any, infer IInitContext> ? IInitContext : never;\n\nexport type InferRoomContextType<TPlatform extends AbstractPlatform> =\n TPlatform extends AbstractPlatform<any, any, infer IRoomContext> ? IRoomContext : never;\n\nexport type AbstractPlatformConfig<TRoomContext extends Record<string, any> = {}> = {\n persistence?: AbstractPersistence;\n pubSub?: AbstractPubSub;\n roomContext?: TRoomContext;\n};\n\nexport interface ConvertWebSocketConfig {\n room: string;\n}\n\nexport abstract class AbstractPlatform<\n TWebSocket extends AbstractWebSocket<any, any> = AbstractWebSocket<any, any>,\n TInitContext extends Record<string, any> = {},\n TRoomContext extends Record<string, any> = {},\n TConfig extends PlatformConfig = PlatformConfig,\n> {\n protected readonly _initContext: TInitContext | undefined;\n protected readonly _roomContext: TRoomContext | undefined;\n\n private _initialized: boolean = false;\n\n public persistence: AbstractPersistence;\n public pubSub: AbstractPubSub;\n\n public abstract readonly id: string;\n public readonly _createToken?: (\n params: JWTEncodeParams<any, any> & {\n authorize: Maybe<ResolvedPluvIOAuthorize<any, any>>;\n },\n ) => Promise<string>;\n public _fetch?: (...args: any[]) => Promise<any>;\n public abstract readonly _config: TConfig;\n public abstract readonly _name: string;\n\n constructor(config: AbstractPlatformConfig<TRoomContext> = {}) {\n const { roomContext, persistence, pubSub } = config;\n\n (this as any)._meta = (config as any)._meta;\n if (!!roomContext) this._roomContext = roomContext;\n\n this.persistence = persistence ?? new Persistence();\n this.pubSub = pubSub ?? new PubSub();\n }\n\n public abstract acceptWebSocket(webSocket: AbstractWebSocket): Promise<void>;\n\n public abstract convertWebSocket(\n webSocket: InferWebSocketSource<TWebSocket>,\n config: ConvertWebSocketConfig,\n ): AbstractWebSocket<any, any>;\n\n public abstract getLastPing(webSocket: AbstractWebSocket<any, any>): number | null;\n\n public abstract getSerializedState(\n webSocket: InferWebSocketSource<TWebSocket>,\n ): WebSocketSerializedState | null;\n\n public abstract getSessionId(webSocket: InferWebSocketSource<TWebSocket>): string | null;\n\n public abstract getWebSockets(): readonly InferWebSocketSource<TWebSocket>[];\n\n public abstract initialize(config: AbstractPlatformConfig<TRoomContext>): this;\n\n public abstract parseData(data: string | ArrayBuffer): Record<string, any>;\n\n public abstract randomUUID(): string;\n\n public abstract setSerializedState(\n webSocket: AbstractWebSocket,\n state: WebSocketSerializedState,\n ): WebSocketSerializedState;\n\n public validateConfig(config: any): void {}\n\n protected _initialize(): typeof this {\n if (this._initialized) throw new Error(\"Platform is already initialized\");\n\n this._initialized = true;\n\n return this;\n }\n}\n","import type {\n BaseUser,\n InferIOAuthorizeUser,\n IOAuthorize,\n IOEventMessage,\n JsonObject,\n MaybePromise,\n} from \"@pluv/types\";\nimport type { AbstractPersistence } from \"./AbstractPersistence\";\nimport type { AbstractPlatform } from \"./AbstractPlatform\";\nimport type { WebSocketSerializedState, WebSocketSession } from \"./types\";\n\nexport type AbstractEvent = {};\n\nexport type AbstractCloseEvent = AbstractEvent & {\n readonly code: number;\n readonly reason: string;\n};\n\nexport type AbstractMessageEvent<T = any> = AbstractEvent & {\n readonly data: T;\n};\n\nexport type AbstractErrorEvent = AbstractEvent & {\n error: unknown;\n message?: string;\n};\n\nexport interface AbstractEventMap {\n close: AbstractCloseEvent;\n message: AbstractMessageEvent;\n error: AbstractErrorEvent;\n}\n\nexport type EventType = keyof AbstractEventMap;\n\nexport interface AbstractWebSocketHandleErrorParams {\n error: unknown;\n message?: string;\n room: string;\n session?: WebSocketSession<any>;\n}\n\nexport type AbstractListener<TType extends keyof AbstractEventMap> = (\n event: AbstractEventMap[TType],\n) => MaybePromise<void>;\n\nexport type InferWebSocketSource<TAbstractWebSocket extends AbstractWebSocket> =\n TAbstractWebSocket extends AbstractWebSocket<infer IWebSocket> ? IWebSocket : never;\n\nexport interface AbstractWebSocketConfig {\n persistence: AbstractPersistence;\n platform: AbstractPlatform<any>;\n room: string;\n}\n\nexport abstract class AbstractWebSocket<\n TWebSocket = any,\n TAuthorize extends IOAuthorize<any, any> | null = null,\n> {\n /** The connection is not yet open. */\n public readonly CONNECTING = 0;\n /** The connection is open and ready to communicate. */\n public readonly OPEN = 1;\n /** The connection is in the process of closing. */\n public readonly CLOSING = 2;\n /** The connection is closed. */\n public readonly CLOSED = 3;\n\n public readonly persistence: AbstractPersistence;\n public readonly room: string;\n public readonly webSocket: TWebSocket;\n\n protected readonly _platform: AbstractPlatform<this>;\n\n public abstract set presence(presence: JsonObject | null);\n public abstract get readyState(): 0 | 1 | 2 | 3;\n public abstract get session(): WebSocketSession<TAuthorize>;\n public abstract get sessionId(): string;\n public abstract get state(): WebSocketSerializedState;\n public abstract set state(state: WebSocketSerializedState);\n public abstract set user(user: InferIOAuthorizeUser<TAuthorize>);\n\n constructor(webSocket: TWebSocket, config: AbstractWebSocketConfig) {\n const { persistence, platform, room } = config;\n\n this.persistence = persistence;\n this._platform = platform;\n this.room = room;\n this.webSocket = webSocket;\n }\n\n /**\n * TODO\n * @description Return an unsubscribe method to remove the event-listener.\n * This is preferred over adding an explicit abstract removeEventListener\n * method, because the implementing class might have to transform the\n * listener, and removing the same listener might be complicated as a\n * result.\n * @date August 28, 2022\n */\n public abstract addEventListener<TType extends EventType>(\n type: TType,\n handler: AbstractListener<TType>,\n ): void;\n\n public abstract close(code?: number | undefined, reason?: string | undefined): void;\n\n public abstract send(message: ArrayBuffer | ArrayBufferView | string): void;\n\n public abstract terminate(): void;\n\n public handleError(params: AbstractWebSocketHandleErrorParams): void {\n const { error, message: _message, room, session } = params;\n\n const message = error instanceof Error ? error.message : \"Unexpected error\";\n const stack: string | null = error instanceof Error ? (error.stack ?? null) : null;\n const connectionId = (session?.id ?? null) as string;\n\n return this.sendMessage({\n connectionId,\n data: { message, stack },\n room,\n type: \"$error\" as const,\n user: session?.user ?? null,\n });\n }\n\n public sendMessage<TMessage extends IOEventMessage<any> = IOEventMessage<any>>(\n data: TMessage,\n ): void {\n return this.send(JSON.stringify(data));\n }\n}\n","import hkdf from \"@panva/hkdf\";\nimport type { BaseUser } from \"@pluv/types\";\nimport { EncryptJWT, jwtDecrypt } from \"jose\";\nimport type { AbstractPlatform, InferInitContextType } from \"./AbstractPlatform\";\n\nconst DEFAULT_MAX_AGE_MS = 60_000;\n\nexport const getEncryptionKey = async (secret: string): Promise<Uint8Array> => {\n return await hkdf(\"sha256\", secret, \"\", \"Pluv.io Generated Encryption Key\", 32);\n};\n\nexport interface JWT<TUser extends BaseUser> {\n room: string;\n sub: string;\n user: TUser;\n}\n\nexport type JWTEncodeParams<\n TUser extends BaseUser | null,\n TPlatform extends AbstractPlatform<any, any>,\n> = {\n maxAge?: number;\n room: string;\n user: TUser extends BaseUser ? TUser : \"Error: Cannot create token without authorization!\";\n} & InferInitContextType<TPlatform>;\n\nexport interface AuthorizeParams {\n platform: AbstractPlatform<any, any>;\n secret: string;\n}\n\nexport interface AuthorizeModule {\n decode: <TUser extends BaseUser>(jwt: string) => Promise<TUser | null>;\n encode: <TUser extends BaseUser, TPlatform extends AbstractPlatform<any, any>>(\n params: JWTEncodeParams<TUser, TPlatform>,\n ) => Promise<string>;\n}\n\nconst now = () => (Date.now() / 1_000) | 0;\n\nexport const authorize = (params: AuthorizeParams) => {\n const { platform, secret } = params;\n\n const encode = async <TUser extends BaseUser, TPlatform extends AbstractPlatform<any, any>>(\n encodeParams: JWTEncodeParams<TUser, TPlatform>,\n ): Promise<string> => {\n const { maxAge = DEFAULT_MAX_AGE_MS, room, user } = encodeParams;\n\n const encryptionSecret = await getEncryptionKey(secret);\n\n const token = await new EncryptJWT({\n room,\n sub: room ? `${room}|${user.id}` : user.id,\n user,\n })\n .setProtectedHeader({ alg: \"dir\", enc: \"A256GCM\" })\n .setIssuedAt()\n .setExpirationTime(now() + maxAge)\n .setJti(platform.randomUUID())\n .encrypt(encryptionSecret);\n\n return token.toString();\n };\n\n const decode = async <TUser extends BaseUser>(jwt: string): Promise<JWT<TUser> | null> => {\n const encryptionSecret = await getEncryptionKey(secret);\n\n const { payload } = await jwtDecrypt(jwt, encryptionSecret, {\n clockTolerance: 15,\n });\n\n return (payload as any) ?? null;\n };\n\n return { decode, encode };\n};\n","import type {\n EventRecord,\n IOAuthorize,\n InputZodLike,\n JsonObject,\n ProcedureLike,\n} from \"@pluv/types\";\nimport type { AbstractPlatform, InferInitContextType } from \"./AbstractPlatform\";\nimport type { EventResolver, EventResolverKind, MergeEventRecords } from \"./types\";\n\nexport interface PluvProcedureConfig<\n TPlatform extends AbstractPlatform<any>,\n TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null,\n TContext extends Record<string, any>,\n TInput extends JsonObject = {},\n TOutput extends EventRecord<string, any> = {},\n> {\n broadcast?: EventResolver<\n \"broadcast\",\n TPlatform,\n TAuthorize,\n TContext,\n TInput,\n Partial<TOutput>\n > | null;\n input?: InputZodLike<TInput>;\n self?: EventResolver<\"self\", TPlatform, TAuthorize, TContext, TInput, Partial<TOutput>> | null;\n sync?: EventResolver<\"sync\", TPlatform, TAuthorize, TContext, TInput, Partial<TOutput>> | null;\n}\n\nexport class PluvProcedure<\n TPlatform extends AbstractPlatform<any>,\n TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null,\n TContext extends Record<string, any>,\n TInput extends JsonObject = {},\n TOutput extends EventRecord<string, any> = {},\n TFilled extends \"input\" | \"broadcast\" | \"self\" | \"sync\" | \"\" = \"\",\n> {\n private _broadcast: EventResolver<\n \"broadcast\",\n TPlatform,\n TAuthorize,\n TContext,\n TInput,\n Partial<TOutput>\n > | null = null;\n private _input: InputZodLike<TInput> | null = null;\n private _self: EventResolver<\n \"self\",\n TPlatform,\n TAuthorize,\n TContext,\n TInput,\n Partial<TOutput>\n > | null = null;\n private _sync: EventResolver<\n \"sync\",\n TPlatform,\n TAuthorize,\n TContext,\n TInput,\n Partial<TOutput>\n > | null = null;\n\n public get config(): ProcedureLike<TInput, TOutput>[\"config\"] {\n return {\n broadcast: this._broadcast?.bind(this) ?? null,\n input: this._input ?? null,\n resolver: this._resolver(),\n self: this._self?.bind(this) ?? null,\n sync: this._sync?.bind(this) ?? null,\n } as ProcedureLike<TInput, TOutput>[\"config\"];\n }\n\n constructor(\n config: PluvProcedureConfig<TPlatform, TAuthorize, TContext, TInput, TOutput> = {},\n ) {\n const { broadcast, input, self, sync } = config;\n\n this._broadcast = broadcast ?? null;\n this._input = input ?? null;\n this._self = self ?? null;\n this._sync = sync ?? null;\n }\n\n public broadcast<TResult extends EventRecord<string, any> = {}>(\n resolver: EventResolver<\"broadcast\", TPlatform, TAuthorize, TContext, TInput, TResult>,\n ): Omit<\n PluvProcedure<\n TPlatform,\n TAuthorize,\n TContext,\n TInput,\n MergeEventRecords<[TOutput, TResult]>,\n TFilled | \"input\" | \"broadcast\"\n >,\n TFilled | \"input\" | \"broadcast\"\n > {\n const { broadcast } = this.config;\n\n if (!!broadcast) throw new Error(\"Broadcast was already defined for this procedure\");\n\n return new PluvProcedure<\n TPlatform,\n TAuthorize,\n TContext,\n TInput,\n MergeEventRecords<[TOutput, TResult]>,\n TFilled | \"input\" | \"broadcast\"\n >({\n ...(this.config as any),\n broadcast: resolver as any,\n });\n }\n\n public input<TData extends JsonObject>(\n input: InputZodLike<TData>,\n ): Omit<\n PluvProcedure<TPlatform, TAuthorize, TContext, TData, {}, TFilled | \"input\">,\n TFilled | \"input\"\n > {\n return new PluvProcedure<TPlatform, TAuthorize, TContext, TData, {}, TFilled | \"input\">({\n input,\n });\n }\n\n public self<TResult extends EventRecord<string, any> = {}>(\n resolver: EventResolver<\"self\", TPlatform, TAuthorize, TContext, TInput, TResult>,\n ): Omit<\n PluvProcedure<\n TPlatform,\n TAuthorize,\n TContext,\n TInput,\n MergeEventRecords<[TOutput, TResult]>,\n TFilled | \"input\" | \"self\"\n >,\n TFilled | \"input\" | \"self\"\n > {\n const { self } = this.config;\n\n if (!!self) throw new Error(\"Self was already defined for this procedure\");\n\n return new PluvProcedure<\n TPlatform,\n TAuthorize,\n TContext,\n TInput,\n MergeEventRecords<[TOutput, TResult]>,\n TFilled | \"input\" | \"self\"\n >({\n ...(this.config as any),\n self: resolver as any,\n });\n }\n\n public sync<TResult extends EventRecord<string, any> = {}>(\n resolver: EventResolver<\"sync\", TPlatform, TAuthorize, TContext, TInput, TResult>,\n ): Omit<\n PluvProcedure<\n TPlatform,\n TAuthorize,\n TContext,\n TInput,\n MergeEventRecords<[TOutput, TResult]>,\n TFilled | \"input\" | \"sync\"\n >,\n TFilled | \"input\" | \"sync\"\n > {\n const { sync } = this.config;\n\n if (!!sync) throw new Error(\"Sync was already defined for this procedure\");\n\n return new PluvProcedure<\n TPlatform,\n TAuthorize,\n TContext,\n TInput,\n MergeEventRecords<[TOutput, TResult]>,\n TFilled | \"input\" | \"sync\"\n >({\n ...(this.config as any),\n sync: resolver as any,\n });\n }\n\n private _resolver(): EventResolver<\n EventResolverKind,\n TPlatform,\n TAuthorize,\n TContext,\n TInput,\n TOutput\n > {\n return (data, context) => {\n return {\n ...this._broadcast?.(data, context),\n ...this._self?.(data, context),\n ...this._sync?.(data, context),\n } as TOutput;\n };\n }\n}\n","import type { IORouterLike } from \"@pluv/types\";\nimport type { AbstractPlatform, InferInitContextType } from \"./AbstractPlatform\";\nimport type { PluvProcedure } from \"./PluvProcedure\";\nimport type { PluvIOAuthorize } from \"./types\";\n\nexport type PluvRouterEventConfig<\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =\n any,\n TContext extends Record<string, any> = {},\n> = { [P: string]: Pick<PluvProcedure<TPlatform, TAuthorize, TContext, any, any>, \"config\"> };\n\nexport type MergedRouter<\n TRouters extends PluvRouter<TPlatform, TAuthorize, TContext, any>[] = [],\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> = any,\n TContext extends Record<string, any> = {},\n TRoot extends TRouters[0][\"_defs\"][\"events\"] = {},\n> = TRouters extends [\n infer IHead extends PluvRouter<TPlatform, TAuthorize, TContext, any>,\n ...infer ITail extends PluvRouter<TPlatform, TAuthorize, TContext, any>[],\n]\n ? MergedRouter<ITail, TPlatform, TAuthorize, TContext, TRoot & IHead[\"_defs\"][\"events\"]>\n : PluvRouter<TPlatform, TAuthorize, TContext, TRoot>;\n\nexport class PluvRouter<\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =\n any,\n TContext extends Record<string, any> = {},\n TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},\n> implements IORouterLike<TEvents> {\n readonly _defs: { events: TEvents } = { events: {} as TEvents };\n\n constructor(events: TEvents) {\n const invalidName = Object.keys(events).find((name) => !this._isValidEventName(name));\n\n if (typeof invalidName === \"string\") {\n throw new Error(\n `Invalid event name. Event names must be formatted as valid JavaScript variable names: \"${invalidName}\"`,\n );\n }\n\n this._defs = { events };\n }\n\n public static merge<TRouters extends PluvRouter<any, any, any, any>[]>(\n ...routers: TRouters\n ): MergedRouter<TRouters> {\n const events = Object.assign(\n Object.create(null),\n ...routers.map((router) => router._defs.events),\n );\n\n return new PluvRouter<any, any, any, any>(events) as MergedRouter<TRouters>;\n }\n\n private _isValidEventName(name: string): boolean {\n return /^[a-z_$][a-z0-9_$]*$/gi.test(name);\n }\n}\n","export const GARBAGE_COLLECT_INTERVAL_MS = 60_000;\nexport const MAX_PRESENCE_SIZE_BYTES = 512;\nexport const MAX_STORAGE_SIZE_BYTES = 31_457_280;\nexport const MAX_USER_ID_LENGTH = 128;\nexport const MAX_USER_SIZE_BYTES = 512;\nexport const PING_TIMEOUT_MS = 30_000;\n","export const oneLine = (strings: TemplateStringsArray, ...values: unknown[]): string => {\n return strings\n .map((str) => str.replace(/\\s+/g, \" \"))\n .reduce((acc, str, i) => acc + str + (i < values.length ? values[i] : \"\"), \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n};\n","export const pickBy = <T extends Record<string, any>>(\n obj: T,\n by: (value: T[keyof T]) => boolean,\n): Partial<T> => {\n const result: Partial<T> = {};\n const keys: (keyof T)[] = Object.keys(obj);\n\n for (let i = 0; i < keys.length; ++i) {\n const key = keys[i];\n const value = obj[key];\n\n if (by(value)) result[key] = value;\n }\n\n return result;\n};\n","import type { AbstractCrdtDocFactory } from \"@pluv/crdt\";\nimport { noop } from \"@pluv/crdt\";\nimport type {\n BaseIOEventRecord,\n BaseUser,\n CrdtDocLike,\n CrdtLibraryType,\n EventMessage,\n IOEventMessage,\n IOLike,\n Id,\n InferEventMessage,\n InferEventsOutput,\n InferIOAuthorize,\n InferIOAuthorizeUser,\n InferIOInput,\n JsonObject,\n Maybe,\n} from \"@pluv/types\";\nimport colors from \"kleur\";\nimport type {\n AbstractPlatform,\n InferInitContextType,\n InferPlatformWebSocketSource,\n InferRoomContextType,\n} from \"./AbstractPlatform\";\nimport type {\n AbstractCloseEvent,\n AbstractErrorEvent,\n AbstractMessageEvent,\n} from \"./AbstractWebSocket\";\nimport { AbstractWebSocket } from \"./AbstractWebSocket\";\nimport type { PluvRouter, PluvRouterEventConfig } from \"./PluvRouter\";\nimport { authorize } from \"./authorize\";\nimport { GARBAGE_COLLECT_INTERVAL_MS, PING_TIMEOUT_MS } from \"./constants\";\nimport type {\n EventResolverContext,\n EventResolverKind,\n GetInitialStorageFn,\n IORoomDestroyedEvent,\n IORoomListenerEvent,\n IORoomMessageEvent,\n IOUserConnectedEvent,\n IOUserDisconnectedEvent,\n PluvContext,\n PluvIOAuthorize,\n ResolvedPluvIOAuthorize,\n WebSocketSession,\n WebSocketType,\n} from \"./types\";\nimport { oneLine } from \"./utils\";\n\ntype BroadcastMessage<TIO extends IORoom<any, any, any, any, any>> =\n | InferEventMessage<InferIOInput<TIO>>\n | InferEventMessage<BaseIOEventRecord<InferIOAuthorize<TIO>>>;\n\ninterface BroadcastParams<TIO extends IORoom<any, any, any, any, any>> {\n message: BroadcastMessage<TIO>;\n senderId?: string;\n}\n\nexport interface IORoomListeners<\n TPlatform extends AbstractPlatform<any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null,\n TContext extends Record<string, any>,\n TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>,\n> {\n onRoomDestroyed: (event: IORoomDestroyedEvent<TPlatform, TContext>) => void;\n onStorageDestroyed: (event: IORoomListenerEvent<TPlatform, TContext>) => void;\n onMessage: (event: IORoomMessageEvent<TPlatform, TAuthorize, TContext, TEvents>) => void;\n onUserConnected: (event: IOUserConnectedEvent<TPlatform, TAuthorize, TContext>) => void;\n onUserDisconnected: (event: IOUserDisconnectedEvent<TPlatform, TAuthorize, TContext>) => void;\n}\n\nexport type BroadcastProxy<TIO extends IORoom<any, any, any, any, any>> = (<\n TEvent extends keyof InferIOInput<TIO>,\n>(\n event: TEvent,\n data: Id<InferIOInput<TIO>[TEvent]>,\n) => Promise<void>) & {\n [event in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>>[event]) => Promise<void>;\n};\n\nexport type IORoomConfig<\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =\n any,\n TContext extends Record<string, any> = {},\n TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},\n> = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {\n authorize?: TAuthorize;\n context: PluvContext<TPlatform, TContext>;\n crdt?: { doc: (value: any) => AbstractCrdtDocFactory<any, any> };\n debug: boolean;\n getInitialStorage: GetInitialStorageFn<TContext>;\n platform: TPlatform;\n roomContext: InferRoomContextType<TPlatform>;\n router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;\n};\n\ninterface SendMessageSender {\n sessionId: string | null;\n user: JsonObject | null;\n}\n\ninterface PatchPresenceParams {\n presence: JsonObject | null;\n sessionId: string;\n timer?: number | null;\n}\n\nexport type WebSocketRegisterConfig<\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n> = {\n token?: string | null;\n} & InferInitContextType<TPlatform>;\n\nexport class IORoom<\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =\n null,\n TContext extends Record<string, any> = {},\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<any>,\n TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},\n> implements IOLike<TAuthorize, TCrdt, TEvents> {\n public readonly id: string;\n\n private _doc: Promise<CrdtDocLike<any, any>>;\n private _lastGarbageCollectMs: number = -1 * (GARBAGE_COLLECT_INTERVAL_MS + 1);\n private _uninitialize: Promise<() => Promise<void>> | null = null;\n\n private readonly _authorize: TAuthorize = null as TAuthorize;\n private readonly _context: PluvContext<TPlatform, TContext>;\n private readonly _crdt: TCrdt;\n private readonly _debug: boolean;\n private readonly _docFactory: AbstractCrdtDocFactory<any, any>;\n private readonly _getInitialStorage: GetInitialStorageFn<TContext>;\n private readonly _listeners: IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>;\n private readonly _platform: TPlatform;\n private readonly _roomContext: InferRoomContextType<TPlatform>;\n private readonly _router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;\n private readonly _sessions = new Map<\n [sessionId: string][0],\n AbstractWebSocket<any, TAuthorize>\n >();\n private readonly _userSessionss = new Map<[userId: string][0], Set<[sessionId: string][0]>>();\n\n private _wasDocEmptyOnInit: boolean = true;\n private _storageInitializedViaSession: boolean = false;\n\n /**\n * @ignore\n * @readonly\n * @deprecated Internal use only. Changes to this will never be marked as breaking.\n */\n public get _defs() {\n return {\n authorize: this._authorize,\n context: this._context,\n crdt: this._crdt,\n events: this._router._defs.events,\n platform: this._platform,\n } as {\n authorize: TAuthorize;\n context: TContext;\n crdt: TCrdt;\n events: TEvents;\n platform: TPlatform;\n };\n }\n\n public get broadcast(): BroadcastProxy<this> {\n const _broadcast = <TEvent extends keyof InferIOInput<this>>(\n event: TEvent,\n data: Id<InferIOInput<this>[TEvent]>,\n ): Promise<void> => {\n const message = { type: event, data } as BroadcastMessage<this>;\n\n return Promise.resolve(this._broadcast({ message }));\n };\n\n return new Proxy(_broadcast, {\n get(fn, prop) {\n return (data: any) => fn(prop as any, data);\n },\n }) as BroadcastProxy<this>;\n }\n\n private get _initialized(): Promise<boolean> {\n return Promise.resolve(!!this._uninitialize?.then((result) => !!result));\n }\n\n constructor(id: string, config: IORoomConfig<TPlatform, TAuthorize, TContext, TEvents>) {\n const {\n _meta,\n authorize,\n context,\n crdt = noop,\n debug,\n getInitialStorage,\n onRoomDestroyed,\n onStorageDestroyed,\n onMessage,\n onUserConnected,\n onUserDisconnected,\n platform,\n roomContext,\n router,\n } = config as IORoomConfig<TPlatform, TAuthorize, TContext, TEvents> & { _meta?: any };\n\n this.id = id;\n\n this._context = context;\n this._crdt = crdt as TCrdt;\n this._debug = debug;\n this._docFactory = crdt.doc(() => ({}));\n this._getInitialStorage = getInitialStorage;\n this._roomContext = roomContext;\n this._router = router;\n this._platform = platform.initialize({ ...(!!_meta ? { _meta } : {}), roomContext });\n\n // Listeners are provided from server-level configuration via createRoom\n this._listeners = {\n onRoomDestroyed: (event) => onRoomDestroyed?.(event),\n onStorageDestroyed: (event) => onStorageDestroyed?.(event),\n onMessage: (event) => onMessage?.(event),\n onUserConnected: (event) => onUserConnected?.(event),\n onUserDisconnected: (event) => onUserDisconnected?.(event),\n };\n\n if (authorize) this._authorize = authorize;\n\n /**\n * @description Everything below this line relates to waking up from hibernation.\n * @see https://developers.cloudflare.com/durable-objects/best-practices/websockets/#websocket-hibernation-api\n * @date April 17, 2025\n */\n const webSockets =\n this._platform.getWebSockets() as readonly InferPlatformWebSocketSource<TPlatform>[];\n\n webSockets.forEach((webSocket) => {\n const deserialized = this._platform.getSerializedState(webSocket);\n const sessionId = this._platform.getSessionId(webSocket);\n\n if (!deserialized) return;\n if (typeof sessionId !== \"string\") return;\n\n const pluvWs = this._platform.convertWebSocket(webSocket, { room: this.id });\n\n this._sessions.set(sessionId, pluvWs);\n\n const userId = pluvWs.session.user?.id;\n\n if (!!userId) this._addUserSession(userId, sessionId);\n });\n\n const { doc, uninitialize } = this._initialize();\n\n this._doc = doc;\n this._uninitialize = uninitialize;\n }\n\n /**\n * @description Closes a connection with the specified sessionId\n * @param sessionId The session id of the connection to close\n */\n public async evict(sessionId: string): Promise<void> {\n const session = this._sessions.get(sessionId);\n\n if (session) session.state.quit = true;\n\n await this._emitQuitters();\n }\n\n /**\n * @description Closes all connections to this room, effectively destroying\n * the room\n */\n public async evictAll(): Promise<void> {\n const sessions = Array.from(this._sessions.values());\n\n sessions.forEach((session) => {\n session.state.quit = true;\n });\n\n await this._emitQuitters();\n }\n\n /**\n * @description The IORoom will garbage collect occasionally as connections ping/pong the\n * server\n */\n public async garbageCollect(): Promise<void> {\n this._lastGarbageCollectMs = Date.now();\n this._emitQuitters();\n this._emitSyncState();\n }\n\n public getSize(): number {\n const currentTime = new Date().getTime();\n\n /**\n * @description Doing this instead of .size because some sessions\n * in the map can be considered as \"omitted\".\n * @date December 21, 2022\n */\n return Array.from(this._sessions.values()).reduce((count, pluvWs) => {\n if (pluvWs.state.quit) return count;\n\n const pingTime = this._platform.getLastPing(pluvWs) ?? pluvWs.state.timers.ping;\n\n return currentTime - pingTime > PING_TIMEOUT_MS ? count : count + 1;\n }, 0);\n }\n\n public onClose(\n webSocket: WebSocketType<TPlatform>,\n ): (event: AbstractCloseEvent) => Promise<void> {\n this._ensureDetached();\n\n const wsSession = this._getAbstractWs(webSocket);\n\n if (!wsSession) return async () => undefined;\n\n return this._onClose(wsSession);\n }\n\n public onError(\n webSocket: WebSocketType<TPlatform>,\n ): (event: AbstractErrorEvent) => Promise<void> {\n this._ensureDetached();\n\n const wsSession = this._getAbstractWs(webSocket);\n\n if (!wsSession) return async () => undefined;\n\n return this._onClose(wsSession);\n }\n\n public onMessage(\n webSocket: WebSocketType<TPlatform>,\n ): (event: AbstractMessageEvent) => Promise<void> {\n this._ensureDetached();\n\n const wsSession = this._getAbstractWs(webSocket);\n\n if (!wsSession) return async () => undefined;\n\n return this._onMessage(wsSession);\n }\n\n public async register(\n webSocket: InferPlatformWebSocketSource<TPlatform>,\n ...options: keyof InferInitContextType<TPlatform> extends never\n ? [{ token?: string }?]\n : [WebSocketRegisterConfig<TPlatform>]\n ): Promise<void> {\n const _options = (options[0] ?? {}) as WebSocketRegisterConfig<TPlatform>;\n const token = _options.token ?? null;\n\n /**\n * TODO\n * @description Update the sessionId strategy so that the userId can be used to retrieve\n * the session in O(1) time if authorized.\n * @date April 15, 2025\n */\n const sessionId = this._platform.getSessionId(webSocket);\n const sessionExists = typeof sessionId === \"string\" && this._sessions.has(sessionId);\n\n if (sessionExists) return;\n\n if (!(await this._initialized)) {\n this._initialize();\n await this._initialized;\n }\n\n const user: BaseUser | null = await this._getAuthorizedUser(token, _options);\n const ioAuthorize = this._getIOAuthorize(_options);\n const isUnauthorized = !!ioAuthorize && !user;\n const pluvWs = this._platform.convertWebSocket(webSocket, { room: this.id });\n\n if (isUnauthorized) {\n this._logDebug(`${colors.blue(\"Authorization failed for connection\")}`);\n pluvWs.handleError({ error: new Error(\"Not authorized\"), room: this.id });\n pluvWs.close(3000, \"WebSocket unauthorized.\");\n\n return;\n }\n\n if (!!user) {\n const latest = this._getLatestPresence(user.id);\n const prevState = pluvWs.state;\n\n pluvWs.user = user;\n\n this._platform.setSerializedState(pluvWs, {\n ...prevState,\n presence: latest.presence,\n timers: { ...prevState.timers, presence: latest.timer },\n });\n this._addUserSession(user.id, pluvWs.sessionId);\n }\n\n this._logDebug(\n `${colors.blue(`Registering connection for room ${this.id}:`)} ${pluvWs.sessionId}`,\n );\n\n await this._platform.acceptWebSocket(pluvWs);\n this._sessions.set(pluvWs.sessionId, pluvWs);\n await this._platform.persistence.addUser(this.id, pluvWs.sessionId, user ?? {});\n\n if (this._platform._config.registrationMode === \"attached\") {\n const onClose = this._onClose(pluvWs).bind(this);\n const onMessage = this._onMessage(pluvWs).bind(this);\n\n pluvWs.addEventListener(\"close\", onClose);\n pluvWs.addEventListener(\"error\", onClose);\n pluvWs.addEventListener(\"message\", onMessage);\n }\n\n await this._emitRegistered(pluvWs);\n\n const size = this.getSize();\n\n this._logDebug(oneLine`\n ${colors.blue(`Registered connection for room ${this.id}:`)}\n ${pluvWs.sessionId}\n `);\n this._logDebug(`${colors.blue(`Room ${this.id} size:`)} ${size}`);\n }\n\n private _addUserSession(userId: string, sessionId: string): Set<[sessionId: string][0]> {\n const set = this._userSessionss.get(userId) ?? new Set<string>();\n const updated = set.add(sessionId);\n\n this._userSessionss.set(userId, updated);\n\n return updated;\n }\n\n private async _broadcast(params: BroadcastParams<this>): Promise<void> {\n const { message, senderId } = params;\n\n const sender = senderId ? (this._sessions.get(senderId) ?? null) : null;\n const session = sender?.session ?? null;\n const user = session?.user ?? null;\n\n if (typeof senderId !== \"string\") return;\n\n this._platform.pubSub.publish(this.id, {\n connectionId: senderId ?? null,\n room: this.id,\n user,\n ...message,\n });\n }\n\n private async _closeWebSockets(\n webSockets: readonly AbstractWebSocket<any, TAuthorize>[],\n ): Promise<void> {\n const closeWebSocket = async (\n webSocket: AbstractWebSocket<any, TAuthorize>,\n ): Promise<void> => {\n webSocket.state = { ...webSocket.state, quit: true };\n\n const sessionId = webSocket.sessionId;\n\n this._logDebug(\n `${colors.blue(`Unregistering connection for room ${this.id}:`)} ${sessionId}`,\n );\n this._sessions.delete(sessionId);\n\n await this._platform.persistence.deleteUser(this.id, sessionId).catch(() => null);\n await this._broadcast({\n message: { type: \"$exit\", data: { sessionId } },\n senderId: sessionId,\n });\n\n const session = webSocket.session;\n const user = session.user;\n\n if (!!user) this._removeUserSession(user.id, sessionId);\n\n try {\n const [doc, context] = await Promise.all([this._doc, this._getContext()]);\n const encodedState = doc.getEncodedState();\n\n await Promise.resolve(\n this._listeners.onUserDisconnected({\n context,\n encodedState,\n platform: this._platform,\n room: this.id,\n user,\n }),\n );\n } catch (error) {\n console.error(error);\n }\n\n this._logDebug(\n `${colors.blue(`Unregistered connection for room ${this.id}:`)} ${webSocket.sessionId}`,\n );\n };\n\n const promises = webSockets.map(async (webSocket) => {\n await closeWebSocket(webSocket).catch(() => null);\n });\n\n await Promise.all(promises).catch(() => null);\n\n const size = this.getSize();\n\n this._logDebug(`${colors.blue(`Room ${this.id} size:`)} ${size}`);\n\n if (!!size) return;\n if (!this._uninitialize) return;\n\n const uninitialize = (await this._uninitialize).bind(this);\n await uninitialize();\n }\n\n private async _emitQuitters(): Promise<void> {\n const currentTime = new Date().getTime();\n const quitters = Array.from(this._sessions.values()).filter((pluvWs) => {\n return pluvWs.state.quit || currentTime - pluvWs.state.timers.ping > PING_TIMEOUT_MS;\n });\n\n this._closeWebSockets(quitters);\n }\n\n private async _emitRegistered(pluvWs: AbstractWebSocket<any, TAuthorize>): Promise<void> {\n const session = pluvWs.session;\n const sessionId = session.id;\n const presence = session.presence;\n const user = session.user;\n\n const [doc, context] = await Promise.all([this._doc, this._getContext()]);\n const encodedState = doc.isEmpty() ? null : doc.getEncodedState();\n\n await this._sendSelfMessage(\n {\n type: \"$registered\",\n data: {\n presence,\n sessionId,\n state: encodedState,\n timers: { presence: session.timers.presence },\n },\n },\n { sessionId, user },\n );\n\n try {\n await Promise.resolve(\n this._listeners.onUserConnected({\n context,\n encodedState,\n platform: this._platform,\n room: this.id,\n user,\n webSocket: pluvWs.webSocket,\n }),\n );\n } catch (err) {\n console.error(err);\n }\n }\n\n private async _emitSyncState(): Promise<void> {\n const connectionIds = await this._platform.persistence\n .getUsers(this.id)\n .then((map) => Object.keys(map));\n\n await this._broadcast({\n message: {\n type: \"$syncStateReceived\",\n data: { connectionIds },\n },\n });\n }\n\n private _ensureDetached(): void {\n if (this._platform._config.registrationMode === \"detached\") return;\n\n throw new Error(\"Platform must use detached mode\");\n }\n\n private _getAbstractWs(\n webSocket: WebSocketType<TPlatform>,\n ): AbstractWebSocket<any, TAuthorize> | null {\n if ((webSocket as unknown as any) instanceof AbstractWebSocket) return webSocket;\n\n const sessionId = this._platform.getSessionId(webSocket);\n\n if (typeof sessionId === \"string\") {\n const session = this._sessions.get(sessionId) ?? null;\n\n if (session) return session;\n }\n\n const sessions = Array.from(this._sessions.values());\n\n return sessions.find((pluvWs) => pluvWs.webSocket === webSocket) ?? null;\n }\n\n /**\n * TODO\n * @description Revisit any cast types\n * @date July 17, 2025\n */\n private async _getAuthorizedUser(\n token: Maybe<string>,\n options: WebSocketRegisterConfig<TPlatform>,\n ): Promise<InferIOAuthorizeUser<TAuthorize>> {\n const ioAuthorize = this._getIOAuthorize(options);\n\n if (!ioAuthorize) return null as any;\n if (!token) return null as any;\n\n if (!ioAuthorize.secret)\n throw new Error(\"`authorize` was specified without a valid secret\");\n\n const payload = await authorize({\n platform: this._platform,\n secret: ioAuthorize.secret,\n }).decode(token);\n\n if (!payload) {\n this._logDebug(colors.blue(\"Could not decode token:\"));\n this._logDebug(token);\n\n return null as any;\n }\n\n if (payload.room !== this.id) {\n this._logDebug(colors.blue(`Token is not authorized for room ${this.id}:`));\n this._logDebug(colors.blue(\"Received:\"), payload.room);\n this._logDebug(token);\n\n return null as any;\n }\n\n try {\n return ioAuthorize.user.parse(payload.user) ?? null;\n } catch {\n this._logDebug(`${colors.blue(\"Token fails validation:\")} ${token}`);\n\n return null as any;\n }\n }\n\n private async _getContext(): Promise<TContext> {\n return typeof this._context === \"function\"\n ? await Promise.resolve(this._context(this._roomContext))\n : await Promise.resolve(this._context);\n }\n\n private async _getInitialDoc(): Promise<CrdtDocLike<any, any>> {\n const doc = this._docFactory.getEmpty();\n\n const [encodedState, context] = await Promise.all([\n this._platform.persistence.getStorageState(this.id),\n this._getContext(),\n ]);\n\n if (!encodedState) {\n const loadedState = await this._getInitialStorage({\n context,\n room: this.id,\n });\n\n if (!!loadedState && !this._docFactory.isEmpty(loadedState)) {\n doc.applyEncodedState({ update: loadedState });\n }\n }\n\n if (typeof encodedState === \"string\") {\n doc.applyEncodedState({ update: encodedState });\n\n // If we loaded storage from persistence and it's non-empty, storage was previously initialized.\n // This handles hibernation wake-up: restore the initialization state so that onStorageDestroyed\n // will fire correctly when the room is destroyed.\n if (!doc.isEmpty()) {\n this._storageInitializedViaSession = true;\n }\n }\n\n return doc;\n }\n\n private _getIOAuthorize(\n options: WebSocketRegisterConfig<TPlatform>,\n ): ResolvedPluvIOAuthorize<any, any> | null {\n if (typeof this._authorize === \"function\") return this._authorize(options);\n\n return this._authorize as ResolvedPluvIOAuthorize<any, any> | null;\n }\n\n private _getLatestPresence(userId: string): {\n timer: number | null;\n presence: JsonObject | null;\n } {\n if (!this._authorize) return { timer: null, presence: null };\n\n const sessionIds = Array.from(this._userSessionss.get(userId)?.values() ?? []);\n\n if (!sessionIds.length) return { timer: null, presence: null };\n\n return sessionIds.reduce(\n (state, sessionId) => {\n const pluvWs = this._sessions.get(sessionId) ?? null;\n\n if (!pluvWs) return state;\n\n const session = pluvWs.session;\n const presence = session.presence;\n const timer = session.timers.presence;\n\n if (session.user?.id !== userId) return state;\n if (typeof state.timer !== \"number\") return { presence, timer };\n if (typeof timer !== \"number\") return state;\n\n return timer > state.timer ? { presence, timer } : state;\n },\n { presence: null, timer: null } as {\n presence: JsonObject | null;\n timer: number | null;\n },\n );\n }\n\n private _getProcedure(\n message: EventMessage<string, any>,\n ):\n | (typeof this._router)[\"_defs\"][\"events\"][keyof (typeof this._router)[\"_defs\"][\"events\"]]\n | null {\n return (\n this._router._defs.events[\n message.type as keyof (typeof this._router)[\"_defs\"][\"events\"]\n ] ?? null\n );\n }\n\n private _getProcedureInputs(\n message: EventMessage<string, any>,\n ): InferIOInput<this>[keyof TEvents] {\n const procedure = this._getProcedure(message);\n\n if (!procedure) return message.data;\n\n return procedure.config.input ? procedure.config.input.parse(message.data) : message.data;\n }\n\n private _getSession(webSocket: WebSocketType<TPlatform>): WebSocketSession<TAuthorize> {\n const pluvWs = this._getAbstractWs(webSocket);\n\n if (!pluvWs) throw new Error(\"Session could not be found\");\n\n return pluvWs.session;\n }\n\n private _getSessions() {\n const sessions = Array.from(this._sessions.values()).map((pluvWs) => pluvWs.session);\n\n return sessions;\n }\n\n private _initialize() {\n const promise = (async () => {\n this._logDebug(`${colors.blue(`Initializing room: ${this.id}`)}`);\n\n if (!!this._uninitialize) {\n const uninitialize = (await this._uninitialize).bind(this);\n await uninitialize();\n\n this._uninitialize = null;\n }\n\n const pubSubId = await this._platform.pubSub.subscribe(\n this.id,\n async ({ options = {}, ...message }): Promise<void> => {\n await promise;\n\n const sender: SendMessageSender = {\n sessionId: message.connectionId,\n user: message.user,\n };\n\n switch (options.type) {\n case \"self\": {\n await this._sendSelfMessage(message, sender);\n return;\n }\n case \"sync\": {\n await this._sendSyncMessage(message, sender);\n return;\n }\n case \"broadcast\":\n default: {\n const sessionIds = options.sessionIds;\n\n await this._sendBroadcastMessage(message, sender, sessionIds);\n }\n }\n },\n );\n\n const doc = await this._getInitialDoc();\n\n // Track if doc was empty when room was first initialized\n this._wasDocEmptyOnInit = doc.isEmpty();\n\n const uninitialize = async () => {\n this._platform.pubSub.unsubscribe(pubSubId);\n\n const [doc, context] = await Promise.all([this._doc, this._getContext()]);\n const encodedState = doc.getEncodedState();\n\n doc.destroy();\n this._doc = Promise.resolve(this._docFactory.getEmpty());\n\n // Always emit onRoomDestroyed\n await Promise.resolve(\n this._listeners.onRoomDestroyed({\n ...(\"_meta\" in this._platform && !!this._platform._meta\n ? { _meta: this._platform._meta }\n : {}),\n context,\n platform: this._platform,\n room: this.id,\n }),\n );\n\n // Only emit onStorageDestroyed if storage was initialized via initializeSession\n if (this._storageInitializedViaSession) {\n await Promise.resolve(\n this._listeners.onStorageDestroyed({\n ...(\"_meta\" in this._platform && !!this._platform._meta\n ? { _meta: this._platform._meta }\n : {}),\n context,\n encodedState,\n platform: this._platform,\n room: this.id,\n }),\n );\n\n this._storageInitializedViaSession = false;\n }\n\n this._uninitialize = null;\n };\n\n return { doc, uninitialize };\n })();\n\n this._doc = promise.then((result) => result.doc);\n this._uninitialize = promise.then((result) => result.uninitialize);\n\n return { doc: this._doc, uninitialize: this._uninitialize };\n }\n\n private _logDebug(...data: any[]): void {\n if (this._debug) console.log(...data);\n }\n\n private _onClose(webSocket: AbstractWebSocket<any, TAuthorize>): () => Promise<void> {\n return async (): Promise<void> => {\n if (!(await this._initialized)) return;\n await this._closeWebSockets([webSocket]);\n };\n }\n\n private _onMessage(\n webSocket: AbstractWebSocket<any, TAuthorize>,\n ): (event: AbstractMessageEvent) => Promise<void> {\n return async (event: AbstractMessageEvent): Promise<void> => {\n if (!(await this._initialized)) return;\n\n const pluvWs = this._getAbstractWs(webSocket as WebSocketType<TPlatform>);\n\n if (!pluvWs) throw new Error(\"Could not get session\");\n\n const session = this._getSession(pluvWs as WebSocketType<TPlatform>);\n const sessions = this._getSessions();\n\n const setPresence = (params: PatchPresenceParams): void => {\n this._setPresence.bind(this)(params);\n };\n\n const [doc, context] = await Promise.all([this._doc, this._getContext()]);\n const eventContext: EventResolverContext<\n EventResolverKind,\n TPlatform,\n TAuthorize,\n TContext\n > = {\n context,\n doc,\n garbageCollect: async () => {\n await this.garbageCollect();\n },\n platform: this._platform,\n get presence() {\n return session.presence as JsonObject | null;\n },\n set presence(presence: JsonObject | null) {\n const sessionId = this.session?.id;\n\n if (!sessionId) return;\n\n setPresence({ presence, sessionId, timer: this.time });\n },\n room: this.id,\n session,\n sessions,\n time: new Date().getTime(),\n };\n\n if (pluvWs.state.quit) {\n await this._closeWebSockets([pluvWs]).catch(() => null);\n pluvWs.close(1011, \"WebSocket broken.\");\n\n return;\n }\n\n const message = this._parseMessage(event);\n\n if (!message) return;\n\n const procedure = this._getProcedure(message);\n\n this._listeners.onMessage({\n ...(\"_meta\" in this._platform && !!this._platform._meta\n ? { _meta: this._platform._meta }\n : {}),\n context,\n encodedState: doc.getEncodedState(),\n message: message as InferEventMessage<\n InferEventsOutput<TEvents>,\n keyof InferEventsOutput<TEvents>\n >,\n platform: this._platform,\n room: this.id,\n user: session.user,\n webSocket: session.webSocket.webSocket,\n });\n\n const sessionId = session.id;\n const user = session.user;\n\n if (!procedure) {\n await this._broadcast({\n message: message as any,\n senderId: sessionId,\n });\n\n return;\n }\n\n let inputs: InferIOInput<this>[keyof TEvents];\n\n try {\n inputs = this._getProcedureInputs(message);\n } catch (error) {\n pluvWs.handleError({\n error,\n message: \"Invalid input\",\n room: this.id,\n session,\n });\n\n return;\n }\n\n await Promise.all([\n procedure.config.broadcast?.(inputs, eventContext),\n procedure.config.self?.(inputs, eventContext),\n procedure.config.sync?.(inputs, eventContext),\n ]).then(async ([broadcast, self, sync]) => {\n const handleBroadcast = async () => {\n if (!broadcast) return;\n\n const messages = Object.entries(broadcast).map(async ([type, data]: any) => {\n await this._broadcast({\n message: { data, type },\n senderId: sessionId,\n });\n });\n\n await Promise.all(messages);\n };\n\n const handleSelf = async () => {\n if (!self) return;\n\n const messages = Object.entries(self).map(async ([type, data]) => {\n await this._sendSelfMessage({ data, type }, { sessionId, user });\n });\n\n await Promise.all(messages);\n };\n\n const handleSync = async () => {\n if (!sync) return;\n\n await this._platform.pubSub.publish(this.id, {\n connectionId: session.id,\n options: { type: \"sync\" },\n room: this.id,\n user,\n ...message,\n });\n\n const messages = Object.entries(sync).map(async ([type, data]) => {\n await this._sendSelfMessage({ data, type }, { sessionId, user });\n });\n\n await Promise.all(messages);\n };\n\n await Promise.all([handleBroadcast(), handleSelf(), handleSync()]);\n });\n\n // After processing messages that might update storage (like $initializeSession),\n // check if storage was initialized via initializeSession\n await this._checkStorageInitializedViaSession();\n };\n }\n\n private _parseMessage(message: {\n data: string | ArrayBuffer;\n }): EventMessage<string, any> | null {\n try {\n const parsed = this._platform.parseData(message.data);\n\n if (typeof parsed !== \"object\") return null;\n if (typeof parsed.type !== \"string\") return null;\n if (typeof parsed.data !== \"object\") return null;\n\n return parsed as EventMessage<string, any>;\n } catch {\n return null;\n }\n }\n\n private _setPresence(params: PatchPresenceParams): void {\n const { presence, sessionId, timer: _timer } = params;\n\n const timer = _timer ?? new Date().getTime();\n const pluvWs = this._sessions.get(sessionId) ?? null;\n\n if (!pluvWs) return;\n\n const wsSession = pluvWs.session;\n const user = wsSession.user as BaseUser | null;\n\n const sessionIds = user\n ? new Set<string>([...(this._userSessionss.get(user.id) ?? []), sessionId])\n : new Set([sessionId]);\n\n sessionIds.forEach((sId) => {\n const pWs = this._sessions.get(sId);\n const session = pWs?.session;\n\n if (!session) return;\n\n const prevState = session.webSocket.state;\n\n this._platform.setSerializedState(session.webSocket, {\n ...prevState,\n presence,\n timers: {\n ...prevState.timers,\n presence: timer,\n },\n });\n });\n }\n\n private _removeUserSession(\n userId: string,\n sessionId: string,\n ): Set<[sessionId: string][0]> | null {\n const set = this._userSessionss.get(userId);\n\n if (!set) return null;\n\n set.delete(sessionId);\n\n if (!!set.size) return set;\n\n this._userSessionss.delete(userId);\n\n return set;\n }\n\n private async _checkStorageInitializedViaSession(): Promise<void> {\n if (this._storageInitializedViaSession) return;\n\n // Only mark as initialized if:\n // 1. Doc was empty when room was first initialized\n // 2. Doc is now non-empty\n // 3. At least one session has been registered\n const doc = await this._doc;\n if (this._wasDocEmptyOnInit && !doc.isEmpty() && this._sessions.size > 0) {\n this._storageInitializedViaSession = true;\n }\n }\n\n private async _sendMessage(\n pluvWs: AbstractWebSocket<any, TAuthorize>,\n message: IOEventMessage<any>,\n ): Promise<void> {\n if (!(await this._initialized)) return;\n\n await Promise.resolve(pluvWs.sendMessage(message));\n }\n\n private async _sendBroadcastMessage(\n message: EventMessage<string, any>,\n sender: SendMessageSender | null,\n sessionIds?: readonly string[],\n ): Promise<void> {\n const { data, type } = message;\n const connectionId = sender?.sessionId ?? null;\n const room = this.id;\n\n if (typeof connectionId !== \"string\") return;\n\n const webSockets =\n sessionIds?.reduce((dict, id) => {\n const pluvWs = this._sessions.get(id);\n\n return pluvWs ? dict.set(id, pluvWs) : dict;\n }, new Map<string, AbstractWebSocket<any, TAuthorize>>()) ?? this._sessions;\n\n const promises = Array.from(webSockets.values()).map(async (pluvWs) => {\n const session = pluvWs.session;\n const user = session.user ?? null;\n\n this._sendMessage(pluvWs, { connectionId, data, room, type, user });\n });\n\n await Promise.all(promises);\n }\n\n private async _sendSelfMessage(\n message: EventMessage<string, any>,\n sender: SendMessageSender | null,\n ): Promise<void> {\n const senderId = sender?.sessionId ?? null;\n\n if (typeof senderId !== \"string\") return;\n\n const pluvWs = this._sessions.get(senderId);\n\n if (!pluvWs) return;\n\n /**\n * @description Note that this will not fire for Cloudflare's hibernatable websockets\n * because the server will auto-respond with the $pong event without calling any\n * functions.\n * @date April 18, 2025\n */\n if (message.type === \"$pong\") {\n const elapsedMs = Date.now() - this._lastGarbageCollectMs;\n\n if (elapsedMs > GARBAGE_COLLECT_INTERVAL_MS) {\n await this.garbageCollect();\n }\n }\n\n const session = pluvWs.session;\n const user = session.user ?? null;\n\n this._sendMessage(pluvWs, {\n connectionId: senderId,\n room: this.id,\n user,\n ...message,\n });\n }\n\n private async _sendSyncMessage(\n message: EventMessage<string, any>,\n sender: SendMessageSender | null,\n ): Promise<void> {\n if (!sender) return;\n\n const connectionId = sender.sessionId;\n\n if (typeof connectionId !== \"string\") return;\n\n const [doc, context] = await Promise.all([this._doc, this._getContext()]);\n const resolverCtx: EventResolverContext<\"sync\", TPlatform, TAuthorize, TContext> = {\n context,\n doc,\n garbageCollect: async () => {\n await this.garbageCollect();\n },\n platform: this._platform,\n get presence() {\n return null;\n },\n set presence(presence: JsonObject | null) {\n return;\n },\n room: this.id,\n session: null,\n sessions: this._getSessions(),\n time: new Date().getTime(),\n };\n\n const resolver = this._getProcedure(message)?.config.sync;\n\n if (!resolver) return;\n\n let inputs: InferIOInput<this>[keyof TEvents];\n\n try {\n inputs = this._getProcedureInputs(message);\n } catch {\n return;\n }\n\n Promise.resolve(resolver(inputs, resolverCtx)).then((output) => {\n if (!output) return;\n\n Object.keys(output).forEach((type: string) => {\n const data = output[type] ?? {};\n\n this._platform.pubSub.publish(this.id, {\n connectionId,\n data,\n options: { type: \"self\" },\n room: this.id,\n type,\n user: (sender.user ?? null) as BaseUser | null,\n });\n });\n });\n }\n}\n","// Generated by genversion.\nexport const version = '4.0.1'\n","import type { AbstractCrdtDocFactory, CrdtLibraryType, NoopCrdtDocFactory } from \"@pluv/crdt\";\nimport { noop } from \"@pluv/crdt\";\nimport type {\n AreAllOptional,\n IOLike,\n Id,\n InferIOAuthorize,\n InferIOAuthorizeUser,\n JsonObject,\n Maybe,\n NonNilProps,\n} from \"@pluv/types\";\nimport colors from \"kleur\";\nimport type {\n AbstractPlatform,\n InferInitContextType,\n InferRoomContextType,\n} from \"./AbstractPlatform\";\nimport { IORoom } from \"./IORoom\";\nimport type { PluvIO } from \"./PluvIO\";\nimport { PluvProcedure } from \"./PluvProcedure\";\nimport type { PluvRouterEventConfig } from \"./PluvRouter\";\nimport { PluvRouter } from \"./PluvRouter\";\nimport type { JWTEncodeParams } from \"./authorize\";\nimport { PING_TIMEOUT_MS } from \"./constants\";\nimport type {\n BasePluvIOListeners,\n GetInitialStorageFn,\n PluvContext,\n PluvIOAuthorize,\n PluvIOLimits,\n PluvIOListeners,\n} from \"./types\";\nimport { oneLine, pickBy } from \"./utils\";\nimport { __PLUV_VERSION } from \"./version\";\n\nexport type InferIORoom<TServer extends PluvServer<any, any, any, any, any>> =\n TServer extends PluvServer<\n infer IPlatform,\n infer IAuthorize,\n infer IContext,\n infer ICrdt,\n infer IEvents\n >\n ? IORoom<IPlatform, IAuthorize, IContext, ICrdt, IEvents>\n : never;\n\nexport type PluvServerConfig<\n TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =\n any,\n TContext extends Record<string, any> = {},\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<any>,\n TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},\n> = Partial<PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>> & {\n authorize?: TAuthorize;\n context?: PluvContext<TPlatform, TContext>;\n crdt?: { doc: (value: any) => AbstractCrdtDocFactory<any, any> };\n debug?: boolean;\n limits: PluvIOLimits;\n io: PluvIO<TPlatform, TAuthorize, TContext>;\n platform: () => TPlatform;\n router?: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;\n} & (TCrdt extends CrdtLibraryType<NoopCrdtDocFactory>\n ? { getInitialStorage?: \"[ERROR]: Must specify crdt to use getInitialStorage\" }\n : { getInitialStorage: GetInitialStorageFn<TContext> });\n\ntype BaseCreateRoomOptions<\n TPlatform extends AbstractPlatform<any, any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null,\n TContext extends Record<string, any>,\n TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>,\n> = {\n debug?: boolean;\n};\n\nexport type CreateRoomOptions<\n TPlatform extends AbstractPlatform<any, any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null,\n TContext extends Record<string, any>,\n TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>,\n> = keyof Omit<InferRoomContextType<TPlatform>, \"meta\"> extends never\n ? [BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>] | []\n : [\n Id<\n BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents> &\n InferRoomContextType<TPlatform>\n >,\n ];\n\nexport class PluvServer<\n TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =\n any,\n TContext extends Record<string, any> = {},\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<any>,\n TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},\n> implements IOLike<TAuthorize, TCrdt, TEvents> {\n public readonly version: string = __PLUV_VERSION as any;\n\n private readonly _config: NonNilProps<\n PluvServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>\n >;\n private readonly _docFactory: AbstractCrdtDocFactory<any, any>;\n\n public get fetch(): (...args: any[]) => Promise<any> {\n return (...args: any[]): Promise<any> => {\n const platform = this._config.platform();\n platform.validateConfig(this._config);\n\n if (!platform._fetch) {\n throw new Error(`\\`${platform._name}\\` does not support \\`fetch\\``);\n }\n\n return platform._fetch(...args);\n };\n }\n\n /**\n * @ignore\n * @readonly\n * @deprecated Internal use only. Changes to this will never be marked as breaking.\n */\n public get _defs() {\n return {\n authorize: this._config.authorize,\n context: this._config.context,\n crdt: this._config.crdt,\n events: this._router._defs.events,\n platform: this._config.platform(),\n } as {\n authorize: TAuthorize;\n context: PluvContext<TPlatform, TContext>;\n crdt: TCrdt;\n events: TEvents;\n platform: TPlatform;\n };\n }\n\n private get _baseRouter(): PluvRouter<TPlatform, TAuthorize, TContext, {}> {\n const listeners = this._getListeners();\n\n return new PluvRouter({\n $getOthers: this._procedure.sync((data, { room, session, sessions }) => {\n const currentTime = Date.now();\n\n const others = sessions\n .filter((wsSession) => {\n if (wsSession.id === session?.id) return false;\n if (wsSession.quit) return false;\n if (currentTime - wsSession.timers.ping > PING_TIMEOUT_MS) return false;\n\n return true;\n })\n .reduce<{\n [connectionId: string]: {\n connectionId: string;\n room: string | null;\n user: JsonObject | null;\n };\n }>(\n (acc, { id, presence, timers, user }) => ({\n ...acc,\n [id]: {\n connectionId: id,\n presence,\n room,\n timers: { presence: timers.presence },\n user,\n },\n }),\n {},\n );\n\n return { $othersReceived: { others } };\n }),\n $initializeSession: this._procedure\n .broadcast((data, { session }) => {\n const presence = (data as any)?.presence;\n const timers = session.timers;\n\n if (!session) return {};\n\n session.presence = presence;\n\n return {\n $userJoined: {\n connectionId: session.id,\n user: session.user,\n presence,\n timers: { presence: timers.presence },\n },\n };\n })\n .self(async (data, { context, doc, platform, room, session }) => {\n const oldState = await platform.persistence.getStorageState(room);\n /**\n * !HACK\n * @description This is the frontend's initialStorage. We only want to\n * apply this if the server's storage is empty (i.e. no initial storage\n * has been applied)\n */\n const update = (data as any)?.update as Maybe<string>;\n\n if (!oldState) {\n const loadedState = await this._getInitialStorage({ context, room });\n\n if (!!loadedState && !this._docFactory.isEmpty(loadedState)) {\n doc.applyEncodedState({ update: loadedState });\n }\n }\n\n /**\n * @description Storage was already initialized. Don't overwrite the current\n * storage state with the incoming initial storage. Return what the current state\n * is without changes.\n * @date May 7, 2025\n */\n if (!doc.isEmpty()) {\n const encodedState = doc.getEncodedState();\n\n return {\n $storageReceived: { changeKind: \"unchanged\", state: encodedState },\n };\n }\n\n /**\n * @description Storage was never initialized, and there is an incoming\n * initialStorage from the client. Apply the initialStorage and persist the state\n * as an update.\n * @date May 7, 2025\n */\n if (!!update) {\n const encodedState = doc.applyEncodedState({ update }).getEncodedState();\n const storageSize = new TextEncoder().encode(encodedState).length;\n\n if (\n !!this._config.limits.storageMaxSize &&\n storageSize > this._config.limits.storageMaxSize\n ) {\n throw new Error(\"Storage has exceeded the size limit\");\n }\n\n await platform.persistence\n .setStorageState(room, encodedState)\n .catch((error) => {\n this._logDebug(error);\n });\n\n listeners.onStorageUpdated({\n context,\n encodedState,\n platform,\n room,\n user: session?.user,\n webSocket: session?.webSocket.webSocket,\n });\n\n return {\n $storageReceived: { changeKind: \"initialized\", state: encodedState },\n };\n }\n\n const encodedState = doc.getEncodedState();\n\n return { $storageReceived: { changeKind: \"empty\", state: encodedState } };\n }),\n $ping: this._procedure.self((data, { platform, session }) => {\n if (!session) return {};\n\n const currentTime = new Date().getTime();\n const prevState = session.webSocket.state;\n\n platform.setSerializedState(session.webSocket, {\n ...prevState,\n timers: {\n ...prevState.timers,\n ping: currentTime,\n },\n });\n\n return { $pong: {} };\n }),\n $updatePresence: this._procedure.broadcast((data, context) => {\n const presence = (data as any)?.presence;\n const { session } = context;\n\n if (!session) return {};\n\n const cleanedPatch = pickBy(\n presence ?? {},\n (value) => typeof value !== \"undefined\",\n );\n const updated = Object.assign(Object.create(null), session.presence, cleanedPatch);\n const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;\n\n if (\n !!this._config.limits.presenceMaxSize &&\n bytes > this._config.limits.presenceMaxSize\n ) {\n throw new Error(oneLine`\n Large presence. Presence must be at most\n ${this._config.limits.presenceMaxSize.toLocaleString()} bytes.\n Current size: ${bytes.toLocaleString()} bytes\n `);\n }\n\n context.presence = updated;\n\n return {\n $presenceUpdated: {\n presence: updated,\n timers: { presence: session.timers.presence },\n },\n };\n }),\n $updateStorage: this._procedure.broadcast((data, { context, doc, platform, room }) => {\n const origin = (data as any)?.origin as Maybe<string>;\n const update: string | null = (data as any)?.update ?? null;\n\n if (origin === \"$initialized\" && Object.keys(doc.toJson()).length) return {};\n\n const updated = update === null ? doc : doc.applyEncodedState({ update });\n const encodedState = updated.getEncodedState();\n const storageSize = new TextEncoder().encode(encodedState).length;\n\n if (\n !!this._config.limits.storageMaxSize &&\n storageSize > this._config.limits.storageMaxSize\n ) {\n throw new Error(oneLine`\n Large Storage. Storage must be at most\n ${this._config.limits.storageMaxSize.toLocaleString()} bytes.\n Current size: ${storageSize.toLocaleString()} bytes\n `);\n }\n\n platform.persistence.setStorageState(room, encodedState).then(() => {\n listeners.onStorageUpdated({\n context,\n encodedState,\n platform,\n room,\n });\n });\n\n return { $storageUpdated: { state: encodedState } };\n }),\n });\n }\n\n private get _procedure(): PluvProcedure<TPlatform, TAuthorize, TContext, {}, {}> {\n return new PluvProcedure();\n }\n\n private get _router(): PluvRouter<TPlatform, TAuthorize, TContext, TEvents> {\n return (\n this._config.router\n ? PluvRouter.merge(this._baseRouter, this._config.router)\n : this._baseRouter\n ) as PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;\n }\n\n constructor(options: PluvServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>) {\n this._config = {\n crdt: noop,\n debug: false,\n router: new PluvRouter<TPlatform, TAuthorize, TContext, TEvents>({} as TEvents),\n ...options,\n } as NonNilProps<PluvServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>>;\n\n const {\n onRoomDestroyed,\n onRoomMessage,\n onStorageDestroyed,\n onStorageUpdated,\n onUserConnected,\n onUserDisconnected,\n } = options as Partial<BasePluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>>;\n\n this._docFactory = this._config.crdt.doc(() => ({}));\n (this as any)._listeners = {\n onRoomDestroyed: (event) => onRoomDestroyed?.(event),\n onRoomMessage: (event) => onRoomMessage?.(event),\n onStorageDestroyed: (event) => onStorageDestroyed?.(event),\n onStorageUpdated: (event) => onStorageUpdated?.(event),\n onUserConnected: (event) => onUserConnected?.(event),\n onUserDisconnected: (event) => onUserDisconnected?.(event),\n } as BasePluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>;\n }\n\n public createRoom(\n room: string,\n ...options: CreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>\n ): IORoom<TPlatform, TAuthorize, TContext, TCrdt, TEvents> {\n const { _meta, debug, ...platformRoomContext } = (options[0] ?? {}) as CreateRoomOptions<\n TPlatform,\n TAuthorize,\n TContext,\n TEvents\n >[0] & {\n _meta?: any;\n };\n\n const platform = this._config.platform();\n\n platform.validateConfig(this._config);\n\n if (platform._config.handleMode !== \"io\") {\n throw new Error(`\\`createRoom\\` is unsupported for \\`${platform._name}\\``);\n }\n\n if (!/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/i.test(room))\n throw new Error(\"Unsupported room name\");\n\n const roomContext = platformRoomContext as InferRoomContextType<TPlatform>;\n const listeners = this._getListeners();\n const logDebug = this._logDebug.bind(this);\n\n const newRoom = new IORoom<TPlatform, TAuthorize, TContext, TCrdt, TEvents>(room, {\n ...(!!_meta ? { _meta } : {}),\n authorize: this._config.authorize ?? undefined,\n context: this._config.context,\n crdt: this._config.crdt,\n debug: debug ?? this._config.debug,\n getInitialStorage: this._getInitialStorage,\n async onRoomDestroyed(event) {\n logDebug(`${colors.blue(\"Deleting empty room:\")} ${room}`);\n\n // Always fire server-level onRoomDestroyed\n await Promise.resolve(listeners.onRoomDestroyed(event));\n\n logDebug(`${colors.blue(\"Deleted room:\")} ${room}`);\n },\n async onStorageDestroyed(event) {\n logDebug(`${colors.blue(\"Destroying storage for room:\")} ${room}`);\n\n // Always fire server-level onStorageDestroyed\n await Promise.resolve(listeners.onStorageDestroyed(event));\n\n await event.platform.persistence.deleteStorageState(room);\n\n logDebug(`${colors.blue(\"Destroyed storage for room:\")} ${room}`);\n },\n async onMessage(event) {\n await Promise.resolve(listeners.onRoomMessage(event));\n },\n async onUserConnected(event) {\n await Promise.resolve(listeners.onUserConnected(event));\n },\n async onUserDisconnected(event) {\n await Promise.resolve(listeners.onUserDisconnected(event));\n },\n platform,\n roomContext,\n router: this._router,\n });\n\n this._logDebug(`${colors.blue(\"Created room:\")} ${room}`);\n\n return newRoom;\n }\n\n public async createToken(\n params: JWTEncodeParams<InferIOAuthorizeUser<InferIOAuthorize<this>>, TPlatform>,\n ): Promise<string> {\n return await this._config.io.createToken(params);\n }\n\n private _getInitialStorage: GetInitialStorageFn<TContext> = (...args) => {\n const getInitialStorage =\n typeof this._config.getInitialStorage !== \"string\"\n ? (this._config.getInitialStorage ??\n ((() => null) as GetInitialStorageFn<TContext>))\n : ((() => null) as GetInitialStorageFn<TContext>);\n\n return getInitialStorage(...args);\n };\n\n private _getListeners(): BasePluvIOListeners<TPlatform, TAuthorize, TContext, TEvents> {\n return (this as any)._listeners;\n }\n\n private _logDebug(...data: any[]): void {\n if (this._config.debug) console.log(...data);\n }\n}\n","import type { AbstractCrdtDocFactory, CrdtLibraryType, NoopCrdtDocFactory } from \"@pluv/crdt\";\nimport { noop } from \"@pluv/crdt\";\nimport type { BaseUser, HasRequiredProperty, IOAuthorize, InferIOAuthorizeUser } from \"@pluv/types\";\nimport type { AbstractPlatform, InferInitContextType } from \"./AbstractPlatform\";\nimport type { WebSocketRegisterConfig } from \"./IORoom\";\nimport { PluvProcedure } from \"./PluvProcedure\";\nimport type { MergedRouter, PluvRouterEventConfig } from \"./PluvRouter\";\nimport { PluvRouter } from \"./PluvRouter\";\nimport { PluvServer, PluvServerConfig } from \"./PluvServer\";\nimport type { JWTEncodeParams } from \"./authorize\";\nimport { authorize } from \"./authorize\";\nimport {\n MAX_PRESENCE_SIZE_BYTES,\n MAX_STORAGE_SIZE_BYTES,\n MAX_USER_ID_LENGTH,\n MAX_USER_SIZE_BYTES,\n} from \"./constants\";\nimport type {\n GetInitialStorageFn,\n PluvContext,\n PluvIOAuthorize,\n PluvIOLimits,\n PluvIOListeners,\n PluvIORouter,\n ResolvedPluvIOAuthorize,\n} from \"./types\";\nimport { oneLine } from \"./utils\";\nimport { __PLUV_VERSION } from \"./version\";\n\nexport type PluvIOConfig<\n TPlatform extends AbstractPlatform<any>,\n TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null,\n TContext extends Record<string, any>,\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<any>,\n> = {\n authorize?: TAuthorize;\n context?: PluvContext<TPlatform, TContext>;\n crdt?: TCrdt;\n debug?: boolean;\n limits?: PluvIOLimits;\n platform: () => TPlatform;\n};\n\ntype ResolvedServerConfig<\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =\n any,\n TContext extends Record<string, any> = {},\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,\n TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},\n> = Partial<PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>> &\n PluvIORouter<TPlatform, TAuthorize, TContext, TEvents> &\n (TCrdt extends CrdtLibraryType<NoopCrdtDocFactory>\n ? { getInitialStorage?: \"[ERROR]: Must specify crdt to use getInitialStorage\" }\n : { getInitialStorage: GetInitialStorageFn<TContext> });\n\nexport type BaseServerConfig<\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =\n any,\n TContext extends Record<string, any> = {},\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,\n TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},\n> = {\n [P in keyof ResolvedServerConfig<\n TPlatform,\n TAuthorize,\n TContext,\n TCrdt,\n TEvents\n > as ResolvedServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>[P] extends undefined\n ? never\n : P]: ResolvedServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>[P];\n};\n\nexport type ServerConfig<\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =\n any,\n TContext extends Record<string, any> = {},\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,\n TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},\n> =\n HasRequiredProperty<\n BaseServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>\n > extends true\n ? [BaseServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>]\n : [BaseServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>?];\n\nexport class PluvIO<\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =\n any,\n TContext extends Record<string, any> = {},\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<any>,\n> {\n public readonly version: string = __PLUV_VERSION as any;\n\n private readonly _authorize: TAuthorize = null as TAuthorize;\n private readonly _context: PluvContext<TPlatform, TContext> = {} as PluvContext<\n TPlatform,\n TContext\n >;\n private readonly _crdt: { doc: (value: any) => AbstractCrdtDocFactory<any, any> };\n private readonly _debug: boolean;\n private readonly _limits: PluvIOLimits;\n private readonly _platform: () => TPlatform;\n\n public get procedure(): PluvProcedure<TPlatform, TAuthorize, TContext, {}, {}> {\n return new PluvProcedure();\n }\n\n constructor(options: PluvIOConfig<TPlatform, TAuthorize, TContext, TCrdt>) {\n const { authorize, context, crdt = noop, debug = false, limits, platform } = options;\n\n this._crdt = crdt as CrdtLibraryType<any>;\n this._debug = debug;\n this._limits = {\n presenceMaxSize: MAX_PRESENCE_SIZE_BYTES,\n storageMaxSize: MAX_STORAGE_SIZE_BYTES,\n userIdMaxLength: MAX_USER_ID_LENGTH,\n userMaxSize: MAX_USER_SIZE_BYTES,\n ...limits,\n };\n this._platform = platform;\n\n if (authorize) this._authorize = authorize;\n if (context) this._context = context;\n }\n\n public async createToken(\n params: JWTEncodeParams<InferIOAuthorizeUser<TAuthorize>, TPlatform>,\n ): Promise<string> {\n if (!this._authorize) {\n throw new Error(\"IO does not specify authorize during initialization.\");\n }\n\n const ioAuthorize = this._getIOAuthorize(params);\n const user = params.user as BaseUser;\n const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;\n\n if (!!this._limits.userIdMaxLength && user.id.length > this._limits.userIdMaxLength) {\n throw new Error(oneLine`\n createToken was called with a long user id. User ID must be at\n most 128 characters. Current length: ${user.id.length.toLocaleString()}\n `);\n }\n\n const bytes = new TextEncoder().encode(JSON.stringify(parsed)).length;\n\n if (!!this._limits.userMaxSize && bytes > this._limits.userMaxSize) {\n throw new Error(oneLine`\n createToken called with large payload. User must be at most 512\n bytes. Current size: ${bytes.toLocaleString()}\n `);\n }\n\n const platform = this._platform();\n\n /**\n * !HACK\n * @description Allow the platform to overwrite this behavior as needed. This is introduced\n * to support platformPluv\n * @date December 15, 2024\n */\n if (platform._createToken) {\n return await platform._createToken({ ...params, authorize: ioAuthorize });\n }\n\n const secret = ioAuthorize?.secret ?? null;\n\n if (!secret) throw new Error(\"`authorize` was specified without a valid secret\");\n\n return await authorize({ platform, secret }).encode(\n params as JWTEncodeParams<any, TPlatform>,\n );\n }\n\n public mergeRouters<TRouters extends PluvRouter<TPlatform, TAuthorize, TContext, any>[]>(\n ...routers: TRouters\n ): MergedRouter<TRouters> {\n return PluvRouter.merge(...routers);\n }\n\n public router<TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>>(\n events: TEvents,\n ): PluvRouter<TPlatform, TAuthorize, TContext, TEvents> {\n const invalidName = Object.keys(events).find((name) => name.includes(\"$\"));\n\n if (typeof invalidName === \"string\") {\n throw new Error(oneLine`\n Invalid event name. Event names must not contain $: \"${invalidName}\"\n `);\n }\n\n return new PluvRouter<TPlatform, TAuthorize, TContext, TEvents>(events);\n }\n\n public server<TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}>(\n ...config: ServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>\n ): PluvServer<TPlatform, TAuthorize, TContext, TCrdt, TEvents> {\n const serverConfig = (config[0] ?? {}) as PluvServerConfig<\n TPlatform,\n TAuthorize,\n TContext,\n TCrdt,\n TEvents\n >;\n\n return new PluvServer<TPlatform, TAuthorize, TContext, TCrdt, TEvents>({\n ...serverConfig,\n authorize: this._authorize ?? undefined,\n context: this._context,\n crdt: this._crdt,\n debug: this._debug,\n io: this as PluvIO<TPlatform, TAuthorize, TContext, TCrdt>,\n limits: this._limits,\n platform: this._platform,\n });\n }\n\n private _getIOAuthorize(\n options: WebSocketRegisterConfig<TPlatform>,\n ): ResolvedPluvIOAuthorize<any, any> | null {\n if (typeof this._authorize === \"function\") {\n return this._authorize(options);\n }\n\n return this._authorize as ResolvedPluvIOAuthorize<any, any> | null;\n }\n}\n","import type { CrdtLibraryType, NoopCrdtDocFactory } from \"@pluv/crdt\";\nimport type { BaseUser } from \"@pluv/types\";\nimport type { AbstractPlatform, InferInitContextType } from \"./AbstractPlatform\";\nimport type { PluvIOConfig } from \"./PluvIO\";\nimport { PluvIO } from \"./PluvIO\";\nimport type { PluvContext, PluvIOAuthorize, PluvIOLimits } from \"./types\";\n\nexport type CreateIOParams<\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n TContext extends Record<string, any> = {},\n TUser extends BaseUser | null = null,\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,\n> = {\n authorize?: PluvIOAuthorize<TPlatform, TUser, InferInitContextType<TPlatform>>;\n context?: PluvContext<TPlatform, TContext>;\n crdt?: TCrdt;\n debug?: boolean;\n limits?: PluvIOLimits;\n platform: () => TPlatform;\n};\n\nexport const createIO = <\n TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,\n TContext extends Record<string, any> = {},\n TUser extends BaseUser | null = null,\n TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,\n>(\n params: CreateIOParams<TPlatform, TContext, TUser, TCrdt>,\n): PluvIO<\n TPlatform,\n PluvIOAuthorize<TPlatform, TUser, InferInitContextType<TPlatform>>,\n TContext,\n TCrdt\n> => {\n const { authorize, context, crdt, debug, limits, platform } = params;\n\n return new PluvIO<\n TPlatform,\n PluvIOAuthorize<TPlatform, TUser, InferInitContextType<TPlatform>>,\n TContext,\n TCrdt\n >({\n authorize,\n context,\n crdt,\n debug,\n limits,\n platform,\n } as PluvIOConfig<\n TPlatform,\n PluvIOAuthorize<TPlatform, TUser, InferInitContextType<TPlatform>>,\n TContext,\n TCrdt\n >);\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/io",
3
- "version": "3.2.2",
3
+ "version": "4.0.1",
4
4
  "description": "@pluv/io server-side websocket manager",
5
5
  "author": {
6
6
  "email": "david@pluv.io",
@@ -14,32 +14,36 @@
14
14
  "url": "git+https://github.com/pluv-io/pluv.git",
15
15
  "directory": "packages/server"
16
16
  },
17
- "main": "./dist/index.js",
18
- "module": "./dist/index.mjs",
19
- "types": "./dist/index.d.ts",
17
+ "type": "module",
18
+ "types": "./dist/index.d.mts",
20
19
  "publishConfig": {
21
20
  "access": "public"
22
21
  },
23
22
  "dependencies": {
24
23
  "@panva/hkdf": "^1.2.1",
25
- "jose": "^5.10.0",
24
+ "jose": "^6.1.3",
26
25
  "kleur": "^4.1.5",
27
26
  "wonka": "^6.3.5",
28
- "@pluv/crdt": "^3.2.2",
29
- "@pluv/types": "^3.2.2"
27
+ "@pluv/crdt": "^4.0.1",
28
+ "@pluv/types": "^4.0.1"
30
29
  },
31
30
  "devDependencies": {
32
- "eslint": "^9.31.0",
31
+ "eslint": "^9.39.2",
33
32
  "genversion": "^3.2.0",
34
- "tsup": "^8.5.0",
35
- "typescript": "^5.8.3",
36
- "@pluv/tsconfig": "^3.2.2",
37
- "eslint-config-pluv": "^3.2.2"
33
+ "tsdown": "0.20.0-beta.4",
34
+ "typescript": "^5.9.3",
35
+ "@pluv/tsconfig": "^4.0.1",
36
+ "eslint-config-pluv": "^4.0.1"
37
+ },
38
+ "exports": {
39
+ ".": "./dist/index.mjs",
40
+ "./*": "./*"
38
41
  },
39
42
  "scripts": {
40
- "build": "pnpm run version && tsup src/index.ts --format esm,cjs --dts",
41
- "dev": "tsup src/index.ts --format esm,cjs --watch --dts",
43
+ "build": "pnpm run version && tsdown",
44
+ "dev": "tsdown --watch",
42
45
  "lint": "eslint \"src/**/*.ts*\" --fix --max-warnings 0",
46
+ "typecheck": "tsc --noEmit",
43
47
  "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
44
48
  "version": "genversion ./src/version.generated.ts --esm"
45
49
  }
package/src/IORoom.ts CHANGED
@@ -37,6 +37,7 @@ import type {
37
37
  EventResolverContext,
38
38
  EventResolverKind,
39
39
  GetInitialStorageFn,
40
+ IORoomDestroyedEvent,
40
41
  IORoomListenerEvent,
41
42
  IORoomMessageEvent,
42
43
  IOUserConnectedEvent,
@@ -64,7 +65,8 @@ export interface IORoomListeners<
64
65
  TContext extends Record<string, any>,
65
66
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>,
66
67
  > {
67
- onDestroy: (event: IORoomListenerEvent<TPlatform, TContext>) => void;
68
+ onRoomDestroyed: (event: IORoomDestroyedEvent<TPlatform, TContext>) => void;
69
+ onStorageDestroyed: (event: IORoomListenerEvent<TPlatform, TContext>) => void;
68
70
  onMessage: (event: IORoomMessageEvent<TPlatform, TAuthorize, TContext, TEvents>) => void;
69
71
  onUserConnected: (event: IOUserConnectedEvent<TPlatform, TAuthorize, TContext>) => void;
70
72
  onUserDisconnected: (event: IOUserDisconnectedEvent<TPlatform, TAuthorize, TContext>) => void;
@@ -81,11 +83,8 @@ export type BroadcastProxy<TIO extends IORoom<any, any, any, any, any>> = (<
81
83
 
82
84
  export type IORoomConfig<
83
85
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
84
- TAuthorize extends PluvIOAuthorize<
85
- TPlatform,
86
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
86
87
  any,
87
- InferInitContextType<TPlatform>
88
- > | null = any,
89
88
  TContext extends Record<string, any> = {},
90
89
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
91
90
  > = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
@@ -118,16 +117,12 @@ export type WebSocketRegisterConfig<
118
117
 
119
118
  export class IORoom<
120
119
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
121
- TAuthorize extends PluvIOAuthorize<
122
- TPlatform,
123
- any,
124
- InferInitContextType<TPlatform>
125
- > | null = null,
120
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
121
+ null,
126
122
  TContext extends Record<string, any> = {},
127
123
  TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<any>,
128
124
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
129
- > implements IOLike<TAuthorize, TCrdt, TEvents>
130
- {
125
+ > implements IOLike<TAuthorize, TCrdt, TEvents> {
131
126
  public readonly id: string;
132
127
 
133
128
  private _doc: Promise<CrdtDocLike<any, any>>;
@@ -150,6 +145,9 @@ export class IORoom<
150
145
  >();
151
146
  private readonly _userSessionss = new Map<[userId: string][0], Set<[sessionId: string][0]>>();
152
147
 
148
+ private _wasDocEmptyOnInit: boolean = true;
149
+ private _storageInitializedViaSession: boolean = false;
150
+
153
151
  /**
154
152
  * @ignore
155
153
  * @readonly
@@ -200,7 +198,8 @@ export class IORoom<
200
198
  crdt = noop,
201
199
  debug,
202
200
  getInitialStorage,
203
- onDestroy,
201
+ onRoomDestroyed,
202
+ onStorageDestroyed,
204
203
  onMessage,
205
204
  onUserConnected,
206
205
  onUserDisconnected,
@@ -220,8 +219,10 @@ export class IORoom<
220
219
  this._router = router;
221
220
  this._platform = platform.initialize({ ...(!!_meta ? { _meta } : {}), roomContext });
222
221
 
222
+ // Listeners are provided from server-level configuration via createRoom
223
223
  this._listeners = {
224
- onDestroy: (event) => onDestroy?.(event),
224
+ onRoomDestroyed: (event) => onRoomDestroyed?.(event),
225
+ onStorageDestroyed: (event) => onStorageDestroyed?.(event),
225
226
  onMessage: (event) => onMessage?.(event),
226
227
  onUserConnected: (event) => onUserConnected?.(event),
227
228
  onUserDisconnected: (event) => onUserDisconnected?.(event),
@@ -674,7 +675,16 @@ export class IORoom<
674
675
  }
675
676
  }
676
677
 
677
- if (typeof encodedState === "string") doc.applyEncodedState({ update: encodedState });
678
+ if (typeof encodedState === "string") {
679
+ doc.applyEncodedState({ update: encodedState });
680
+
681
+ // If we loaded storage from persistence and it's non-empty, storage was previously initialized.
682
+ // This handles hibernation wake-up: restore the initialization state so that onStorageDestroyed
683
+ // will fire correctly when the room is destroyed.
684
+ if (!doc.isEmpty()) {
685
+ this._storageInitializedViaSession = true;
686
+ }
687
+ }
678
688
 
679
689
  return doc;
680
690
  }
@@ -797,6 +807,10 @@ export class IORoom<
797
807
  );
798
808
 
799
809
  const doc = await this._getInitialDoc();
810
+
811
+ // Track if doc was empty when room was first initialized
812
+ this._wasDocEmptyOnInit = doc.isEmpty();
813
+
800
814
  const uninitialize = async () => {
801
815
  this._platform.pubSub.unsubscribe(pubSubId);
802
816
 
@@ -806,18 +820,35 @@ export class IORoom<
806
820
  doc.destroy();
807
821
  this._doc = Promise.resolve(this._docFactory.getEmpty());
808
822
 
823
+ // Always emit onRoomDestroyed
809
824
  await Promise.resolve(
810
- this._listeners.onDestroy({
825
+ this._listeners.onRoomDestroyed({
811
826
  ...("_meta" in this._platform && !!this._platform._meta
812
827
  ? { _meta: this._platform._meta }
813
828
  : {}),
814
829
  context,
815
- encodedState,
816
830
  platform: this._platform,
817
831
  room: this.id,
818
832
  }),
819
833
  );
820
834
 
835
+ // Only emit onStorageDestroyed if storage was initialized via initializeSession
836
+ if (this._storageInitializedViaSession) {
837
+ await Promise.resolve(
838
+ this._listeners.onStorageDestroyed({
839
+ ...("_meta" in this._platform && !!this._platform._meta
840
+ ? { _meta: this._platform._meta }
841
+ : {}),
842
+ context,
843
+ encodedState,
844
+ platform: this._platform,
845
+ room: this.id,
846
+ }),
847
+ );
848
+
849
+ this._storageInitializedViaSession = false;
850
+ }
851
+
821
852
  this._uninitialize = null;
822
853
  };
823
854
 
@@ -991,6 +1022,10 @@ export class IORoom<
991
1022
 
992
1023
  await Promise.all([handleBroadcast(), handleSelf(), handleSync()]);
993
1024
  });
1025
+
1026
+ // After processing messages that might update storage (like $initializeSession),
1027
+ // check if storage was initialized via initializeSession
1028
+ await this._checkStorageInitializedViaSession();
994
1029
  };
995
1030
  }
996
1031
 
@@ -1061,6 +1096,19 @@ export class IORoom<
1061
1096
  return set;
1062
1097
  }
1063
1098
 
1099
+ private async _checkStorageInitializedViaSession(): Promise<void> {
1100
+ if (this._storageInitializedViaSession) return;
1101
+
1102
+ // Only mark as initialized if:
1103
+ // 1. Doc was empty when room was first initialized
1104
+ // 2. Doc is now non-empty
1105
+ // 3. At least one session has been registered
1106
+ const doc = await this._doc;
1107
+ if (this._wasDocEmptyOnInit && !doc.isEmpty() && this._sessions.size > 0) {
1108
+ this._storageInitializedViaSession = true;
1109
+ }
1110
+ }
1111
+
1064
1112
  private async _sendMessage(
1065
1113
  pluvWs: AbstractWebSocket<any, TAuthorize>,
1066
1114
  message: IOEventMessage<any>,
package/src/PluvIO.ts CHANGED
@@ -43,11 +43,8 @@ export type PluvIOConfig<
43
43
 
44
44
  type ResolvedServerConfig<
45
45
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
46
- TAuthorize extends PluvIOAuthorize<
47
- TPlatform,
46
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
48
47
  any,
49
- InferInitContextType<TPlatform>
50
- > | null = any,
51
48
  TContext extends Record<string, any> = {},
52
49
  TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,
53
50
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
@@ -59,11 +56,8 @@ type ResolvedServerConfig<
59
56
 
60
57
  export type BaseServerConfig<
61
58
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
62
- TAuthorize extends PluvIOAuthorize<
63
- TPlatform,
59
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
64
60
  any,
65
- InferInitContextType<TPlatform>
66
- > | null = any,
67
61
  TContext extends Record<string, any> = {},
68
62
  TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,
69
63
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
@@ -81,11 +75,8 @@ export type BaseServerConfig<
81
75
 
82
76
  export type ServerConfig<
83
77
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
84
- TAuthorize extends PluvIOAuthorize<
85
- TPlatform,
78
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
86
79
  any,
87
- InferInitContextType<TPlatform>
88
- > | null = any,
89
80
  TContext extends Record<string, any> = {},
90
81
  TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,
91
82
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
@@ -98,11 +89,8 @@ export type ServerConfig<
98
89
 
99
90
  export class PluvIO<
100
91
  TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
101
- TAuthorize extends PluvIOAuthorize<
102
- TPlatform,
92
+ TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
103
93
  any,
104
- InferInitContextType<TPlatform>
105
- > | null = any,
106
94
  TContext extends Record<string, any> = {},
107
95
  TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<any>,
108
96
  > {